]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/edit.c
ebl add Error status in update volume=xxx status=yyyy
[bacula/bacula] / bacula / src / lib / edit.c
1 /*
2  *   edit.c  edit string to ascii, and ascii to internal
3  *
4  *    Kern Sibbald, December MMII
5  *
6  *   Version $Id$
7  */
8 /*
9    Bacula® - The Network Backup Solution
10
11    Copyright (C) 2002-2006 Free Software Foundation Europe e.V.
12
13    The main author of Bacula is Kern Sibbald, with contributions from
14    many others, a complete list can be found in the file AUTHORS.
15    This program is Free Software; you can redistribute it and/or
16    modify it under the terms of version two of the GNU General Public
17    License as published by the Free Software Foundation plus additions
18    that are listed in the file LICENSE.
19
20    This program is distributed in the hope that it will be useful, but
21    WITHOUT ANY WARRANTY; without even the implied warranty of
22    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23    General Public License for more details.
24
25    You should have received a copy of the GNU General Public License
26    along with this program; if not, write to the Free Software
27    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
28    02110-1301, USA.
29
30    Bacula® is a registered trademark of John Walker.
31    The licensor of Bacula is the Free Software Foundation Europe
32    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
33    Switzerland, email:ftf@fsfeurope.org.
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    /*
95     * Replacement for sprintf(buf, "%" llu, val)
96     */
97    char mbuf[50];
98    mbuf[sizeof(mbuf)-1] = 0;
99    int i = sizeof(mbuf)-2;                 /* edit backward */
100    if (val == 0) {
101       mbuf[i--] = '0';
102    } else {
103       while (val != 0) {
104          mbuf[i--] = "0123456789"[val%10];
105          val /= 10;
106       }
107    }
108    bstrncpy(buf, &mbuf[i+1], 27);
109    return add_commas(buf, buf);
110 }
111
112 /*
113  * Edit an integer into "human-readable" format with four or fewer
114  * significant digits followed by a suffix that indicates the scale
115  * factor.  The buf array inherits a 27 byte minimim length
116  * requirement from edit_unit64_with_commas(), although the output
117  * string is limited to eight characters.
118  */
119 char *edit_uint64_with_suffix(uint64_t val, char *buf)
120 {
121   int commas = 0;
122   char *c, mbuf[50];
123   char *suffix[] =
124     { "", "K", "M", "G", "T", "P", "E", "Z", "Y", "FIX ME" };
125   int suffixes = sizeof(suffix) / sizeof(*suffix);
126
127   edit_uint64_with_commas(val, mbuf);
128
129   if ((c = strchr(mbuf, ',')) != NULL) {
130     commas++;
131     *c++ = '.';
132     while  ((c = strchr(c, ',')) != NULL) {
133       commas++;
134       *c++ = '\0';
135     }
136     mbuf[5] = '\0'; // drop this to get '123.456 TB' rather than '123.4 TB'
137   }
138
139   if (commas >= suffixes)
140     commas = suffixes - 1;
141   bsnprintf(buf, 27, "%s %s", mbuf, suffix[commas]);
142   return buf;
143 }
144
145 /*
146  * Edit an integer number, the supplied buffer
147  * must be at least 27 bytes long.  The incoming number
148  * is always widened to 64 bits.
149  */
150 char *edit_uint64(uint64_t val, char *buf)
151 {
152    /*
153     * Replacement for sprintf(buf, "%" llu, val)
154     */
155    char mbuf[50];
156    mbuf[sizeof(mbuf)-1] = 0;
157    int i = sizeof(mbuf)-2;                 /* edit backward */
158    if (val == 0) {
159       mbuf[i--] = '0';
160    } else {
161       while (val != 0) {
162          mbuf[i--] = "0123456789"[val%10];
163          val /= 10;
164       }
165    }
166    bstrncpy(buf, &mbuf[i+1], 27);
167    return buf;
168 }
169
170 char *edit_int64(int64_t val, char *buf)
171 {
172    /*
173     * Replacement for sprintf(buf, "%" llu, val)
174     */
175    char mbuf[50];
176    bool negative = false;
177    mbuf[sizeof(mbuf)-1] = 0;
178    int i = sizeof(mbuf)-2;                 /* edit backward */
179    if (val == 0) {
180       mbuf[i--] = '0';
181    } else {
182       if (val < 0) {
183          negative = true;
184          val = -val;
185       }
186       while (val != 0) {
187          mbuf[i--] = "0123456789"[val%10];
188          val /= 10;
189       }
190    }
191    if (negative) {
192       mbuf[i--] = '-';
193    }
194    bstrncpy(buf, &mbuf[i+1], 27);
195    return buf;
196 }
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 /*
340  * Convert a size in bytes to uint64_t
341  * Returns false: if error
342            true:  if OK, and value stored in value
343  */
344 bool size_to_uint64(char *str, int str_len, uint64_t *value)
345 {
346    int i, mod_len;
347    double val;
348    char mod_str[20];
349    char num_str[50];
350    static const char *mod[]  = {"*", "k", "kb", "m", "mb",  "g", "gb",  NULL}; /* first item * not used */
351    const int64_t mult[] = {1,             /* byte */
352                            1024,          /* kilobyte */
353                            1000,          /* kb kilobyte */
354                            1048576,       /* megabyte */
355                            1000000,       /* mb megabyte */
356                            1073741824,    /* gigabyte */
357                            1000000000};   /* gb gigabyte */
358
359    if (!get_modifier(str, num_str, sizeof(num_str), mod_str, sizeof(mod_str))) {
360       return 0;
361    }
362    /* Now find the multiplier corresponding to the modifier */
363    mod_len = strlen(mod_str);
364    if (mod_len == 0) {
365       i = 0;                          /* default with no modifier = 1 */
366    } else {
367       for (i=0; mod[i]; i++) {
368          if (strncasecmp(mod_str, mod[i], mod_len) == 0) {
369             break;
370          }
371       }
372       if (mod[i] == NULL) {
373          return false;
374       }
375    }
376    Dmsg2(900, "str=%s: mult=%d\n", str, mult[i]);
377    errno = 0;
378    val = strtod(num_str, NULL);
379    if (errno != 0 || val < 0) {
380       return false;
381    }
382    *value = (utime_t)(val * mult[i]);
383    return true;
384 }
385
386 /*
387  * Check if specified string is a number or not.
388  *  Taken from SQLite, cool, thanks.
389  */
390 bool is_a_number(const char *n)
391 {
392    bool digit_seen = false;
393
394    if( *n == '-' || *n == '+' ) {
395       n++;
396    }
397    while (B_ISDIGIT(*n)) {
398       digit_seen = true;
399       n++;
400    }
401    if (digit_seen && *n == '.') {
402       n++;
403       while (B_ISDIGIT(*n)) { n++; }
404    }
405    if (digit_seen && (*n == 'e' || *n == 'E')
406        && (B_ISDIGIT(n[1]) || ((n[1]=='-' || n[1] == '+') && B_ISDIGIT(n[2])))) {
407       n += 2;                         /* skip e- or e+ or e digit */
408       while (B_ISDIGIT(*n)) { n++; }
409    }
410    return digit_seen && *n==0;
411 }
412
413 /*
414  * Check if the specified string is an integer
415  */
416 bool is_an_integer(const char *n)
417 {
418    bool digit_seen = false;
419    while (B_ISDIGIT(*n)) {
420       digit_seen = true;
421       n++;
422    }
423    return digit_seen && *n==0;
424 }
425
426 /*
427  * Check if Bacula Resoure Name is valid
428  */
429 /*
430  * Check if the Volume name has legal characters
431  * If ua is non-NULL send the message
432  */
433 bool is_name_valid(char *name, POOLMEM **msg)
434 {
435    int len;
436    char *p;
437    /* Special characters to accept */
438    const char *accept = ":.-_ ";
439
440    /* Restrict the characters permitted in the Volume name */
441    for (p=name; *p; p++) {
442       if (B_ISALPHA(*p) || B_ISDIGIT(*p) || strchr(accept, (int)(*p))) {
443          continue;
444       }
445       if (msg) {
446          Mmsg(msg, _("Illegal character \"%c\" in name.\n"), *p);
447       }
448       return false;
449    }
450    len = strlen(name);
451    if (len >= MAX_NAME_LENGTH) {
452       if (msg) {
453          Mmsg(msg, _("Name too long.\n"));
454       }
455       return false;
456    }
457    if (len == 0) {
458       if (msg) {
459          Mmsg(msg,  _("Volume name must be at least one character long.\n"));
460       }
461       return false;
462    }
463    return true;
464 }
465
466
467
468 /*
469  * Add commas to a string, which is presumably
470  * a number.
471  */
472 char *add_commas(char *val, char *buf)
473 {
474    int len, nc;
475    char *p, *q;
476    int i;
477
478    if (val != buf) {
479       strcpy(buf, val);
480    }
481    len = strlen(buf);
482    if (len < 1) {
483       len = 1;
484    }
485    nc = (len - 1) / 3;
486    p = buf+len;
487    q = p + nc;
488    *q-- = *p--;
489    for ( ; nc; nc--) {
490       for (i=0; i < 3; i++) {
491           *q-- = *p--;
492       }
493       *q-- = ',';
494    }
495    return buf;
496 }
497
498 #ifdef TEST_PROGRAM
499 void d_msg(const char*, int, int, const char*, ...)
500 {}
501 int main(int argc, char *argv[])
502 {
503    char *str[] = {"3", "3n", "3 hours", "3.5 day", "3 week", "3 m", "3 q", "3 years"};
504    utime_t val;
505    char buf[100];
506    char outval[100];
507
508    for (int i=0; i<8; i++) {
509       strcpy(buf, str[i]);
510       if (!duration_to_utime(buf, &val)) {
511          printf("Error return from duration_to_utime for in=%s\n", str[i]);
512          continue;
513       }
514       edit_utime(val, outval);
515       printf("in=%s val=%" lld " outval=%s\n", str[i], val, outval);
516    }
517 }
518 #endif