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