]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/edit.c
Make out of freespace non-fatal for removable devices -- i.e. behaves like tape
[bacula/bacula] / bacula / src / lib / edit.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2017 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                            1099511627776LL,  /* terabyte */
354                            1000000000000LL}; /* tb terabyte */
355
356    if (!get_modifier(str, num_str, sizeof(num_str), mod_str, sizeof(mod_str))) {
357       return 0;
358    }
359    /* Now find the multiplier corresponding to the modifier */
360    mod_len = strlen(mod_str);
361    if (mod_len == 0) {
362       i = 0;                          /* default with no modifier = 1 */
363    } else {
364       for (i=0; mod[i]; i++) {
365          if (strncasecmp(mod_str, mod[i], mod_len) == 0) {
366             break;
367          }
368       }
369       if (mod[i] == NULL) {
370          return false;
371       }
372    }
373    Dmsg2(900, "str=%s: mult=%d\n", str, mult[i]);
374    errno = 0;
375    val = strtod(num_str, NULL);
376    if (errno != 0 || val < 0) {
377       return false;
378    }
379    *value = (utime_t)(val * mult[i]);
380    return true;
381 }
382
383 /*
384  * Convert a size in bytes to uint64_t
385  * Returns false: if error
386            true:  if OK, and value stored in value
387  */
388 bool size_to_uint64(char *str, int str_len, uint64_t *value)
389 {
390    /* first item * not used */
391    static const char *mod[]  = {"*", "k", "kb", "m", "mb",
392                                      "g", "gb", "t", "tb", NULL};
393    return strunit_to_uint64(str, str_len, value, mod);
394 }
395
396 /*
397  * Convert a speed in bytes/s to uint64_t
398  * Returns false: if error
399            true:  if OK, and value stored in value
400  */
401 bool speed_to_uint64(char *str, int str_len, uint64_t *value)
402 {
403    /* first item * not used */
404    static const char *mod[]  = {"*", "k/s", "kb/s", "m/s", "mb/s",  NULL};
405    return strunit_to_uint64(str, str_len, value, mod);
406 }
407
408 /*
409  * Check if specified string is a number or not.
410  *  Taken from SQLite, cool, thanks.
411  */
412 bool is_a_number(const char *n)
413 {
414    bool digit_seen = false;
415
416    if (n == NULL) {
417       return false;
418    }
419
420    if( *n == '-' || *n == '+' ) {
421       n++;
422    }
423    while (B_ISDIGIT(*n)) {
424       digit_seen = true;
425       n++;
426    }
427    if (digit_seen && *n == '.') {
428       n++;
429       while (B_ISDIGIT(*n)) { n++; }
430    }
431    if (digit_seen && (*n == 'e' || *n == 'E')
432        && (B_ISDIGIT(n[1]) || ((n[1]=='-' || n[1] == '+') && B_ISDIGIT(n[2])))) {
433       n += 2;                         /* skip e- or e+ or e digit */
434       while (B_ISDIGIT(*n)) { n++; }
435    }
436    return digit_seen && *n==0;
437 }
438
439 /*
440  * Check if specified string is a list of numbers or not
441  */
442 bool is_a_number_list(const char *n)
443 {
444    bool previous_digit = false;
445    bool digit_seen = false;
446    if (n == NULL) {
447       return false;
448    }
449    while (*n) {
450       if (B_ISDIGIT(*n)) {
451          previous_digit=true;
452          digit_seen = true;
453       } else if (*n == ',' && previous_digit) {
454          previous_digit = false;
455       } else {
456          return false;
457       }
458       n++;
459    }
460    return digit_seen && *n==0;
461 }
462
463 /*
464  * Check if the specified string is an integer
465  */
466 bool is_an_integer(const char *n)
467 {
468    bool digit_seen = false;
469    if (n == NULL) {
470       return false;
471    }
472    while (B_ISDIGIT(*n)) {
473       digit_seen = true;
474       n++;
475    }
476    return digit_seen && *n==0;
477 }
478
479 /*
480  * Check if Bacula Resoure Name is valid
481  */
482 /*
483  * Check if the Volume name has legal characters
484  * If ua is non-NULL send the message
485  */
486 bool is_name_valid(const char *name, POOLMEM **msg)
487 {
488    int len;
489    const char *p;
490    /* Special characters to accept */
491    const char *accept = ":.-_ ";
492
493    /* No name is invalid */
494    if (!name) {
495       if (msg) {
496          Mmsg(msg, _("Empty name not allowed.\n"));
497       }
498       return false;
499    }
500    /* Restrict the characters permitted in the Volume name */
501    for (p=name; *p; p++) {
502       if (B_ISALPHA(*p) || B_ISDIGIT(*p) || strchr(accept, (int)(*p))) {
503          continue;
504       }
505       if (msg) {
506          Mmsg(msg, _("Illegal character \"%c\" in name.\n"), *p);
507       }
508       return false;
509    }
510    len = p - name;
511    if (len >= MAX_NAME_LENGTH) {
512       if (msg) {
513          Mmsg(msg, _("Name too long.\n"));
514       }
515       return false;
516    }
517    if (len == 0) {
518       if (msg) {
519          Mmsg(msg,  _("Volume name must be at least one character long.\n"));
520       }
521       return false;
522    }
523    return true;
524 }
525
526
527
528 /*
529  * Add commas to a string, which is presumably
530  * a number.
531  */
532 char *add_commas(char *val, char *buf)
533 {
534    int len, nc;
535    char *p, *q;
536    int i;
537
538    if (val != buf) {
539       strcpy(buf, val);
540    }
541    len = strlen(buf);
542    if (len < 1) {
543       len = 1;
544    }
545    nc = (len - 1) / 3;
546    p = buf+len;
547    q = p + nc;
548    *q-- = *p--;
549    for ( ; nc; nc--) {
550       for (i=0; i < 3; i++) {
551           *q-- = *p--;
552       }
553       *q-- = ',';
554    }
555    return buf;
556 }
557
558 #ifdef TEST_PROGRAM
559 void d_msg(const char*, int, int, const char*, ...)
560 {}
561 int main(int argc, char *argv[])
562 {
563    char *str[] = {"3", "3n", "3 hours", "3.5 day", "3 week", "3 m", "3 q", "3 years"};
564    utime_t val;
565    char buf[100];
566    char outval[100];
567
568    for (int i=0; i<8; i++) {
569       strcpy(buf, str[i]);
570       if (!duration_to_utime(buf, &val)) {
571          printf("Error return from duration_to_utime for in=%s\n", str[i]);
572          continue;
573       }
574       edit_utime(val, outval);
575       printf("in=%s val=%" lld " outval=%s\n", str[i], val, outval);
576    }
577 }
578 #endif