]> git.ipfire.org Git - thirdparty/util-linux.git/blame_incremental - lib/strutils.c
taskset: Accept 0 pid for current process
[thirdparty/util-linux.git] / lib / strutils.c
... / ...
CommitLineData
1/*
2 * No copyright is claimed. This code is in the public domain; do with
3 * it what you wish.
4 *
5 * Authors: Karel Zak <kzak@redhat.com> [2010]
6 * Davidlohr Bueso <dave@gnu.org> [2010]
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
24static int STRTOXX_EXIT_CODE = EXIT_FAILURE;
25
26void strutils_set_exitcode(int ex) {
27 STRTOXX_EXIT_CODE = ex;
28}
29
30static 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 */
67int ul_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 */
114check_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 }
213done:
214 *res = x;
215err:
216 if (rc < 0)
217 errno = -rc;
218 return rc;
219}
220
221int strtosize(const char *str, uintmax_t *res)
222{
223 return ul_parse_size(str, res, NULL);
224}
225
226int 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
237int 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 * For example: ul_parse_switch(argv[i], "on", "off", "yes", "no", NULL);
251 */
252int ul_parse_switch(const char *arg, ...)
253{
254 const char *a, *b;
255 va_list ap;
256
257 va_start(ap, arg);
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, _("unsupported argument: %s"), arg);
279}
280
281#ifndef HAVE_MEMPCPY
282void *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
289size_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
302char *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
312char *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 */
326int 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
343int 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
370int 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
383int 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
396int ul_strtou16(const char *str, uint16_t *num, int base)
397{
398 uint64_t tmp;
399 int rc;
400
401 rc = ul_strtou64(str, &tmp, base);
402 if (rc == 0 && tmp > UINT16_MAX)
403 rc = -(errno = ERANGE);
404 if (rc == 0)
405 *num = (uint16_t) tmp;
406 return rc;
407}
408
409/*
410 * Convert strings to numbers in defined range and print message on error.
411 *
412 * These functions are used when we read input from users (getopt() etc.). It's
413 * better to consolidate the code and keep it all based on 64-bit numbers than
414 * implement it for 32 and 16-bit numbers too.
415 */
416int64_t str2num_or_err(const char *str, int base, const char *errmesg,
417 int64_t low, int64_t up)
418{
419 int64_t num = 0;
420 int rc;
421
422 rc = ul_strtos64(str, &num, base);
423 if (rc == 0 && ((low && num < low) || (up && num > up)))
424 rc = -(errno = ERANGE);
425
426 if (rc) {
427 if (errno == ERANGE)
428 err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
429 errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
430 }
431 return num;
432}
433
434uint64_t str2unum_or_err(const char *str, int base, const char *errmesg, uint64_t up)
435{
436 uint64_t num = 0;
437 int rc;
438
439 rc = ul_strtou64(str, &num, base);
440 if (rc == 0 && (up && num > up))
441 rc = -(errno = ERANGE);
442
443 if (rc) {
444 if (errno == ERANGE)
445 err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
446 errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
447 }
448 return num;
449}
450
451double strtod_or_err(const char *str, const char *errmesg)
452{
453 double num;
454 char *end = NULL;
455
456 errno = 0;
457 if (str == NULL || *str == '\0')
458 goto err;
459 num = strtod(str, &end);
460
461 if (errno || str == end || (end && *end))
462 goto err;
463
464 return num;
465err:
466 if (errno == ERANGE)
467 err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
468
469 errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
470}
471
472int ul_strtold(const char *str, long double *num)
473{
474 char *end = NULL;
475
476 errno = 0;
477 if (str == NULL || *str == '\0')
478 return -(errno = EINVAL);
479 *num = strtold(str, &end);
480
481 if (errno != 0)
482 return -errno;
483 if (str == end || (end && *end))
484 return -(errno = EINVAL);
485 return 0;
486}
487
488long double strtold_or_err(const char *str, const char *errmesg)
489{
490 long double num = 0;
491
492 if (ul_strtold(str, &num) == 0)
493 return num;
494 if (errno == ERANGE)
495 err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
496
497 errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
498}
499
500uintmax_t strtosize_or_err(const char *str, const char *errmesg)
501{
502 uintmax_t num;
503
504 if (strtosize(str, &num) == 0)
505 return num;
506
507 if (errno)
508 err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
509
510 errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
511}
512
513
514void strtotimeval_or_err(const char *str, struct timeval *tv, const char *errmesg)
515{
516 long double user_input;
517
518 user_input = strtold_or_err(str, errmesg);
519 tv->tv_sec = (time_t) user_input;
520 tv->tv_usec = (suseconds_t)((user_input - tv->tv_sec) * 1000000);
521}
522
523void strtotimespec_or_err(const char *str, struct timespec *ts, const char *errmesg)
524{
525 long double user_input;
526
527 user_input = strtold_or_err(str, errmesg);
528 ts->tv_sec = (time_t) user_input;
529 ts->tv_nsec = (long)((user_input - ts->tv_sec) * 1000000000);
530}
531
532time_t strtotime_or_err(const char *str, const char *errmesg)
533{
534 int64_t user_input;
535
536 user_input = strtos64_or_err(str, errmesg);
537 return (time_t) user_input;
538}
539
540bool hyperlinkwanted(const char *mode)
541{
542 if (mode && strcmp(mode, "never") == 0)
543 return false;
544
545 if (mode && strcmp(mode, "always") == 0)
546 return true;
547
548 if (!mode || strcmp(mode, "auto") == 0)
549 return isatty(STDOUT_FILENO) ? true : false;
550
551 errx(EXIT_FAILURE, _("invalid argument of --hyperlink: %s"), mode);
552}
553
554/*
555 * Converts stat->st_mode to ls(1)-like mode string. The size of "str" must
556 * be 11 bytes.
557 */
558char *xstrmode(mode_t mode, char *str)
559{
560 unsigned short i = 0;
561
562 if (S_ISDIR(mode))
563 str[i++] = 'd';
564 else if (S_ISLNK(mode))
565 str[i++] = 'l';
566 else if (S_ISCHR(mode))
567 str[i++] = 'c';
568 else if (S_ISBLK(mode))
569 str[i++] = 'b';
570 else if (S_ISSOCK(mode))
571 str[i++] = 's';
572 else if (S_ISFIFO(mode))
573 str[i++] = 'p';
574 else if (S_ISREG(mode))
575 str[i++] = '-';
576
577 str[i++] = mode & S_IRUSR ? 'r' : '-';
578 str[i++] = mode & S_IWUSR ? 'w' : '-';
579 str[i++] = (mode & S_ISUID
580 ? (mode & S_IXUSR ? 's' : 'S')
581 : (mode & S_IXUSR ? 'x' : '-'));
582 str[i++] = mode & S_IRGRP ? 'r' : '-';
583 str[i++] = mode & S_IWGRP ? 'w' : '-';
584 str[i++] = (mode & S_ISGID
585 ? (mode & S_IXGRP ? 's' : 'S')
586 : (mode & S_IXGRP ? 'x' : '-'));
587 str[i++] = mode & S_IROTH ? 'r' : '-';
588 str[i++] = mode & S_IWOTH ? 'w' : '-';
589 str[i++] = (mode & S_ISVTX
590 ? (mode & S_IXOTH ? 't' : 'T')
591 : (mode & S_IXOTH ? 'x' : '-'));
592 str[i] = '\0';
593
594 return str;
595}
596
597/*
598 * returns exponent (2^x=n) in range KiB..EiB (2^10..2^60)
599 */
600static int get_exp(uint64_t n)
601{
602 int shft;
603
604 for (shft = 10; shft <= 60; shft += 10) {
605 if (n < (1ULL << shft))
606 break;
607 }
608 return shft - 10;
609}
610
611char *size_to_human_string(int options, uint64_t bytes)
612{
613 char buf[32];
614 int dec, exp;
615 uint64_t frac;
616 const char *letters = "BKMGTPE";
617 char suffix[sizeof(" KiB")], *psuf = suffix;
618 char c;
619
620 if (options & SIZE_SUFFIX_SPACE)
621 *psuf++ = ' ';
622
623
624 exp = get_exp(bytes);
625 c = *(letters + (exp ? exp / 10 : 0));
626 dec = exp ? bytes / (1ULL << exp) : bytes;
627 frac = exp ? bytes % (1ULL << exp) : 0;
628
629 *psuf++ = c;
630
631 if ((options & SIZE_SUFFIX_3LETTER) && (c != 'B')) {
632 *psuf++ = 'i';
633 *psuf++ = 'B';
634 }
635
636 *psuf = '\0';
637
638 /* fprintf(stderr, "exp: %d, unit: %c, dec: %d, frac: %jd\n",
639 * exp, suffix[0], dec, frac);
640 */
641
642 /* round */
643 if (frac) {
644 /* get 3 digits after decimal point */
645 if (frac >= UINT64_MAX / 1000)
646 frac = ((frac / 1024) * 1000) / (1ULL << (exp - 10)) ;
647 else
648 frac = (frac * 1000) / (1ULL << (exp)) ;
649
650 if (options & SIZE_DECIMAL_2DIGITS) {
651 /* round 4/5 and keep 2 digits after decimal point */
652 frac = (frac + 5) / 10 ;
653 } else {
654 /* round 4/5 and keep 1 digit after decimal point */
655 frac = ((frac + 50) / 100) * 10 ;
656 }
657
658 /* rounding could have overflowed */
659 if (frac == 100) {
660 dec++;
661 frac = 0;
662 }
663 }
664
665 if (frac) {
666 struct lconv const *l = localeconv();
667 char *dp = l ? l->decimal_point : NULL;
668 int len;
669
670 if (!dp || !*dp)
671 dp = ".";
672
673 len = snprintf(buf, sizeof(buf), "%d%s%02" PRIu64, dec, dp, frac);
674 if (len > 0 && (size_t) len < sizeof(buf)) {
675 /* remove potential extraneous zero */
676 if (buf[len - 1] == '0')
677 buf[len--] = '\0';
678 /* append suffix */
679 xstrncpy(buf+len, suffix, sizeof(buf) - len);
680 } else
681 *buf = '\0'; /* snprintf error */
682 } else
683 snprintf(buf, sizeof(buf), "%d%s", dec, suffix);
684
685 return strdup(buf);
686}
687
688/*
689 * Parses comma delimited list to array with IDs, for example:
690 *
691 * "aaa,bbb,ccc" --> ary[0] = FOO_AAA;
692 * ary[1] = FOO_BBB;
693 * ary[3] = FOO_CCC;
694 *
695 * The function name2id() provides conversion from string to ID.
696 *
697 * Returns: >= 0 : number of items added to ary[]
698 * -1 : parse error or unknown item
699 * -2 : arysz reached
700 */
701int string_to_idarray(const char *list, int ary[], size_t arysz,
702 int (name2id)(const char *, size_t))
703{
704 const char *begin = NULL, *p;
705 size_t n = 0;
706
707 if (!list || !*list || !ary || !arysz || !name2id)
708 return -1;
709
710 for (p = list; p && *p; p++) {
711 const char *end = NULL;
712 int id;
713
714 if (n >= arysz)
715 return -2;
716 if (!begin)
717 begin = p; /* begin of the column name */
718 if (*p == ',')
719 end = p; /* terminate the name */
720 if (*(p + 1) == '\0')
721 end = p + 1; /* end of string */
722 if (!begin || !end)
723 continue;
724 if (end <= begin)
725 return -1;
726
727 id = name2id(begin, end - begin);
728 if (id == -1)
729 return -1;
730 ary[ n++ ] = id;
731 begin = NULL;
732 if (end && !*end)
733 break;
734 }
735 return n;
736}
737
738/*
739 * Parses the array like string_to_idarray but if format is "+aaa,bbb"
740 * it adds fields to array instead of replacing them.
741 */
742int string_add_to_idarray(const char *list, int ary[], size_t arysz,
743 size_t *ary_pos, int (name2id)(const char *, size_t))
744{
745 const char *list_add;
746 int r;
747
748 if (!list || !*list || !ary_pos || *ary_pos > arysz)
749 return -1;
750
751 if (list[0] == '+')
752 list_add = &list[1];
753 else {
754 list_add = list;
755 *ary_pos = 0;
756 }
757
758 r = string_to_idarray(list_add, &ary[*ary_pos], arysz - *ary_pos, name2id);
759 if (r > 0)
760 *ary_pos += r;
761 return r;
762}
763
764/*
765 * LIST ::= <item> [, <item>]
766 *
767 * The <item> is translated to 'id' by name2id() function and the 'id' is used
768 * as a position in the 'ary' bit array. It means that the 'id' has to be in
769 * range <0..N> where N < sizeof(ary) * NBBY.
770 *
771 * If allow_range is enabled:
772 * An item ending in '+' also sets all bits in <0..N>.
773 * An item beginning with '+' also sets all bits in <N..allow_minus>.
774 *
775 * Returns: 0 on success, <0 on error.
776 */
777int string_to_bitarray(const char *list,
778 char *ary,
779 int (*name2bit)(const char *, size_t),
780 size_t allow_range)
781{
782 const char *begin = NULL, *p;
783
784 if (!list || !name2bit || !ary)
785 return -EINVAL;
786
787 for (p = list; p && *p; p++) {
788 const char *end = NULL;
789 int bit, set_lower = 0, set_higher = 0;
790
791 if (!begin)
792 begin = p; /* begin of the level name */
793 if (*p == ',')
794 end = p; /* terminate the name */
795 if (*(p + 1) == '\0')
796 end = p + 1; /* end of string */
797 if (!begin || !end)
798 continue;
799 if (end <= begin)
800 return -1;
801 if (allow_range) {
802 if (*(end - 1) == '+') {
803 end--;
804 set_lower = 1;
805 } else if (*begin == '+') {
806 begin++;
807 set_higher = 1;
808 }
809 }
810
811 bit = name2bit(begin, end - begin);
812 if (bit < 0)
813 return bit;
814 setbit(ary, bit);
815 if (set_lower)
816 while (--bit >= 0)
817 setbit(ary, bit);
818 else if (set_higher)
819 while (++bit < (int) allow_range)
820 setbit(ary, bit);
821 begin = NULL;
822 if (end && !*end)
823 break;
824 }
825 return 0;
826}
827
828/*
829 * LIST ::= <item> [, <item>]
830 *
831 * The <item> is translated to 'id' by name2flag() function and the flags is
832 * set to the 'mask'
833*
834 * Returns: 0 on success, <0 on error.
835 */
836int string_to_bitmask(const char *list,
837 unsigned long *mask,
838 long (*name2flag)(const char *, size_t))
839{
840 const char *begin = NULL, *p;
841
842 if (!list || !name2flag || !mask)
843 return -EINVAL;
844
845 for (p = list; p && *p; p++) {
846 const char *end = NULL;
847 long flag;
848
849 if (!begin)
850 begin = p; /* begin of the level name */
851 if (*p == ',')
852 end = p; /* terminate the name */
853 if (*(p + 1) == '\0')
854 end = p + 1; /* end of string */
855 if (!begin || !end)
856 continue;
857 if (end <= begin)
858 return -1;
859
860 flag = name2flag(begin, end - begin);
861 if (flag < 0)
862 return flag; /* error */
863 *mask |= flag;
864 begin = NULL;
865 if (end && !*end)
866 break;
867 }
868 return 0;
869}
870
871/*
872 * Parse the lower and higher values in a string containing
873 * "lower:higher" or "lower-higher" format. Note that either
874 * the lower or the higher values may be missing, and the def
875 * value will be assigned to it by default.
876 *
877 * Returns: 0 on success, <0 on error.
878 */
879int ul_parse_range(const char *str, int *lower, int *upper, int def)
880{
881 char *end = NULL;
882
883 if (!str)
884 return 0;
885
886 *upper = *lower = def;
887 errno = 0;
888
889 if (*str == ':') { /* <:N> */
890 str++;
891 *upper = strtol(str, &end, 10);
892 if (errno || !end || *end || end == str)
893 return -1;
894 } else {
895 *upper = *lower = strtol(str, &end, 10);
896 if (errno || !end || end == str)
897 return -1;
898
899 if (*end == ':' && !*(end + 1)) /* <M:> */
900 *upper = def;
901 else if (*end == '-' || *end == ':') { /* <M:N> <M-N> */
902 str = end + 1;
903 end = NULL;
904 errno = 0;
905 *upper = strtol(str, &end, 10);
906
907 if (errno || !end || *end || end == str)
908 return -1;
909 }
910 }
911 return 0;
912}
913
914static const char *next_path_segment(const char *str, size_t *sz)
915{
916 const char *start, *p;
917
918 start = str;
919 *sz = 0;
920 while (start && *start == '/' && *(start + 1) == '/')
921 start++;
922
923 if (!start || !*start)
924 return NULL;
925
926 for (*sz = 1, p = start + 1; *p && *p != '/'; p++) {
927 (*sz)++;
928 }
929
930 return start;
931}
932
933int streq_paths(const char *a, const char *b)
934{
935 while (a && b) {
936 size_t a_sz, b_sz;
937 const char *a_seg = next_path_segment(a, &a_sz);
938 const char *b_seg = next_path_segment(b, &b_sz);
939
940 /*
941 fprintf(stderr, "A>>>(%zu) '%s'\n", a_sz, a_seg);
942 fprintf(stderr, "B>>>(%zu) '%s'\n", b_sz, b_seg);
943 */
944
945 /* end of the path */
946 if (a_sz + b_sz == 0)
947 return 1;
948
949 /* ignore trailing slash */
950 if (a_sz + b_sz == 1 &&
951 ((a_seg && *a_seg == '/') || (b_seg && *b_seg == '/')))
952 return 1;
953
954 if (!a_seg || !b_seg)
955 break;
956 if (a_sz != b_sz || strncmp(a_seg, b_seg, a_sz) != 0)
957 break;
958
959 a = a_seg + a_sz;
960 b = b_seg + b_sz;
961 };
962
963 return 0;
964}
965
966/* concatenate two strings to a new string, the size of the second string is limited by @b */
967char *ul_strnconcat(const char *s, const char *suffix, size_t b)
968{
969 size_t a;
970 char *r;
971
972 if (!s && !suffix)
973 return strdup("");
974 if (!s)
975 return strndup(suffix, b);
976 if (!suffix)
977 return strdup(s);
978
979 assert(s);
980 assert(suffix);
981
982 a = strlen(s);
983 if (b > ((size_t) -1) - a)
984 return NULL;
985
986 r = malloc(a + b + 1);
987 if (!r)
988 return NULL;
989
990 memcpy(r, s, a);
991 memcpy(r + a, suffix, b);
992 r[a+b] = 0;
993
994 return r;
995}
996
997/* concatenate two strings to a new string */
998char *ul_strconcat(const char *s, const char *suffix)
999{
1000 return ul_strnconcat(s, suffix, suffix ? strlen(suffix) : 0);
1001}
1002
1003/* concatenate @s and string defined by @format to a new string */
1004char *ul_strfconcat(const char *s, const char *format, ...)
1005{
1006 va_list ap;
1007 char *val, *res;
1008 int sz;
1009
1010 va_start(ap, format);
1011 sz = vasprintf(&val, format, ap);
1012 va_end(ap);
1013
1014 if (sz < 0)
1015 return NULL;
1016
1017 res = ul_strnconcat(s, val, sz);
1018 free(val);
1019 return res;
1020}
1021
1022int ul_strappend(char **a, const char *b)
1023{
1024 size_t al, bl;
1025 char *tmp;
1026
1027 if (!a)
1028 return -EINVAL;
1029 if (!b || !*b)
1030 return 0;
1031 if (!*a) {
1032 *a = strdup(b);
1033 return !*a ? -ENOMEM : 0;
1034 }
1035
1036 al = strlen(*a);
1037 bl = strlen(b);
1038
1039 tmp = realloc(*a, al + bl + 1);
1040 if (!tmp)
1041 return -ENOMEM;
1042 *a = tmp;
1043 memcpy((*a) + al, b, bl + 1);
1044 return 0;
1045}
1046
1047/* the hybrid version of strfconcat and strappend. */
1048int strfappend(char **a, const char *format, ...)
1049{
1050 va_list ap;
1051 int res;
1052
1053 va_start(ap, format);
1054 res = ul_strvfappend(a, format, ap);
1055 va_end(ap);
1056
1057 return res;
1058}
1059
1060extern int ul_strvfappend(char **a, const char *format, va_list ap)
1061{
1062 char *val;
1063 int sz;
1064 int res;
1065
1066 sz = vasprintf(&val, format, ap);
1067 if (sz < 0)
1068 return -errno;
1069
1070 res = ul_strappend(a, val);
1071 free(val);
1072 return res;
1073}
1074
1075static size_t strcspn_escaped(const char *s, const char *reject)
1076{
1077 int escaped = 0;
1078 int n;
1079
1080 for (n=0; s[n]; n++) {
1081 if (escaped)
1082 escaped = 0;
1083 else if (s[n] == '\\')
1084 escaped = 1;
1085 else if (strchr(reject, s[n]))
1086 break;
1087 }
1088
1089 /* if s ends in \, return index of previous char */
1090 return n - escaped;
1091}
1092
1093/*
1094 * Like strchr() but ignores @c if escaped by '\', '\\' is interpreted like '\'.
1095 *
1096 * For example for @c='X':
1097 *
1098 * "abcdXefgXh" --> "XefgXh"
1099 * "abcd\XefgXh" --> "Xh"
1100 * "abcd\\XefgXh" --> "XefgXh"
1101 * "abcd\\\XefgXh" --> "Xh"
1102 * "abcd\Xefg\Xh" --> (null)
1103 *
1104 * "abcd\\XefgXh" --> "\XefgXh" for @c='\\'
1105 */
1106char *ul_strchr_escaped(const char *s, int c)
1107{
1108 char *p;
1109 int esc = 0;
1110
1111 for (p = (char *) s; p && *p; p++) {
1112 if (!esc && *p == '\\') {
1113 esc = 1;
1114 continue;
1115 }
1116 if (*p == c && (!esc || c == '\\'))
1117 return p;
1118 esc = 0;
1119 }
1120
1121 return NULL;
1122}
1123
1124/* Split a string into words. */
1125const char *ul_split(const char **state, size_t *l, const char *separator, int quoted)
1126{
1127 const char *current;
1128
1129 current = *state;
1130
1131 if (!*current) {
1132 assert(**state == '\0');
1133 return NULL;
1134 }
1135
1136 current += strspn(current, separator);
1137 if (!*current) {
1138 *state = current;
1139 return NULL;
1140 }
1141
1142 if (quoted && strchr("\'\"", *current)) {
1143 char quotechars[2] = {*current, '\0'};
1144
1145 *l = strcspn_escaped(current + 1, quotechars);
1146 if (current[*l + 1] == '\0' || current[*l + 1] != quotechars[0] ||
1147 (current[*l + 2] && !strchr(separator, current[*l + 2]))) {
1148 /* right quote missing or garbage at the end */
1149 *state = current;
1150 return NULL;
1151 }
1152 *state = current++ + *l + 2;
1153 } else if (quoted) {
1154 *l = strcspn_escaped(current, separator);
1155 if (current[*l] && !strchr(separator, current[*l])) {
1156 /* unfinished escape */
1157 *state = current;
1158 return NULL;
1159 }
1160 *state = current + *l;
1161 } else {
1162 *l = strcspn(current, separator);
1163 *state = current + *l;
1164 }
1165
1166 return current;
1167}
1168
1169/* Rewind file pointer forward to new line. */
1170int skip_fline(FILE *fp)
1171{
1172 int ch;
1173
1174 do {
1175 if ((ch = fgetc(fp)) == EOF)
1176 return 1;
1177 if (ch == '\n')
1178 return 0;
1179 } while (1);
1180}
1181
1182
1183/* compare two strings, but ignoring non-alnum and case of the characters, for example
1184 * "Hello (123)!" is the same as "hello123".
1185 */
1186int ul_stralnumcmp(const char *p1, const char *p2)
1187{
1188 const unsigned char *s1 = (const unsigned char *) p1;
1189 const unsigned char *s2 = (const unsigned char *) p2;
1190 unsigned char c1, c2;
1191
1192 do {
1193 do {
1194 c1 = (unsigned char) *s1++;
1195 } while (c1 != '\0' && !isalnum((unsigned int) c1));
1196
1197 do {
1198 c2 = (unsigned char) *s2++;
1199 } while (c2 != '\0' && !isalnum((unsigned int) c2));
1200
1201 if (c1 != '\0')
1202 c1 = tolower(c1);
1203 if (c2 != '\0')
1204 c2 = tolower(c2);
1205 if (c1 == '\0')
1206 return c1 - c2;
1207 } while (c1 == c2);
1208
1209 return c1 - c2;
1210}
1211
1212/*
1213 * Parses the first option from @optstr. The @optstr pointer is set to the beginning
1214 * of the next option. The options string looks like 'aaa,bbb=data,foo,bar="xxx"'.
1215 *
1216 * Note this function is used by libmount to parse mount options. Be careful when modify.
1217 *
1218 * Returns -EINVAL on parse error, 1 at the end of optstr and 0 on success.
1219 */
1220int ul_optstr_next(char **optstr, char **name, size_t *namesz,
1221 char **value, size_t *valsz)
1222{
1223 int open_quote = 0;
1224 char *start = NULL, *stop = NULL, *p, *sep = NULL;
1225 char *optstr0;
1226
1227 assert(optstr);
1228 assert(*optstr);
1229
1230 optstr0 = *optstr;
1231
1232 if (name)
1233 *name = NULL;
1234 if (namesz)
1235 *namesz = 0;
1236 if (value)
1237 *value = NULL;
1238 if (valsz)
1239 *valsz = 0;
1240
1241 /* trim leading commas as to not invalidate option
1242 * strings with multiple consecutive commas */
1243 while (optstr0 && *optstr0 == ',')
1244 optstr0++;
1245
1246 for (p = optstr0; p && *p; p++) {
1247 if (!start && *p == '=')
1248 return -EINVAL;
1249 if (!start)
1250 start = p; /* beginning of the option item */
1251 if (*p == '"' && (p == optstr0 || *(p - 1) != '\\'))
1252 open_quote ^= 1; /* reverse the status */
1253 if (open_quote)
1254 continue; /* still in quoted block */
1255 if (!sep && p > start && *p == '=')
1256 sep = p; /* name and value separator */
1257 if (*p == ',' && (p == optstr0 || *(p - 1) != '\\'))
1258 stop = p; /* terminate the option item */
1259 else if (*(p + 1) == '\0')
1260 stop = p + 1; /* end of optstr */
1261 if (!start || !stop)
1262 continue;
1263 if (stop <= start)
1264 return -EINVAL;
1265
1266 if (name)
1267 *name = start;
1268 if (namesz)
1269 *namesz = sep ? sep - start : stop - start;
1270 *optstr = *stop ? stop + 1 : stop;
1271
1272 if (sep) {
1273 if (value)
1274 *value = sep + 1;
1275 if (valsz)
1276 *valsz = stop - sep - 1;
1277 }
1278 return 0;
1279 }
1280
1281 return 1; /* end of optstr */
1282}
1283
1284int ul_optstr_is_valid(const char *optstr)
1285{
1286 int rc;
1287 char *p = (char *) optstr;
1288
1289 while ((rc = ul_optstr_next(&p, NULL, NULL, NULL, NULL)) == 0);
1290 return rc < 0 ? 0 : 1;
1291}
1292
1293#ifdef TEST_PROGRAM_STRUTILS
1294
1295#include "cctype.h"
1296
1297struct testS {
1298 char *name;
1299 char *value;
1300};
1301
1302static int test_strdup_to_member(int argc, char *argv[])
1303{
1304 struct testS *xx;
1305
1306 if (argc < 3)
1307 return EXIT_FAILURE;
1308
1309 xx = calloc(1, sizeof(*xx));
1310 if (!xx)
1311 err(EXIT_FAILURE, "calloc() failed");
1312
1313 strdup_to_struct_member(xx, name, argv[1]);
1314 strdup_to_struct_member(xx, value, argv[2]);
1315
1316 if (strcmp(xx->name, argv[1]) != 0 &&
1317 strcmp(xx->value, argv[2]) != 0)
1318 errx(EXIT_FAILURE, "strdup_to_struct_member() failed");
1319
1320 printf("1: '%s', 2: '%s'\n", xx->name, xx->value);
1321
1322 free(xx->name);
1323 free(xx->value);
1324 free(xx);
1325 return EXIT_SUCCESS;
1326}
1327
1328static int test_strutils_sizes(int argc, char *argv[])
1329{
1330 uintmax_t size = 0;
1331 char *hum1, *hum2, *hum3;
1332
1333 if (argc < 2)
1334 return EXIT_FAILURE;
1335
1336 if (strtosize(argv[1], &size))
1337 errx(EXIT_FAILURE, "invalid size '%s' value", argv[1]);
1338
1339 hum1 = size_to_human_string(SIZE_SUFFIX_1LETTER, size);
1340 hum2 = size_to_human_string(SIZE_SUFFIX_3LETTER |
1341 SIZE_SUFFIX_SPACE, size);
1342 hum3 = size_to_human_string(SIZE_SUFFIX_3LETTER |
1343 SIZE_SUFFIX_SPACE |
1344 SIZE_DECIMAL_2DIGITS, size);
1345
1346 printf("%25s : %20ju : %8s : %12s : %13s\n", argv[1], size, hum1, hum2, hum3);
1347 free(hum1);
1348 free(hum2);
1349 free(hum3);
1350
1351 return EXIT_SUCCESS;
1352}
1353
1354static int test_strutils_cmp_paths(int argc, char *argv[])
1355{
1356 int rc = streq_paths(argv[1], argv[2]);
1357
1358 if (argc < 3)
1359 return EXIT_FAILURE;
1360
1361 printf("%s: '%s' '%s'\n", rc == 1 ? "YES" : "NOT", argv[1], argv[2]);
1362 return EXIT_SUCCESS;
1363}
1364
1365static int test_strutils_normalize(int argc, char *argv[])
1366{
1367 unsigned char *src, *dst, *org;
1368 size_t sz, len;
1369
1370 if (argc < 2)
1371 return EXIT_FAILURE;
1372
1373 org = (unsigned char *) strdup(argv[1]);
1374 src = (unsigned char *) strdup((char *) org);
1375 len = strlen((char *) src);
1376 dst = malloc(len + 1);
1377
1378 if (!org || !src || !dst)
1379 goto done;
1380
1381 /* two buffers */
1382 sz = __normalize_whitespace(src, len, dst, len + 1);
1383 printf("1: '%s' --> '%s' [sz=%zu]\n", src, dst, sz);
1384
1385 /* one buffer */
1386 sz = normalize_whitespace(src);
1387 printf("2: '%s' --> '%s' [sz=%zu]\n", org, src, sz);
1388
1389done:
1390 free(src);
1391 free(dst);
1392 free(org);
1393
1394 return EXIT_SUCCESS;
1395}
1396
1397static int test_strutils_cstrcasecmp(int argc, char *argv[])
1398{
1399 char *a, *b;
1400
1401 if (argc < 3)
1402 return EXIT_FAILURE;
1403
1404 a = argv[1];
1405 b = argv[2];
1406
1407 if (!a || !b)
1408 return EXIT_FAILURE;
1409
1410 printf("cmp '%s' '%s' = %d\n", a, b, strcasecmp(a, b));
1411 printf("c_cmp '%s' '%s' = %d\n", a, b, c_strcasecmp(a, b));
1412 printf("c_ncmp '%s' '%s' = %d\n", a, b, c_strncasecmp(a, b, strlen(a)));
1413
1414 return EXIT_SUCCESS;
1415}
1416
1417int main(int argc, char *argv[])
1418{
1419 if (argc == 3 && strcmp(argv[1], "--size") == 0) {
1420 return test_strutils_sizes(argc - 1, argv + 1);
1421
1422 } else if (argc == 3 && strcmp(argv[1], "--parse-switch") == 0) {
1423 printf("'%s'-->%d\n", argv[2], ul_parse_switch(argv[2],
1424 "on", "off",
1425 "enable", "disable",
1426 "yes", "no",
1427 "1", "0",
1428 NULL));
1429 return EXIT_SUCCESS;
1430 } else if (argc == 4 && strcmp(argv[1], "--cmp-paths") == 0) {
1431 return test_strutils_cmp_paths(argc - 1, argv + 1);
1432
1433 } else if (argc == 4 && strcmp(argv[1], "--strdup-member") == 0) {
1434 return test_strdup_to_member(argc - 1, argv + 1);
1435
1436 } else if (argc == 4 && strcmp(argv[1], "--stralnumcmp") == 0) {
1437 printf("%s\n", ul_stralnumcmp(argv[2], argv[3]) == 0 ?
1438 "match" : "dismatch");
1439 return EXIT_SUCCESS;
1440
1441 } else if (argc == 4 && strcmp(argv[1], "--cstrcasecmp") == 0) {
1442 return test_strutils_cstrcasecmp(argc - 1, argv + 1);
1443
1444 } else if (argc == 3 && strcmp(argv[1], "--normalize") == 0) {
1445 return test_strutils_normalize(argc - 1, argv + 1);
1446
1447 } else if (argc == 3 && strcmp(argv[1], "--strtos64") == 0) {
1448 printf("'%s'-->%jd\n", argv[2], strtos64_or_err(argv[2], "strtos64 failed"));
1449 return EXIT_SUCCESS;
1450 } else if (argc == 3 && strcmp(argv[1], "--strtou64") == 0) {
1451 printf("'%s'-->%ju\n", argv[2], strtou64_or_err(argv[2], "strtou64 failed"));
1452 return EXIT_SUCCESS;
1453 } else if (argc == 3 && strcmp(argv[1], "--strtos32") == 0) {
1454 printf("'%s'-->%d\n", argv[2], strtos32_or_err(argv[2], "strtos32 failed"));
1455 return EXIT_SUCCESS;
1456 } else if (argc == 3 && strcmp(argv[1], "--strtou32") == 0) {
1457 printf("'%s'-->%u\n", argv[2], strtou32_or_err(argv[2], "strtou32 failed"));
1458 return EXIT_SUCCESS;
1459 } else if (argc == 3 && strcmp(argv[1], "--strtos16") == 0) {
1460 printf("'%s'-->%hd\n", argv[2], strtos16_or_err(argv[2], "strtos16 failed"));
1461 return EXIT_SUCCESS;
1462 } else if (argc == 3 && strcmp(argv[1], "--strtou16") == 0) {
1463 printf("'%s'-->%hu\n", argv[2], strtou16_or_err(argv[2], "strtou16 failed"));
1464 return EXIT_SUCCESS;
1465
1466 } else if (argc == 4 && strcmp(argv[1], "--strchr-escaped") == 0) {
1467 printf("\"%s\" --> \"%s\"\n", argv[2], ul_strchr_escaped(argv[2], *argv[3]));
1468 return EXIT_SUCCESS;
1469
1470 } else if (argc == 2 && strcmp(argv[1], "--next-string") == 0) {
1471 char *buf = "abc\0Y\0\0xyz\0X";
1472 char *end = buf + 12;
1473 char *p = buf;
1474
1475 do {
1476 printf("str: '%s'\n", p);
1477 } while ((p = ul_next_string(p, end)));
1478
1479 return EXIT_SUCCESS;
1480
1481 } else if (argc == 3 && strcmp(argv[1], "--optstr") == 0) {
1482
1483 size_t namesz, valsz;
1484 char *name = NULL, *val = NULL;
1485 char *p = argv[2];
1486 int rc;
1487
1488 if (!ul_optstr_is_valid(p))
1489 errx(EXIT_FAILURE, _("unsupported option format: %s"), p);
1490
1491 while ((rc = ul_optstr_next(&p, &name, &namesz, &val, &valsz)) == 0) {
1492 printf("'%.*s' : '%.*s'\n", (int) namesz, name,
1493 (int) valsz, val);
1494 }
1495 if (rc == 1)
1496 return EXIT_SUCCESS;
1497 } else {
1498 fprintf(stderr, "usage: %1$s --size <number>[suffix]\n"
1499 " %1$s --parse-switch <str>\n"
1500 " %1$s --cmp-paths <path> <path>\n"
1501 " %1$s --strdup-member <str> <str>\n"
1502 " %1$s --stralnumcmp <str> <str>\n"
1503 " %1$s --cstrcasecmp <str> <str>\n"
1504 " %1$s --normalize <str>\n"
1505 " %1$s --strto{s,u}{16,32,64} <str>\n"
1506 " %1$s --optstr <str>\n",
1507 argv[0]);
1508 exit(EXIT_FAILURE);
1509 }
1510
1511 return EXIT_FAILURE;
1512}
1513#endif /* TEST_PROGRAM_STRUTILS */