]>
git.ipfire.org Git - thirdparty/util-linux.git/blob - lib/strutils.c
2 * Copyright (C) 2010 Karel Zak <kzak@redhat.com>
3 * Copyright (C) 2010 Davidlohr Bueso <dave@gnu.org>
5 * No copyright is claimed. This code is in the public domain; do with
22 #include "pathnames.h"
24 static int STRTOXX_EXIT_CODE
= EXIT_FAILURE
;
26 void strutils_set_exitcode(int ex
) {
27 STRTOXX_EXIT_CODE
= ex
;
30 static int do_scale_by_power (uintmax_t *x
, int base
, int power
)
33 if (UINTMAX_MAX
/ base
< *x
)
41 * strtosize() - convert string to size (uintmax_t).
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)
53 * where X = {K,M,G,T,P,E,Z,Y}
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}.
60 * The function also supports decimal point, for example:
64 * Note that the function does not accept numbers with '-' (negative sign)
67 int parse_size(const char *str
, uintmax_t *res
, int *power
)
71 uintmax_t x
, frac
= 0;
72 int base
= 1024, rc
= 0, pwr
= 0, frac_zeros
= 0;
74 static const char *suf
= "KMGTPEZY";
75 static const char *suf2
= "kmgtpezy";
85 /* Only positive numbers are acceptable
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...
92 while (isspace((unsigned char) *p
))
99 errno
= 0, end
= NULL
;
100 x
= strtoumax(str
, &end
, 0);
103 (errno
!= 0 && (x
== UINTMAX_MAX
|| x
== 0))) {
104 rc
= errno
? -errno
: -EINVAL
;
108 goto done
; /* without suffix */
112 * Check size suffixes
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 */
120 struct lconv
const *l
= localeconv();
121 const char *dp
= l
? l
->decimal_point
: NULL
;
122 size_t dpsz
= dp
? strlen(dp
) : 0;
124 if (frac
== 0 && *p
&& dp
&& strncmp(dp
, p
, dpsz
) == 0) {
125 const char *fstr
= p
+ dpsz
;
127 for (p
= fstr
; *p
== '0'; p
++)
130 if (isdigit(*fstr
)) {
131 errno
= 0, end
= NULL
;
132 frac
= strtoumax(fstr
, &end
, 0);
134 (errno
!= 0 && (frac
== UINTMAX_MAX
|| frac
== 0))) {
135 rc
= errno
? -errno
: -EINVAL
;
141 if (frac
&& (!end
|| !*end
)) {
143 goto err
; /* without suffix, but with frac */
149 goto err
; /* unexpected suffix */
152 sp
= strchr(suf
, *p
);
154 pwr
= (sp
- suf
) + 1;
156 sp
= strchr(suf2
, *p
);
158 pwr
= (sp
- suf2
) + 1;
165 rc
= do_scale_by_power(&x
, base
, pwr
);
170 uintmax_t frac_div
= 10, frac_poz
= 1, frac_base
= 1;
172 /* mega, giga, ... */
173 do_scale_by_power(&frac_base
, base
, pwr
);
175 /* maximal divisor for last digit (e.g. for 0.05 is
176 * frac_div=100, for 0.054 is frac_div=1000, etc.)
178 * Reduce frac if too large.
180 while (frac_div
< frac
) {
181 if (frac_div
<= UINTMAX_MAX
/10)
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)
196 * Go backwardly from last digit and add to result what the
197 * digit represents in the frac_base. For example 0.25G
199 * 5 means 1GiB / (100/5)
200 * 2 means 1GiB / (10/2)
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, .. */
206 frac
/= 10; /* remove last digit from frac */
209 if (seg
&& seg_div
/ seg
)
210 x
+= frac_base
/ (seg_div
/ seg
);
221 int strtosize(const char *str
, uintmax_t *res
)
223 return parse_size(str
, res
, NULL
);
226 int isdigit_strend(const char *str
, const char **end
)
230 for (p
= str
; p
&& *p
&& isdigit((unsigned char) *p
); p
++);
234 return p
&& p
> str
&& !*p
;
237 int isxdigit_strend(const char *str
, const char **end
)
241 for (p
= str
; p
&& *p
&& isxdigit((unsigned char) *p
); p
++);
246 return p
&& p
> str
&& !*p
;
250 * parse_switch(argv[i], "on", "off", "yes", "no", NULL);
252 int parse_switch(const char *arg
, const char *errmesg
, ...)
257 va_start(ap
, errmesg
);
259 a
= va_arg(ap
, char *);
262 b
= va_arg(ap
, char *);
266 if (strcmp(arg
, a
) == 0) {
271 if (strcmp(arg
, b
) == 0) {
278 errx(STRTOXX_EXIT_CODE
, "%s: '%s'", errmesg
, arg
);
282 void *mempcpy(void *restrict dest
, const void *restrict src
, size_t n
)
284 return ((char *)memcpy(dest
, src
, n
)) + n
;
289 size_t strnlen(const char *s
, size_t maxlen
)
293 for (i
= 0; i
< maxlen
; i
++) {
302 char *strnchr(const char *s
, size_t maxlen
, int c
)
304 for (; maxlen
-- && *s
!= '\0'; ++s
)
312 char *strndup(const char *s
, size_t n
)
314 size_t len
= strnlen(s
, n
);
315 char *new = malloc((len
+ 1) * sizeof(char));
319 return (char *) memcpy(new, s
, len
);
324 * convert strings to numbers; returns <0 on error, and 0 on success
326 int ul_strtos64(const char *str
, int64_t *num
, int base
)
330 if (str
== NULL
|| *str
== '\0')
331 return -(errno
= EINVAL
);
334 *num
= (int64_t) strtoimax(str
, &end
, base
);
338 if (str
== end
|| (end
&& *end
))
339 return -(errno
= EINVAL
);
343 int ul_strtou64(const char *str
, uint64_t *num
, int base
)
348 if (str
== NULL
|| *str
== '\0')
349 return -(errno
= EINVAL
);
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 */
355 tmp
= (int64_t) strtoimax(str
, &end
, base
);
360 *num
= strtoumax(str
, &end
, base
);
365 if (str
== end
|| (end
&& *end
))
366 return -(errno
= EINVAL
);
370 int ul_strtos32(const char *str
, int32_t *num
, int base
)
375 rc
= ul_strtos64(str
, &tmp
, base
);
376 if (rc
== 0 && (tmp
< INT32_MIN
|| tmp
> INT32_MAX
))
377 rc
= -(errno
= ERANGE
);
379 *num
= (int32_t) tmp
;
383 int ul_strtou32(const char *str
, uint32_t *num
, int base
)
388 rc
= ul_strtou64(str
, &tmp
, base
);
389 if (rc
== 0 && tmp
> UINT32_MAX
)
390 rc
= -(errno
= ERANGE
);
392 *num
= (uint32_t) tmp
;
397 * Convert strings to numbers in defined range and print message on error.
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.
403 int64_t str2num_or_err(const char *str
, int base
, const char *errmesg
,
404 int64_t low
, int64_t up
)
409 rc
= ul_strtos64(str
, &num
, base
);
410 if (rc
== 0 && ((low
&& num
< low
) || (up
&& num
> up
)))
411 rc
= -(errno
= ERANGE
);
415 err(STRTOXX_EXIT_CODE
, "%s: '%s'", errmesg
, str
);
416 errx(STRTOXX_EXIT_CODE
, "%s: '%s'", errmesg
, str
);
421 uint64_t str2unum_or_err(const char *str
, int base
, const char *errmesg
, uint64_t up
)
426 rc
= ul_strtou64(str
, &num
, base
);
427 if (rc
== 0 && (up
&& num
> up
))
428 rc
= -(errno
= ERANGE
);
432 err(STRTOXX_EXIT_CODE
, "%s: '%s'", errmesg
, str
);
433 errx(STRTOXX_EXIT_CODE
, "%s: '%s'", errmesg
, str
);
438 double strtod_or_err(const char *str
, const char *errmesg
)
444 if (str
== NULL
|| *str
== '\0')
446 num
= strtod(str
, &end
);
448 if (errno
|| str
== end
|| (end
&& *end
))
454 err(STRTOXX_EXIT_CODE
, "%s: '%s'", errmesg
, str
);
456 errx(STRTOXX_EXIT_CODE
, "%s: '%s'", errmesg
, str
);
459 int ul_strtold(const char *str
, long double *num
)
464 if (str
== NULL
|| *str
== '\0')
465 return -(errno
= EINVAL
);
466 *num
= strtold(str
, &end
);
470 if (str
== end
|| (end
&& *end
))
471 return -(errno
= EINVAL
);
475 long double strtold_or_err(const char *str
, const char *errmesg
)
479 if (ul_strtold(str
, &num
) == 0)
482 err(STRTOXX_EXIT_CODE
, "%s: '%s'", errmesg
, str
);
484 errx(STRTOXX_EXIT_CODE
, "%s: '%s'", errmesg
, str
);
487 uintmax_t strtosize_or_err(const char *str
, const char *errmesg
)
491 if (strtosize(str
, &num
) == 0)
495 err(STRTOXX_EXIT_CODE
, "%s: '%s'", errmesg
, str
);
497 errx(STRTOXX_EXIT_CODE
, "%s: '%s'", errmesg
, str
);
501 void strtotimeval_or_err(const char *str
, struct timeval
*tv
, const char *errmesg
)
503 long double user_input
;
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);
510 void strtotimespec_or_err(const char *str
, struct timespec
*ts
, const char *errmesg
)
512 long double user_input
;
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);
519 time_t strtotime_or_err(const char *str
, const char *errmesg
)
523 user_input
= strtos64_or_err(str
, errmesg
);
524 return (time_t) user_input
;
528 * Converts stat->st_mode to ls(1)-like mode string. The size of "str" must
531 char *xstrmode(mode_t mode
, char *str
)
533 unsigned short i
= 0;
537 else if (S_ISLNK(mode
))
539 else if (S_ISCHR(mode
))
541 else if (S_ISBLK(mode
))
543 else if (S_ISSOCK(mode
))
545 else if (S_ISFIFO(mode
))
547 else if (S_ISREG(mode
))
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' : '-'));
571 * returns exponent (2^x=n) in range KiB..EiB (2^10..2^60)
573 static int get_exp(uint64_t n
)
577 for (shft
= 10; shft
<= 60; shft
+= 10) {
578 if (n
< (1ULL << shft
))
584 char *size_to_human_string(int options
, uint64_t bytes
)
589 const char *letters
= "BKMGTPE";
590 char suffix
[sizeof(" KiB")], *psuf
= suffix
;
593 if (options
& SIZE_SUFFIX_SPACE
)
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;
604 if ((options
& SIZE_SUFFIX_3LETTER
) && (c
!= 'B')) {
611 /* fprintf(stderr, "exp: %d, unit: %c, dec: %d, frac: %jd\n",
612 * exp, suffix[0], dec, frac);
617 /* get 3 digits after decimal point */
618 if (frac
>= UINT64_MAX
/ 1000)
619 frac
= ((frac
/ 1024) * 1000) / (1ULL << (exp
- 10)) ;
621 frac
= (frac
* 1000) / (1ULL << (exp
)) ;
623 if (options
& SIZE_DECIMAL_2DIGITS
) {
624 /* round 4/5 and keep 2 digits after decimal point */
625 frac
= (frac
+ 5) / 10 ;
627 /* round 4/5 and keep 1 digit after decimal point */
628 frac
= ((frac
+ 50) / 100) * 10 ;
631 /* rounding could have overflowed */
639 struct lconv
const *l
= localeconv();
640 char *dp
= l
? l
->decimal_point
: NULL
;
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')
652 xstrncpy(buf
+len
, suffix
, sizeof(buf
) - len
);
654 *buf
= '\0'; /* snprintf error */
656 snprintf(buf
, sizeof(buf
), "%d%s", dec
, suffix
);
662 * Parses comma delimited list to array with IDs, for example:
664 * "aaa,bbb,ccc" --> ary[0] = FOO_AAA;
668 * The function name2id() provides conversion from string to ID.
670 * Returns: >= 0 : number of items added to ary[]
671 * -1 : parse error or unknown item
674 int string_to_idarray(const char *list
, int ary
[], size_t arysz
,
675 int (name2id
)(const char *, size_t))
677 const char *begin
= NULL
, *p
;
680 if (!list
|| !*list
|| !ary
|| !arysz
|| !name2id
)
683 for (p
= list
; p
&& *p
; p
++) {
684 const char *end
= NULL
;
690 begin
= p
; /* begin of the column name */
692 end
= p
; /* terminate the name */
693 if (*(p
+ 1) == '\0')
694 end
= p
+ 1; /* end of string */
700 id
= name2id(begin
, end
- begin
);
712 * Parses the array like string_to_idarray but if format is "+aaa,bbb"
713 * it adds fields to array instead of replacing them.
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))
718 const char *list_add
;
721 if (!list
|| !*list
|| !ary_pos
|| *ary_pos
> arysz
)
731 r
= string_to_idarray(list_add
, &ary
[*ary_pos
], arysz
- *ary_pos
, name2id
);
738 * LIST ::= <item> [, <item>]
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.
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>.
748 * Returns: 0 on success, <0 on error.
750 int string_to_bitarray(const char *list
,
752 int (*name2bit
)(const char *, size_t),
755 const char *begin
= NULL
, *p
;
757 if (!list
|| !name2bit
|| !ary
)
760 for (p
= list
; p
&& *p
; p
++) {
761 const char *end
= NULL
;
762 int bit
, set_lower
= 0, set_higher
= 0;
765 begin
= p
; /* begin of the level name */
767 end
= p
; /* terminate the name */
768 if (*(p
+ 1) == '\0')
769 end
= p
+ 1; /* end of string */
775 if (*(end
- 1) == '+') {
778 } else if (*begin
== '+') {
784 bit
= name2bit(begin
, end
- begin
);
792 while (++bit
< (int) allow_range
)
802 * LIST ::= <item> [, <item>]
804 * The <item> is translated to 'id' by name2flag() function and the flags is
807 * Returns: 0 on success, <0 on error.
809 int string_to_bitmask(const char *list
,
811 long (*name2flag
)(const char *, size_t))
813 const char *begin
= NULL
, *p
;
815 if (!list
|| !name2flag
|| !mask
)
818 for (p
= list
; p
&& *p
; p
++) {
819 const char *end
= NULL
;
823 begin
= p
; /* begin of the level name */
825 end
= p
; /* terminate the name */
826 if (*(p
+ 1) == '\0')
827 end
= p
+ 1; /* end of string */
833 flag
= name2flag(begin
, end
- begin
);
835 return flag
; /* error */
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.
850 * Returns: 0 on success, <0 on error.
852 int parse_range(const char *str
, int *lower
, int *upper
, int def
)
859 *upper
= *lower
= def
;
862 if (*str
== ':') { /* <:N> */
864 *upper
= strtol(str
, &end
, 10);
865 if (errno
|| !end
|| *end
|| end
== str
)
868 *upper
= *lower
= strtol(str
, &end
, 10);
869 if (errno
|| !end
|| end
== str
)
872 if (*end
== ':' && !*(end
+ 1)) /* <M:> */
874 else if (*end
== '-' || *end
== ':') { /* <M:N> <M-N> */
878 *upper
= strtol(str
, &end
, 10);
880 if (errno
|| !end
|| *end
|| end
== str
)
887 static const char *next_path_segment(const char *str
, size_t *sz
)
889 const char *start
, *p
;
893 while (start
&& *start
== '/' && *(start
+ 1) == '/')
896 if (!start
|| !*start
)
899 for (*sz
= 1, p
= start
+ 1; *p
&& *p
!= '/'; p
++) {
906 int streq_paths(const char *a
, const char *b
)
910 const char *a_seg
= next_path_segment(a
, &a_sz
);
911 const char *b_seg
= next_path_segment(b
, &b_sz
);
914 fprintf(stderr, "A>>>(%zu) '%s'\n", a_sz, a_seg);
915 fprintf(stderr, "B>>>(%zu) '%s'\n", b_sz, b_seg);
918 /* end of the path */
919 if (a_sz
+ b_sz
== 0)
922 /* ignore tailing slash */
923 if (a_sz
+ b_sz
== 1 &&
924 ((a_seg
&& *a_seg
== '/') || (b_seg
&& *b_seg
== '/')))
927 if (!a_seg
|| !b_seg
)
929 if (a_sz
!= b_sz
|| strncmp(a_seg
, b_seg
, a_sz
) != 0)
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
)
948 return strndup(suffix
, b
);
956 if (b
> ((size_t) -1) - a
)
959 r
= malloc(a
+ b
+ 1);
964 memcpy(r
+ a
, suffix
, b
);
970 /* concatenate two strings to a new string */
971 char *strconcat(const char *s
, const char *suffix
)
973 return strnconcat(s
, suffix
, suffix
? strlen(suffix
) : 0);
976 /* concatenate @s and string defined by @format to a new string */
977 char *strfconcat(const char *s
, const char *format
, ...)
983 va_start(ap
, format
);
984 sz
= vasprintf(&val
, format
, ap
);
990 res
= strnconcat(s
, val
, sz
);
995 int strappend(char **a
, const char *b
)
1006 return !*a
? -ENOMEM
: 0;
1012 tmp
= realloc(*a
, al
+ bl
+ 1);
1016 memcpy((*a
) + al
, b
, bl
+ 1);
1020 /* the hybrid version of strfconcat and strappend. */
1021 int strfappend(char **a
, const char *format
, ...)
1026 va_start(ap
, format
);
1027 res
= strvfappend(a
, format
, ap
);
1033 extern int strvfappend(char **a
, const char *format
, va_list ap
)
1039 sz
= vasprintf(&val
, format
, ap
);
1043 res
= strappend(a
, val
);
1048 static size_t strcspn_escaped(const char *s
, const char *reject
)
1053 for (n
=0; s
[n
]; n
++) {
1056 else if (s
[n
] == '\\')
1058 else if (strchr(reject
, s
[n
]))
1062 /* if s ends in \, return index of previous char */
1067 * Like strchr() but ignores @c if escaped by '\', '\\' is interpreted like '\'.
1069 * For example for @c='X':
1071 * "abcdXefgXh" --> "XefgXh"
1072 * "abcd\XefgXh" --> "Xh"
1073 * "abcd\\XefgXh" --> "XefgXh"
1074 * "abcd\\\XefgXh" --> "Xh"
1075 * "abcd\Xefg\Xh" --> (null)
1077 * "abcd\\XefgXh" --> "\XefgXh" for @c='\\'
1079 char *ul_strchr_escaped(const char *s
, int c
)
1084 for (p
= (char *) s
; p
&& *p
; p
++) {
1085 if (!esc
&& *p
== '\\') {
1089 if (*p
== c
&& (!esc
|| c
== '\\'))
1097 /* Split a string into words. */
1098 const char *split(const char **state
, size_t *l
, const char *separator
, int quoted
)
1100 const char *current
;
1105 assert(**state
== '\0');
1109 current
+= strspn(current
, separator
);
1115 if (quoted
&& strchr("\'\"", *current
)) {
1116 char quotechars
[2] = {*current
, '\0'};
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 */
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 */
1133 *state
= current
+ *l
;
1135 *l
= strcspn(current
, separator
);
1136 *state
= current
+ *l
;
1142 /* Rewind file pointer forward to new line. */
1143 int skip_fline(FILE *fp
)
1148 if ((ch
= fgetc(fp
)) == EOF
)
1156 /* compare two strings, but ignoring non-alnum and case of the characters, for example
1157 * "Hello (123)!" is the same as "hello123".
1159 int ul_stralnumcmp(const char *p1
, const char *p2
)
1161 const unsigned char *s1
= (const unsigned char *) p1
;
1162 const unsigned char *s2
= (const unsigned char *) p2
;
1163 unsigned char c1
, c2
;
1167 c1
= (unsigned char) *s1
++;
1168 } while (c1
!= '\0' && !isalnum((unsigned int) c1
));
1171 c2
= (unsigned char) *s2
++;
1172 } while (c2
!= '\0' && !isalnum((unsigned int) c2
));
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"'.
1189 * Note this function is used by libmount to parse mount options. Be careful when modify.
1191 * Returns -EINVAL on parse error, 1 at the end of optstr and 0 on success.
1193 int ul_optstr_next(char **optstr
, char **name
, size_t *namesz
,
1194 char **value
, size_t *valsz
)
1197 char *start
= NULL
, *stop
= NULL
, *p
, *sep
= NULL
;
1214 /* trim leading commas as to not invalidate option
1215 * strings with multiple consecutive commas */
1216 while (optstr0
&& *optstr0
== ',')
1219 for (p
= optstr0
; p
&& *p
; p
++) {
1221 start
= p
; /* beginning of the option item */
1223 open_quote
^= 1; /* reverse the status */
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
)
1240 *namesz
= sep
? sep
- start
: stop
- start
;
1241 *optstr
= *stop
? stop
+ 1 : stop
;
1247 *valsz
= stop
- sep
- 1;
1252 return 1; /* end of optstr */
1255 #ifdef TEST_PROGRAM_STRUTILS
1264 static int test_strdup_to_member(int argc
, char *argv
[])
1269 return EXIT_FAILURE
;
1271 xx
= calloc(1, sizeof(*xx
));
1273 err(EXIT_FAILURE
, "calloc() failed");
1275 strdup_to_struct_member(xx
, name
, argv
[1]);
1276 strdup_to_struct_member(xx
, value
, argv
[2]);
1278 if (strcmp(xx
->name
, argv
[1]) != 0 &&
1279 strcmp(xx
->value
, argv
[2]) != 0)
1280 errx(EXIT_FAILURE
, "strdup_to_struct_member() failed");
1282 printf("1: '%s', 2: '%s'\n", xx
->name
, xx
->value
);
1287 return EXIT_SUCCESS
;
1290 static int test_strutils_sizes(int argc
, char *argv
[])
1293 char *hum1
, *hum2
, *hum3
;
1296 return EXIT_FAILURE
;
1298 if (strtosize(argv
[1], &size
))
1299 errx(EXIT_FAILURE
, "invalid size '%s' value", argv
[1]);
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
|
1306 SIZE_DECIMAL_2DIGITS
, size
);
1308 printf("%25s : %20ju : %8s : %12s : %13s\n", argv
[1], size
, hum1
, hum2
, hum3
);
1313 return EXIT_SUCCESS
;
1316 static int test_strutils_cmp_paths(int argc
, char *argv
[])
1318 int rc
= streq_paths(argv
[1], argv
[2]);
1321 return EXIT_FAILURE
;
1323 printf("%s: '%s' '%s'\n", rc
== 1 ? "YES" : "NOT", argv
[1], argv
[2]);
1324 return EXIT_SUCCESS
;
1327 static int test_strutils_normalize(int argc
, char *argv
[])
1329 unsigned char *src
, *dst
, *org
;
1333 return EXIT_FAILURE
;
1335 org
= (unsigned char *) strdup(argv
[1]);
1336 src
= (unsigned char *) strdup((char *) org
);
1337 len
= strlen((char *) src
);
1338 dst
= malloc(len
+ 1);
1340 if (!org
|| !src
|| !dst
)
1344 sz
= __normalize_whitespace(src
, len
, dst
, len
+ 1);
1345 printf("1: '%s' --> '%s' [sz=%zu]\n", src
, dst
, sz
);
1348 sz
= normalize_whitespace(src
);
1349 printf("2: '%s' --> '%s' [sz=%zu]\n", org
, src
, sz
);
1356 return EXIT_SUCCESS
;
1359 static int test_strutils_cstrcasecmp(int argc
, char *argv
[])
1364 return EXIT_FAILURE
;
1370 return EXIT_FAILURE
;
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
)));
1376 return EXIT_SUCCESS
;
1379 int main(int argc
, char *argv
[])
1381 if (argc
== 3 && strcmp(argv
[1], "--size") == 0) {
1382 return test_strutils_sizes(argc
- 1, argv
+ 1);
1384 } else if (argc
== 4 && strcmp(argv
[1], "--cmp-paths") == 0) {
1385 return test_strutils_cmp_paths(argc
- 1, argv
+ 1);
1387 } else if (argc
== 4 && strcmp(argv
[1], "--strdup-member") == 0) {
1388 return test_strdup_to_member(argc
- 1, argv
+ 1);
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
;
1395 } else if (argc
== 4 && strcmp(argv
[1], "--cstrcasecmp") == 0) {
1396 return test_strutils_cstrcasecmp(argc
- 1, argv
+ 1);
1398 } else if (argc
== 3 && strcmp(argv
[1], "--normalize") == 0) {
1399 return test_strutils_normalize(argc
- 1, argv
+ 1);
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
;
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
;
1424 } else if (argc
== 2 && strcmp(argv
[1], "--next-string") == 0) {
1425 char *buf
= "abc\0Y\0\0xyz\0X";
1426 char *end
= buf
+ 12;
1430 printf("str: '%s'\n", p
);
1431 } while ((p
= ul_next_string(p
, end
)));
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",
1445 return EXIT_FAILURE
;
1447 #endif /* TEST_PROGRAM_STRUTILS */