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