]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/edit.c
480253920fb337ec8c61445f8b59ec36abff8ecb
[bacula/bacula] / bacula / src / lib / edit.c
1 /*
2  *   edit.c  edit string to ascii, and ascii to internal
3  *
4  *    Kern Sibbald, December MMII
5  *
6  *   Version $Id$
7  */
8 /*
9    Bacula® - The Network Backup Solution
10
11    Copyright (C) 2002-2006 Free Software Foundation Europe e.V.
12
13    The main author of Bacula is Kern Sibbald, with contributions from
14    many others, a complete list can be found in the file AUTHORS.
15    This program is Free Software; you can redistribute it and/or
16    modify it under the terms of version two of the GNU General Public
17    License as published by the Free Software Foundation and included
18    in the file LICENSE.
19
20    This program is distributed in the hope that it will be useful, but
21    WITHOUT ANY WARRANTY; without even the implied warranty of
22    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23    General Public License for more details.
24
25    You should have received a copy of the GNU General Public License
26    along with this program; if not, write to the Free Software
27    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
28    02110-1301, USA.
29
30    Bacula® is a registered trademark of Kern Sibbald.
31    The licensor of Bacula is the Free Software Foundation Europe
32    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
33    Switzerland, email:ftf@fsfeurope.org.
34 */
35
36 #include "bacula.h"
37 #include <math.h>
38
39 /* We assume ASCII input and don't worry about overflow */
40 uint64_t str_to_uint64(char *str)
41 {
42    register char *p = str;
43    register uint64_t value = 0;
44
45    if (!p) {
46       return 0;
47    }
48    while (B_ISSPACE(*p)) {
49       p++;
50    }
51    if (*p == '+') {
52       p++;
53    }
54    while (B_ISDIGIT(*p)) {
55       value = B_TIMES10(value) + *p - '0';
56       p++;
57    }
58    return value;
59 }
60
61 int64_t str_to_int64(char *str)
62 {
63    register char *p = str;
64    register int64_t value;
65    bool negative = false;
66
67    if (!p) {
68       return 0;
69    }
70    while (B_ISSPACE(*p)) {
71       p++;
72    }
73    if (*p == '+') {
74       p++;
75    } else if (*p == '-') {
76       negative = true;
77       p++;
78    }
79    value = str_to_uint64(p);
80    if (negative) {
81       value = -value;
82    }
83    return value;
84 }
85
86
87 /*
88  * Edit an integer number with commas, the supplied buffer
89  * must be at least 27 bytes long.  The incoming number
90  * is always widened to 64 bits.
91  */
92 char *edit_uint64_with_commas(uint64_t val, char *buf)
93 {
94    edit_uint64(val, buf);
95    return add_commas(buf, buf);
96 }
97
98 /*
99  * Edit an integer into "human-readable" format with four or fewer
100  * significant digits followed by a suffix that indicates the scale
101  * factor.  The buf array inherits a 27 byte minimim length
102  * requirement from edit_unit64_with_commas(), although the output
103  * string is limited to eight characters.
104  */
105 char *edit_uint64_with_suffix(uint64_t val, char *buf)
106 {
107   int commas = 0;
108   char *c, mbuf[50];
109   const char *suffix[] =
110     { "", "K", "M", "G", "T", "P", "E", "Z", "Y", "FIX ME" };
111   int suffixes = sizeof(suffix) / sizeof(*suffix);
112
113   edit_uint64_with_commas(val, mbuf);
114
115   if ((c = strchr(mbuf, ',')) != NULL) {
116     commas++;
117     *c++ = '.';
118     while  ((c = strchr(c, ',')) != NULL) {
119       commas++;
120       *c++ = '\0';
121     }
122     mbuf[5] = '\0'; // drop this to get '123.456 TB' rather than '123.4 TB'
123   }
124
125   if (commas >= suffixes)
126     commas = suffixes - 1;
127   bsnprintf(buf, 27, "%s %s", mbuf, suffix[commas]);
128   return buf;
129 }
130
131 /*
132  * Edit an integer number, the supplied buffer
133  * must be at least 27 bytes long.  The incoming number
134  * is always widened to 64 bits.
135  */
136 char *edit_uint64(uint64_t val, char *buf)
137 {
138    /*
139     * Replacement for sprintf(buf, "%" llu, val)
140     */
141    char mbuf[50];
142    mbuf[sizeof(mbuf)-1] = 0;
143    int i = sizeof(mbuf)-2;                 /* edit backward */
144    if (val == 0) {
145       mbuf[i--] = '0';
146    } else {
147       while (val != 0) {
148          mbuf[i--] = "0123456789"[val%10];
149          val /= 10;
150       }
151    }
152    bstrncpy(buf, &mbuf[i+1], 27);
153    return buf;
154 }
155
156 char *edit_int64(int64_t val, char *buf)
157 {
158    /*
159     * Replacement for sprintf(buf, "%" llu, val)
160     */
161    char mbuf[50];
162    bool negative = false;
163    mbuf[sizeof(mbuf)-1] = 0;
164    int i = sizeof(mbuf)-2;                 /* edit backward */
165    if (val == 0) {
166       mbuf[i--] = '0';
167    } else {
168       if (val < 0) {
169          negative = true;
170          val = -val;
171       }
172       while (val != 0) {
173          mbuf[i--] = "0123456789"[val%10];
174          val /= 10;
175       }
176    }
177    if (negative) {
178       mbuf[i--] = '-';
179    }
180    bstrncpy(buf, &mbuf[i+1], 27);
181    return buf;
182 }
183
184 /*
185  * Edit an integer number with commas, the supplied buffer
186  * must be at least 27 bytes long.  The incoming number
187  * is always widened to 64 bits.
188  */
189 char *edit_int64_with_commas(int64_t val, char *buf)
190 {
191    edit_int64(val, buf);
192    return add_commas(buf, buf);
193 }
194
195 /*
196  * Given a string "str", separate the numeric part into
197  *   str, and the modifier into mod.
198  */
199 static bool get_modifier(char *str, char *num, int num_len, char *mod, int mod_len)
200 {
201    int i, len, num_begin, num_end, mod_begin, mod_end;
202
203    strip_trailing_junk(str);
204    len = strlen(str);
205
206    for (i=0; i<len; i++) {
207       if (!B_ISSPACE(str[i])) {
208          break;
209       }
210    }
211    num_begin = i;
212
213    /* Walk through integer part */
214    for ( ; i<len; i++) {
215       if (!B_ISDIGIT(str[i]) && str[i] != '.') {
216          break;
217       }
218    }
219    num_end = i;
220    if (num_len > (num_end - num_begin + 1)) {
221       num_len = num_end - num_begin + 1;
222    }
223    if (num_len == 0) {
224       return false;
225    }
226    /* Eat any spaces in front of modifier */
227    for ( ; i<len; i++) {
228       if (!B_ISSPACE(str[i])) {
229          break;
230       }
231    }
232    mod_begin = i;
233    for ( ; i<len; i++) {
234       if (!B_ISALPHA(str[i])) {
235          break;
236       }
237    }
238    mod_end = i;
239    if (mod_len > (mod_end - mod_begin + 1)) {
240       mod_len = mod_end - mod_begin + 1;
241    }
242    Dmsg5(900, "str=%s: num_beg=%d num_end=%d mod_beg=%d mod_end=%d\n",
243       str, num_begin, num_end, mod_begin, mod_end);
244    bstrncpy(num, &str[num_begin], num_len);
245    bstrncpy(mod, &str[mod_begin], mod_len);
246    if (!is_a_number(num)) {
247       return false;
248    }
249    bstrncpy(str, &str[mod_end], len);
250    Dmsg2(900, "num=%s mod=%s\n", num, mod);
251
252    return true;
253 }
254
255 /*
256  * Convert a string duration to utime_t (64 bit seconds)
257  * Returns false: if error
258            true:  if OK, and value stored in value
259  */
260 bool duration_to_utime(char *str, utime_t *value)
261 {
262    int i, mod_len;
263    double val, total = 0.0;
264    char mod_str[20];
265    char num_str[50];
266    /*
267     * The "n" = mins and months appears before minutes so that m maps
268     *   to months. These "kludges" make it compatible with pre 1.31
269     *   Baculas.
270     */
271    static const char *mod[] = {"n", "seconds", "months", "minutes", "mins",
272                   "hours", "days", "weeks",   "quarters",   "years", NULL};
273    static const int32_t mult[] = {60,   1, 60*60*24*30, 60, 60,
274                   3600, 3600*24, 3600*24*7, 3600*24*91, 3600*24*365};
275
276    while (*str) {
277       if (!get_modifier(str, num_str, sizeof(num_str), mod_str, sizeof(mod_str))) {
278          return false;
279       }
280       /* Now find the multiplier corresponding to the modifier */
281       mod_len = strlen(mod_str);
282       if (mod_len == 0) {
283          i = 1;                          /* default to seconds */
284       } else {
285          for (i=0; mod[i]; i++) {
286             if (strncasecmp(mod_str, mod[i], mod_len) == 0) {
287                break;
288             }
289          }
290          if (mod[i] == NULL) {
291             return false;
292          }
293       }
294       Dmsg2(900, "str=%s: mult=%d\n", num_str, mult[i]);
295       errno = 0;
296       val = strtod(num_str, NULL);
297       if (errno != 0 || val < 0) {
298          return false;
299       }
300       total += val * mult[i];
301    }
302    *value = (utime_t)total;
303    return true;
304 }
305
306 /*
307  * Edit a utime "duration" into ASCII
308  */
309 char *edit_utime(utime_t val, char *buf, int buf_len)
310 {
311    char mybuf[200];
312    static const int32_t mult[] = {60*60*24*365, 60*60*24*30, 60*60*24, 60*60, 60};
313    static const char *mod[]  = {"year",  "month",  "day", "hour", "min"};
314    int i;
315    uint32_t times;
316
317    *buf = 0;
318    for (i=0; i<5; i++) {
319       times = (uint32_t)(val / mult[i]);
320       if (times > 0) {
321          val = val - (utime_t)times * mult[i];
322          bsnprintf(mybuf, sizeof(mybuf), "%d %s%s ", times, mod[i], times>1?"s":"");
323          bstrncat(buf, mybuf, buf_len);
324       }
325    }
326    if (val == 0 && strlen(buf) == 0) {
327       bstrncat(buf, "0 secs", buf_len);
328    } else if (val != 0) {
329       bsnprintf(mybuf, sizeof(mybuf), "%d sec%s", (uint32_t)val, val>1?"s":"");
330       bstrncat(buf, mybuf, buf_len);
331    }
332    return buf;
333 }
334
335 /*
336  * Convert a size in bytes to uint64_t
337  * Returns false: if error
338            true:  if OK, and value stored in value
339  */
340 bool size_to_uint64(char *str, int str_len, uint64_t *value)
341 {
342    int i, mod_len;
343    double val;
344    char mod_str[20];
345    char num_str[50];
346    static const char *mod[]  = {"*", "k", "kb", "m", "mb",  "g", "gb",  NULL}; /* first item * not used */
347    const int64_t mult[] = {1,             /* byte */
348                            1024,          /* kilobyte */
349                            1000,          /* kb kilobyte */
350                            1048576,       /* megabyte */
351                            1000000,       /* mb megabyte */
352                            1073741824,    /* gigabyte */
353                            1000000000};   /* gb gigabyte */
354
355    if (!get_modifier(str, num_str, sizeof(num_str), mod_str, sizeof(mod_str))) {
356       return 0;
357    }
358    /* Now find the multiplier corresponding to the modifier */
359    mod_len = strlen(mod_str);
360    if (mod_len == 0) {
361       i = 0;                          /* default with no modifier = 1 */
362    } else {
363       for (i=0; mod[i]; i++) {
364          if (strncasecmp(mod_str, mod[i], mod_len) == 0) {
365             break;
366          }
367       }
368       if (mod[i] == NULL) {
369          return false;
370       }
371    }
372    Dmsg2(900, "str=%s: mult=%d\n", str, mult[i]);
373    errno = 0;
374    val = strtod(num_str, NULL);
375    if (errno != 0 || val < 0) {
376       return false;
377    }
378    *value = (utime_t)(val * mult[i]);
379    return true;
380 }
381
382 /*
383  * Check if specified string is a number or not.
384  *  Taken from SQLite, cool, thanks.
385  */
386 bool is_a_number(const char *n)
387 {
388    bool digit_seen = false;
389
390    if( *n == '-' || *n == '+' ) {
391       n++;
392    }
393    while (B_ISDIGIT(*n)) {
394       digit_seen = true;
395       n++;
396    }
397    if (digit_seen && *n == '.') {
398       n++;
399       while (B_ISDIGIT(*n)) { n++; }
400    }
401    if (digit_seen && (*n == 'e' || *n == 'E')
402        && (B_ISDIGIT(n[1]) || ((n[1]=='-' || n[1] == '+') && B_ISDIGIT(n[2])))) {
403       n += 2;                         /* skip e- or e+ or e digit */
404       while (B_ISDIGIT(*n)) { n++; }
405    }
406    return digit_seen && *n==0;
407 }
408
409 /*
410  * Check if the specified string is an integer
411  */
412 bool is_an_integer(const char *n)
413 {
414    bool digit_seen = false;
415    while (B_ISDIGIT(*n)) {
416       digit_seen = true;
417       n++;
418    }
419    return digit_seen && *n==0;
420 }
421
422 /*
423  * Check if Bacula Resoure Name is valid
424  */
425 /*
426  * Check if the Volume name has legal characters
427  * If ua is non-NULL send the message
428  */
429 bool is_name_valid(char *name, POOLMEM **msg)
430 {
431    int len;
432    char *p;
433    /* Special characters to accept */
434    const char *accept = ":.-_ ";
435
436    /* Restrict the characters permitted in the Volume name */
437    for (p=name; *p; p++) {
438       if (B_ISALPHA(*p) || B_ISDIGIT(*p) || strchr(accept, (int)(*p))) {
439          continue;
440       }
441       if (msg) {
442          Mmsg(msg, _("Illegal character \"%c\" in name.\n"), *p);
443       }
444       return false;
445    }
446    len = strlen(name);
447    if (len >= MAX_NAME_LENGTH) {
448       if (msg) {
449          Mmsg(msg, _("Name too long.\n"));
450       }
451       return false;
452    }
453    if (len == 0) {
454       if (msg) {
455          Mmsg(msg,  _("Volume name must be at least one character long.\n"));
456       }
457       return false;
458    }
459    return true;
460 }
461
462
463
464 /*
465  * Add commas to a string, which is presumably
466  * a number.
467  */
468 char *add_commas(char *val, char *buf)
469 {
470    int len, nc;
471    char *p, *q;
472    int i;
473
474    if (val != buf) {
475       strcpy(buf, val);
476    }
477    len = strlen(buf);
478    if (len < 1) {
479       len = 1;
480    }
481    nc = (len - 1) / 3;
482    p = buf+len;
483    q = p + nc;
484    *q-- = *p--;
485    for ( ; nc; nc--) {
486       for (i=0; i < 3; i++) {
487           *q-- = *p--;
488       }
489       *q-- = ',';
490    }
491    return buf;
492 }
493
494 #ifdef TEST_PROGRAM
495 void d_msg(const char*, int, int, const char*, ...)
496 {}
497 int main(int argc, char *argv[])
498 {
499    char *str[] = {"3", "3n", "3 hours", "3.5 day", "3 week", "3 m", "3 q", "3 years"};
500    utime_t val;
501    char buf[100];
502    char outval[100];
503
504    for (int i=0; i<8; i++) {
505       strcpy(buf, str[i]);
506       if (!duration_to_utime(buf, &val)) {
507          printf("Error return from duration_to_utime for in=%s\n", str[i]);
508          continue;
509       }
510       edit_utime(val, outval);
511       printf("in=%s val=%" lld " outval=%s\n", str[i], val, outval);
512    }
513 }
514 #endif