]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/edit.c
090370437599de1c6e100b749258cc30422f474f
[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    Copyright (C) 2002-2005 Kern Sibbald
10
11    This program is free software; you can redistribute it and/or
12    modify it under the terms of the GNU General Public License
13    version 2 as amended with additional clauses defined in the
14    file LICENSE in the main source directory.
15
16    This program is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
19    the file LICENSE for additional details.
20
21  */
22
23 #include "bacula.h"
24 #include <math.h>
25
26 /* We assume ASCII input and don't worry about overflow */
27 uint64_t str_to_uint64(char *str)
28 {
29    register char *p = str;
30    register uint64_t value = 0;
31
32    if (!p) {
33       return 0;
34    }
35    while (B_ISSPACE(*p)) {
36       p++;
37    }
38    if (*p == '+') {
39       p++;
40    }
41    while (B_ISDIGIT(*p)) {
42       value = B_TIMES10(value) + *p - '0';
43       p++;
44    }
45    return value;
46 }
47
48 int64_t str_to_int64(char *str)
49 {
50    register char *p = str;
51    register int64_t value;
52    bool negative = false;
53
54    if (!p) {
55       return 0;
56    }
57    while (B_ISSPACE(*p)) {
58       p++;
59    }
60    if (*p == '+') {
61       p++;
62    } else if (*p == '-') {
63       negative = true;
64       p++;
65    }
66    value = str_to_uint64(p);
67    if (negative) {
68       value = -value;
69    }
70    return value;
71 }
72
73
74 /*
75  * Edit an integer number with commas, the supplied buffer
76  * must be at least 27 bytes long.  The incoming number
77  * is always widened to 64 bits.
78  */
79 char *edit_uint64_with_commas(uint64_t val, char *buf)
80 {
81    /*
82     * Replacement for sprintf(buf, "%" llu, val)
83     */
84    char mbuf[50];
85    mbuf[sizeof(mbuf)-1] = 0;
86    int i = sizeof(mbuf)-2;                 /* edit backward */
87    if (val == 0) {
88       mbuf[i--] = '0';
89    } else {
90       while (val != 0) {
91          mbuf[i--] = "0123456789"[val%10];
92          val /= 10;
93       }
94    }
95    bstrncpy(buf, &mbuf[i+1], 27);
96    return add_commas(buf, buf);
97 }
98
99 /*
100  * Edit an integer number, the supplied buffer
101  * must be at least 27 bytes long.  The incoming number
102  * is always widened to 64 bits.
103  */
104 char *edit_uint64(uint64_t val, char *buf)
105 {
106    /*
107     * Replacement for sprintf(buf, "%" llu, val)
108     */
109    char mbuf[50];
110    mbuf[sizeof(mbuf)-1] = 0;
111    int i = sizeof(mbuf)-2;                 /* edit backward */
112    if (val == 0) {
113       mbuf[i--] = '0';
114    } else {
115       while (val != 0) {
116          mbuf[i--] = "0123456789"[val%10];
117          val /= 10;
118       }
119    }
120    bstrncpy(buf, &mbuf[i+1], 27);
121    return buf;
122 }
123
124 char *edit_int64(int64_t val, char *buf)
125 {
126    /*
127     * Replacement for sprintf(buf, "%" llu, val)
128     */
129    char mbuf[50];
130    bool negative = false;
131    mbuf[sizeof(mbuf)-1] = 0;
132    int i = sizeof(mbuf)-2;                 /* edit backward */
133    if (val == 0) {
134       mbuf[i--] = '0';
135    } else {
136       if (val < 0) {
137          negative = true;
138          val = -val;
139       }
140       while (val != 0) {
141          mbuf[i--] = "0123456789"[val%10];
142          val /= 10;
143       }
144    }
145    if (negative) {
146       mbuf[i--] = '-';
147    }
148    bstrncpy(buf, &mbuf[i+1], 27);
149    return buf;
150 }
151
152
153 /*
154  * Given a string "str", separate the numeric part into
155  *   str, and the modifier into mod.
156  */
157 static bool get_modifier(char *str, char *num, int num_len, char *mod, int mod_len)
158 {
159    int i, len, num_begin, num_end, mod_begin, mod_end;
160
161    strip_trailing_junk(str);
162    len = strlen(str);
163
164    for (i=0; i<len; i++) {
165       if (!B_ISSPACE(str[i])) {
166          break;
167       }
168    }
169    num_begin = i;
170
171    /* Walk through integer part */
172    for ( ; i<len; i++) {
173       if (!B_ISDIGIT(str[i]) && str[i] != '.') {
174          break;
175       }
176    }
177    num_end = i;
178    if (num_len > (num_end - num_begin + 1)) {
179       num_len = num_end - num_begin + 1;
180    }
181    if (num_len == 0) {
182       return false;
183    }
184    /* Eat any spaces in front of modifier */
185    for ( ; i<len; i++) {
186       if (!B_ISSPACE(str[i])) {
187          break;
188       }
189    }
190    mod_begin = i;
191    for ( ; i<len; i++) {
192       if (!B_ISALPHA(str[i])) {
193          break;
194       }
195    }
196    mod_end = i;
197    if (mod_len > (mod_end - mod_begin + 1)) {
198       mod_len = mod_end - mod_begin + 1;
199    }
200    Dmsg5(900, "str=%s: num_beg=%d num_end=%d mod_beg=%d mod_end=%d\n",
201       str, num_begin, num_end, mod_begin, mod_end);
202    bstrncpy(num, &str[num_begin], num_len);
203    bstrncpy(mod, &str[mod_begin], mod_len);
204    if (!is_a_number(num)) {
205       return false;
206    }
207    bstrncpy(str, &str[mod_end], len);
208    Dmsg2(900, "num=%s mod=%s\n", num, mod);
209
210    return true;
211 }
212
213 /*
214  * Convert a string duration to utime_t (64 bit seconds)
215  * Returns false: if error
216            true:  if OK, and value stored in value
217  */
218 bool duration_to_utime(char *str, utime_t *value)
219 {
220    int i, mod_len;
221    double val, total = 0.0;
222    char mod_str[20];
223    char num_str[50];
224    /*
225     * The "n" = mins and months appears before minutes so that m maps
226     *   to months. These "kludges" make it compatible with pre 1.31
227     *   Baculas.
228     */
229    static const char *mod[] = {"n", "seconds", "months", "minutes",
230                   "hours", "days", "weeks",   "quarters",   "years", NULL};
231    static const int32_t mult[] = {60,   1, 60*60*24*30, 60,
232                   60*60, 60*60*24, 60*60*24*7, 60*60*24*91, 60*60*24*365};
233
234    while (*str) {
235       if (!get_modifier(str, num_str, sizeof(num_str), mod_str, sizeof(mod_str))) {
236          return false;
237       }
238       /* Now find the multiplier corresponding to the modifier */
239       mod_len = strlen(mod_str);
240       if (mod_len == 0) {
241          i = 1;                          /* default to seconds */
242       } else {
243          for (i=0; mod[i]; i++) {
244             if (strncasecmp(mod_str, mod[i], mod_len) == 0) {
245                break;
246             }
247          }
248          if (mod[i] == NULL) {
249             return false;
250          }
251       }
252       Dmsg2(900, "str=%s: mult=%d\n", num_str, mult[i]);
253       errno = 0;
254       val = strtod(num_str, NULL);
255       if (errno != 0 || val < 0) {
256          return false;
257       }
258       total += val * mult[i];
259    }
260    *value = (utime_t)total;
261    return true;
262 }
263
264 /*
265  * Edit a utime "duration" into ASCII
266  */
267 char *edit_utime(utime_t val, char *buf, int buf_len)
268 {
269    char mybuf[200];
270    static const int32_t mult[] = {60*60*24*365, 60*60*24*30, 60*60*24, 60*60, 60};
271    static const char *mod[]  = {"year",  "month",  "day", "hour", "min"};
272    int i;
273    uint32_t times;
274
275    *buf = 0;
276    for (i=0; i<5; i++) {
277       times = (uint32_t)(val / mult[i]);
278       if (times > 0) {
279          val = val - (utime_t)times * mult[i];
280          bsnprintf(mybuf, sizeof(mybuf), "%d %s%s ", times, mod[i], times>1?"s":"");
281          bstrncat(buf, mybuf, buf_len);
282       }
283    }
284    if (val == 0 && strlen(buf) == 0) {
285       bstrncat(buf, "0 secs", buf_len);
286    } else if (val != 0) {
287       bsnprintf(mybuf, sizeof(mybuf), "%d sec%s", (uint32_t)val, val>1?"s":"");
288       bstrncat(buf, mybuf, buf_len);
289    }
290    return buf;
291 }
292
293 /*
294  * Convert a size in bytes to uint64_t
295  * Returns false: if error
296            true:  if OK, and value stored in value
297  */
298 bool size_to_uint64(char *str, int str_len, uint64_t *value)
299 {
300    int i, mod_len;
301    double val;
302    char mod_str[20];
303    char num_str[50];
304    static const char *mod[]  = {"*", "k", "kb", "m", "mb",  "g", "gb",  NULL}; /* first item * not used */
305    const int64_t mult[] = {1,             /* byte */
306                            1024,          /* kilobyte */
307                            1000,          /* kb kilobyte */
308                            1048576,       /* megabyte */
309                            1000000,       /* mb megabyte */
310                            1073741824,    /* gigabyte */
311                            1000000000};   /* gb gigabyte */
312
313    if (!get_modifier(str, num_str, sizeof(num_str), mod_str, sizeof(mod_str))) {
314       return 0;
315    }
316    /* Now find the multiplier corresponding to the modifier */
317    mod_len = strlen(mod_str);
318    if (mod_len == 0) {
319       i = 0;                          /* default with no modifier = 1 */
320    } else {
321       for (i=0; mod[i]; i++) {
322          if (strncasecmp(mod_str, mod[i], mod_len) == 0) {
323             break;
324          }
325       }
326       if (mod[i] == NULL) {
327          return false;
328       }
329    }
330    Dmsg2(900, "str=%s: mult=%d\n", str, mult[i]);
331    errno = 0;
332    val = strtod(num_str, NULL);
333    if (errno != 0 || val < 0) {
334       return false;
335    }
336    *value = (utime_t)(val * mult[i]);
337    return true;
338 }
339
340 /*
341  * Check if specified string is a number or not.
342  *  Taken from SQLite, cool, thanks.
343  */
344 bool is_a_number(const char *n)
345 {
346    bool digit_seen = false;
347
348    if( *n == '-' || *n == '+' ) {
349       n++;
350    }
351    while (B_ISDIGIT(*n)) {
352       digit_seen = true;
353       n++;
354    }
355    if (digit_seen && *n == '.') {
356       n++;
357       while (B_ISDIGIT(*n)) { n++; }
358    }
359    if (digit_seen && (*n == 'e' || *n == 'E')
360        && (B_ISDIGIT(n[1]) || ((n[1]=='-' || n[1] == '+') && B_ISDIGIT(n[2])))) {
361       n += 2;                         /* skip e- or e+ or e digit */
362       while (B_ISDIGIT(*n)) { n++; }
363    }
364    return digit_seen && *n==0;
365 }
366
367 /*
368  * Check if the specified string is an integer
369  */
370 bool is_an_integer(const char *n)
371 {
372    bool digit_seen = false;
373    while (B_ISDIGIT(*n)) {
374       digit_seen = true;
375       n++;
376    }
377    return digit_seen && *n==0;
378 }
379
380 /*
381  * Check if Bacula Resoure Name is valid
382  */
383 /*
384  * Check if the Volume name has legal characters
385  * If ua is non-NULL send the message
386  */
387 bool is_name_valid(char *name, POOLMEM **msg)
388 {
389    int len;
390    char *p;
391    /* Special characters to accept */
392    const char *accept = ":.-_ ";
393
394    /* Restrict the characters permitted in the Volume name */
395    for (p=name; *p; p++) {
396       if (B_ISALPHA(*p) || B_ISDIGIT(*p) || strchr(accept, (int)(*p))) {
397          continue;
398       }
399       if (msg) {
400          Mmsg(msg, _("Illegal character \"%c\" in name.\n"), *p);
401       }
402       return false;
403    }
404    len = strlen(name);
405    if (len >= MAX_NAME_LENGTH) {
406       if (msg) {
407          Mmsg(msg, _("Name too long.\n"));
408       }
409       return false;
410    }
411    if (len == 0) {
412       if (msg) {
413          Mmsg(msg,  _("Volume name must be at least one character long.\n"));
414       }
415       return false;
416    }
417    return true;
418 }
419
420
421
422 /*
423  * Add commas to a string, which is presumably
424  * a number.
425  */
426 char *add_commas(char *val, char *buf)
427 {
428    int len, nc;
429    char *p, *q;
430    int i;
431
432    if (val != buf) {
433       strcpy(buf, val);
434    }
435    len = strlen(buf);
436    if (len < 1) {
437       len = 1;
438    }
439    nc = (len - 1) / 3;
440    p = buf+len;
441    q = p + nc;
442    *q-- = *p--;
443    for ( ; nc; nc--) {
444       for (i=0; i < 3; i++) {
445           *q-- = *p--;
446       }
447       *q-- = ',';
448    }
449    return buf;
450 }
451
452 #ifdef TEST_PROGRAM
453 void d_msg(const char*, int, int, const char*, ...)
454 {}
455 int main(int argc, char *argv[])
456 {
457    char *str[] = {"3", "3n", "3 hours", "3.5 day", "3 week", "3 m", "3 q", "3 years"};
458    utime_t val;
459    char buf[100];
460    char outval[100];
461
462    for (int i=0; i<8; i++) {
463       strcpy(buf, str[i]);
464       if (!duration_to_utime(buf, &val)) {
465          printf("Error return from duration_to_utime for in=%s\n", str[i]);
466          continue;
467       }
468       edit_utime(val, outval);
469       printf("in=%s val=%" lld " outval=%s\n", str[i], val, outval);
470    }
471 }
472 #endif