2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2015 Kern Sibbald
5 Copyright (C) 2002-2014 Free Software Foundation Europe e.V.
7 The original author of Bacula is Kern Sibbald, with contributions
8 from many others, a complete list can be found in the file AUTHORS.
10 You may use this file and others of this release according to the
11 license defined in the LICENSE file, which includes the Affero General
12 Public License, v3.0 ("AGPLv3") and some additional permissions and
13 terms pursuant to its AGPLv3 Section 7.
15 This notice must be preserved when any source code is
16 conveyed and/or propagated.
18 Bacula(R) is a registered trademark of Kern Sibbald.
21 * edit.c edit string to ascii, and ascii to internal
23 * Kern Sibbald, December MMII
30 /* We assume ASCII input and don't worry about overflow */
31 uint64_t str_to_uint64(char *str)
33 register char *p = str;
34 register uint64_t value = 0;
39 while (B_ISSPACE(*p)) {
45 if (*p == '0' && *(p+1) == 'x') {
46 p = p + 2; /* skip 0x */
48 while (B_ISXDIGIT(*p)) {
50 value = (value<<4) + (*p - '0');
53 value = (value<<4) + (tolower(*p) - 'a' + 10);
58 while (B_ISDIGIT(*p)) {
59 value = B_TIMES10(value) + *p - '0';
66 int64_t str_to_int64(char *str)
68 register char *p = str;
69 register int64_t value;
70 bool negative = false;
75 while (B_ISSPACE(*p)) {
80 } else if (*p == '-') {
84 value = str_to_uint64(p);
93 * Edit an integer number with commas, the supplied buffer
94 * must be at least 27 bytes long. The incoming number
95 * is always widened to 64 bits.
97 char *edit_uint64_with_commas(uint64_t val, char *buf)
99 edit_uint64(val, buf);
100 return add_commas(buf, buf);
104 * Edit an integer into "human-readable" format with four or fewer
105 * significant digits followed by a suffix that indicates the scale
106 * factor. The buf array inherits a 27 byte minimim length
107 * requirement from edit_unit64_with_commas(), although the output
108 * string is limited to eight characters.
110 char *edit_uint64_with_suffix(uint64_t val, char *buf)
114 const char *suffix[] =
115 { "", "K", "M", "G", "T", "P", "E", "Z", "Y", "FIX ME" };
116 int suffixes = sizeof(suffix) / sizeof(*suffix);
118 edit_uint64_with_commas(val, mbuf);
120 if ((c = strchr(mbuf, ',')) != NULL) {
123 while ((c = strchr(c, ',')) != NULL) {
127 mbuf[5] = '\0'; // drop this to get '123.456 TB' rather than '123.4 TB'
130 if (commas >= suffixes)
131 commas = suffixes - 1;
132 bsnprintf(buf, 27, "%s %s", mbuf, suffix[commas]);
137 * Edit an integer number, the supplied buffer
138 * must be at least 27 bytes long. The incoming number
139 * is always widened to 64 bits.
141 char *edit_uint64(uint64_t val, char *buf)
144 * Replacement for sprintf(buf, "%" llu, val)
147 mbuf[sizeof(mbuf)-1] = 0;
148 int i = sizeof(mbuf)-2; /* edit backward */
153 mbuf[i--] = "0123456789"[val%10];
157 bstrncpy(buf, &mbuf[i+1], 27);
161 char *edit_int64(int64_t val, char *buf)
164 * Replacement for sprintf(buf, "%" llu, val)
167 bool negative = false;
168 mbuf[sizeof(mbuf)-1] = 0;
169 int i = sizeof(mbuf)-2; /* edit backward */
178 mbuf[i--] = "0123456789"[val%10];
185 bstrncpy(buf, &mbuf[i+1], 27);
190 * Edit an integer number with commas, the supplied buffer
191 * must be at least 27 bytes long. The incoming number
192 * is always widened to 64 bits.
194 char *edit_int64_with_commas(int64_t val, char *buf)
196 edit_int64(val, buf);
197 return add_commas(buf, buf);
201 * Given a string "str", separate the numeric part into
202 * str, and the modifier into mod.
204 static bool get_modifier(char *str, char *num, int num_len, char *mod, int mod_len)
206 int i, len, num_begin, num_end, mod_begin, mod_end;
208 strip_trailing_junk(str);
211 for (i=0; i<len; i++) {
212 if (!B_ISSPACE(str[i])) {
218 /* Walk through integer part */
219 for ( ; i<len; i++) {
220 if (!B_ISDIGIT(str[i]) && str[i] != '.') {
225 if (num_len > (num_end - num_begin + 1)) {
226 num_len = num_end - num_begin + 1;
231 /* Eat any spaces in front of modifier */
232 for ( ; i<len; i++) {
233 if (!B_ISSPACE(str[i])) {
238 for ( ; i<len; i++) {
239 if (!B_ISALPHA(str[i])) {
244 if (mod_len > (mod_end - mod_begin + 1)) {
245 mod_len = mod_end - mod_begin + 1;
247 Dmsg5(900, "str=%s: num_beg=%d num_end=%d mod_beg=%d mod_end=%d\n",
248 str, num_begin, num_end, mod_begin, mod_end);
249 bstrncpy(num, &str[num_begin], num_len);
250 bstrncpy(mod, &str[mod_begin], mod_len);
251 if (!is_a_number(num)) {
254 bstrncpy(str, &str[mod_end], len);
255 Dmsg2(900, "num=%s mod=%s\n", num, mod);
261 * Convert a string duration to utime_t (64 bit seconds)
262 * Returns false: if error
263 true: if OK, and value stored in value
265 bool duration_to_utime(char *str, utime_t *value)
268 double val, total = 0.0;
272 * The "n" = mins and months appears before minutes so that m maps
273 * to months. These "kludges" make it compatible with pre 1.31
276 static const char *mod[] = {"n", "seconds", "months", "minutes", "mins",
277 "hours", "days", "weeks", "quarters", "years", NULL};
278 static const int32_t mult[] = {60, 1, 60*60*24*30, 60, 60,
279 3600, 3600*24, 3600*24*7, 3600*24*91, 3600*24*365};
282 if (!get_modifier(str, num_str, sizeof(num_str), mod_str, sizeof(mod_str))) {
285 /* Now find the multiplier corresponding to the modifier */
286 mod_len = strlen(mod_str);
288 i = 1; /* default to seconds */
290 for (i=0; mod[i]; i++) {
291 if (strncasecmp(mod_str, mod[i], mod_len) == 0) {
295 if (mod[i] == NULL) {
299 Dmsg2(900, "str=%s: mult=%d\n", num_str, mult[i]);
301 val = strtod(num_str, NULL);
302 if (errno != 0 || val < 0) {
305 total += val * mult[i];
307 *value = (utime_t)total;
312 * Edit a utime "duration" into ASCII
314 char *edit_utime(utime_t val, char *buf, int buf_len)
317 static const int32_t mult[] = {60*60*24*365, 60*60*24*30, 60*60*24, 60*60, 60};
318 static const char *mod[] = {"year", "month", "day", "hour", "min"};
323 for (i=0; i<5; i++) {
324 times = (uint32_t)(val / mult[i]);
326 val = val - (utime_t)times * mult[i];
327 bsnprintf(mybuf, sizeof(mybuf), "%d %s%s ", times, mod[i], times>1?"s":"");
328 bstrncat(buf, mybuf, buf_len);
331 if (val == 0 && strlen(buf) == 0) {
332 bstrncat(buf, "0 secs", buf_len);
333 } else if (val != 0) {
334 bsnprintf(mybuf, sizeof(mybuf), "%d sec%s", (uint32_t)val, val>1?"s":"");
335 bstrncat(buf, mybuf, buf_len);
340 static bool strunit_to_uint64(char *str, int str_len, uint64_t *value,
347 const int64_t mult[] = {1, /* byte */
349 1000, /* kb kilobyte */
350 1048576, /* megabyte */
351 1000000, /* mb megabyte */
352 1073741824, /* gigabyte */
353 1000000000}; /* gb gigabyte */
355 if (!get_modifier(str, num_str, sizeof(num_str), mod_str, sizeof(mod_str))) {
358 /* Now find the multiplier corresponding to the modifier */
359 mod_len = strlen(mod_str);
361 i = 0; /* default with no modifier = 1 */
363 for (i=0; mod[i]; i++) {
364 if (strncasecmp(mod_str, mod[i], mod_len) == 0) {
368 if (mod[i] == NULL) {
372 Dmsg2(900, "str=%s: mult=%d\n", str, mult[i]);
374 val = strtod(num_str, NULL);
375 if (errno != 0 || val < 0) {
378 *value = (utime_t)(val * mult[i]);
383 * Convert a size in bytes to uint64_t
384 * Returns false: if error
385 true: if OK, and value stored in value
387 bool size_to_uint64(char *str, int str_len, uint64_t *value)
389 /* first item * not used */
390 static const char *mod[] = {"*", "k", "kb", "m", "mb", "g", "gb", NULL};
391 return strunit_to_uint64(str, str_len, value, mod);
395 * Convert a speed in bytes/s to uint64_t
396 * Returns false: if error
397 true: if OK, and value stored in value
399 bool speed_to_uint64(char *str, int str_len, uint64_t *value)
401 /* first item * not used */
402 static const char *mod[] = {"*", "k/s", "kb/s", "m/s", "mb/s", NULL};
403 return strunit_to_uint64(str, str_len, value, mod);
407 * Check if specified string is a number or not.
408 * Taken from SQLite, cool, thanks.
410 bool is_a_number(const char *n)
412 bool digit_seen = false;
418 if( *n == '-' || *n == '+' ) {
421 while (B_ISDIGIT(*n)) {
425 if (digit_seen && *n == '.') {
427 while (B_ISDIGIT(*n)) { n++; }
429 if (digit_seen && (*n == 'e' || *n == 'E')
430 && (B_ISDIGIT(n[1]) || ((n[1]=='-' || n[1] == '+') && B_ISDIGIT(n[2])))) {
431 n += 2; /* skip e- or e+ or e digit */
432 while (B_ISDIGIT(*n)) { n++; }
434 return digit_seen && *n==0;
438 * Check if specified string is a list of numbers or not
440 bool is_a_number_list(const char *n)
442 bool previous_digit = false;
443 bool digit_seen = false;
451 } else if (*n == ',' && previous_digit) {
452 previous_digit = false;
458 return digit_seen && *n==0;
462 * Check if the specified string is an integer
464 bool is_an_integer(const char *n)
466 bool digit_seen = false;
470 while (B_ISDIGIT(*n)) {
474 return digit_seen && *n==0;
478 * Check if Bacula Resoure Name is valid
481 * Check if the Volume name has legal characters
482 * If ua is non-NULL send the message
484 bool is_name_valid(const char *name, POOLMEM **msg)
488 /* Special characters to accept */
489 const char *accept = ":.-_ ";
491 /* No name is invalid */
494 Mmsg(msg, _("Empty name not allowed.\n"));
498 /* Restrict the characters permitted in the Volume name */
499 for (p=name; *p; p++) {
500 if (B_ISALPHA(*p) || B_ISDIGIT(*p) || strchr(accept, (int)(*p))) {
504 Mmsg(msg, _("Illegal character \"%c\" in name.\n"), *p);
509 if (len >= MAX_NAME_LENGTH) {
511 Mmsg(msg, _("Name too long.\n"));
517 Mmsg(msg, _("Volume name must be at least one character long.\n"));
527 * Add commas to a string, which is presumably
530 char *add_commas(char *val, char *buf)
548 for (i=0; i < 3; i++) {
557 void d_msg(const char*, int, int, const char*, ...)
559 int main(int argc, char *argv[])
561 char *str[] = {"3", "3n", "3 hours", "3.5 day", "3 week", "3 m", "3 q", "3 years"};
566 for (int i=0; i<8; i++) {
568 if (!duration_to_utime(buf, &val)) {
569 printf("Error return from duration_to_utime for in=%s\n", str[i]);
572 edit_utime(val, outval);
573 printf("in=%s val=%" lld " outval=%s\n", str[i], val, outval);