]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/edit.c
This commit was manufactured by cvs2svn to create tag
[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 into "human-readable" format with four or fewer
101  * significant digits followed by a suffix that indicates the scale
102  * factor.  The buf array inherits a 27 byte minimim length
103  * requirement from edit_unit64_with_commas(), although the output
104  * string is limited to eight characters.
105  */
106 char *edit_uint64_with_suffix(uint64_t val, char *buf)
107 {
108   int commas = 0;
109   char *c, mbuf[50];
110   char *suffix[] =
111     { "", "K", "M", "G", "T", "P", "E", "Z", "Y", "FIX ME" };
112   int suffixes = sizeof(suffix) / sizeof(*suffix);
113
114   edit_uint64_with_commas(val, mbuf);
115
116   if ((c = strchr(mbuf, ',')) != NULL) {
117     commas++;
118     *c++ = '.';
119     while  ((c = strchr(c, ',')) != NULL) {
120       commas++;
121       *c++ = '\0';
122     }
123     mbuf[5] = '\0'; // drop this to get '123.456 TB' rather than '123.4 TB'
124   }
125
126   if (commas >= suffixes)
127     commas = suffixes - 1;
128   bsnprintf(buf, 27, "%s %s", mbuf, suffix[commas]);
129   return buf;
130 }
131
132 /*
133  * Edit an integer number, the supplied buffer
134  * must be at least 27 bytes long.  The incoming number
135  * is always widened to 64 bits.
136  */
137 char *edit_uint64(uint64_t val, char *buf)
138 {
139    /*
140     * Replacement for sprintf(buf, "%" llu, val)
141     */
142    char mbuf[50];
143    mbuf[sizeof(mbuf)-1] = 0;
144    int i = sizeof(mbuf)-2;                 /* edit backward */
145    if (val == 0) {
146       mbuf[i--] = '0';
147    } else {
148       while (val != 0) {
149          mbuf[i--] = "0123456789"[val%10];
150          val /= 10;
151       }
152    }
153    bstrncpy(buf, &mbuf[i+1], 27);
154    return buf;
155 }
156
157 char *edit_int64(int64_t val, char *buf)
158 {
159    /*
160     * Replacement for sprintf(buf, "%" llu, val)
161     */
162    char mbuf[50];
163    bool negative = false;
164    mbuf[sizeof(mbuf)-1] = 0;
165    int i = sizeof(mbuf)-2;                 /* edit backward */
166    if (val == 0) {
167       mbuf[i--] = '0';
168    } else {
169       if (val < 0) {
170          negative = true;
171          val = -val;
172       }
173       while (val != 0) {
174          mbuf[i--] = "0123456789"[val%10];
175          val /= 10;
176       }
177    }
178    if (negative) {
179       mbuf[i--] = '-';
180    }
181    bstrncpy(buf, &mbuf[i+1], 27);
182    return buf;
183 }
184
185
186 /*
187  * Given a string "str", separate the numeric part into
188  *   str, and the modifier into mod.
189  */
190 static bool get_modifier(char *str, char *num, int num_len, char *mod, int mod_len)
191 {
192    int i, len, num_begin, num_end, mod_begin, mod_end;
193
194    strip_trailing_junk(str);
195    len = strlen(str);
196
197    for (i=0; i<len; i++) {
198       if (!B_ISSPACE(str[i])) {
199          break;
200       }
201    }
202    num_begin = i;
203
204    /* Walk through integer part */
205    for ( ; i<len; i++) {
206       if (!B_ISDIGIT(str[i]) && str[i] != '.') {
207          break;
208       }
209    }
210    num_end = i;
211    if (num_len > (num_end - num_begin + 1)) {
212       num_len = num_end - num_begin + 1;
213    }
214    if (num_len == 0) {
215       return false;
216    }
217    /* Eat any spaces in front of modifier */
218    for ( ; i<len; i++) {
219       if (!B_ISSPACE(str[i])) {
220          break;
221       }
222    }
223    mod_begin = i;
224    for ( ; i<len; i++) {
225       if (!B_ISALPHA(str[i])) {
226          break;
227       }
228    }
229    mod_end = i;
230    if (mod_len > (mod_end - mod_begin + 1)) {
231       mod_len = mod_end - mod_begin + 1;
232    }
233    Dmsg5(900, "str=%s: num_beg=%d num_end=%d mod_beg=%d mod_end=%d\n",
234       str, num_begin, num_end, mod_begin, mod_end);
235    bstrncpy(num, &str[num_begin], num_len);
236    bstrncpy(mod, &str[mod_begin], mod_len);
237    if (!is_a_number(num)) {
238       return false;
239    }
240    bstrncpy(str, &str[mod_end], len);
241    Dmsg2(900, "num=%s mod=%s\n", num, mod);
242
243    return true;
244 }
245
246 /*
247  * Convert a string duration to utime_t (64 bit seconds)
248  * Returns false: if error
249            true:  if OK, and value stored in value
250  */
251 bool duration_to_utime(char *str, utime_t *value)
252 {
253    int i, mod_len;
254    double val, total = 0.0;
255    char mod_str[20];
256    char num_str[50];
257    /*
258     * The "n" = mins and months appears before minutes so that m maps
259     *   to months. These "kludges" make it compatible with pre 1.31
260     *   Baculas.
261     */
262    static const char *mod[] = {"n", "seconds", "months", "minutes", "mins",
263                   "hours", "days", "weeks",   "quarters",   "years", NULL};
264    static const int32_t mult[] = {60,   1, 60*60*24*30, 60, 60,
265                   3600, 3600*24, 3600*24*7, 3600*24*91, 3600*24*365};
266
267    while (*str) {
268       if (!get_modifier(str, num_str, sizeof(num_str), mod_str, sizeof(mod_str))) {
269          return false;
270       }
271       /* Now find the multiplier corresponding to the modifier */
272       mod_len = strlen(mod_str);
273       if (mod_len == 0) {
274          i = 1;                          /* default to seconds */
275       } else {
276          for (i=0; mod[i]; i++) {
277             if (strncasecmp(mod_str, mod[i], mod_len) == 0) {
278                break;
279             }
280          }
281          if (mod[i] == NULL) {
282             return false;
283          }
284       }
285       Dmsg2(900, "str=%s: mult=%d\n", num_str, mult[i]);
286       errno = 0;
287       val = strtod(num_str, NULL);
288       if (errno != 0 || val < 0) {
289          return false;
290       }
291       total += val * mult[i];
292    }
293    *value = (utime_t)total;
294    return true;
295 }
296
297 /*
298  * Edit a utime "duration" into ASCII
299  */
300 char *edit_utime(utime_t val, char *buf, int buf_len)
301 {
302    char mybuf[200];
303    static const int32_t mult[] = {60*60*24*365, 60*60*24*30, 60*60*24, 60*60, 60};
304    static const char *mod[]  = {"year",  "month",  "day", "hour", "min"};
305    int i;
306    uint32_t times;
307
308    *buf = 0;
309    for (i=0; i<5; i++) {
310       times = (uint32_t)(val / mult[i]);
311       if (times > 0) {
312          val = val - (utime_t)times * mult[i];
313          bsnprintf(mybuf, sizeof(mybuf), "%d %s%s ", times, mod[i], times>1?"s":"");
314          bstrncat(buf, mybuf, buf_len);
315       }
316    }
317    if (val == 0 && strlen(buf) == 0) {
318       bstrncat(buf, "0 secs", buf_len);
319    } else if (val != 0) {
320       bsnprintf(mybuf, sizeof(mybuf), "%d sec%s", (uint32_t)val, val>1?"s":"");
321       bstrncat(buf, mybuf, buf_len);
322    }
323    return buf;
324 }
325
326 /*
327  * Convert a size in bytes to uint64_t
328  * Returns false: if error
329            true:  if OK, and value stored in value
330  */
331 bool size_to_uint64(char *str, int str_len, uint64_t *value)
332 {
333    int i, mod_len;
334    double val;
335    char mod_str[20];
336    char num_str[50];
337    static const char *mod[]  = {"*", "k", "kb", "m", "mb",  "g", "gb",  NULL}; /* first item * not used */
338    const int64_t mult[] = {1,             /* byte */
339                            1024,          /* kilobyte */
340                            1000,          /* kb kilobyte */
341                            1048576,       /* megabyte */
342                            1000000,       /* mb megabyte */
343                            1073741824,    /* gigabyte */
344                            1000000000};   /* gb gigabyte */
345
346    if (!get_modifier(str, num_str, sizeof(num_str), mod_str, sizeof(mod_str))) {
347       return 0;
348    }
349    /* Now find the multiplier corresponding to the modifier */
350    mod_len = strlen(mod_str);
351    if (mod_len == 0) {
352       i = 0;                          /* default with no modifier = 1 */
353    } else {
354       for (i=0; mod[i]; i++) {
355          if (strncasecmp(mod_str, mod[i], mod_len) == 0) {
356             break;
357          }
358       }
359       if (mod[i] == NULL) {
360          return false;
361       }
362    }
363    Dmsg2(900, "str=%s: mult=%d\n", str, mult[i]);
364    errno = 0;
365    val = strtod(num_str, NULL);
366    if (errno != 0 || val < 0) {
367       return false;
368    }
369    *value = (utime_t)(val * mult[i]);
370    return true;
371 }
372
373 /*
374  * Check if specified string is a number or not.
375  *  Taken from SQLite, cool, thanks.
376  */
377 bool is_a_number(const char *n)
378 {
379    bool digit_seen = false;
380
381    if( *n == '-' || *n == '+' ) {
382       n++;
383    }
384    while (B_ISDIGIT(*n)) {
385       digit_seen = true;
386       n++;
387    }
388    if (digit_seen && *n == '.') {
389       n++;
390       while (B_ISDIGIT(*n)) { n++; }
391    }
392    if (digit_seen && (*n == 'e' || *n == 'E')
393        && (B_ISDIGIT(n[1]) || ((n[1]=='-' || n[1] == '+') && B_ISDIGIT(n[2])))) {
394       n += 2;                         /* skip e- or e+ or e digit */
395       while (B_ISDIGIT(*n)) { n++; }
396    }
397    return digit_seen && *n==0;
398 }
399
400 /*
401  * Check if the specified string is an integer
402  */
403 bool is_an_integer(const char *n)
404 {
405    bool digit_seen = false;
406    while (B_ISDIGIT(*n)) {
407       digit_seen = true;
408       n++;
409    }
410    return digit_seen && *n==0;
411 }
412
413 /*
414  * Check if Bacula Resoure Name is valid
415  */
416 /*
417  * Check if the Volume name has legal characters
418  * If ua is non-NULL send the message
419  */
420 bool is_name_valid(char *name, POOLMEM **msg)
421 {
422    int len;
423    char *p;
424    /* Special characters to accept */
425    const char *accept = ":.-_ ";
426
427    /* Restrict the characters permitted in the Volume name */
428    for (p=name; *p; p++) {
429       if (B_ISALPHA(*p) || B_ISDIGIT(*p) || strchr(accept, (int)(*p))) {
430          continue;
431       }
432       if (msg) {
433          Mmsg(msg, _("Illegal character \"%c\" in name.\n"), *p);
434       }
435       return false;
436    }
437    len = strlen(name);
438    if (len >= MAX_NAME_LENGTH) {
439       if (msg) {
440          Mmsg(msg, _("Name too long.\n"));
441       }
442       return false;
443    }
444    if (len == 0) {
445       if (msg) {
446          Mmsg(msg,  _("Volume name must be at least one character long.\n"));
447       }
448       return false;
449    }
450    return true;
451 }
452
453
454
455 /*
456  * Add commas to a string, which is presumably
457  * a number.
458  */
459 char *add_commas(char *val, char *buf)
460 {
461    int len, nc;
462    char *p, *q;
463    int i;
464
465    if (val != buf) {
466       strcpy(buf, val);
467    }
468    len = strlen(buf);
469    if (len < 1) {
470       len = 1;
471    }
472    nc = (len - 1) / 3;
473    p = buf+len;
474    q = p + nc;
475    *q-- = *p--;
476    for ( ; nc; nc--) {
477       for (i=0; i < 3; i++) {
478           *q-- = *p--;
479       }
480       *q-- = ',';
481    }
482    return buf;
483 }
484
485 #ifdef TEST_PROGRAM
486 void d_msg(const char*, int, int, const char*, ...)
487 {}
488 int main(int argc, char *argv[])
489 {
490    char *str[] = {"3", "3n", "3 hours", "3.5 day", "3 week", "3 m", "3 q", "3 years"};
491    utime_t val;
492    char buf[100];
493    char outval[100];
494
495    for (int i=0; i<8; i++) {
496       strcpy(buf, str[i]);
497       if (!duration_to_utime(buf, &val)) {
498          printf("Error return from duration_to_utime for in=%s\n", str[i]);
499          continue;
500       }
501       edit_utime(val, outval);
502       printf("in=%s val=%" lld " outval=%s\n", str[i], val, outval);
503    }
504 }
505 #endif