]> git.ipfire.org Git - thirdparty/util-linux.git/blob - lib/strutils.c
Merge branch 'ci/meson-werror' of https://github.com/t-8ch/util-linux
[thirdparty/util-linux.git] / lib / strutils.c
1 /*
2 * Copyright (C) 2010 Karel Zak <kzak@redhat.com>
3 * Copyright (C) 2010 Davidlohr Bueso <dave@gnu.org>
4 *
5 * No copyright is claimed. This code is in the public domain; do with
6 * it what you wish.
7 */
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <inttypes.h>
11 #include <ctype.h>
12 #include <errno.h>
13 #include <sys/stat.h>
14 #include <string.h>
15 #include <strings.h>
16 #include <assert.h>
17
18 #include "c.h"
19 #include "nls.h"
20 #include "strutils.h"
21 #include "bitops.h"
22 #include "pathnames.h"
23
24 static int STRTOXX_EXIT_CODE = EXIT_FAILURE;
25
26 void strutils_set_exitcode(int ex) {
27 STRTOXX_EXIT_CODE = ex;
28 }
29
30 static int do_scale_by_power (uintmax_t *x, int base, int power)
31 {
32 while (power--) {
33 if (UINTMAX_MAX / base < *x)
34 return -ERANGE;
35 *x *= base;
36 }
37 return 0;
38 }
39
40 /*
41 * strtosize() - convert string to size (uintmax_t).
42 *
43 * Supported suffixes:
44 *
45 * XiB or X for 2^N
46 * where X = {K,M,G,T,P,E,Z,Y}
47 * or X = {k,m,g,t,p,e} (undocumented for backward compatibility only)
48 * for example:
49 * 10KiB = 10240
50 * 10K = 10240
51 *
52 * XB for 10^N
53 * where X = {K,M,G,T,P,E,Z,Y}
54 * for example:
55 * 10KB = 10000
56 *
57 * The optional 'power' variable returns number associated with used suffix
58 * {K,M,G,T,P,E,Z,Y} = {1,2,3,4,5,6,7,8}.
59 *
60 * The function also supports decimal point, for example:
61 * 0.5MB = 500000
62 * 0.5MiB = 512000
63 *
64 * Note that the function does not accept numbers with '-' (negative sign)
65 * prefix.
66 */
67 int parse_size(const char *str, uintmax_t *res, int *power)
68 {
69 const char *p;
70 char *end;
71 uintmax_t x, frac = 0;
72 int base = 1024, rc = 0, pwr = 0, frac_zeros = 0;
73
74 static const char *suf = "KMGTPEZY";
75 static const char *suf2 = "kmgtpezy";
76 const char *sp;
77
78 *res = 0;
79
80 if (!str || !*str) {
81 rc = -EINVAL;
82 goto err;
83 }
84
85 /* Only positive numbers are acceptable
86 *
87 * Note that this check is not perfect, it would be better to
88 * use lconv->negative_sign. But coreutils use the same solution,
89 * so it's probably good enough...
90 */
91 p = str;
92 while (isspace((unsigned char) *p))
93 p++;
94 if (*p == '-') {
95 rc = -EINVAL;
96 goto err;
97 }
98
99 errno = 0, end = NULL;
100 x = strtoumax(str, &end, 0);
101
102 if (end == str ||
103 (errno != 0 && (x == UINTMAX_MAX || x == 0))) {
104 rc = errno ? -errno : -EINVAL;
105 goto err;
106 }
107 if (!end || !*end)
108 goto done; /* without suffix */
109 p = end;
110
111 /*
112 * Check size suffixes
113 */
114 check_suffix:
115 if (*(p + 1) == 'i' && (*(p + 2) == 'B' || *(p + 2) == 'b') && !*(p + 3))
116 base = 1024; /* XiB, 2^N */
117 else if ((*(p + 1) == 'B' || *(p + 1) == 'b') && !*(p + 2))
118 base = 1000; /* XB, 10^N */
119 else if (*(p + 1)) {
120 struct lconv const *l = localeconv();
121 const char *dp = l ? l->decimal_point : NULL;
122 size_t dpsz = dp ? strlen(dp) : 0;
123
124 if (frac == 0 && *p && dp && strncmp(dp, p, dpsz) == 0) {
125 const char *fstr = p + dpsz;
126
127 for (p = fstr; *p == '0'; p++)
128 frac_zeros++;
129 fstr = p;
130 if (isdigit(*fstr)) {
131 errno = 0, end = NULL;
132 frac = strtoumax(fstr, &end, 0);
133 if (end == fstr ||
134 (errno != 0 && (frac == UINTMAX_MAX || frac == 0))) {
135 rc = errno ? -errno : -EINVAL;
136 goto err;
137 }
138 } else
139 end = (char *) p;
140
141 if (frac && (!end || !*end)) {
142 rc = -EINVAL;
143 goto err; /* without suffix, but with frac */
144 }
145 p = end;
146 goto check_suffix;
147 }
148 rc = -EINVAL;
149 goto err; /* unexpected suffix */
150 }
151
152 sp = strchr(suf, *p);
153 if (sp)
154 pwr = (sp - suf) + 1;
155 else {
156 sp = strchr(suf2, *p);
157 if (sp)
158 pwr = (sp - suf2) + 1;
159 else {
160 rc = -EINVAL;
161 goto err;
162 }
163 }
164
165 rc = do_scale_by_power(&x, base, pwr);
166 if (power)
167 *power = pwr;
168 if (frac && pwr) {
169 int i;
170 uintmax_t frac_div = 10, frac_poz = 1, frac_base = 1;
171
172 /* mega, giga, ... */
173 do_scale_by_power(&frac_base, base, pwr);
174
175 /* maximal divisor for last digit (e.g. for 0.05 is
176 * frac_div=100, for 0.054 is frac_div=1000, etc.)
177 *
178 * Reduce frac if too large.
179 */
180 while (frac_div < frac) {
181 if (frac_div <= UINTMAX_MAX/10)
182 frac_div *= 10;
183 else
184 frac /= 10;
185 }
186
187 /* 'frac' is without zeros (5 means 0.5 as well as 0.05) */
188 for (i = 0; i < frac_zeros; i++) {
189 if (frac_div <= UINTMAX_MAX/10)
190 frac_div *= 10;
191 else
192 frac /= 10;
193 }
194
195 /*
196 * Go backwardly from last digit and add to result what the
197 * digit represents in the frac_base. For example 0.25G
198 *
199 * 5 means 1GiB / (100/5)
200 * 2 means 1GiB / (10/2)
201 */
202 do {
203 unsigned int seg = frac % 10; /* last digit of the frac */
204 uintmax_t seg_div = frac_div / frac_poz; /* what represents the segment 1000, 100, .. */
205
206 frac /= 10; /* remove last digit from frac */
207 frac_poz *= 10;
208
209 if (seg && seg_div / seg)
210 x += frac_base / (seg_div / seg);
211 } while (frac);
212 }
213 done:
214 *res = x;
215 err:
216 if (rc < 0)
217 errno = -rc;
218 return rc;
219 }
220
221 int strtosize(const char *str, uintmax_t *res)
222 {
223 return parse_size(str, res, NULL);
224 }
225
226 int isdigit_strend(const char *str, const char **end)
227 {
228 const char *p;
229
230 for (p = str; p && *p && isdigit((unsigned char) *p); p++);
231
232 if (end)
233 *end = p;
234 return p && p > str && !*p;
235 }
236
237 int isxdigit_strend(const char *str, const char **end)
238 {
239 const char *p;
240
241 for (p = str; p && *p && isxdigit((unsigned char) *p); p++);
242
243 if (end)
244 *end = p;
245
246 return p && p > str && !*p;
247 }
248
249 /*
250 * parse_switch(argv[i], "on", "off", "yes", "no", NULL);
251 */
252 int parse_switch(const char *arg, const char *errmesg, ...)
253 {
254 const char *a, *b;
255 va_list ap;
256
257 va_start(ap, errmesg);
258 do {
259 a = va_arg(ap, char *);
260 if (!a)
261 break;
262 b = va_arg(ap, char *);
263 if (!b)
264 break;
265
266 if (strcmp(arg, a) == 0) {
267 va_end(ap);
268 return 1;
269 }
270
271 if (strcmp(arg, b) == 0) {
272 va_end(ap);
273 return 0;
274 }
275 } while (1);
276 va_end(ap);
277
278 errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, arg);
279 }
280
281 #ifndef HAVE_MEMPCPY
282 void *mempcpy(void *restrict dest, const void *restrict src, size_t n)
283 {
284 return ((char *)memcpy(dest, src, n)) + n;
285 }
286 #endif
287
288 #ifndef HAVE_STRNLEN
289 size_t strnlen(const char *s, size_t maxlen)
290 {
291 size_t i;
292
293 for (i = 0; i < maxlen; i++) {
294 if (s[i] == '\0')
295 return i;
296 }
297 return maxlen;
298 }
299 #endif
300
301 #ifndef HAVE_STRNCHR
302 char *strnchr(const char *s, size_t maxlen, int c)
303 {
304 for (; maxlen-- && *s != '\0'; ++s)
305 if (*s == (char)c)
306 return (char *)s;
307 return NULL;
308 }
309 #endif
310
311 #ifndef HAVE_STRNDUP
312 char *strndup(const char *s, size_t n)
313 {
314 size_t len = strnlen(s, n);
315 char *new = malloc((len + 1) * sizeof(char));
316 if (!new)
317 return NULL;
318 new[len] = '\0';
319 return (char *) memcpy(new, s, len);
320 }
321 #endif
322
323 /*
324 * convert strings to numbers; returns <0 on error, and 0 on success
325 */
326 int ul_strtos64(const char *str, int64_t *num, int base)
327 {
328 char *end = NULL;
329
330 if (str == NULL || *str == '\0')
331 return -(errno = EINVAL);
332
333 errno = 0;
334 *num = (int64_t) strtoimax(str, &end, base);
335
336 if (errno != 0)
337 return -errno;
338 if (str == end || (end && *end))
339 return -(errno = EINVAL);
340 return 0;
341 }
342
343 int ul_strtou64(const char *str, uint64_t *num, int base)
344 {
345 char *end = NULL;
346 int64_t tmp;
347
348 if (str == NULL || *str == '\0')
349 return -(errno = EINVAL);
350
351 /* we need to ignore negative numbers, note that for invalid negative
352 * number strtoimax() returns negative number too, so we do not
353 * need to check errno here */
354 errno = 0;
355 tmp = (int64_t) strtoimax(str, &end, base);
356 if (tmp < 0)
357 errno = ERANGE;
358 else {
359 errno = 0;
360 *num = strtoumax(str, &end, base);
361 }
362
363 if (errno != 0)
364 return -errno;
365 if (str == end || (end && *end))
366 return -(errno = EINVAL);
367 return 0;
368 }
369
370 int ul_strtos32(const char *str, int32_t *num, int base)
371 {
372 int64_t tmp;
373 int rc;
374
375 rc = ul_strtos64(str, &tmp, base);
376 if (rc == 0 && (tmp < INT32_MIN || tmp > INT32_MAX))
377 rc = -(errno = ERANGE);
378 if (rc == 0)
379 *num = (int32_t) tmp;
380 return rc;
381 }
382
383 int ul_strtou32(const char *str, uint32_t *num, int base)
384 {
385 uint64_t tmp;
386 int rc;
387
388 rc = ul_strtou64(str, &tmp, base);
389 if (rc == 0 && tmp > UINT32_MAX)
390 rc = -(errno = ERANGE);
391 if (rc == 0)
392 *num = (uint32_t) tmp;
393 return rc;
394 }
395
396 /*
397 * Convert strings to numbers in defined range and print message on error.
398 *
399 * These functions are used when we read input from users (getopt() etc.). It's
400 * better to consolidate the code and keep it all based on 64-bit numbers than
401 * implement it for 32 and 16-bit numbers too.
402 */
403 int64_t str2num_or_err(const char *str, int base, const char *errmesg,
404 int64_t low, int64_t up)
405 {
406 int64_t num = 0;
407 int rc;
408
409 rc = ul_strtos64(str, &num, base);
410 if (rc == 0 && ((low && num < low) || (up && num > up)))
411 rc = -(errno = ERANGE);
412
413 if (rc) {
414 if (errno == ERANGE)
415 err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
416 errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
417 }
418 return num;
419 }
420
421 uint64_t str2unum_or_err(const char *str, int base, const char *errmesg, uint64_t up)
422 {
423 uint64_t num = 0;
424 int rc;
425
426 rc = ul_strtou64(str, &num, base);
427 if (rc == 0 && (up && num > up))
428 rc = -(errno = ERANGE);
429
430 if (rc) {
431 if (errno == ERANGE)
432 err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
433 errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
434 }
435 return num;
436 }
437
438 double strtod_or_err(const char *str, const char *errmesg)
439 {
440 double num;
441 char *end = NULL;
442
443 errno = 0;
444 if (str == NULL || *str == '\0')
445 goto err;
446 num = strtod(str, &end);
447
448 if (errno || str == end || (end && *end))
449 goto err;
450
451 return num;
452 err:
453 if (errno == ERANGE)
454 err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
455
456 errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
457 }
458
459 int ul_strtold(const char *str, long double *num)
460 {
461 char *end = NULL;
462
463 errno = 0;
464 if (str == NULL || *str == '\0')
465 return -(errno = EINVAL);
466 *num = strtold(str, &end);
467
468 if (errno != 0)
469 return -errno;
470 if (str == end || (end && *end))
471 return -(errno = EINVAL);
472 return 0;
473 }
474
475 long double strtold_or_err(const char *str, const char *errmesg)
476 {
477 long double num = 0;
478
479 if (ul_strtold(str, &num) == 0)
480 return num;
481 if (errno == ERANGE)
482 err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
483
484 errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
485 }
486
487 uintmax_t strtosize_or_err(const char *str, const char *errmesg)
488 {
489 uintmax_t num;
490
491 if (strtosize(str, &num) == 0)
492 return num;
493
494 if (errno)
495 err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
496
497 errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
498 }
499
500
501 void strtotimeval_or_err(const char *str, struct timeval *tv, const char *errmesg)
502 {
503 long double user_input;
504
505 user_input = strtold_or_err(str, errmesg);
506 tv->tv_sec = (time_t) user_input;
507 tv->tv_usec = (suseconds_t)((user_input - tv->tv_sec) * 1000000);
508 }
509
510 void strtotimespec_or_err(const char *str, struct timespec *ts, const char *errmesg)
511 {
512 long double user_input;
513
514 user_input = strtold_or_err(str, errmesg);
515 ts->tv_sec = (time_t) user_input;
516 ts->tv_nsec = (long)((user_input - ts->tv_sec) * 1000000000);
517 }
518
519 time_t strtotime_or_err(const char *str, const char *errmesg)
520 {
521 int64_t user_input;
522
523 user_input = strtos64_or_err(str, errmesg);
524 return (time_t) user_input;
525 }
526
527 /*
528 * Converts stat->st_mode to ls(1)-like mode string. The size of "str" must
529 * be 11 bytes.
530 */
531 char *xstrmode(mode_t mode, char *str)
532 {
533 unsigned short i = 0;
534
535 if (S_ISDIR(mode))
536 str[i++] = 'd';
537 else if (S_ISLNK(mode))
538 str[i++] = 'l';
539 else if (S_ISCHR(mode))
540 str[i++] = 'c';
541 else if (S_ISBLK(mode))
542 str[i++] = 'b';
543 else if (S_ISSOCK(mode))
544 str[i++] = 's';
545 else if (S_ISFIFO(mode))
546 str[i++] = 'p';
547 else if (S_ISREG(mode))
548 str[i++] = '-';
549
550 str[i++] = mode & S_IRUSR ? 'r' : '-';
551 str[i++] = mode & S_IWUSR ? 'w' : '-';
552 str[i++] = (mode & S_ISUID
553 ? (mode & S_IXUSR ? 's' : 'S')
554 : (mode & S_IXUSR ? 'x' : '-'));
555 str[i++] = mode & S_IRGRP ? 'r' : '-';
556 str[i++] = mode & S_IWGRP ? 'w' : '-';
557 str[i++] = (mode & S_ISGID
558 ? (mode & S_IXGRP ? 's' : 'S')
559 : (mode & S_IXGRP ? 'x' : '-'));
560 str[i++] = mode & S_IROTH ? 'r' : '-';
561 str[i++] = mode & S_IWOTH ? 'w' : '-';
562 str[i++] = (mode & S_ISVTX
563 ? (mode & S_IXOTH ? 't' : 'T')
564 : (mode & S_IXOTH ? 'x' : '-'));
565 str[i] = '\0';
566
567 return str;
568 }
569
570 /*
571 * returns exponent (2^x=n) in range KiB..EiB (2^10..2^60)
572 */
573 static int get_exp(uint64_t n)
574 {
575 int shft;
576
577 for (shft = 10; shft <= 60; shft += 10) {
578 if (n < (1ULL << shft))
579 break;
580 }
581 return shft - 10;
582 }
583
584 char *size_to_human_string(int options, uint64_t bytes)
585 {
586 char buf[32];
587 int dec, exp;
588 uint64_t frac;
589 const char *letters = "BKMGTPE";
590 char suffix[sizeof(" KiB")], *psuf = suffix;
591 char c;
592
593 if (options & SIZE_SUFFIX_SPACE)
594 *psuf++ = ' ';
595
596
597 exp = get_exp(bytes);
598 c = *(letters + (exp ? exp / 10 : 0));
599 dec = exp ? bytes / (1ULL << exp) : bytes;
600 frac = exp ? bytes % (1ULL << exp) : 0;
601
602 *psuf++ = c;
603
604 if ((options & SIZE_SUFFIX_3LETTER) && (c != 'B')) {
605 *psuf++ = 'i';
606 *psuf++ = 'B';
607 }
608
609 *psuf = '\0';
610
611 /* fprintf(stderr, "exp: %d, unit: %c, dec: %d, frac: %jd\n",
612 * exp, suffix[0], dec, frac);
613 */
614
615 /* round */
616 if (frac) {
617 /* get 3 digits after decimal point */
618 if (frac >= UINT64_MAX / 1000)
619 frac = ((frac / 1024) * 1000) / (1ULL << (exp - 10)) ;
620 else
621 frac = (frac * 1000) / (1ULL << (exp)) ;
622
623 if (options & SIZE_DECIMAL_2DIGITS) {
624 /* round 4/5 and keep 2 digits after decimal point */
625 frac = (frac + 5) / 10 ;
626 } else {
627 /* round 4/5 and keep 1 digit after decimal point */
628 frac = ((frac + 50) / 100) * 10 ;
629 }
630
631 /* rounding could have overflowed */
632 if (frac == 100) {
633 dec++;
634 frac = 0;
635 }
636 }
637
638 if (frac) {
639 struct lconv const *l = localeconv();
640 char *dp = l ? l->decimal_point : NULL;
641 int len;
642
643 if (!dp || !*dp)
644 dp = ".";
645
646 len = snprintf(buf, sizeof(buf), "%d%s%02" PRIu64, dec, dp, frac);
647 if (len > 0 && (size_t) len < sizeof(buf)) {
648 /* remove potential extraneous zero */
649 if (buf[len - 1] == '0')
650 buf[len--] = '\0';
651 /* append suffix */
652 xstrncpy(buf+len, suffix, sizeof(buf) - len);
653 } else
654 *buf = '\0'; /* snprintf error */
655 } else
656 snprintf(buf, sizeof(buf), "%d%s", dec, suffix);
657
658 return strdup(buf);
659 }
660
661 /*
662 * Parses comma delimited list to array with IDs, for example:
663 *
664 * "aaa,bbb,ccc" --> ary[0] = FOO_AAA;
665 * ary[1] = FOO_BBB;
666 * ary[3] = FOO_CCC;
667 *
668 * The function name2id() provides conversion from string to ID.
669 *
670 * Returns: >= 0 : number of items added to ary[]
671 * -1 : parse error or unknown item
672 * -2 : arysz reached
673 */
674 int string_to_idarray(const char *list, int ary[], size_t arysz,
675 int (name2id)(const char *, size_t))
676 {
677 const char *begin = NULL, *p;
678 size_t n = 0;
679
680 if (!list || !*list || !ary || !arysz || !name2id)
681 return -1;
682
683 for (p = list; p && *p; p++) {
684 const char *end = NULL;
685 int id;
686
687 if (n >= arysz)
688 return -2;
689 if (!begin)
690 begin = p; /* begin of the column name */
691 if (*p == ',')
692 end = p; /* terminate the name */
693 if (*(p + 1) == '\0')
694 end = p + 1; /* end of string */
695 if (!begin || !end)
696 continue;
697 if (end <= begin)
698 return -1;
699
700 id = name2id(begin, end - begin);
701 if (id == -1)
702 return -1;
703 ary[ n++ ] = id;
704 begin = NULL;
705 if (end && !*end)
706 break;
707 }
708 return n;
709 }
710
711 /*
712 * Parses the array like string_to_idarray but if format is "+aaa,bbb"
713 * it adds fields to array instead of replacing them.
714 */
715 int string_add_to_idarray(const char *list, int ary[], size_t arysz,
716 size_t *ary_pos, int (name2id)(const char *, size_t))
717 {
718 const char *list_add;
719 int r;
720
721 if (!list || !*list || !ary_pos || *ary_pos > arysz)
722 return -1;
723
724 if (list[0] == '+')
725 list_add = &list[1];
726 else {
727 list_add = list;
728 *ary_pos = 0;
729 }
730
731 r = string_to_idarray(list_add, &ary[*ary_pos], arysz - *ary_pos, name2id);
732 if (r > 0)
733 *ary_pos += r;
734 return r;
735 }
736
737 /*
738 * LIST ::= <item> [, <item>]
739 *
740 * The <item> is translated to 'id' by name2id() function and the 'id' is used
741 * as a position in the 'ary' bit array. It means that the 'id' has to be in
742 * range <0..N> where N < sizeof(ary) * NBBY.
743 *
744 * If allow_range is enabled:
745 * An item ending in '+' also sets all bits in <0..N>.
746 * An item beginning with '+' also sets all bits in <N..allow_minus>.
747 *
748 * Returns: 0 on success, <0 on error.
749 */
750 int string_to_bitarray(const char *list,
751 char *ary,
752 int (*name2bit)(const char *, size_t),
753 size_t allow_range)
754 {
755 const char *begin = NULL, *p;
756
757 if (!list || !name2bit || !ary)
758 return -EINVAL;
759
760 for (p = list; p && *p; p++) {
761 const char *end = NULL;
762 int bit, set_lower = 0, set_higher = 0;
763
764 if (!begin)
765 begin = p; /* begin of the level name */
766 if (*p == ',')
767 end = p; /* terminate the name */
768 if (*(p + 1) == '\0')
769 end = p + 1; /* end of string */
770 if (!begin || !end)
771 continue;
772 if (end <= begin)
773 return -1;
774 if (allow_range) {
775 if (*(end - 1) == '+') {
776 end--;
777 set_lower = 1;
778 } else if (*begin == '+') {
779 begin++;
780 set_higher = 1;
781 }
782 }
783
784 bit = name2bit(begin, end - begin);
785 if (bit < 0)
786 return bit;
787 setbit(ary, bit);
788 if (set_lower)
789 while (--bit >= 0)
790 setbit(ary, bit);
791 else if (set_higher)
792 while (++bit < (int) allow_range)
793 setbit(ary, bit);
794 begin = NULL;
795 if (end && !*end)
796 break;
797 }
798 return 0;
799 }
800
801 /*
802 * LIST ::= <item> [, <item>]
803 *
804 * The <item> is translated to 'id' by name2flag() function and the flags is
805 * set to the 'mask'
806 *
807 * Returns: 0 on success, <0 on error.
808 */
809 int string_to_bitmask(const char *list,
810 unsigned long *mask,
811 long (*name2flag)(const char *, size_t))
812 {
813 const char *begin = NULL, *p;
814
815 if (!list || !name2flag || !mask)
816 return -EINVAL;
817
818 for (p = list; p && *p; p++) {
819 const char *end = NULL;
820 long flag;
821
822 if (!begin)
823 begin = p; /* begin of the level name */
824 if (*p == ',')
825 end = p; /* terminate the name */
826 if (*(p + 1) == '\0')
827 end = p + 1; /* end of string */
828 if (!begin || !end)
829 continue;
830 if (end <= begin)
831 return -1;
832
833 flag = name2flag(begin, end - begin);
834 if (flag < 0)
835 return flag; /* error */
836 *mask |= flag;
837 begin = NULL;
838 if (end && !*end)
839 break;
840 }
841 return 0;
842 }
843
844 /*
845 * Parse the lower and higher values in a string containing
846 * "lower:higher" or "lower-higher" format. Note that either
847 * the lower or the higher values may be missing, and the def
848 * value will be assigned to it by default.
849 *
850 * Returns: 0 on success, <0 on error.
851 */
852 int parse_range(const char *str, int *lower, int *upper, int def)
853 {
854 char *end = NULL;
855
856 if (!str)
857 return 0;
858
859 *upper = *lower = def;
860 errno = 0;
861
862 if (*str == ':') { /* <:N> */
863 str++;
864 *upper = strtol(str, &end, 10);
865 if (errno || !end || *end || end == str)
866 return -1;
867 } else {
868 *upper = *lower = strtol(str, &end, 10);
869 if (errno || !end || end == str)
870 return -1;
871
872 if (*end == ':' && !*(end + 1)) /* <M:> */
873 *upper = def;
874 else if (*end == '-' || *end == ':') { /* <M:N> <M-N> */
875 str = end + 1;
876 end = NULL;
877 errno = 0;
878 *upper = strtol(str, &end, 10);
879
880 if (errno || !end || *end || end == str)
881 return -1;
882 }
883 }
884 return 0;
885 }
886
887 static const char *next_path_segment(const char *str, size_t *sz)
888 {
889 const char *start, *p;
890
891 start = str;
892 *sz = 0;
893 while (start && *start == '/' && *(start + 1) == '/')
894 start++;
895
896 if (!start || !*start)
897 return NULL;
898
899 for (*sz = 1, p = start + 1; *p && *p != '/'; p++) {
900 (*sz)++;
901 }
902
903 return start;
904 }
905
906 int streq_paths(const char *a, const char *b)
907 {
908 while (a && b) {
909 size_t a_sz, b_sz;
910 const char *a_seg = next_path_segment(a, &a_sz);
911 const char *b_seg = next_path_segment(b, &b_sz);
912
913 /*
914 fprintf(stderr, "A>>>(%zu) '%s'\n", a_sz, a_seg);
915 fprintf(stderr, "B>>>(%zu) '%s'\n", b_sz, b_seg);
916 */
917
918 /* end of the path */
919 if (a_sz + b_sz == 0)
920 return 1;
921
922 /* ignore tailing slash */
923 if (a_sz + b_sz == 1 &&
924 ((a_seg && *a_seg == '/') || (b_seg && *b_seg == '/')))
925 return 1;
926
927 if (!a_seg || !b_seg)
928 break;
929 if (a_sz != b_sz || strncmp(a_seg, b_seg, a_sz) != 0)
930 break;
931
932 a = a_seg + a_sz;
933 b = b_seg + b_sz;
934 };
935
936 return 0;
937 }
938
939 /* concatenate two strings to a new string, the size of the second string is limited by @b */
940 char *strnconcat(const char *s, const char *suffix, size_t b)
941 {
942 size_t a;
943 char *r;
944
945 if (!s && !suffix)
946 return strdup("");
947 if (!s)
948 return strndup(suffix, b);
949 if (!suffix)
950 return strdup(s);
951
952 assert(s);
953 assert(suffix);
954
955 a = strlen(s);
956 if (b > ((size_t) -1) - a)
957 return NULL;
958
959 r = malloc(a + b + 1);
960 if (!r)
961 return NULL;
962
963 memcpy(r, s, a);
964 memcpy(r + a, suffix, b);
965 r[a+b] = 0;
966
967 return r;
968 }
969
970 /* concatenate two strings to a new string */
971 char *strconcat(const char *s, const char *suffix)
972 {
973 return strnconcat(s, suffix, suffix ? strlen(suffix) : 0);
974 }
975
976 /* concatenate @s and string defined by @format to a new string */
977 char *strfconcat(const char *s, const char *format, ...)
978 {
979 va_list ap;
980 char *val, *res;
981 int sz;
982
983 va_start(ap, format);
984 sz = vasprintf(&val, format, ap);
985 va_end(ap);
986
987 if (sz < 0)
988 return NULL;
989
990 res = strnconcat(s, val, sz);
991 free(val);
992 return res;
993 }
994
995 int strappend(char **a, const char *b)
996 {
997 size_t al, bl;
998 char *tmp;
999
1000 if (!a)
1001 return -EINVAL;
1002 if (!b || !*b)
1003 return 0;
1004 if (!*a) {
1005 *a = strdup(b);
1006 return !*a ? -ENOMEM : 0;
1007 }
1008
1009 al = strlen(*a);
1010 bl = strlen(b);
1011
1012 tmp = realloc(*a, al + bl + 1);
1013 if (!tmp)
1014 return -ENOMEM;
1015 *a = tmp;
1016 memcpy((*a) + al, b, bl + 1);
1017 return 0;
1018 }
1019
1020 /* the hybrid version of strfconcat and strappend. */
1021 int strfappend(char **a, const char *format, ...)
1022 {
1023 va_list ap;
1024 int res;
1025
1026 va_start(ap, format);
1027 res = strvfappend(a, format, ap);
1028 va_end(ap);
1029
1030 return res;
1031 }
1032
1033 extern int strvfappend(char **a, const char *format, va_list ap)
1034 {
1035 char *val;
1036 int sz;
1037 int res;
1038
1039 sz = vasprintf(&val, format, ap);
1040 if (sz < 0)
1041 return -errno;
1042
1043 res = strappend(a, val);
1044 free(val);
1045 return res;
1046 }
1047
1048 static size_t strcspn_escaped(const char *s, const char *reject)
1049 {
1050 int escaped = 0;
1051 int n;
1052
1053 for (n=0; s[n]; n++) {
1054 if (escaped)
1055 escaped = 0;
1056 else if (s[n] == '\\')
1057 escaped = 1;
1058 else if (strchr(reject, s[n]))
1059 break;
1060 }
1061
1062 /* if s ends in \, return index of previous char */
1063 return n - escaped;
1064 }
1065
1066 /*
1067 * Like strchr() but ignores @c if escaped by '\', '\\' is interpreted like '\'.
1068 *
1069 * For example for @c='X':
1070 *
1071 * "abcdXefgXh" --> "XefgXh"
1072 * "abcd\XefgXh" --> "Xh"
1073 * "abcd\\XefgXh" --> "XefgXh"
1074 * "abcd\\\XefgXh" --> "Xh"
1075 * "abcd\Xefg\Xh" --> (null)
1076 *
1077 * "abcd\\XefgXh" --> "\XefgXh" for @c='\\'
1078 */
1079 char *ul_strchr_escaped(const char *s, int c)
1080 {
1081 char *p;
1082 int esc = 0;
1083
1084 for (p = (char *) s; p && *p; p++) {
1085 if (!esc && *p == '\\') {
1086 esc = 1;
1087 continue;
1088 }
1089 if (*p == c && (!esc || c == '\\'))
1090 return p;
1091 esc = 0;
1092 }
1093
1094 return NULL;
1095 }
1096
1097 /* Split a string into words. */
1098 const char *split(const char **state, size_t *l, const char *separator, int quoted)
1099 {
1100 const char *current;
1101
1102 current = *state;
1103
1104 if (!*current) {
1105 assert(**state == '\0');
1106 return NULL;
1107 }
1108
1109 current += strspn(current, separator);
1110 if (!*current) {
1111 *state = current;
1112 return NULL;
1113 }
1114
1115 if (quoted && strchr("\'\"", *current)) {
1116 char quotechars[2] = {*current, '\0'};
1117
1118 *l = strcspn_escaped(current + 1, quotechars);
1119 if (current[*l + 1] == '\0' || current[*l + 1] != quotechars[0] ||
1120 (current[*l + 2] && !strchr(separator, current[*l + 2]))) {
1121 /* right quote missing or garbage at the end */
1122 *state = current;
1123 return NULL;
1124 }
1125 *state = current++ + *l + 2;
1126 } else if (quoted) {
1127 *l = strcspn_escaped(current, separator);
1128 if (current[*l] && !strchr(separator, current[*l])) {
1129 /* unfinished escape */
1130 *state = current;
1131 return NULL;
1132 }
1133 *state = current + *l;
1134 } else {
1135 *l = strcspn(current, separator);
1136 *state = current + *l;
1137 }
1138
1139 return current;
1140 }
1141
1142 /* Rewind file pointer forward to new line. */
1143 int skip_fline(FILE *fp)
1144 {
1145 int ch;
1146
1147 do {
1148 if ((ch = fgetc(fp)) == EOF)
1149 return 1;
1150 if (ch == '\n')
1151 return 0;
1152 } while (1);
1153 }
1154
1155
1156 /* compare two strings, but ignoring non-alnum and case of the characters, for example
1157 * "Hello (123)!" is the same as "hello123".
1158 */
1159 int ul_stralnumcmp(const char *p1, const char *p2)
1160 {
1161 const unsigned char *s1 = (const unsigned char *) p1;
1162 const unsigned char *s2 = (const unsigned char *) p2;
1163 unsigned char c1, c2;
1164
1165 do {
1166 do {
1167 c1 = (unsigned char) *s1++;
1168 } while (c1 != '\0' && !isalnum((unsigned int) c1));
1169
1170 do {
1171 c2 = (unsigned char) *s2++;
1172 } while (c2 != '\0' && !isalnum((unsigned int) c2));
1173
1174 if (c1 != '\0')
1175 c1 = tolower(c1);
1176 if (c2 != '\0')
1177 c2 = tolower(c2);
1178 if (c1 == '\0')
1179 return c1 - c2;
1180 } while (c1 == c2);
1181
1182 return c1 - c2;
1183 }
1184
1185 /*
1186 * Parses the first option from @optstr. The @optstr pointer is set to the beginning
1187 * of the next option. The options string looks like 'aaa,bbb=data,foo,bar="xxx"'.
1188 *
1189 * Note this function is used by libmount to parse mount options. Be careful when modify.
1190 *
1191 * Returns -EINVAL on parse error, 1 at the end of optstr and 0 on success.
1192 */
1193 int ul_optstr_next(char **optstr, char **name, size_t *namesz,
1194 char **value, size_t *valsz)
1195 {
1196 int open_quote = 0;
1197 char *start = NULL, *stop = NULL, *p, *sep = NULL;
1198 char *optstr0;
1199
1200 assert(optstr);
1201 assert(*optstr);
1202
1203 optstr0 = *optstr;
1204
1205 if (name)
1206 *name = NULL;
1207 if (namesz)
1208 *namesz = 0;
1209 if (value)
1210 *value = NULL;
1211 if (valsz)
1212 *valsz = 0;
1213
1214 /* trim leading commas as to not invalidate option
1215 * strings with multiple consecutive commas */
1216 while (optstr0 && *optstr0 == ',')
1217 optstr0++;
1218
1219 for (p = optstr0; p && *p; p++) {
1220 if (!start)
1221 start = p; /* beginning of the option item */
1222 if (*p == '"')
1223 open_quote ^= 1; /* reverse the status */
1224 if (open_quote)
1225 continue; /* still in quoted block */
1226 if (!sep && p > start && *p == '=')
1227 sep = p; /* name and value separator */
1228 if (*p == ',' && (p == optstr0 || *(p - 1) != '\\'))
1229 stop = p; /* terminate the option item */
1230 else if (*(p + 1) == '\0')
1231 stop = p + 1; /* end of optstr */
1232 if (!start || !stop)
1233 continue;
1234 if (stop <= start)
1235 return -EINVAL;
1236
1237 if (name)
1238 *name = start;
1239 if (namesz)
1240 *namesz = sep ? sep - start : stop - start;
1241 *optstr = *stop ? stop + 1 : stop;
1242
1243 if (sep) {
1244 if (value)
1245 *value = sep + 1;
1246 if (valsz)
1247 *valsz = stop - sep - 1;
1248 }
1249 return 0;
1250 }
1251
1252 return 1; /* end of optstr */
1253 }
1254
1255 #ifdef TEST_PROGRAM_STRUTILS
1256
1257 #include "cctype.h"
1258
1259 struct testS {
1260 char *name;
1261 char *value;
1262 };
1263
1264 static int test_strdup_to_member(int argc, char *argv[])
1265 {
1266 struct testS *xx;
1267
1268 if (argc < 3)
1269 return EXIT_FAILURE;
1270
1271 xx = calloc(1, sizeof(*xx));
1272 if (!xx)
1273 err(EXIT_FAILURE, "calloc() failed");
1274
1275 strdup_to_struct_member(xx, name, argv[1]);
1276 strdup_to_struct_member(xx, value, argv[2]);
1277
1278 if (strcmp(xx->name, argv[1]) != 0 &&
1279 strcmp(xx->value, argv[2]) != 0)
1280 errx(EXIT_FAILURE, "strdup_to_struct_member() failed");
1281
1282 printf("1: '%s', 2: '%s'\n", xx->name, xx->value);
1283
1284 free(xx->name);
1285 free(xx->value);
1286 free(xx);
1287 return EXIT_SUCCESS;
1288 }
1289
1290 static int test_strutils_sizes(int argc, char *argv[])
1291 {
1292 uintmax_t size = 0;
1293 char *hum1, *hum2, *hum3;
1294
1295 if (argc < 2)
1296 return EXIT_FAILURE;
1297
1298 if (strtosize(argv[1], &size))
1299 errx(EXIT_FAILURE, "invalid size '%s' value", argv[1]);
1300
1301 hum1 = size_to_human_string(SIZE_SUFFIX_1LETTER, size);
1302 hum2 = size_to_human_string(SIZE_SUFFIX_3LETTER |
1303 SIZE_SUFFIX_SPACE, size);
1304 hum3 = size_to_human_string(SIZE_SUFFIX_3LETTER |
1305 SIZE_SUFFIX_SPACE |
1306 SIZE_DECIMAL_2DIGITS, size);
1307
1308 printf("%25s : %20ju : %8s : %12s : %13s\n", argv[1], size, hum1, hum2, hum3);
1309 free(hum1);
1310 free(hum2);
1311 free(hum3);
1312
1313 return EXIT_SUCCESS;
1314 }
1315
1316 static int test_strutils_cmp_paths(int argc, char *argv[])
1317 {
1318 int rc = streq_paths(argv[1], argv[2]);
1319
1320 if (argc < 3)
1321 return EXIT_FAILURE;
1322
1323 printf("%s: '%s' '%s'\n", rc == 1 ? "YES" : "NOT", argv[1], argv[2]);
1324 return EXIT_SUCCESS;
1325 }
1326
1327 static int test_strutils_normalize(int argc, char *argv[])
1328 {
1329 unsigned char *src, *dst, *org;
1330 size_t sz, len;
1331
1332 if (argc < 2)
1333 return EXIT_FAILURE;
1334
1335 org = (unsigned char *) strdup(argv[1]);
1336 src = (unsigned char *) strdup((char *) org);
1337 len = strlen((char *) src);
1338 dst = malloc(len + 1);
1339
1340 if (!org || !src || !dst)
1341 goto done;
1342
1343 /* two buffers */
1344 sz = __normalize_whitespace(src, len, dst, len + 1);
1345 printf("1: '%s' --> '%s' [sz=%zu]\n", src, dst, sz);
1346
1347 /* one buffer */
1348 sz = normalize_whitespace(src);
1349 printf("2: '%s' --> '%s' [sz=%zu]\n", org, src, sz);
1350
1351 done:
1352 free(src);
1353 free(dst);
1354 free(org);
1355
1356 return EXIT_SUCCESS;
1357 }
1358
1359 static int test_strutils_cstrcasecmp(int argc, char *argv[])
1360 {
1361 char *a, *b;
1362
1363 if (argc < 3)
1364 return EXIT_FAILURE;
1365
1366 a = argv[1];
1367 b = argv[2];
1368
1369 if (!a || !b)
1370 return EXIT_FAILURE;
1371
1372 printf("cmp '%s' '%s' = %d\n", a, b, strcasecmp(a, b));
1373 printf("c_cmp '%s' '%s' = %d\n", a, b, c_strcasecmp(a, b));
1374 printf("c_ncmp '%s' '%s' = %d\n", a, b, c_strncasecmp(a, b, strlen(a)));
1375
1376 return EXIT_SUCCESS;
1377 }
1378
1379 int main(int argc, char *argv[])
1380 {
1381 if (argc == 3 && strcmp(argv[1], "--size") == 0) {
1382 return test_strutils_sizes(argc - 1, argv + 1);
1383
1384 } else if (argc == 4 && strcmp(argv[1], "--cmp-paths") == 0) {
1385 return test_strutils_cmp_paths(argc - 1, argv + 1);
1386
1387 } else if (argc == 4 && strcmp(argv[1], "--strdup-member") == 0) {
1388 return test_strdup_to_member(argc - 1, argv + 1);
1389
1390 } else if (argc == 4 && strcmp(argv[1], "--stralnumcmp") == 0) {
1391 printf("%s\n", ul_stralnumcmp(argv[2], argv[3]) == 0 ?
1392 "match" : "dismatch");
1393 return EXIT_SUCCESS;
1394
1395 } else if (argc == 4 && strcmp(argv[1], "--cstrcasecmp") == 0) {
1396 return test_strutils_cstrcasecmp(argc - 1, argv + 1);
1397
1398 } else if (argc == 3 && strcmp(argv[1], "--normalize") == 0) {
1399 return test_strutils_normalize(argc - 1, argv + 1);
1400
1401 } else if (argc == 3 && strcmp(argv[1], "--strtos64") == 0) {
1402 printf("'%s'-->%jd\n", argv[2], strtos64_or_err(argv[2], "strtos64 failed"));
1403 return EXIT_SUCCESS;
1404 } else if (argc == 3 && strcmp(argv[1], "--strtou64") == 0) {
1405 printf("'%s'-->%ju\n", argv[2], strtou64_or_err(argv[2], "strtou64 failed"));
1406 return EXIT_SUCCESS;
1407 } else if (argc == 3 && strcmp(argv[1], "--strtos32") == 0) {
1408 printf("'%s'-->%d\n", argv[2], strtos32_or_err(argv[2], "strtos32 failed"));
1409 return EXIT_SUCCESS;
1410 } else if (argc == 3 && strcmp(argv[1], "--strtou32") == 0) {
1411 printf("'%s'-->%u\n", argv[2], strtou32_or_err(argv[2], "strtou32 failed"));
1412 return EXIT_SUCCESS;
1413 } else if (argc == 3 && strcmp(argv[1], "--strtos16") == 0) {
1414 printf("'%s'-->%hd\n", argv[2], strtos16_or_err(argv[2], "strtos16 failed"));
1415 return EXIT_SUCCESS;
1416 } else if (argc == 3 && strcmp(argv[1], "--strtou16") == 0) {
1417 printf("'%s'-->%hu\n", argv[2], strtou16_or_err(argv[2], "strtou16 failed"));
1418 return EXIT_SUCCESS;
1419
1420 } else if (argc == 4 && strcmp(argv[1], "--strchr-escaped") == 0) {
1421 printf("\"%s\" --> \"%s\"\n", argv[2], ul_strchr_escaped(argv[2], *argv[3]));
1422 return EXIT_SUCCESS;
1423
1424 } else if (argc == 2 && strcmp(argv[1], "--next-string") == 0) {
1425 char *buf = "abc\0Y\0\0xyz\0X";
1426 char *end = buf + 12;
1427 char *p = buf;
1428
1429 do {
1430 printf("str: '%s'\n", p);
1431 } while ((p = ul_next_string(p, end)));
1432
1433 } else {
1434 fprintf(stderr, "usage: %1$s --size <number>[suffix]\n"
1435 " %1$s --cmp-paths <path> <path>\n"
1436 " %1$s --strdup-member <str> <str>\n"
1437 " %1$s --stralnumcmp <str> <str>\n"
1438 " %1$s --cstrcasecmp <str> <str>\n"
1439 " %1$s --normalize <str>\n"
1440 " %1$s --strto{s,u}{16,32,64} <str>\n",
1441 argv[0]);
1442 exit(EXIT_FAILURE);
1443 }
1444
1445 return EXIT_FAILURE;
1446 }
1447 #endif /* TEST_PROGRAM_STRUTILS */