2 * edit.c edit string to ascii, and ascii to internal
4 * Kern Sibbald, December MMII
9 Copyright (C) 2002-2005 Kern Sibbald
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.
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.
26 /* We assume ASCII input and don't worry about overflow */
27 uint64_t str_to_uint64(char *str)
29 register char *p = str;
30 register uint64_t value = 0;
35 while (B_ISSPACE(*p)) {
41 while (B_ISDIGIT(*p)) {
42 value = B_TIMES10(value) + *p - '0';
48 int64_t str_to_int64(char *str)
50 register char *p = str;
51 register int64_t value;
52 bool negative = false;
57 while (B_ISSPACE(*p)) {
62 } else if (*p == '-') {
66 value = str_to_uint64(p);
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.
79 char *edit_uint64_with_commas(uint64_t val, char *buf)
82 * Replacement for sprintf(buf, "%" llu, val)
85 mbuf[sizeof(mbuf)-1] = 0;
86 int i = sizeof(mbuf)-2; /* edit backward */
91 mbuf[i--] = "0123456789"[val%10];
95 bstrncpy(buf, &mbuf[i+1], 27);
96 return add_commas(buf, buf);
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.
106 char *edit_uint64_with_suffix(uint64_t val, char *buf)
111 { "", "K", "M", "G", "T", "P", "E", "Z", "Y", "FIX ME" };
112 int suffixes = sizeof(suffix) / sizeof(*suffix);
114 edit_uint64_with_commas(val, mbuf);
116 if ((c = strchr(mbuf, ',')) != NULL) {
119 while ((c = strchr(c, ',')) != NULL) {
123 mbuf[5] = '\0'; // drop this to get '123.456 TB' rather than '123.4 TB'
126 if (commas >= suffixes)
127 commas = suffixes - 1;
128 bsnprintf(buf, 27, "%s %s", mbuf, suffix[commas]);
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.
137 char *edit_uint64(uint64_t val, char *buf)
140 * Replacement for sprintf(buf, "%" llu, val)
143 mbuf[sizeof(mbuf)-1] = 0;
144 int i = sizeof(mbuf)-2; /* edit backward */
149 mbuf[i--] = "0123456789"[val%10];
153 bstrncpy(buf, &mbuf[i+1], 27);
157 char *edit_int64(int64_t val, char *buf)
160 * Replacement for sprintf(buf, "%" llu, val)
163 bool negative = false;
164 mbuf[sizeof(mbuf)-1] = 0;
165 int i = sizeof(mbuf)-2; /* edit backward */
174 mbuf[i--] = "0123456789"[val%10];
181 bstrncpy(buf, &mbuf[i+1], 27);
187 * Given a string "str", separate the numeric part into
188 * str, and the modifier into mod.
190 static bool get_modifier(char *str, char *num, int num_len, char *mod, int mod_len)
192 int i, len, num_begin, num_end, mod_begin, mod_end;
194 strip_trailing_junk(str);
197 for (i=0; i<len; i++) {
198 if (!B_ISSPACE(str[i])) {
204 /* Walk through integer part */
205 for ( ; i<len; i++) {
206 if (!B_ISDIGIT(str[i]) && str[i] != '.') {
211 if (num_len > (num_end - num_begin + 1)) {
212 num_len = num_end - num_begin + 1;
217 /* Eat any spaces in front of modifier */
218 for ( ; i<len; i++) {
219 if (!B_ISSPACE(str[i])) {
224 for ( ; i<len; i++) {
225 if (!B_ISALPHA(str[i])) {
230 if (mod_len > (mod_end - mod_begin + 1)) {
231 mod_len = mod_end - mod_begin + 1;
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)) {
240 bstrncpy(str, &str[mod_end], len);
241 Dmsg2(900, "num=%s mod=%s\n", num, mod);
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
251 bool duration_to_utime(char *str, utime_t *value)
254 double val, total = 0.0;
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
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};
268 if (!get_modifier(str, num_str, sizeof(num_str), mod_str, sizeof(mod_str))) {
271 /* Now find the multiplier corresponding to the modifier */
272 mod_len = strlen(mod_str);
274 i = 1; /* default to seconds */
276 for (i=0; mod[i]; i++) {
277 if (strncasecmp(mod_str, mod[i], mod_len) == 0) {
281 if (mod[i] == NULL) {
285 Dmsg2(900, "str=%s: mult=%d\n", num_str, mult[i]);
287 val = strtod(num_str, NULL);
288 if (errno != 0 || val < 0) {
291 total += val * mult[i];
293 *value = (utime_t)total;
298 * Edit a utime "duration" into ASCII
300 char *edit_utime(utime_t val, char *buf, int buf_len)
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"};
309 for (i=0; i<5; i++) {
310 times = (uint32_t)(val / mult[i]);
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);
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);
327 * Convert a size in bytes to uint64_t
328 * Returns false: if error
329 true: if OK, and value stored in value
331 bool size_to_uint64(char *str, int str_len, uint64_t *value)
337 static const char *mod[] = {"*", "k", "kb", "m", "mb", "g", "gb", NULL}; /* first item * not used */
338 const int64_t mult[] = {1, /* byte */
340 1000, /* kb kilobyte */
341 1048576, /* megabyte */
342 1000000, /* mb megabyte */
343 1073741824, /* gigabyte */
344 1000000000}; /* gb gigabyte */
346 if (!get_modifier(str, num_str, sizeof(num_str), mod_str, sizeof(mod_str))) {
349 /* Now find the multiplier corresponding to the modifier */
350 mod_len = strlen(mod_str);
352 i = 0; /* default with no modifier = 1 */
354 for (i=0; mod[i]; i++) {
355 if (strncasecmp(mod_str, mod[i], mod_len) == 0) {
359 if (mod[i] == NULL) {
363 Dmsg2(900, "str=%s: mult=%d\n", str, mult[i]);
365 val = strtod(num_str, NULL);
366 if (errno != 0 || val < 0) {
369 *value = (utime_t)(val * mult[i]);
374 * Check if specified string is a number or not.
375 * Taken from SQLite, cool, thanks.
377 bool is_a_number(const char *n)
379 bool digit_seen = false;
381 if( *n == '-' || *n == '+' ) {
384 while (B_ISDIGIT(*n)) {
388 if (digit_seen && *n == '.') {
390 while (B_ISDIGIT(*n)) { n++; }
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++; }
397 return digit_seen && *n==0;
401 * Check if the specified string is an integer
403 bool is_an_integer(const char *n)
405 bool digit_seen = false;
406 while (B_ISDIGIT(*n)) {
410 return digit_seen && *n==0;
414 * Check if Bacula Resoure Name is valid
417 * Check if the Volume name has legal characters
418 * If ua is non-NULL send the message
420 bool is_name_valid(char *name, POOLMEM **msg)
424 /* Special characters to accept */
425 const char *accept = ":.-_ ";
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))) {
433 Mmsg(msg, _("Illegal character \"%c\" in name.\n"), *p);
438 if (len >= MAX_NAME_LENGTH) {
440 Mmsg(msg, _("Name too long.\n"));
446 Mmsg(msg, _("Volume name must be at least one character long.\n"));
456 * Add commas to a string, which is presumably
459 char *add_commas(char *val, char *buf)
477 for (i=0; i < 3; i++) {
486 void d_msg(const char*, int, int, const char*, ...)
488 int main(int argc, char *argv[])
490 char *str[] = {"3", "3n", "3 hours", "3.5 day", "3 week", "3 m", "3 q", "3 years"};
495 for (int i=0; i<8; i++) {
497 if (!duration_to_utime(buf, &val)) {
498 printf("Error return from duration_to_utime for in=%s\n", str[i]);
501 edit_utime(val, outval);
502 printf("in=%s val=%" lld " outval=%s\n", str[i], val, outval);