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 and included
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 Kern Sibbald.
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)
94 edit_uint64(val, buf);
95 return add_commas(buf, buf);
99 * Edit an integer into "human-readable" format with four or fewer
100 * significant digits followed by a suffix that indicates the scale
101 * factor. The buf array inherits a 27 byte minimim length
102 * requirement from edit_unit64_with_commas(), although the output
103 * string is limited to eight characters.
105 char *edit_uint64_with_suffix(uint64_t val, char *buf)
109 const char *suffix[] =
110 { "", "K", "M", "G", "T", "P", "E", "Z", "Y", "FIX ME" };
111 int suffixes = sizeof(suffix) / sizeof(*suffix);
113 edit_uint64_with_commas(val, mbuf);
115 if ((c = strchr(mbuf, ',')) != NULL) {
118 while ((c = strchr(c, ',')) != NULL) {
122 mbuf[5] = '\0'; // drop this to get '123.456 TB' rather than '123.4 TB'
125 if (commas >= suffixes)
126 commas = suffixes - 1;
127 bsnprintf(buf, 27, "%s %s", mbuf, suffix[commas]);
132 * Edit an integer number, the supplied buffer
133 * must be at least 27 bytes long. The incoming number
134 * is always widened to 64 bits.
136 char *edit_uint64(uint64_t val, char *buf)
139 * Replacement for sprintf(buf, "%" llu, val)
142 mbuf[sizeof(mbuf)-1] = 0;
143 int i = sizeof(mbuf)-2; /* edit backward */
148 mbuf[i--] = "0123456789"[val%10];
152 bstrncpy(buf, &mbuf[i+1], 27);
156 char *edit_int64(int64_t val, char *buf)
159 * Replacement for sprintf(buf, "%" llu, val)
162 bool negative = false;
163 mbuf[sizeof(mbuf)-1] = 0;
164 int i = sizeof(mbuf)-2; /* edit backward */
173 mbuf[i--] = "0123456789"[val%10];
180 bstrncpy(buf, &mbuf[i+1], 27);
185 * Edit an integer number with commas, the supplied buffer
186 * must be at least 27 bytes long. The incoming number
187 * is always widened to 64 bits.
189 char *edit_int64_with_commas(int64_t val, char *buf)
191 edit_int64(val, buf);
192 return add_commas(buf, buf);
196 * Given a string "str", separate the numeric part into
197 * str, and the modifier into mod.
199 static bool get_modifier(char *str, char *num, int num_len, char *mod, int mod_len)
201 int i, len, num_begin, num_end, mod_begin, mod_end;
203 strip_trailing_junk(str);
206 for (i=0; i<len; i++) {
207 if (!B_ISSPACE(str[i])) {
213 /* Walk through integer part */
214 for ( ; i<len; i++) {
215 if (!B_ISDIGIT(str[i]) && str[i] != '.') {
220 if (num_len > (num_end - num_begin + 1)) {
221 num_len = num_end - num_begin + 1;
226 /* Eat any spaces in front of modifier */
227 for ( ; i<len; i++) {
228 if (!B_ISSPACE(str[i])) {
233 for ( ; i<len; i++) {
234 if (!B_ISALPHA(str[i])) {
239 if (mod_len > (mod_end - mod_begin + 1)) {
240 mod_len = mod_end - mod_begin + 1;
242 Dmsg5(900, "str=%s: num_beg=%d num_end=%d mod_beg=%d mod_end=%d\n",
243 str, num_begin, num_end, mod_begin, mod_end);
244 bstrncpy(num, &str[num_begin], num_len);
245 bstrncpy(mod, &str[mod_begin], mod_len);
246 if (!is_a_number(num)) {
249 bstrncpy(str, &str[mod_end], len);
250 Dmsg2(900, "num=%s mod=%s\n", num, mod);
256 * Convert a string duration to utime_t (64 bit seconds)
257 * Returns false: if error
258 true: if OK, and value stored in value
260 bool duration_to_utime(char *str, utime_t *value)
263 double val, total = 0.0;
267 * The "n" = mins and months appears before minutes so that m maps
268 * to months. These "kludges" make it compatible with pre 1.31
271 static const char *mod[] = {"n", "seconds", "months", "minutes", "mins",
272 "hours", "days", "weeks", "quarters", "years", NULL};
273 static const int32_t mult[] = {60, 1, 60*60*24*30, 60, 60,
274 3600, 3600*24, 3600*24*7, 3600*24*91, 3600*24*365};
277 if (!get_modifier(str, num_str, sizeof(num_str), mod_str, sizeof(mod_str))) {
280 /* Now find the multiplier corresponding to the modifier */
281 mod_len = strlen(mod_str);
283 i = 1; /* default to seconds */
285 for (i=0; mod[i]; i++) {
286 if (strncasecmp(mod_str, mod[i], mod_len) == 0) {
290 if (mod[i] == NULL) {
294 Dmsg2(900, "str=%s: mult=%d\n", num_str, mult[i]);
296 val = strtod(num_str, NULL);
297 if (errno != 0 || val < 0) {
300 total += val * mult[i];
302 *value = (utime_t)total;
307 * Edit a utime "duration" into ASCII
309 char *edit_utime(utime_t val, char *buf, int buf_len)
312 static const int32_t mult[] = {60*60*24*365, 60*60*24*30, 60*60*24, 60*60, 60};
313 static const char *mod[] = {"year", "month", "day", "hour", "min"};
318 for (i=0; i<5; i++) {
319 times = (uint32_t)(val / mult[i]);
321 val = val - (utime_t)times * mult[i];
322 bsnprintf(mybuf, sizeof(mybuf), "%d %s%s ", times, mod[i], times>1?"s":"");
323 bstrncat(buf, mybuf, buf_len);
326 if (val == 0 && strlen(buf) == 0) {
327 bstrncat(buf, "0 secs", buf_len);
328 } else if (val != 0) {
329 bsnprintf(mybuf, sizeof(mybuf), "%d sec%s", (uint32_t)val, val>1?"s":"");
330 bstrncat(buf, mybuf, buf_len);
336 * Convert a size in bytes to uint64_t
337 * Returns false: if error
338 true: if OK, and value stored in value
340 bool size_to_uint64(char *str, int str_len, uint64_t *value)
346 static const char *mod[] = {"*", "k", "kb", "m", "mb", "g", "gb", NULL}; /* first item * not used */
347 const int64_t mult[] = {1, /* byte */
349 1000, /* kb kilobyte */
350 1048576, /* megabyte */
351 1000000, /* mb megabyte */
352 1073741824, /* gigabyte */
353 1000000000}; /* gb gigabyte */
355 if (!get_modifier(str, num_str, sizeof(num_str), mod_str, sizeof(mod_str))) {
358 /* Now find the multiplier corresponding to the modifier */
359 mod_len = strlen(mod_str);
361 i = 0; /* default with no modifier = 1 */
363 for (i=0; mod[i]; i++) {
364 if (strncasecmp(mod_str, mod[i], mod_len) == 0) {
368 if (mod[i] == NULL) {
372 Dmsg2(900, "str=%s: mult=%d\n", str, mult[i]);
374 val = strtod(num_str, NULL);
375 if (errno != 0 || val < 0) {
378 *value = (utime_t)(val * mult[i]);
383 * Check if specified string is a number or not.
384 * Taken from SQLite, cool, thanks.
386 bool is_a_number(const char *n)
388 bool digit_seen = false;
390 if( *n == '-' || *n == '+' ) {
393 while (B_ISDIGIT(*n)) {
397 if (digit_seen && *n == '.') {
399 while (B_ISDIGIT(*n)) { n++; }
401 if (digit_seen && (*n == 'e' || *n == 'E')
402 && (B_ISDIGIT(n[1]) || ((n[1]=='-' || n[1] == '+') && B_ISDIGIT(n[2])))) {
403 n += 2; /* skip e- or e+ or e digit */
404 while (B_ISDIGIT(*n)) { n++; }
406 return digit_seen && *n==0;
410 * Check if the specified string is an integer
412 bool is_an_integer(const char *n)
414 bool digit_seen = false;
415 while (B_ISDIGIT(*n)) {
419 return digit_seen && *n==0;
423 * Check if Bacula Resoure Name is valid
426 * Check if the Volume name has legal characters
427 * If ua is non-NULL send the message
429 bool is_name_valid(char *name, POOLMEM **msg)
433 /* Special characters to accept */
434 const char *accept = ":.-_ ";
436 /* Restrict the characters permitted in the Volume name */
437 for (p=name; *p; p++) {
438 if (B_ISALPHA(*p) || B_ISDIGIT(*p) || strchr(accept, (int)(*p))) {
442 Mmsg(msg, _("Illegal character \"%c\" in name.\n"), *p);
447 if (len >= MAX_NAME_LENGTH) {
449 Mmsg(msg, _("Name too long.\n"));
455 Mmsg(msg, _("Volume name must be at least one character long.\n"));
465 * Add commas to a string, which is presumably
468 char *add_commas(char *val, char *buf)
486 for (i=0; i < 3; i++) {
495 void d_msg(const char*, int, int, const char*, ...)
497 int main(int argc, char *argv[])
499 char *str[] = {"3", "3n", "3 hours", "3.5 day", "3 week", "3 m", "3 q", "3 years"};
504 for (int i=0; i<8; i++) {
506 if (!duration_to_utime(buf, &val)) {
507 printf("Error return from duration_to_utime for in=%s\n", str[i]);
510 edit_utime(val, outval);
511 printf("in=%s val=%" lld " outval=%s\n", str[i], val, outval);