]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/edit.c
Merge branch 'master' into basejobv3
[bacula/bacula] / bacula / src / lib / edit.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2002-2009 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 two of the GNU 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 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  *   Version $Id$
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 specified string is a list of numbers or not
411  */
412 bool is_a_number_list(const char *n)
413 {
414    bool previous_digit = false; 
415    bool digit_seen = false;
416    while (*n) {
417       if (B_ISDIGIT(*n)) {
418          previous_digit=true;
419          digit_seen = true;
420       } else if (*n == ',' && previous_digit) {
421          previous_digit = false;
422       } else {
423          return false;
424       }
425       n++;
426    }
427    return digit_seen && *n==0; 
428 }
429
430 /*
431  * Check if the specified string is an integer
432  */
433 bool is_an_integer(const char *n)
434 {
435    bool digit_seen = false;
436    while (B_ISDIGIT(*n)) {
437       digit_seen = true;
438       n++;
439    }
440    return digit_seen && *n==0;
441 }
442
443 /*
444  * Check if Bacula Resoure Name is valid
445  */
446 /*
447  * Check if the Volume name has legal characters
448  * If ua is non-NULL send the message
449  */
450 bool is_name_valid(char *name, POOLMEM **msg)
451 {
452    int len;
453    char *p;
454    /* Special characters to accept */
455    const char *accept = ":.-_ ";
456
457    /* Restrict the characters permitted in the Volume name */
458    for (p=name; *p; p++) {
459       if (B_ISALPHA(*p) || B_ISDIGIT(*p) || strchr(accept, (int)(*p))) {
460          continue;
461       }
462       if (msg) {
463          Mmsg(msg, _("Illegal character \"%c\" in name.\n"), *p);
464       }
465       return false;
466    }
467    len = strlen(name);
468    if (len >= MAX_NAME_LENGTH) {
469       if (msg) {
470          Mmsg(msg, _("Name too long.\n"));
471       }
472       return false;
473    }
474    if (len == 0) {
475       if (msg) {
476          Mmsg(msg,  _("Volume name must be at least one character long.\n"));
477       }
478       return false;
479    }
480    return true;
481 }
482
483
484
485 /*
486  * Add commas to a string, which is presumably
487  * a number.
488  */
489 char *add_commas(char *val, char *buf)
490 {
491    int len, nc;
492    char *p, *q;
493    int i;
494
495    if (val != buf) {
496       strcpy(buf, val);
497    }
498    len = strlen(buf);
499    if (len < 1) {
500       len = 1;
501    }
502    nc = (len - 1) / 3;
503    p = buf+len;
504    q = p + nc;
505    *q-- = *p--;
506    for ( ; nc; nc--) {
507       for (i=0; i < 3; i++) {
508           *q-- = *p--;
509       }
510       *q-- = ',';
511    }
512    return buf;
513 }
514
515 #ifdef TEST_PROGRAM
516 void d_msg(const char*, int, int, const char*, ...)
517 {}
518 int main(int argc, char *argv[])
519 {
520    char *str[] = {"3", "3n", "3 hours", "3.5 day", "3 week", "3 m", "3 q", "3 years"};
521    utime_t val;
522    char buf[100];
523    char outval[100];
524
525    for (int i=0; i<8; i++) {
526       strcpy(buf, str[i]);
527       if (!duration_to_utime(buf, &val)) {
528          printf("Error return from duration_to_utime for in=%s\n", str[i]);
529          continue;
530       }
531       edit_utime(val, outval);
532       printf("in=%s val=%" lld " outval=%s\n", str[i], val, outval);
533    }
534 }
535 #endif