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