2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2016 Kern Sibbald
6 The original author of Bacula is Kern Sibbald, with contributions
7 from many others, a complete list can be found in the file AUTHORS.
9 You may use this file and others of this release according to the
10 license defined in the LICENSE file, which includes the Affero General
11 Public License, v3.0 ("AGPLv3") and some additional permissions and
12 terms pursuant to its AGPLv3 Section 7.
14 This notice must be preserved when any source code is
15 conveyed and/or propagated.
17 Bacula(R) is a registered trademark of Kern Sibbald.
20 * edit.c edit string to ascii, and ascii to internal
22 * Kern Sibbald, December MMII
29 /* We assume ASCII input and don't worry about overflow */
30 uint64_t str_to_uint64(char *str)
32 register char *p = str;
33 register uint64_t value = 0;
38 while (B_ISSPACE(*p)) {
44 if (*p == '0' && *(p+1) == 'x') {
45 p = p + 2; /* skip 0x */
47 while (B_ISXDIGIT(*p)) {
49 value = (value<<4) + (*p - '0');
52 value = (value<<4) + (tolower(*p) - 'a' + 10);
57 while (B_ISDIGIT(*p)) {
58 value = B_TIMES10(value) + *p - '0';
65 int64_t str_to_int64(char *str)
67 register char *p = str;
68 register int64_t value;
69 bool negative = false;
74 while (B_ISSPACE(*p)) {
79 } else if (*p == '-') {
83 value = str_to_uint64(p);
92 * Edit an integer number with commas, the supplied buffer
93 * must be at least 27 bytes long. The incoming number
94 * is always widened to 64 bits.
96 char *edit_uint64_with_commas(uint64_t val, char *buf)
98 edit_uint64(val, buf);
99 return add_commas(buf, buf);
103 * Edit an integer into "human-readable" format with four or fewer
104 * significant digits followed by a suffix that indicates the scale
105 * factor. The buf array inherits a 27 byte minimim length
106 * requirement from edit_unit64_with_commas(), although the output
107 * string is limited to eight characters.
109 char *edit_uint64_with_suffix(uint64_t val, char *buf)
113 const char *suffix[] =
114 { "", "K", "M", "G", "T", "P", "E", "Z", "Y", "FIX ME" };
115 int suffixes = sizeof(suffix) / sizeof(*suffix);
117 edit_uint64_with_commas(val, mbuf);
119 if ((c = strchr(mbuf, ',')) != NULL) {
122 while ((c = strchr(c, ',')) != NULL) {
126 mbuf[5] = '\0'; // drop this to get '123.456 TB' rather than '123.4 TB'
129 if (commas >= suffixes)
130 commas = suffixes - 1;
131 bsnprintf(buf, 27, "%s %s", mbuf, suffix[commas]);
136 * Edit an integer number, the supplied buffer
137 * must be at least 27 bytes long. The incoming number
138 * is always widened to 64 bits.
140 char *edit_uint64(uint64_t val, char *buf)
143 * Replacement for sprintf(buf, "%" llu, val)
146 mbuf[sizeof(mbuf)-1] = 0;
147 int i = sizeof(mbuf)-2; /* edit backward */
152 mbuf[i--] = "0123456789"[val%10];
156 bstrncpy(buf, &mbuf[i+1], 27);
160 char *edit_int64(int64_t val, char *buf)
163 * Replacement for sprintf(buf, "%" llu, val)
166 bool negative = false;
167 mbuf[sizeof(mbuf)-1] = 0;
168 int i = sizeof(mbuf)-2; /* edit backward */
177 mbuf[i--] = "0123456789"[val%10];
184 bstrncpy(buf, &mbuf[i+1], 27);
189 * Edit an integer number with commas, the supplied buffer
190 * must be at least 27 bytes long. The incoming number
191 * is always widened to 64 bits.
193 char *edit_int64_with_commas(int64_t val, char *buf)
195 edit_int64(val, buf);
196 return add_commas(buf, buf);
200 * Given a string "str", separate the numeric part into
201 * str, and the modifier into mod.
203 static bool get_modifier(char *str, char *num, int num_len, char *mod, int mod_len)
205 int i, len, num_begin, num_end, mod_begin, mod_end;
207 strip_trailing_junk(str);
210 for (i=0; i<len; i++) {
211 if (!B_ISSPACE(str[i])) {
217 /* Walk through integer part */
218 for ( ; i<len; i++) {
219 if (!B_ISDIGIT(str[i]) && str[i] != '.') {
224 if (num_len > (num_end - num_begin + 1)) {
225 num_len = num_end - num_begin + 1;
230 /* Eat any spaces in front of modifier */
231 for ( ; i<len; i++) {
232 if (!B_ISSPACE(str[i])) {
237 for ( ; i<len; i++) {
238 if (!B_ISALPHA(str[i])) {
243 if (mod_len > (mod_end - mod_begin + 1)) {
244 mod_len = mod_end - mod_begin + 1;
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)) {
253 bstrncpy(str, &str[mod_end], len);
254 Dmsg2(900, "num=%s mod=%s\n", num, mod);
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
264 bool duration_to_utime(char *str, utime_t *value)
267 double val, total = 0.0;
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
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};
281 if (!get_modifier(str, num_str, sizeof(num_str), mod_str, sizeof(mod_str))) {
284 /* Now find the multiplier corresponding to the modifier */
285 mod_len = strlen(mod_str);
287 i = 1; /* default to seconds */
289 for (i=0; mod[i]; i++) {
290 if (strncasecmp(mod_str, mod[i], mod_len) == 0) {
294 if (mod[i] == NULL) {
298 Dmsg2(900, "str=%s: mult=%d\n", num_str, mult[i]);
300 val = strtod(num_str, NULL);
301 if (errno != 0 || val < 0) {
304 total += val * mult[i];
306 *value = (utime_t)total;
311 * Edit a utime "duration" into ASCII
313 char *edit_utime(utime_t val, char *buf, int buf_len)
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"};
322 for (i=0; i<5; i++) {
323 times = (uint32_t)(val / mult[i]);
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);
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);
339 static bool strunit_to_uint64(char *str, int str_len, uint64_t *value,
346 const int64_t mult[] = {1, /* byte */
348 1000, /* kb kilobyte */
349 1048576, /* megabyte */
350 1000000, /* mb megabyte */
351 1073741824, /* gigabyte */
352 1000000000}; /* gb gigabyte */
354 if (!get_modifier(str, num_str, sizeof(num_str), mod_str, sizeof(mod_str))) {
357 /* Now find the multiplier corresponding to the modifier */
358 mod_len = strlen(mod_str);
360 i = 0; /* default with no modifier = 1 */
362 for (i=0; mod[i]; i++) {
363 if (strncasecmp(mod_str, mod[i], mod_len) == 0) {
367 if (mod[i] == NULL) {
371 Dmsg2(900, "str=%s: mult=%d\n", str, mult[i]);
373 val = strtod(num_str, NULL);
374 if (errno != 0 || val < 0) {
377 *value = (utime_t)(val * mult[i]);
382 * Convert a size in bytes to uint64_t
383 * Returns false: if error
384 true: if OK, and value stored in value
386 bool size_to_uint64(char *str, int str_len, uint64_t *value)
388 /* first item * not used */
389 static const char *mod[] = {"*", "k", "kb", "m", "mb", "g", "gb", NULL};
390 return strunit_to_uint64(str, str_len, value, mod);
394 * Convert a speed in bytes/s to uint64_t
395 * Returns false: if error
396 true: if OK, and value stored in value
398 bool speed_to_uint64(char *str, int str_len, uint64_t *value)
400 /* first item * not used */
401 static const char *mod[] = {"*", "k/s", "kb/s", "m/s", "mb/s", NULL};
402 return strunit_to_uint64(str, str_len, value, mod);
406 * Check if specified string is a number or not.
407 * Taken from SQLite, cool, thanks.
409 bool is_a_number(const char *n)
411 bool digit_seen = false;
417 if( *n == '-' || *n == '+' ) {
420 while (B_ISDIGIT(*n)) {
424 if (digit_seen && *n == '.') {
426 while (B_ISDIGIT(*n)) { n++; }
428 if (digit_seen && (*n == 'e' || *n == 'E')
429 && (B_ISDIGIT(n[1]) || ((n[1]=='-' || n[1] == '+') && B_ISDIGIT(n[2])))) {
430 n += 2; /* skip e- or e+ or e digit */
431 while (B_ISDIGIT(*n)) { n++; }
433 return digit_seen && *n==0;
437 * Check if specified string is a list of numbers or not
439 bool is_a_number_list(const char *n)
441 bool previous_digit = false;
442 bool digit_seen = false;
450 } else if (*n == ',' && previous_digit) {
451 previous_digit = false;
457 return digit_seen && *n==0;
461 * Check if the specified string is an integer
463 bool is_an_integer(const char *n)
465 bool digit_seen = false;
469 while (B_ISDIGIT(*n)) {
473 return digit_seen && *n==0;
477 * Check if Bacula Resoure Name is valid
480 * Check if the Volume name has legal characters
481 * If ua is non-NULL send the message
483 bool is_name_valid(const char *name, POOLMEM **msg)
487 /* Special characters to accept */
488 const char *accept = ":.-_ ";
490 /* No name is invalid */
493 Mmsg(msg, _("Empty name not allowed.\n"));
497 /* Restrict the characters permitted in the Volume name */
498 for (p=name; *p; p++) {
499 if (B_ISALPHA(*p) || B_ISDIGIT(*p) || strchr(accept, (int)(*p))) {
503 Mmsg(msg, _("Illegal character \"%c\" in name.\n"), *p);
508 if (len >= MAX_NAME_LENGTH) {
510 Mmsg(msg, _("Name too long.\n"));
516 Mmsg(msg, _("Volume name must be at least one character long.\n"));
526 * Add commas to a string, which is presumably
529 char *add_commas(char *val, char *buf)
547 for (i=0; i < 3; i++) {
556 void d_msg(const char*, int, int, const char*, ...)
558 int main(int argc, char *argv[])
560 char *str[] = {"3", "3n", "3 hours", "3.5 day", "3 week", "3 m", "3 q", "3 years"};
565 for (int i=0; i<8; i++) {
567 if (!duration_to_utime(buf, &val)) {
568 printf("Error return from duration_to_utime for in=%s\n", str[i]);
571 edit_utime(val, outval);
572 printf("in=%s val=%" lld " outval=%s\n", str[i], val, outval);