]>
git.ipfire.org Git - thirdparty/util-linux.git/blob - lib/strutils.c
c27d645c21b8200914e5f22a105fa876af97aa0c
2 * No copyright is claimed. This code is in the public domain; do with
5 * Authors: Karel Zak <kzak@redhat.com> [2010]
6 * Davidlohr Bueso <dave@gnu.org> [2010]
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 ul_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 ul_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 * For example: ul_parse_switch(argv[i], "on", "off", "yes", "no", NULL);
252 int ul_parse_switch(const char *arg
, ...)
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
, _("unsupported argument: %s"), 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
;
396 int ul_strtou16(const char *str
, uint16_t *num
, int base
)
401 rc
= ul_strtou64(str
, &tmp
, base
);
402 if (rc
== 0 && tmp
> UINT16_MAX
)
403 rc
= -(errno
= ERANGE
);
405 *num
= (uint16_t) tmp
;
410 * Convert strings to numbers in defined range and print message on error.
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.
416 int64_t str2num_or_err(const char *str
, int base
, const char *errmesg
,
417 int64_t low
, int64_t up
)
422 rc
= ul_strtos64(str
, &num
, base
);
423 if (rc
== 0 && ((low
&& num
< low
) || (up
&& num
> up
)))
424 rc
= -(errno
= ERANGE
);
428 err(STRTOXX_EXIT_CODE
, "%s: '%s'", errmesg
, str
);
429 errx(STRTOXX_EXIT_CODE
, "%s: '%s'", errmesg
, str
);
434 uint64_t str2unum_or_err(const char *str
, int base
, const char *errmesg
, uint64_t up
)
439 rc
= ul_strtou64(str
, &num
, base
);
440 if (rc
== 0 && (up
&& num
> up
))
441 rc
= -(errno
= ERANGE
);
445 err(STRTOXX_EXIT_CODE
, "%s: '%s'", errmesg
, str
);
446 errx(STRTOXX_EXIT_CODE
, "%s: '%s'", errmesg
, str
);
451 double strtod_or_err(const char *str
, const char *errmesg
)
457 if (str
== NULL
|| *str
== '\0')
459 num
= strtod(str
, &end
);
461 if (errno
|| str
== end
|| (end
&& *end
))
467 err(STRTOXX_EXIT_CODE
, "%s: '%s'", errmesg
, str
);
469 errx(STRTOXX_EXIT_CODE
, "%s: '%s'", errmesg
, str
);
472 int ul_strtold(const char *str
, long double *num
)
477 if (str
== NULL
|| *str
== '\0')
478 return -(errno
= EINVAL
);
479 *num
= strtold(str
, &end
);
483 if (str
== end
|| (end
&& *end
))
484 return -(errno
= EINVAL
);
488 long double strtold_or_err(const char *str
, const char *errmesg
)
492 if (ul_strtold(str
, &num
) == 0)
495 err(STRTOXX_EXIT_CODE
, "%s: '%s'", errmesg
, str
);
497 errx(STRTOXX_EXIT_CODE
, "%s: '%s'", errmesg
, str
);
500 uintmax_t strtosize_or_err(const char *str
, const char *errmesg
)
504 if (strtosize(str
, &num
) == 0)
508 err(STRTOXX_EXIT_CODE
, "%s: '%s'", errmesg
, str
);
510 errx(STRTOXX_EXIT_CODE
, "%s: '%s'", errmesg
, str
);
514 void strtotimeval_or_err(const char *str
, struct timeval
*tv
, const char *errmesg
)
516 long double user_input
;
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);
523 void strtotimespec_or_err(const char *str
, struct timespec
*ts
, const char *errmesg
)
525 long double user_input
;
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);
532 time_t strtotime_or_err(const char *str
, const char *errmesg
)
536 user_input
= strtos64_or_err(str
, errmesg
);
537 return (time_t) user_input
;
540 bool hyperlinkwanted(const char *mode
)
542 if (mode
&& strcmp(mode
, "never") == 0)
545 if (mode
&& strcmp(mode
, "always") == 0)
548 if (!mode
|| strcmp(mode
, "auto") == 0)
549 return isatty(STDOUT_FILENO
) ? true : false;
551 errx(EXIT_FAILURE
, _("invalid argument of --hyperlink: %s"), mode
);
555 * Converts stat->st_mode to ls(1)-like mode string. The size of "str" must
558 char *xstrmode(mode_t mode
, char *str
)
560 unsigned short i
= 0;
564 else if (S_ISLNK(mode
))
566 else if (S_ISCHR(mode
))
568 else if (S_ISBLK(mode
))
570 else if (S_ISSOCK(mode
))
572 else if (S_ISFIFO(mode
))
574 else if (S_ISREG(mode
))
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' : '-'));
598 * returns exponent (2^x=n) in range KiB..EiB (2^10..2^60)
600 static int get_exp(uint64_t n
)
604 for (shft
= 10; shft
<= 60; shft
+= 10) {
605 if (n
< (1ULL << shft
))
611 char *size_to_human_string(int options
, uint64_t bytes
)
616 const char *letters
= "BKMGTPE";
617 char suffix
[sizeof(" KiB")], *psuf
= suffix
;
620 if (options
& SIZE_SUFFIX_SPACE
)
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;
631 if ((options
& SIZE_SUFFIX_3LETTER
) && (c
!= 'B')) {
638 /* fprintf(stderr, "exp: %d, unit: %c, dec: %d, frac: %jd\n",
639 * exp, suffix[0], dec, frac);
644 /* get 3 digits after decimal point */
645 if (frac
>= UINT64_MAX
/ 1000)
646 frac
= ((frac
/ 1024) * 1000) / (1ULL << (exp
- 10)) ;
648 frac
= (frac
* 1000) / (1ULL << (exp
)) ;
650 if (options
& SIZE_DECIMAL_2DIGITS
) {
651 /* round 4/5 and keep 2 digits after decimal point */
652 frac
= (frac
+ 5) / 10 ;
654 /* round 4/5 and keep 1 digit after decimal point */
655 frac
= ((frac
+ 50) / 100) * 10 ;
658 /* rounding could have overflowed */
666 struct lconv
const *l
= localeconv();
667 char *dp
= l
? l
->decimal_point
: NULL
;
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')
679 xstrncpy(buf
+len
, suffix
, sizeof(buf
) - len
);
681 *buf
= '\0'; /* snprintf error */
683 snprintf(buf
, sizeof(buf
), "%d%s", dec
, suffix
);
689 * Parses comma delimited list to array with IDs, for example:
691 * "aaa,bbb,ccc" --> ary[0] = FOO_AAA;
695 * The function name2id() provides conversion from string to ID.
697 * Returns: >= 0 : number of items added to ary[]
698 * -1 : parse error or unknown item
701 int string_to_idarray(const char *list
, int ary
[], size_t arysz
,
702 int (name2id
)(const char *, size_t))
704 const char *begin
= NULL
, *p
;
707 if (!list
|| !*list
|| !ary
|| !arysz
|| !name2id
)
710 for (p
= list
; p
&& *p
; p
++) {
711 const char *end
= NULL
;
717 begin
= p
; /* begin of the column name */
719 end
= p
; /* terminate the name */
720 if (*(p
+ 1) == '\0')
721 end
= p
+ 1; /* end of string */
727 id
= name2id(begin
, end
- begin
);
739 * Parses the array like string_to_idarray but if format is "+aaa,bbb"
740 * it adds fields to array instead of replacing them.
742 int string_add_to_idarray(const char *list
, int ary
[], size_t arysz
,
743 size_t *ary_pos
, int (name2id
)(const char *, size_t))
745 const char *list_add
;
748 if (!list
|| !*list
|| !ary_pos
|| *ary_pos
> arysz
)
758 r
= string_to_idarray(list_add
, &ary
[*ary_pos
], arysz
- *ary_pos
, name2id
);
765 * LIST ::= <item> [, <item>]
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.
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>.
775 * Returns: 0 on success, <0 on error.
777 int string_to_bitarray(const char *list
,
779 int (*name2bit
)(const char *, size_t),
782 const char *begin
= NULL
, *p
;
784 if (!list
|| !name2bit
|| !ary
)
787 for (p
= list
; p
&& *p
; p
++) {
788 const char *end
= NULL
;
789 int bit
, set_lower
= 0, set_higher
= 0;
792 begin
= p
; /* begin of the level name */
794 end
= p
; /* terminate the name */
795 if (*(p
+ 1) == '\0')
796 end
= p
+ 1; /* end of string */
802 if (*(end
- 1) == '+') {
805 } else if (*begin
== '+') {
811 bit
= name2bit(begin
, end
- begin
);
819 while (++bit
< (int) allow_range
)
829 * LIST ::= <item> [, <item>]
831 * The <item> is translated to 'id' by name2flag() function and the flags is
834 * Returns: 0 on success, <0 on error.
836 int string_to_bitmask(const char *list
,
838 long (*name2flag
)(const char *, size_t))
840 const char *begin
= NULL
, *p
;
842 if (!list
|| !name2flag
|| !mask
)
845 for (p
= list
; p
&& *p
; p
++) {
846 const char *end
= NULL
;
850 begin
= p
; /* begin of the level name */
852 end
= p
; /* terminate the name */
853 if (*(p
+ 1) == '\0')
854 end
= p
+ 1; /* end of string */
860 flag
= name2flag(begin
, end
- begin
);
862 return flag
; /* error */
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.
877 * Returns: 0 on success, <0 on error.
879 int ul_parse_range(const char *str
, int *lower
, int *upper
, int def
)
886 *upper
= *lower
= def
;
889 if (*str
== ':') { /* <:N> */
891 *upper
= strtol(str
, &end
, 10);
892 if (errno
|| !end
|| *end
|| end
== str
)
895 *upper
= *lower
= strtol(str
, &end
, 10);
896 if (errno
|| !end
|| end
== str
)
899 if (*end
== ':' && !*(end
+ 1)) /* <M:> */
901 else if (*end
== '-' || *end
== ':') { /* <M:N> <M-N> */
905 *upper
= strtol(str
, &end
, 10);
907 if (errno
|| !end
|| *end
|| end
== str
)
914 static const char *next_path_segment(const char *str
, size_t *sz
)
916 const char *start
, *p
;
920 while (start
&& *start
== '/' && *(start
+ 1) == '/')
923 if (!start
|| !*start
)
926 for (*sz
= 1, p
= start
+ 1; *p
&& *p
!= '/'; p
++) {
933 int streq_paths(const char *a
, const char *b
)
937 const char *a_seg
= next_path_segment(a
, &a_sz
);
938 const char *b_seg
= next_path_segment(b
, &b_sz
);
941 fprintf(stderr, "A>>>(%zu) '%s'\n", a_sz, a_seg);
942 fprintf(stderr, "B>>>(%zu) '%s'\n", b_sz, b_seg);
945 /* end of the path */
946 if (a_sz
+ b_sz
== 0)
949 /* ignore trailing slash */
950 if (a_sz
+ b_sz
== 1 &&
951 ((a_seg
&& *a_seg
== '/') || (b_seg
&& *b_seg
== '/')))
954 if (!a_seg
|| !b_seg
)
956 if (a_sz
!= b_sz
|| strncmp(a_seg
, b_seg
, a_sz
) != 0)
966 /* concatenate two strings to a new string, the size of the second string is limited by @b */
967 char *ul_strnconcat(const char *s
, const char *suffix
, size_t b
)
975 return strndup(suffix
, b
);
983 if (b
> ((size_t) -1) - a
)
986 r
= malloc(a
+ b
+ 1);
991 memcpy(r
+ a
, suffix
, b
);
997 /* concatenate two strings to a new string */
998 char *ul_strconcat(const char *s
, const char *suffix
)
1000 return ul_strnconcat(s
, suffix
, suffix
? strlen(suffix
) : 0);
1003 /* concatenate @s and string defined by @format to a new string */
1004 char *ul_strfconcat(const char *s
, const char *format
, ...)
1010 va_start(ap
, format
);
1011 sz
= vasprintf(&val
, format
, ap
);
1017 res
= ul_strnconcat(s
, val
, sz
);
1022 int ul_strappend(char **a
, const char *b
)
1033 return !*a
? -ENOMEM
: 0;
1039 tmp
= realloc(*a
, al
+ bl
+ 1);
1043 memcpy((*a
) + al
, b
, bl
+ 1);
1047 /* the hybrid version of strfconcat and strappend. */
1048 int strfappend(char **a
, const char *format
, ...)
1053 va_start(ap
, format
);
1054 res
= ul_strvfappend(a
, format
, ap
);
1060 extern int ul_strvfappend(char **a
, const char *format
, va_list ap
)
1066 sz
= vasprintf(&val
, format
, ap
);
1070 res
= ul_strappend(a
, val
);
1075 static size_t strcspn_escaped(const char *s
, const char *reject
)
1080 for (n
=0; s
[n
]; n
++) {
1083 else if (s
[n
] == '\\')
1085 else if (strchr(reject
, s
[n
]))
1089 /* if s ends in \, return index of previous char */
1094 * Like strchr() but ignores @c if escaped by '\', '\\' is interpreted like '\'.
1096 * For example for @c='X':
1098 * "abcdXefgXh" --> "XefgXh"
1099 * "abcd\XefgXh" --> "Xh"
1100 * "abcd\\XefgXh" --> "XefgXh"
1101 * "abcd\\\XefgXh" --> "Xh"
1102 * "abcd\Xefg\Xh" --> (null)
1104 * "abcd\\XefgXh" --> "\XefgXh" for @c='\\'
1106 char *ul_strchr_escaped(const char *s
, int c
)
1111 for (p
= (char *) s
; p
&& *p
; p
++) {
1112 if (!esc
&& *p
== '\\') {
1116 if (*p
== c
&& (!esc
|| c
== '\\'))
1124 /* Split a string into words. */
1125 const char *ul_split(const char **state
, size_t *l
, const char *separator
, int quoted
)
1127 const char *current
;
1132 assert(**state
== '\0');
1136 current
+= strspn(current
, separator
);
1142 if (quoted
&& strchr("\'\"", *current
)) {
1143 char quotechars
[2] = {*current
, '\0'};
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 */
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 */
1160 *state
= current
+ *l
;
1162 *l
= strcspn(current
, separator
);
1163 *state
= current
+ *l
;
1169 /* Rewind file pointer forward to new line. */
1170 int skip_fline(FILE *fp
)
1175 if ((ch
= fgetc(fp
)) == EOF
)
1183 /* compare two strings, but ignoring non-alnum and case of the characters, for example
1184 * "Hello (123)!" is the same as "hello123".
1186 int ul_stralnumcmp(const char *p1
, const char *p2
)
1188 const unsigned char *s1
= (const unsigned char *) p1
;
1189 const unsigned char *s2
= (const unsigned char *) p2
;
1190 unsigned char c1
, c2
;
1194 c1
= (unsigned char) *s1
++;
1195 } while (c1
!= '\0' && !isalnum((unsigned int) c1
));
1198 c2
= (unsigned char) *s2
++;
1199 } while (c2
!= '\0' && !isalnum((unsigned int) c2
));
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"'.
1216 * Note this function is used by libmount to parse mount options. Be careful when modify.
1218 * Returns -EINVAL on parse error, 1 at the end of optstr and 0 on success.
1220 int ul_optstr_next(char **optstr
, char **name
, size_t *namesz
,
1221 char **value
, size_t *valsz
)
1224 char *start
= NULL
, *stop
= NULL
, *p
, *sep
= NULL
;
1241 /* trim leading commas as to not invalidate option
1242 * strings with multiple consecutive commas */
1243 while (optstr0
&& *optstr0
== ',')
1246 for (p
= optstr0
; p
&& *p
; p
++) {
1247 if (!start
&& *p
== '=')
1250 start
= p
; /* beginning of the option item */
1251 if (*p
== '"' && (p
== optstr0
|| *(p
- 1) != '\\'))
1252 open_quote
^= 1; /* reverse the status */
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
)
1269 *namesz
= sep
? sep
- start
: stop
- start
;
1270 *optstr
= *stop
? stop
+ 1 : stop
;
1276 *valsz
= stop
- sep
- 1;
1281 return 1; /* end of optstr */
1284 int ul_optstr_is_valid(const char *optstr
)
1287 char *p
= (char *) optstr
;
1289 while ((rc
= ul_optstr_next(&p
, NULL
, NULL
, NULL
, NULL
)) == 0);
1290 return rc
< 0 ? 0 : 1;
1293 #ifdef TEST_PROGRAM_STRUTILS
1302 static int test_strdup_to_member(int argc
, char *argv
[])
1307 return EXIT_FAILURE
;
1309 xx
= calloc(1, sizeof(*xx
));
1311 err(EXIT_FAILURE
, "calloc() failed");
1313 strdup_to_struct_member(xx
, name
, argv
[1]);
1314 strdup_to_struct_member(xx
, value
, argv
[2]);
1316 if (strcmp(xx
->name
, argv
[1]) != 0 &&
1317 strcmp(xx
->value
, argv
[2]) != 0)
1318 errx(EXIT_FAILURE
, "strdup_to_struct_member() failed");
1320 printf("1: '%s', 2: '%s'\n", xx
->name
, xx
->value
);
1325 return EXIT_SUCCESS
;
1328 static int test_strutils_sizes(int argc
, char *argv
[])
1331 char *hum1
, *hum2
, *hum3
;
1334 return EXIT_FAILURE
;
1336 if (strtosize(argv
[1], &size
))
1337 errx(EXIT_FAILURE
, "invalid size '%s' value", argv
[1]);
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
|
1344 SIZE_DECIMAL_2DIGITS
, size
);
1346 printf("%25s : %20ju : %8s : %12s : %13s\n", argv
[1], size
, hum1
, hum2
, hum3
);
1351 return EXIT_SUCCESS
;
1354 static int test_strutils_cmp_paths(int argc
, char *argv
[])
1356 int rc
= streq_paths(argv
[1], argv
[2]);
1359 return EXIT_FAILURE
;
1361 printf("%s: '%s' '%s'\n", rc
== 1 ? "YES" : "NOT", argv
[1], argv
[2]);
1362 return EXIT_SUCCESS
;
1365 static int test_strutils_normalize(int argc
, char *argv
[])
1367 unsigned char *src
, *dst
, *org
;
1371 return EXIT_FAILURE
;
1373 org
= (unsigned char *) strdup(argv
[1]);
1374 src
= (unsigned char *) strdup((char *) org
);
1375 len
= strlen((char *) src
);
1376 dst
= malloc(len
+ 1);
1378 if (!org
|| !src
|| !dst
)
1382 sz
= __normalize_whitespace(src
, len
, dst
, len
+ 1);
1383 printf("1: '%s' --> '%s' [sz=%zu]\n", src
, dst
, sz
);
1386 sz
= normalize_whitespace(src
);
1387 printf("2: '%s' --> '%s' [sz=%zu]\n", org
, src
, sz
);
1394 return EXIT_SUCCESS
;
1397 static int test_strutils_cstrcasecmp(int argc
, char *argv
[])
1402 return EXIT_FAILURE
;
1408 return EXIT_FAILURE
;
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
)));
1414 return EXIT_SUCCESS
;
1417 int main(int argc
, char *argv
[])
1419 if (argc
== 3 && strcmp(argv
[1], "--size") == 0) {
1420 return test_strutils_sizes(argc
- 1, argv
+ 1);
1422 } else if (argc
== 3 && strcmp(argv
[1], "--parse-switch") == 0) {
1423 printf("'%s'-->%d\n", argv
[2], ul_parse_switch(argv
[2],
1425 "enable", "disable",
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);
1433 } else if (argc
== 4 && strcmp(argv
[1], "--strdup-member") == 0) {
1434 return test_strdup_to_member(argc
- 1, argv
+ 1);
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
;
1441 } else if (argc
== 4 && strcmp(argv
[1], "--cstrcasecmp") == 0) {
1442 return test_strutils_cstrcasecmp(argc
- 1, argv
+ 1);
1444 } else if (argc
== 3 && strcmp(argv
[1], "--normalize") == 0) {
1445 return test_strutils_normalize(argc
- 1, argv
+ 1);
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
;
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
;
1470 } else if (argc
== 2 && strcmp(argv
[1], "--next-string") == 0) {
1471 char *buf
= "abc\0Y\0\0xyz\0X";
1472 char *end
= buf
+ 12;
1476 printf("str: '%s'\n", p
);
1477 } while ((p
= ul_next_string(p
, end
)));
1479 return EXIT_SUCCESS
;
1481 } else if (argc
== 3 && strcmp(argv
[1], "--optstr") == 0) {
1483 size_t namesz
, valsz
;
1484 char *name
= NULL
, *val
= NULL
;
1488 if (!ul_optstr_is_valid(p
))
1489 errx(EXIT_FAILURE
, _("unsupported option format: %s"), p
);
1491 while ((rc
= ul_optstr_next(&p
, &name
, &namesz
, &val
, &valsz
)) == 0) {
1492 printf("'%.*s' : '%.*s'\n", (int) namesz
, name
,
1496 return EXIT_SUCCESS
;
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",
1511 return EXIT_FAILURE
;
1513 #endif /* TEST_PROGRAM_STRUTILS */