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 ammended 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 number, the supplied buffer
101 * must be at least 27 bytes long. The incoming number
102 * is always widened to 64 bits.
104 char *edit_uint64(uint64_t val, char *buf)
107 * Replacement for sprintf(buf, "%" llu, val)
110 mbuf[sizeof(mbuf)-1] = 0;
111 int i = sizeof(mbuf)-2; /* edit backward */
116 mbuf[i--] = "0123456789"[val%10];
120 bstrncpy(buf, &mbuf[i+1], 27);
124 char *edit_int64(int64_t val, char *buf)
127 * Replacement for sprintf(buf, "%" llu, val)
130 bool negative = false;
131 mbuf[sizeof(mbuf)-1] = 0;
132 int i = sizeof(mbuf)-2; /* edit backward */
141 mbuf[i--] = "0123456789"[val%10];
148 bstrncpy(buf, &mbuf[i+1], 27);
154 * Given a string "str", separate the numeric part into
155 * str, and the modifier into mod.
157 static bool get_modifier(char *str, char *num, int num_len, char *mod, int mod_len)
159 int i, len, num_begin, num_end, mod_begin, mod_end;
161 strip_trailing_junk(str);
164 for (i=0; i<len; i++) {
165 if (!B_ISSPACE(str[i])) {
171 /* Walk through integer part */
172 for ( ; i<len; i++) {
173 if (!B_ISDIGIT(str[i]) && str[i] != '.') {
178 if (num_len > (num_end - num_begin + 1)) {
179 num_len = num_end - num_begin + 1;
184 /* Eat any spaces in front of modifier */
185 for ( ; i<len; i++) {
186 if (!B_ISSPACE(str[i])) {
191 for ( ; i<len; i++) {
192 if (!B_ISALPHA(str[i])) {
197 if (mod_len > (mod_end - mod_begin + 1)) {
198 mod_len = mod_end - mod_begin + 1;
200 Dmsg5(900, "str=%s: num_beg=%d num_end=%d mod_beg=%d mod_end=%d\n",
201 str, num_begin, num_end, mod_begin, mod_end);
202 bstrncpy(num, &str[num_begin], num_len);
203 bstrncpy(mod, &str[mod_begin], mod_len);
204 if (!is_a_number(num)) {
207 bstrncpy(str, &str[mod_end], len);
208 Dmsg2(900, "num=%s mod=%s\n", num, mod);
214 * Convert a string duration to utime_t (64 bit seconds)
215 * Returns false: if error
216 true: if OK, and value stored in value
218 bool duration_to_utime(char *str, utime_t *value)
221 double val, total = 0.0;
225 * The "n" = mins and months appears before minutes so that m maps
226 * to months. These "kludges" make it compatible with pre 1.31
229 static const char *mod[] = {"n", "seconds", "months", "minutes",
230 "hours", "days", "weeks", "quarters", "years", NULL};
231 static const int32_t mult[] = {60, 1, 60*60*24*30, 60,
232 60*60, 60*60*24, 60*60*24*7, 60*60*24*91, 60*60*24*365};
235 if (!get_modifier(str, num_str, sizeof(num_str), mod_str, sizeof(mod_str))) {
238 /* Now find the multiplier corresponding to the modifier */
239 mod_len = strlen(mod_str);
241 i = 1; /* default to seconds */
243 for (i=0; mod[i]; i++) {
244 if (strncasecmp(mod_str, mod[i], mod_len) == 0) {
248 if (mod[i] == NULL) {
252 Dmsg2(900, "str=%s: mult=%d\n", num_str, mult[i]);
254 val = strtod(num_str, NULL);
255 if (errno != 0 || val < 0) {
258 total += val * mult[i];
260 *value = (utime_t)total;
265 * Edit a utime "duration" into ASCII
267 char *edit_utime(utime_t val, char *buf, int buf_len)
270 static const int32_t mult[] = {60*60*24*365, 60*60*24*30, 60*60*24, 60*60, 60};
271 static const char *mod[] = {"year", "month", "day", "hour", "min"};
276 for (i=0; i<5; i++) {
277 times = (uint32_t)(val / mult[i]);
279 val = val - (utime_t)times * mult[i];
280 bsnprintf(mybuf, sizeof(mybuf), "%d %s%s ", times, mod[i], times>1?"s":"");
281 bstrncat(buf, mybuf, buf_len);
284 if (val == 0 && strlen(buf) == 0) {
285 bstrncat(buf, "0 secs", buf_len);
286 } else if (val != 0) {
287 bsnprintf(mybuf, sizeof(mybuf), "%d sec%s", (uint32_t)val, val>1?"s":"");
288 bstrncat(buf, mybuf, buf_len);
294 * Convert a size in bytes to uint64_t
295 * Returns false: if error
296 true: if OK, and value stored in value
298 bool size_to_uint64(char *str, int str_len, uint64_t *value)
304 static const char *mod[] = {"*", "k", "kb", "m", "mb", "g", "gb", NULL}; /* first item * not used */
305 const int64_t mult[] = {1, /* byte */
307 1000, /* kb kilobyte */
308 1048576, /* megabyte */
309 1000000, /* mb megabyte */
310 1073741824, /* gigabyte */
311 1000000000}; /* gb gigabyte */
313 if (!get_modifier(str, num_str, sizeof(num_str), mod_str, sizeof(mod_str))) {
316 /* Now find the multiplier corresponding to the modifier */
317 mod_len = strlen(mod_str);
319 i = 0; /* default with no modifier = 1 */
321 for (i=0; mod[i]; i++) {
322 if (strncasecmp(mod_str, mod[i], mod_len) == 0) {
326 if (mod[i] == NULL) {
330 Dmsg2(900, "str=%s: mult=%d\n", str, mult[i]);
332 val = strtod(num_str, NULL);
333 if (errno != 0 || val < 0) {
336 *value = (utime_t)(val * mult[i]);
341 * Check if specified string is a number or not.
342 * Taken from SQLite, cool, thanks.
344 bool is_a_number(const char *n)
346 bool digit_seen = false;
348 if( *n == '-' || *n == '+' ) {
351 while (B_ISDIGIT(*n)) {
355 if (digit_seen && *n == '.') {
357 while (B_ISDIGIT(*n)) { n++; }
359 if (digit_seen && (*n == 'e' || *n == 'E')
360 && (B_ISDIGIT(n[1]) || ((n[1]=='-' || n[1] == '+') && B_ISDIGIT(n[2])))) {
361 n += 2; /* skip e- or e+ or e digit */
362 while (B_ISDIGIT(*n)) { n++; }
364 return digit_seen && *n==0;
368 * Check if the specified string is an integer
370 bool is_an_integer(const char *n)
372 bool digit_seen = false;
373 while (B_ISDIGIT(*n)) {
377 return digit_seen && *n==0;
381 * Check if Bacula Resoure Name is valid
384 * Check if the Volume name has legal characters
385 * If ua is non-NULL send the message
387 bool is_name_valid(char *name, POOLMEM **msg)
391 /* Special characters to accept */
392 const char *accept = ":.-_ ";
394 /* Restrict the characters permitted in the Volume name */
395 for (p=name; *p; p++) {
396 if (B_ISALPHA(*p) || B_ISDIGIT(*p) || strchr(accept, (int)(*p))) {
400 Mmsg(msg, _("Illegal character \"%c\" in name.\n"), *p);
405 if (len >= MAX_NAME_LENGTH) {
407 Mmsg(msg, _("Name too long.\n"));
413 Mmsg(msg, _("Volume name must be at least one character long.\n"));
423 * Add commas to a string, which is presumably
426 char *add_commas(char *val, char *buf)
444 for (i=0; i < 3; i++) {
453 void d_msg(const char*, int, int, const char*, ...)
455 int main(int argc, char *argv[])
457 char *str[] = {"3", "3n", "3 hours", "3.5 day", "3 week", "3 m", "3 q", "3 years"};
462 for (int i=0; i<8; i++) {
464 if (!duration_to_utime(buf, &val)) {
465 printf("Error return from duration_to_utime for in=%s\n", str[i]);
468 edit_utime(val, outval);
469 printf("in=%s val=%" lld " outval=%s\n", str[i], val, outval);