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