2 * edit.c edit string to ascii, and ascii to internal
4 * Kern Sibbald, December MMII
9 Bacula® - The Network Backup Solution
11 Copyright (C) 2002-2006 Free Software Foundation Europe e.V.
13 The main author of Bacula is Kern Sibbald, with contributions from
14 many others, a complete list can be found in the file AUTHORS.
15 This program is Free Software; you can redistribute it and/or
16 modify it under the terms of version two of the GNU General Public
17 License as published by the Free Software Foundation plus additions
18 that are listed in the file LICENSE.
20 This program is distributed in the hope that it will be useful, but
21 WITHOUT ANY WARRANTY; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 General Public License for more details.
25 You should have received a copy of the GNU General Public License
26 along with this program; if not, write to the Free Software
27 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
30 Bacula® is a registered trademark of John Walker.
31 The licensor of Bacula is the Free Software Foundation Europe
32 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
33 Switzerland, email:ftf@fsfeurope.org.
39 /* We assume ASCII input and don't worry about overflow */
40 uint64_t str_to_uint64(char *str)
42 register char *p = str;
43 register uint64_t value = 0;
48 while (B_ISSPACE(*p)) {
54 while (B_ISDIGIT(*p)) {
55 value = B_TIMES10(value) + *p - '0';
61 int64_t str_to_int64(char *str)
63 register char *p = str;
64 register int64_t value;
65 bool negative = false;
70 while (B_ISSPACE(*p)) {
75 } else if (*p == '-') {
79 value = str_to_uint64(p);
88 * Edit an integer number with commas, the supplied buffer
89 * must be at least 27 bytes long. The incoming number
90 * is always widened to 64 bits.
92 char *edit_uint64_with_commas(uint64_t val, char *buf)
95 * Replacement for sprintf(buf, "%" llu, val)
98 mbuf[sizeof(mbuf)-1] = 0;
99 int i = sizeof(mbuf)-2; /* edit backward */
104 mbuf[i--] = "0123456789"[val%10];
108 bstrncpy(buf, &mbuf[i+1], 27);
109 return add_commas(buf, buf);
113 * Edit an integer into "human-readable" format with four or fewer
114 * significant digits followed by a suffix that indicates the scale
115 * factor. The buf array inherits a 27 byte minimim length
116 * requirement from edit_unit64_with_commas(), although the output
117 * string is limited to eight characters.
119 char *edit_uint64_with_suffix(uint64_t val, char *buf)
124 { "", "K", "M", "G", "T", "P", "E", "Z", "Y", "FIX ME" };
125 int suffixes = sizeof(suffix) / sizeof(*suffix);
127 edit_uint64_with_commas(val, mbuf);
129 if ((c = strchr(mbuf, ',')) != NULL) {
132 while ((c = strchr(c, ',')) != NULL) {
136 mbuf[5] = '\0'; // drop this to get '123.456 TB' rather than '123.4 TB'
139 if (commas >= suffixes)
140 commas = suffixes - 1;
141 bsnprintf(buf, 27, "%s %s", mbuf, suffix[commas]);
146 * Edit an integer number, the supplied buffer
147 * must be at least 27 bytes long. The incoming number
148 * is always widened to 64 bits.
150 char *edit_uint64(uint64_t val, char *buf)
153 * Replacement for sprintf(buf, "%" llu, val)
156 mbuf[sizeof(mbuf)-1] = 0;
157 int i = sizeof(mbuf)-2; /* edit backward */
162 mbuf[i--] = "0123456789"[val%10];
166 bstrncpy(buf, &mbuf[i+1], 27);
170 char *edit_int64(int64_t val, char *buf)
173 * Replacement for sprintf(buf, "%" llu, val)
176 bool negative = false;
177 mbuf[sizeof(mbuf)-1] = 0;
178 int i = sizeof(mbuf)-2; /* edit backward */
187 mbuf[i--] = "0123456789"[val%10];
194 bstrncpy(buf, &mbuf[i+1], 27);
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);
340 * Convert a size in bytes to uint64_t
341 * Returns false: if error
342 true: if OK, and value stored in value
344 bool size_to_uint64(char *str, int str_len, uint64_t *value)
350 static const char *mod[] = {"*", "k", "kb", "m", "mb", "g", "gb", NULL}; /* first item * not used */
351 const int64_t mult[] = {1, /* byte */
353 1000, /* kb kilobyte */
354 1048576, /* megabyte */
355 1000000, /* mb megabyte */
356 1073741824, /* gigabyte */
357 1000000000}; /* gb gigabyte */
359 if (!get_modifier(str, num_str, sizeof(num_str), mod_str, sizeof(mod_str))) {
362 /* Now find the multiplier corresponding to the modifier */
363 mod_len = strlen(mod_str);
365 i = 0; /* default with no modifier = 1 */
367 for (i=0; mod[i]; i++) {
368 if (strncasecmp(mod_str, mod[i], mod_len) == 0) {
372 if (mod[i] == NULL) {
376 Dmsg2(900, "str=%s: mult=%d\n", str, mult[i]);
378 val = strtod(num_str, NULL);
379 if (errno != 0 || val < 0) {
382 *value = (utime_t)(val * mult[i]);
387 * Check if specified string is a number or not.
388 * Taken from SQLite, cool, thanks.
390 bool is_a_number(const char *n)
392 bool digit_seen = false;
394 if( *n == '-' || *n == '+' ) {
397 while (B_ISDIGIT(*n)) {
401 if (digit_seen && *n == '.') {
403 while (B_ISDIGIT(*n)) { n++; }
405 if (digit_seen && (*n == 'e' || *n == 'E')
406 && (B_ISDIGIT(n[1]) || ((n[1]=='-' || n[1] == '+') && B_ISDIGIT(n[2])))) {
407 n += 2; /* skip e- or e+ or e digit */
408 while (B_ISDIGIT(*n)) { n++; }
410 return digit_seen && *n==0;
414 * Check if the specified string is an integer
416 bool is_an_integer(const char *n)
418 bool digit_seen = false;
419 while (B_ISDIGIT(*n)) {
423 return digit_seen && *n==0;
427 * Check if Bacula Resoure Name is valid
430 * Check if the Volume name has legal characters
431 * If ua is non-NULL send the message
433 bool is_name_valid(char *name, POOLMEM **msg)
437 /* Special characters to accept */
438 const char *accept = ":.-_ ";
440 /* Restrict the characters permitted in the Volume name */
441 for (p=name; *p; p++) {
442 if (B_ISALPHA(*p) || B_ISDIGIT(*p) || strchr(accept, (int)(*p))) {
446 Mmsg(msg, _("Illegal character \"%c\" in name.\n"), *p);
451 if (len >= MAX_NAME_LENGTH) {
453 Mmsg(msg, _("Name too long.\n"));
459 Mmsg(msg, _("Volume name must be at least one character long.\n"));
469 * Add commas to a string, which is presumably
472 char *add_commas(char *val, char *buf)
490 for (i=0; i < 3; i++) {
499 void d_msg(const char*, int, int, const char*, ...)
501 int main(int argc, char *argv[])
503 char *str[] = {"3", "3n", "3 hours", "3.5 day", "3 week", "3 m", "3 q", "3 years"};
508 for (int i=0; i<8; i++) {
510 if (!duration_to_utime(buf, &val)) {
511 printf("Error return from duration_to_utime for in=%s\n", str[i]);
514 edit_utime(val, outval);
515 printf("in=%s val=%" lld " outval=%s\n", str[i], val, outval);