2 Bacula® - The Network Backup Solution
4 Copyright (C) 2002-2011 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version three of the GNU Affero General Public
10 License as published by the Free Software Foundation and included
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU Affero General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of Kern Sibbald.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
29 * edit.c edit string to ascii, and ascii to internal
31 * Kern Sibbald, December MMII
38 /* We assume ASCII input and don't worry about overflow */
39 uint64_t str_to_uint64(char *str)
41 register char *p = str;
42 register uint64_t value = 0;
47 while (B_ISSPACE(*p)) {
53 while (B_ISDIGIT(*p)) {
54 value = B_TIMES10(value) + *p - '0';
60 int64_t str_to_int64(char *str)
62 register char *p = str;
63 register int64_t value;
64 bool negative = false;
69 while (B_ISSPACE(*p)) {
74 } else if (*p == '-') {
78 value = str_to_uint64(p);
87 * Edit an integer number with commas, the supplied buffer
88 * must be at least 27 bytes long. The incoming number
89 * is always widened to 64 bits.
91 char *edit_uint64_with_commas(uint64_t val, char *buf)
93 edit_uint64(val, buf);
94 return add_commas(buf, buf);
98 * Edit an integer into "human-readable" format with four or fewer
99 * significant digits followed by a suffix that indicates the scale
100 * factor. The buf array inherits a 27 byte minimim length
101 * requirement from edit_unit64_with_commas(), although the output
102 * string is limited to eight characters.
104 char *edit_uint64_with_suffix(uint64_t val, char *buf)
108 const char *suffix[] =
109 { "", "K", "M", "G", "T", "P", "E", "Z", "Y", "FIX ME" };
110 int suffixes = sizeof(suffix) / sizeof(*suffix);
112 edit_uint64_with_commas(val, mbuf);
114 if ((c = strchr(mbuf, ',')) != NULL) {
117 while ((c = strchr(c, ',')) != NULL) {
121 mbuf[5] = '\0'; // drop this to get '123.456 TB' rather than '123.4 TB'
124 if (commas >= suffixes)
125 commas = suffixes - 1;
126 bsnprintf(buf, 27, "%s %s", mbuf, suffix[commas]);
131 * Edit an integer number, the supplied buffer
132 * must be at least 27 bytes long. The incoming number
133 * is always widened to 64 bits.
135 char *edit_uint64(uint64_t val, char *buf)
138 * Replacement for sprintf(buf, "%" llu, val)
141 mbuf[sizeof(mbuf)-1] = 0;
142 int i = sizeof(mbuf)-2; /* edit backward */
147 mbuf[i--] = "0123456789"[val%10];
151 bstrncpy(buf, &mbuf[i+1], 27);
155 char *edit_int64(int64_t val, char *buf)
158 * Replacement for sprintf(buf, "%" llu, val)
161 bool negative = false;
162 mbuf[sizeof(mbuf)-1] = 0;
163 int i = sizeof(mbuf)-2; /* edit backward */
172 mbuf[i--] = "0123456789"[val%10];
179 bstrncpy(buf, &mbuf[i+1], 27);
184 * Edit an integer number with commas, the supplied buffer
185 * must be at least 27 bytes long. The incoming number
186 * is always widened to 64 bits.
188 char *edit_int64_with_commas(int64_t val, char *buf)
190 edit_int64(val, buf);
191 return add_commas(buf, buf);
195 * Given a string "str", separate the numeric part into
196 * str, and the modifier into mod.
198 static bool get_modifier(char *str, char *num, int num_len, char *mod, int mod_len)
200 int i, len, num_begin, num_end, mod_begin, mod_end;
202 strip_trailing_junk(str);
205 for (i=0; i<len; i++) {
206 if (!B_ISSPACE(str[i])) {
212 /* Walk through integer part */
213 for ( ; i<len; i++) {
214 if (!B_ISDIGIT(str[i]) && str[i] != '.') {
219 if (num_len > (num_end - num_begin + 1)) {
220 num_len = num_end - num_begin + 1;
225 /* Eat any spaces in front of modifier */
226 for ( ; i<len; i++) {
227 if (!B_ISSPACE(str[i])) {
232 for ( ; i<len; i++) {
233 if (!B_ISALPHA(str[i])) {
238 if (mod_len > (mod_end - mod_begin + 1)) {
239 mod_len = mod_end - mod_begin + 1;
241 Dmsg5(900, "str=%s: num_beg=%d num_end=%d mod_beg=%d mod_end=%d\n",
242 str, num_begin, num_end, mod_begin, mod_end);
243 bstrncpy(num, &str[num_begin], num_len);
244 bstrncpy(mod, &str[mod_begin], mod_len);
245 if (!is_a_number(num)) {
248 bstrncpy(str, &str[mod_end], len);
249 Dmsg2(900, "num=%s mod=%s\n", num, mod);
255 * Convert a string duration to utime_t (64 bit seconds)
256 * Returns false: if error
257 true: if OK, and value stored in value
259 bool duration_to_utime(char *str, utime_t *value)
262 double val, total = 0.0;
266 * The "n" = mins and months appears before minutes so that m maps
267 * to months. These "kludges" make it compatible with pre 1.31
270 static const char *mod[] = {"n", "seconds", "months", "minutes", "mins",
271 "hours", "days", "weeks", "quarters", "years", NULL};
272 static const int32_t mult[] = {60, 1, 60*60*24*30, 60, 60,
273 3600, 3600*24, 3600*24*7, 3600*24*91, 3600*24*365};
276 if (!get_modifier(str, num_str, sizeof(num_str), mod_str, sizeof(mod_str))) {
279 /* Now find the multiplier corresponding to the modifier */
280 mod_len = strlen(mod_str);
282 i = 1; /* default to seconds */
284 for (i=0; mod[i]; i++) {
285 if (strncasecmp(mod_str, mod[i], mod_len) == 0) {
289 if (mod[i] == NULL) {
293 Dmsg2(900, "str=%s: mult=%d\n", num_str, mult[i]);
295 val = strtod(num_str, NULL);
296 if (errno != 0 || val < 0) {
299 total += val * mult[i];
301 *value = (utime_t)total;
306 * Edit a utime "duration" into ASCII
308 char *edit_utime(utime_t val, char *buf, int buf_len)
311 static const int32_t mult[] = {60*60*24*365, 60*60*24*30, 60*60*24, 60*60, 60};
312 static const char *mod[] = {"year", "month", "day", "hour", "min"};
317 for (i=0; i<5; i++) {
318 times = (uint32_t)(val / mult[i]);
320 val = val - (utime_t)times * mult[i];
321 bsnprintf(mybuf, sizeof(mybuf), "%d %s%s ", times, mod[i], times>1?"s":"");
322 bstrncat(buf, mybuf, buf_len);
325 if (val == 0 && strlen(buf) == 0) {
326 bstrncat(buf, "0 secs", buf_len);
327 } else if (val != 0) {
328 bsnprintf(mybuf, sizeof(mybuf), "%d sec%s", (uint32_t)val, val>1?"s":"");
329 bstrncat(buf, mybuf, buf_len);
334 static bool strunit_to_uint64(char *str, int str_len, uint64_t *value,
341 const int64_t mult[] = {1, /* byte */
343 1000, /* kb kilobyte */
344 1048576, /* megabyte */
345 1000000, /* mb megabyte */
346 1073741824, /* gigabyte */
347 1000000000}; /* gb gigabyte */
349 if (!get_modifier(str, num_str, sizeof(num_str), mod_str, sizeof(mod_str))) {
352 /* Now find the multiplier corresponding to the modifier */
353 mod_len = strlen(mod_str);
355 i = 0; /* default with no modifier = 1 */
357 for (i=0; mod[i]; i++) {
358 if (strncasecmp(mod_str, mod[i], mod_len) == 0) {
362 if (mod[i] == NULL) {
366 Dmsg2(900, "str=%s: mult=%d\n", str, mult[i]);
368 val = strtod(num_str, NULL);
369 if (errno != 0 || val < 0) {
372 *value = (utime_t)(val * mult[i]);
377 * Convert a size in bytes to uint64_t
378 * Returns false: if error
379 true: if OK, and value stored in value
381 bool size_to_uint64(char *str, int str_len, uint64_t *value)
383 /* first item * not used */
384 static const char *mod[] = {"*", "k", "kb", "m", "mb", "g", "gb", NULL};
385 return strunit_to_uint64(str, str_len, value, mod);
389 * Convert a speed in bytes/s to uint64_t
390 * Returns false: if error
391 true: if OK, and value stored in value
393 bool speed_to_uint64(char *str, int str_len, uint64_t *value)
395 /* first item * not used */
396 static const char *mod[] = {"*", "k/s", "kb/s", "m/s", "mb/s", NULL};
397 return strunit_to_uint64(str, str_len, value, mod);
401 * Check if specified string is a number or not.
402 * Taken from SQLite, cool, thanks.
404 bool is_a_number(const char *n)
406 bool digit_seen = false;
408 if( *n == '-' || *n == '+' ) {
411 while (B_ISDIGIT(*n)) {
415 if (digit_seen && *n == '.') {
417 while (B_ISDIGIT(*n)) { n++; }
419 if (digit_seen && (*n == 'e' || *n == 'E')
420 && (B_ISDIGIT(n[1]) || ((n[1]=='-' || n[1] == '+') && B_ISDIGIT(n[2])))) {
421 n += 2; /* skip e- or e+ or e digit */
422 while (B_ISDIGIT(*n)) { n++; }
424 return digit_seen && *n==0;
428 * Check if specified string is a list of numbers or not
430 bool is_a_number_list(const char *n)
432 bool previous_digit = false;
433 bool digit_seen = false;
438 } else if (*n == ',' && previous_digit) {
439 previous_digit = false;
445 return digit_seen && *n==0;
449 * Check if the specified string is an integer
451 bool is_an_integer(const char *n)
453 bool digit_seen = false;
454 while (B_ISDIGIT(*n)) {
458 return digit_seen && *n==0;
462 * Check if Bacula Resoure Name is valid
465 * Check if the Volume name has legal characters
466 * If ua is non-NULL send the message
468 bool is_name_valid(const char *name, POOLMEM **msg)
472 /* Special characters to accept */
473 const char *accept = ":.-_ ";
475 /* No name is invalid */
478 Mmsg(msg, _("Empty name not allowed.\n"));
482 /* Restrict the characters permitted in the Volume name */
483 for (p=name; *p; p++) {
484 if (B_ISALPHA(*p) || B_ISDIGIT(*p) || strchr(accept, (int)(*p))) {
488 Mmsg(msg, _("Illegal character \"%c\" in name.\n"), *p);
493 if (len >= MAX_NAME_LENGTH) {
495 Mmsg(msg, _("Name too long.\n"));
501 Mmsg(msg, _("Volume name must be at least one character long.\n"));
511 * Add commas to a string, which is presumably
514 char *add_commas(char *val, char *buf)
532 for (i=0; i < 3; i++) {
541 void d_msg(const char*, int, int, const char*, ...)
543 int main(int argc, char *argv[])
545 char *str[] = {"3", "3n", "3 hours", "3.5 day", "3 week", "3 m", "3 q", "3 years"};
550 for (int i=0; i<8; i++) {
552 if (!duration_to_utime(buf, &val)) {
553 printf("Error return from duration_to_utime for in=%s\n", str[i]);
556 edit_utime(val, outval);
557 printf("in=%s val=%" lld " outval=%s\n", str[i], val, outval);