]> git.ipfire.org Git - thirdparty/util-linux.git/blame - lib/strutils.c
docs: Fix various typos
[thirdparty/util-linux.git] / lib / strutils.c
CommitLineData
8abcf290
DB
1/*
2 * Copyright (C) 2010 Karel Zak <kzak@redhat.com>
3 * Copyright (C) 2010 Davidlohr Bueso <dave@gnu.org>
4 */
5
c4ecaf21 6#include <stdio.h>
8abcf290
DB
7#include <stdlib.h>
8#include <inttypes.h>
9#include <ctype.h>
10#include <errno.h>
ce877f2d 11#include <sys/stat.h>
c4ecaf21 12#include <string.h>
199e939d 13#include <assert.h>
c87638ad 14
eb76ca98 15#include "c.h"
ab65d635 16#include "nls.h"
5d2a9849 17#include "strutils.h"
c87638ad 18#include "bitops.h"
8abcf290
DB
19
20static int do_scale_by_power (uintmax_t *x, int base, int power)
21{
22 while (power--) {
23 if (UINTMAX_MAX / base < *x)
3643958f 24 return -ERANGE;
8abcf290
DB
25 *x *= base;
26 }
27 return 0;
28}
29
cf8de26a
KZ
30/*
31 * strtosize() - convert string to size (uintmax_t).
32 *
33 * Supported suffixes:
34 *
35 * XiB or X for 2^N
23106a29 36 * where X = {K,M,G,T,P,E,Z,Y}
cf8de26a
KZ
37 * or X = {k,m,g,t,p,e} (undocumented for backward compatibility only)
38 * for example:
39 * 10KiB = 10240
40 * 10K = 10240
41 *
42 * XB for 10^N
23106a29 43 * where X = {K,M,G,T,P,E,Z,Y}
cf8de26a
KZ
44 * for example:
45 * 10KB = 10000
46 *
23106a29
KZ
47 * The optinal 'power' variable returns number associated with used suffix
48 * {K,M,G,T,P,E,Z,Y} = {1,2,3,4,5,6,7,8}.
49 *
0e65dcde 50 * The function also supports decimal point, for example:
6c6750db
KZ
51 * 0.5MB = 500000
52 * 0.5MiB = 512000
53 *
cf8de26a
KZ
54 * Note that the function does not accept numbers with '-' (negative sign)
55 * prefix.
cf8de26a 56 */
23106a29 57int parse_size(const char *str, uintmax_t *res, int *power)
cf8de26a
KZ
58{
59 char *p;
6c6750db
KZ
60 uintmax_t x, frac = 0;
61 int base = 1024, rc = 0, pwr = 0, frac_zeros = 0;
23106a29
KZ
62
63 static const char *suf = "KMGTPEYZ";
64 static const char *suf2 = "kmgtpeyz";
65 const char *sp;
cf8de26a
KZ
66
67 *res = 0;
68
5296523d
KZ
69 if (!str || !*str) {
70 rc = -EINVAL;
cf8de26a 71 goto err;
5296523d 72 }
cf8de26a
KZ
73
74 /* Only positive numbers are acceptable
75 *
76 * Note that this check is not perfect, it would be better to
77 * use lconv->negative_sign. But coreutils use the same solution,
78 * so it's probably good enough...
79 */
80 p = (char *) str;
81 while (isspace((unsigned char) *p))
82 p++;
3643958f
KZ
83 if (*p == '-') {
84 rc = -EINVAL;
cf8de26a 85 goto err;
3643958f 86 }
cf8de26a
KZ
87 p = NULL;
88
89 errno = 0;
90 x = strtoumax(str, &p, 0);
91
92 if (p == str ||
3643958f 93 (errno != 0 && (x == UINTMAX_MAX || x == 0))) {
f643d334 94 rc = errno ? -errno : -EINVAL;
cf8de26a 95 goto err;
3643958f 96 }
cf8de26a
KZ
97 if (!p || !*p)
98 goto done; /* without suffix */
99
100 /*
101 * Check size suffixes
102 */
6c6750db 103check_suffix:
25e3dd17 104 if (*(p + 1) == 'i' && (*(p + 2) == 'B' || *(p + 2) == 'b') && !*(p + 3))
cf8de26a 105 base = 1024; /* XiB, 2^N */
25e3dd17 106 else if ((*(p + 1) == 'B' || *(p + 1) == 'b') && !*(p + 2))
cf8de26a 107 base = 1000; /* XB, 10^N */
3643958f 108 else if (*(p + 1)) {
6c6750db
KZ
109 struct lconv const *l = localeconv();
110 char *dp = l ? l->decimal_point : NULL;
111 size_t dpsz = dp ? strlen(dp) : 0;
112
113 if (frac == 0 && *p && dp && strncmp(dp, p, dpsz) == 0) {
114 char *fstr = p + dpsz;
115
116 for (p = fstr; *p && *p == '0'; p++)
117 frac_zeros++;
118 errno = 0, p = NULL;
119 frac = strtoumax(fstr, &p, 0);
120 if (p == fstr ||
121 (errno != 0 && (frac == UINTMAX_MAX || frac == 0))) {
f643d334 122 rc = errno ? -errno : -EINVAL;
6c6750db
KZ
123 goto err;
124 }
125 if (frac && (!p || !*p)) {
126 rc = -EINVAL;
127 goto err; /* without suffix, but with frac */
128 }
129 goto check_suffix;
130 }
3643958f 131 rc = -EINVAL;
cf8de26a 132 goto err; /* unexpected suffix */
3643958f 133 }
6c6750db 134
23106a29
KZ
135 sp = strchr(suf, *p);
136 if (sp)
137 pwr = (sp - suf) + 1;
138 else {
139 sp = strchr(suf2, *p);
140 if (sp)
141 pwr = (sp - suf2) + 1;
3643958f
KZ
142 else {
143 rc = -EINVAL;
23106a29 144 goto err;
3643958f 145 }
cf8de26a
KZ
146 }
147
23106a29
KZ
148 rc = do_scale_by_power(&x, base, pwr);
149 if (power)
150 *power = pwr;
6c6750db
KZ
151 if (frac && pwr) {
152 int zeros_in_pwr = frac_zeros % 3;
153 int frac_pwr = pwr - (frac_zeros / 3) - 1;
154 uintmax_t y = frac * (zeros_in_pwr == 0 ? 100 :
155 zeros_in_pwr == 1 ? 10 : 1);
156
157 if (frac_pwr < 0) {
158 rc = -EINVAL;
159 goto err;
160 }
161 do_scale_by_power(&y, base, frac_pwr);
162 x += y;
163 }
cf8de26a
KZ
164done:
165 *res = x;
cf8de26a 166err:
f643d334
RM
167 if (rc < 0)
168 errno = -rc;
3643958f 169 return rc;
cf8de26a
KZ
170}
171
23106a29
KZ
172int strtosize(const char *str, uintmax_t *res)
173{
174 return parse_size(str, res, NULL);
175}
176
416c43a9
KZ
177int isdigit_string(const char *str)
178{
179 const char *p;
180
181 for (p = str; p && *p && isdigit((unsigned char) *p); p++);
182
183 return p && p > str && !*p;
184}
185
40f00b4f
KZ
186int isxdigit_string(const char *str)
187{
188 const char *p;
189
190 for (p = str; p && *p && isxdigit((unsigned char) *p); p++);
191
192 return p && p > str && !*p;
193}
194
30b294c4
KZ
195/*
196 * parse_switch(argv[i], "on", "off", "yes", "no", NULL);
197 */
198int parse_switch(const char *arg, const char *errmesg, ...)
e5cf1476 199{
30b294c4
KZ
200 const char *a, *b;
201 va_list ap;
202
203 va_start(ap, errmesg);
204 do {
205 a = va_arg(ap, char *);
206 if (!a)
207 break;
208 b = va_arg(ap, char *);
209 if (!b)
210 break;
211
74819b5f
AH
212 if (strcmp(arg, a) == 0) {
213 va_end(ap);
30b294c4 214 return 1;
74819b5f
AH
215 } else if (strcmp(arg, b) == 0) {
216 va_end(ap);
30b294c4 217 return 0;
74819b5f 218 }
30b294c4
KZ
219 } while (1);
220 va_end(ap);
221
222 errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, arg);
e5cf1476 223}
416c43a9 224
02887b73
DT
225#ifndef HAVE_MEMPCPY
226void *mempcpy(void *restrict dest, const void *restrict src, size_t n)
227{
228 return ((char *)memcpy(dest, src, n)) + n;
229}
230#endif
231
8abcf290
DB
232#ifndef HAVE_STRNLEN
233size_t strnlen(const char *s, size_t maxlen)
234{
235 int i;
cf8de26a 236
8abcf290
DB
237 for (i = 0; i < maxlen; i++) {
238 if (s[i] == '\0')
239 return i + 1;
240 }
241 return maxlen;
242}
243#endif
cf8de26a 244
8abcf290
DB
245#ifndef HAVE_STRNCHR
246char *strnchr(const char *s, size_t maxlen, int c)
cf8de26a 247{
8abcf290
DB
248 for (; maxlen-- && *s != '\0'; ++s)
249 if (*s == (char)c)
250 return (char *)s;
251 return NULL;
252}
253#endif
cf8de26a 254
8abcf290
DB
255#ifndef HAVE_STRNDUP
256char *strndup(const char *s, size_t n)
257{
258 size_t len = strnlen(s, n);
fea1cbf7 259 char *new = malloc((len + 1) * sizeof(char));
8abcf290
DB
260 if (!new)
261 return NULL;
262 new[len] = '\0';
263 return (char *) memcpy(new, s, len);
264}
265#endif
cf8de26a 266
551dae40 267int16_t strtos16_or_err(const char *str, const char *errmesg)
a9f97001 268{
551dae40
KZ
269 int32_t num = strtos32_or_err(str, errmesg);
270
ca8eff2a
RM
271 if (num < INT16_MIN || num > INT16_MAX) {
272 errno = ERANGE;
273 err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
274 }
551dae40
KZ
275 return num;
276}
277
278uint16_t strtou16_or_err(const char *str, const char *errmesg)
279{
280 uint32_t num = strtou32_or_err(str, errmesg);
281
ca8eff2a
RM
282 if (num > UINT16_MAX) {
283 errno = ERANGE;
284 err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
285 }
551dae40
KZ
286 return num;
287}
288
289int32_t strtos32_or_err(const char *str, const char *errmesg)
290{
291 int64_t num = strtos64_or_err(str, errmesg);
292
ca8eff2a
RM
293 if (num < INT32_MIN || num > INT32_MAX) {
294 errno = ERANGE;
295 err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
296 }
551dae40
KZ
297 return num;
298}
299
300uint32_t strtou32_or_err(const char *str, const char *errmesg)
301{
302 uint64_t num = strtou64_or_err(str, errmesg);
303
ca8eff2a
RM
304 if (num > UINT32_MAX) {
305 errno = ERANGE;
306 err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
307 }
551dae40
KZ
308 return num;
309}
310
311int64_t strtos64_or_err(const char *str, const char *errmesg)
312{
313 int64_t num;
a9f97001
SK
314 char *end = NULL;
315
73b9e094 316 errno = 0;
a9f97001
SK
317 if (str == NULL || *str == '\0')
318 goto err;
551dae40 319 num = strtoimax(str, &end, 10);
a9f97001
SK
320
321 if (errno || str == end || (end && *end))
322 goto err;
323
324 return num;
a99c9130 325err:
73b9e094 326 if (errno == ERANGE)
a99c9130
KZ
327 err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
328
329 errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
a9f97001 330}
551dae40
KZ
331
332uint64_t strtou64_or_err(const char *str, const char *errmesg)
8abcf290 333{
551dae40 334 uintmax_t num;
a99c9130 335 char *end = NULL;
cf8de26a 336
73b9e094 337 errno = 0;
a99c9130
KZ
338 if (str == NULL || *str == '\0')
339 goto err;
551dae40 340 num = strtoumax(str, &end, 10);
cf8de26a 341
a99c9130
KZ
342 if (errno || str == end || (end && *end))
343 goto err;
8abcf290 344
a99c9130 345 return num;
8abcf290 346err:
73b9e094 347 if (errno == ERANGE)
a99c9130 348 err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
551dae40 349
a99c9130 350 errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
8abcf290 351}
551dae40
KZ
352
353
354double strtod_or_err(const char *str, const char *errmesg)
94d32126 355{
551dae40 356 double num;
a99c9130 357 char *end = NULL;
94d32126 358
73b9e094 359 errno = 0;
a99c9130
KZ
360 if (str == NULL || *str == '\0')
361 goto err;
551dae40 362 num = strtod(str, &end);
94d32126 363
a99c9130
KZ
364 if (errno || str == end || (end && *end))
365 goto err;
94d32126 366
a99c9130 367 return num;
94d32126 368err:
73b9e094 369 if (errno == ERANGE)
a99c9130
KZ
370 err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
371
372 errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
94d32126 373}
551dae40
KZ
374
375long strtol_or_err(const char *str, const char *errmesg)
376{
377 long num;
378 char *end = NULL;
379
73b9e094 380 errno = 0;
551dae40
KZ
381 if (str == NULL || *str == '\0')
382 goto err;
551dae40
KZ
383 num = strtol(str, &end, 10);
384
385 if (errno || str == end || (end && *end))
386 goto err;
387
388 return num;
389err:
73b9e094 390 if (errno == ERANGE)
551dae40 391 err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
73b9e094 392
551dae40
KZ
393 errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
394}
395
e53bc960
SK
396unsigned long strtoul_or_err(const char *str, const char *errmesg)
397{
398 unsigned long num;
399 char *end = NULL;
400
73b9e094 401 errno = 0;
e53bc960
SK
402 if (str == NULL || *str == '\0')
403 goto err;
e53bc960
SK
404 num = strtoul(str, &end, 10);
405
406 if (errno || str == end || (end && *end))
407 goto err;
408
409 return num;
410err:
73b9e094 411 if (errno == ERANGE)
a99c9130
KZ
412 err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
413
414 errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
415}
416
417uintmax_t strtosize_or_err(const char *str, const char *errmesg)
418{
419 uintmax_t num;
420
421 if (strtosize(str, &num) == 0)
422 return num;
423
424 if (errno)
425 err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
426
427 errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
e53bc960 428}
ce877f2d 429
477254da
KZ
430
431void strtotimeval_or_err(const char *str, struct timeval *tv, const char *errmesg)
432{
433 double user_input;
434
435 user_input = strtod_or_err(str, errmesg);
436 tv->tv_sec = (time_t) user_input;
437 tv->tv_usec = (long)((user_input - tv->tv_sec) * 1000000);
438}
439
ce877f2d
KZ
440/*
441 * Converts stat->st_mode to ls(1)-like mode string. The size of "str" must
d9554c97 442 * be 11 bytes.
ce877f2d 443 */
b0b24b11 444void xstrmode(mode_t mode, char *str)
ce877f2d 445{
7015df49
KZ
446 unsigned short i = 0;
447
ce877f2d 448 if (S_ISDIR(mode))
7015df49 449 str[i++] = 'd';
ce877f2d 450 else if (S_ISLNK(mode))
7015df49 451 str[i++] = 'l';
ce877f2d 452 else if (S_ISCHR(mode))
7015df49 453 str[i++] = 'c';
ce877f2d 454 else if (S_ISBLK(mode))
7015df49 455 str[i++] = 'b';
ce877f2d 456 else if (S_ISSOCK(mode))
7015df49 457 str[i++] = 's';
ce877f2d 458 else if (S_ISFIFO(mode))
7015df49 459 str[i++] = 'p';
ce877f2d 460 else if (S_ISREG(mode))
7015df49 461 str[i++] = '-';
ce877f2d 462
7015df49
KZ
463 str[i++] = mode & S_IRUSR ? 'r' : '-';
464 str[i++] = mode & S_IWUSR ? 'w' : '-';
465 str[i++] = (mode & S_ISUID
ce877f2d
KZ
466 ? (mode & S_IXUSR ? 's' : 'S')
467 : (mode & S_IXUSR ? 'x' : '-'));
7015df49
KZ
468 str[i++] = mode & S_IRGRP ? 'r' : '-';
469 str[i++] = mode & S_IWGRP ? 'w' : '-';
470 str[i++] = (mode & S_ISGID
ce877f2d
KZ
471 ? (mode & S_IXGRP ? 's' : 'S')
472 : (mode & S_IXGRP ? 'x' : '-'));
7015df49
KZ
473 str[i++] = mode & S_IROTH ? 'r' : '-';
474 str[i++] = mode & S_IWOTH ? 'w' : '-';
475 str[i++] = (mode & S_ISVTX
ce877f2d
KZ
476 ? (mode & S_IXOTH ? 't' : 'T')
477 : (mode & S_IXOTH ? 'x' : '-'));
7015df49 478 str[i] = '\0';
ce877f2d 479}
c4ecaf21
DB
480
481/*
482 * returns exponent (2^x=n) in range KiB..PiB
483 */
484static int get_exp(uint64_t n)
485{
486 int shft;
487
488 for (shft = 10; shft <= 60; shft += 10) {
489 if (n < (1ULL << shft))
490 break;
491 }
492 return shft - 10;
493}
494
5d2a9849 495char *size_to_human_string(int options, uint64_t bytes)
c4ecaf21
DB
496{
497 char buf[32];
f9e05daf
DR
498 int dec, exp;
499 uint64_t frac;
500 const char *letters = "BKMGTPE";
5d2a9849 501 char suffix[sizeof(" KiB")], *psuf = suffix;
c4ecaf21
DB
502 char c;
503
5d2a9849
FC
504 if (options & SIZE_SUFFIX_SPACE)
505 *psuf++ = ' ';
506
c4ecaf21
DB
507 exp = get_exp(bytes);
508 c = *(letters + (exp ? exp / 10 : 0));
509 dec = exp ? bytes / (1ULL << exp) : bytes;
510 frac = exp ? bytes % (1ULL << exp) : 0;
511
5d2a9849
FC
512 *psuf++ = c;
513
514 if ((options & SIZE_SUFFIX_3LETTER) && (c != 'B')) {
515 *psuf++ = 'i';
516 *psuf++ = 'B';
517 }
518
519 *psuf = '\0';
520
521 /* fprintf(stderr, "exp: %d, unit: %c, dec: %d, frac: %jd\n",
522 * exp, suffix[0], dec, frac);
f9e05daf
DR
523 */
524
c4ecaf21
DB
525 if (frac) {
526 /* round */
527 frac = (frac / (1ULL << (exp - 10)) + 50) / 100;
528 if (frac == 10)
529 dec++, frac = 0;
530 }
531
532 if (frac) {
533 struct lconv const *l = localeconv();
534 char *dp = l ? l->decimal_point : NULL;
535
536 if (!dp || !*dp)
537 dp = ".";
7231fb2a 538 snprintf(buf, sizeof(buf), "%d%s%" PRIu64 "%s", dec, dp, frac, suffix);
c4ecaf21 539 } else
5d2a9849 540 snprintf(buf, sizeof(buf), "%d%s", dec, suffix);
c4ecaf21
DB
541
542 return strdup(buf);
543}
013bff51 544
c87638ad
KZ
545/*
546 * Parses comma delimited list to array with IDs, for example:
547 *
548 * "aaa,bbb,ccc" --> ary[0] = FOO_AAA;
549 * ary[1] = FOO_BBB;
550 * ary[3] = FOO_CCC;
551 *
552 * The function name2id() provides conversion from string to ID.
553 *
554 * Returns: >= 0 : number of items added to ary[]
555 * -1 : parse error or unknown item
556 * -2 : arysz reached
557 */
558int string_to_idarray(const char *list, int ary[], size_t arysz,
559 int (name2id)(const char *, size_t))
560{
561 const char *begin = NULL, *p;
b28f2267 562 size_t n = 0;
c87638ad
KZ
563
564 if (!list || !*list || !ary || !arysz || !name2id)
565 return -1;
566
567 for (p = list; p && *p; p++) {
568 const char *end = NULL;
569 int id;
570
179b923a
CR
571 if (n >= arysz)
572 return -2;
c87638ad
KZ
573 if (!begin)
574 begin = p; /* begin of the column name */
575 if (*p == ',')
576 end = p; /* terminate the name */
577 if (*(p + 1) == '\0')
578 end = p + 1; /* end of string */
579 if (!begin || !end)
580 continue;
581 if (end <= begin)
582 return -1;
583
584 id = name2id(begin, end - begin);
585 if (id == -1)
586 return -1;
587 ary[ n++ ] = id;
c87638ad
KZ
588 begin = NULL;
589 if (end && !*end)
590 break;
591 }
592 return n;
593}
594
f5077b51
MB
595/*
596 * Parses the array like string_to_idarray but if format is "+aaa,bbb"
597 * it adds fields to array instead of replacing them.
598 */
599int string_add_to_idarray(const char *list, int ary[], size_t arysz,
40b17508 600 size_t *ary_pos, int (name2id)(const char *, size_t))
f5077b51
MB
601{
602 const char *list_add;
603 int r;
604
40b17508 605 if (!list || !*list || !ary_pos || *ary_pos > arysz)
f5077b51
MB
606 return -1;
607
608 if (list[0] == '+')
609 list_add = &list[1];
610 else {
611 list_add = list;
612 *ary_pos = 0;
613 }
614
615 r = string_to_idarray(list_add, &ary[*ary_pos], arysz - *ary_pos, name2id);
616 if (r > 0)
617 *ary_pos += r;
618 return r;
619}
620
c87638ad
KZ
621/*
622 * LIST ::= <item> [, <item>]
623 *
624 * The <item> is translated to 'id' by name2id() function and the 'id' is used
455fe9a0 625 * as a position in the 'ary' bit array. It means that the 'id' has to be in
c87638ad
KZ
626 * range <0..N> where N < sizeof(ary) * NBBY.
627 *
a883c634 628 * Returns: 0 on success, <0 on error.
c87638ad
KZ
629 */
630int string_to_bitarray(const char *list,
631 char *ary,
632 int (*name2bit)(const char *, size_t))
633{
634 const char *begin = NULL, *p;
635
636 if (!list || !name2bit || !ary)
637 return -EINVAL;
638
639 for (p = list; p && *p; p++) {
640 const char *end = NULL;
641 int bit;
642
643 if (!begin)
644 begin = p; /* begin of the level name */
645 if (*p == ',')
646 end = p; /* terminate the name */
647 if (*(p + 1) == '\0')
648 end = p + 1; /* end of string */
649 if (!begin || !end)
650 continue;
651 if (end <= begin)
652 return -1;
653
654 bit = name2bit(begin, end - begin);
655 if (bit < 0)
656 return bit;
657 setbit(ary, bit);
658 begin = NULL;
659 if (end && !*end)
660 break;
661 }
5ef16771
KZ
662 return 0;
663}
664
665/*
666 * LIST ::= <item> [, <item>]
667 *
668 * The <item> is translated to 'id' by name2flag() function and the flags is
669 * set to the 'mask'
670*
671 * Returns: 0 on success, <0 on error.
672 */
673int string_to_bitmask(const char *list,
674 unsigned long *mask,
675 long (*name2flag)(const char *, size_t))
676{
677 const char *begin = NULL, *p;
678
679 if (!list || !name2flag || !mask)
680 return -EINVAL;
681
682 for (p = list; p && *p; p++) {
683 const char *end = NULL;
684 long flag;
685
686 if (!begin)
687 begin = p; /* begin of the level name */
688 if (*p == ',')
689 end = p; /* terminate the name */
690 if (*(p + 1) == '\0')
691 end = p + 1; /* end of string */
692 if (!begin || !end)
693 continue;
694 if (end <= begin)
695 return -1;
696
697 flag = name2flag(begin, end - begin);
698 if (flag < 0)
699 return flag; /* error */
700 *mask |= flag;
701 begin = NULL;
702 if (end && !*end)
703 break;
704 }
c87638ad
KZ
705 return 0;
706}
013bff51 707
a883c634
DB
708/*
709 * Parse the lower and higher values in a string containing
710 * "lower:higher" or "lower-higher" format. Note that either
af7df9ee
DB
711 * the lower or the higher values may be missing, and the def
712 * value will be assigned to it by default.
a883c634
DB
713 *
714 * Returns: 0 on success, <0 on error.
715 */
af7df9ee 716int parse_range(const char *str, int *lower, int *upper, int def)
a883c634
DB
717{
718 char *end = NULL;
719
720 if (!str)
721 return 0;
722
af7df9ee 723 *upper = *lower = def;
a883c634
DB
724 errno = 0;
725
726 if (*str == ':') { /* <:N> */
727 str++;
728 *upper = strtol(str, &end, 10);
729 if (errno || !end || *end || end == str)
730 return -1;
731 } else {
732 *upper = *lower = strtol(str, &end, 10);
733 if (errno || !end || end == str)
734 return -1;
735
736 if (*end == ':' && !*(end + 1)) /* <M:> */
2d47fa39 737 *upper = def;
a883c634
DB
738 else if (*end == '-' || *end == ':') { /* <M:N> <M-N> */
739 str = end + 1;
740 end = NULL;
741 errno = 0;
742 *upper = strtol(str, &end, 10);
743
744 if (errno || !end || *end || end == str)
745 return -1;
746 }
747 }
748 return 0;
749}
750
b106d052
PU
751/*
752 * Compare two strings for equality, ignoring at most one trailing
753 * slash.
754 */
755int streq_except_trailing_slash(const char *s1, const char *s2)
756{
757 int equal;
758
759 if (!s1 && !s2)
760 return 1;
761 if (!s1 || !s2)
762 return 0;
763
764 equal = !strcmp(s1, s2);
765
766 if (!equal) {
767 size_t len1 = strlen(s1);
768 size_t len2 = strlen(s2);
769
770 if (len1 && *(s1 + len1 - 1) == '/')
771 len1--;
772 if (len2 && *(s2 + len2 - 1) == '/')
773 len2--;
774 if (len1 != len2)
775 return 0;
776
777 equal = !strncmp(s1, s2, len1);
778 }
779
780 return equal;
781}
782
a883c634 783
548b9714
KZ
784char *strnappend(const char *s, const char *suffix, size_t b)
785{
786 size_t a;
787 char *r;
788
789 if (!s && !suffix)
790 return strdup("");
791 if (!s)
792 return strndup(suffix, b);
793 if (!suffix)
794 return strdup(s);
795
796 assert(s);
797 assert(suffix);
798
799 a = strlen(s);
800 if (b > ((size_t) -1) - a)
801 return NULL;
802
803 r = malloc(a + b + 1);
804 if (!r)
805 return NULL;
806
807 memcpy(r, s, a);
808 memcpy(r + a, suffix, b);
809 r[a+b] = 0;
810
811 return r;
812}
813
814char *strappend(const char *s, const char *suffix)
815{
816 return strnappend(s, suffix, suffix ? strlen(suffix) : 0);
817}
818
3c201431
KZ
819char *strfappend(const char *s, const char *format, ...)
820{
821 va_list ap;
822 char *val, *res;
823 int sz;
824
825 va_start(ap, format);
826 sz = vasprintf(&val, format, ap);
827 va_end(ap);
828
829 if (sz < 0)
830 return NULL;
831
832 res = strnappend(s, val, sz);
833 free(val);
834 return res;
835}
548b9714
KZ
836
837static size_t strcspn_escaped(const char *s, const char *reject)
838{
839 int escaped = 0;
840 int n;
841
842 for (n=0; s[n]; n++) {
843 if (escaped)
844 escaped = 0;
845 else if (s[n] == '\\')
846 escaped = 1;
847 else if (strchr(reject, s[n]))
848 break;
849 }
850
851 /* if s ends in \, return index of previous char */
852 return n - escaped;
853}
854
855/* Split a string into words. */
856const char *split(const char **state, size_t *l, const char *separator, int quoted)
857{
858 const char *current;
859
860 current = *state;
861
862 if (!*current) {
863 assert(**state == '\0');
864 return NULL;
865 }
866
867 current += strspn(current, separator);
868 if (!*current) {
869 *state = current;
870 return NULL;
871 }
872
873 if (quoted && strchr("\'\"", *current)) {
874 char quotechars[2] = {*current, '\0'};
875
876 *l = strcspn_escaped(current + 1, quotechars);
877 if (current[*l + 1] == '\0' || current[*l + 1] != quotechars[0] ||
878 (current[*l + 2] && !strchr(separator, current[*l + 2]))) {
879 /* right quote missing or garbage at the end */
880 *state = current;
881 return NULL;
882 }
883 *state = current++ + *l + 2;
884 } else if (quoted) {
885 *l = strcspn_escaped(current, separator);
886 if (current[*l] && !strchr(separator, current[*l])) {
887 /* unfinished escape */
888 *state = current;
889 return NULL;
890 }
891 *state = current + *l;
892 } else {
893 *l = strcspn(current, separator);
894 *state = current + *l;
895 }
896
897 return current;
898}
899
3e5a5455
SK
900/* Rewind file pointer forward to new line. */
901int skip_fline(FILE *fp)
902{
d551d668 903 int ch;
3e5a5455
SK
904
905 do {
906 if ((ch = fgetc(fp)) == EOF)
907 return 1;
908 if (ch == '\n')
909 return 0;
910 } while (1);
911}
548b9714 912
013bff51
KZ
913#ifdef TEST_PROGRAM
914
013bff51
KZ
915int main(int argc, char *argv[])
916{
917 uintmax_t size = 0;
5d2a9849 918 char *hum, *hum2;
013bff51
KZ
919
920 if (argc < 2) {
921 fprintf(stderr, "usage: %s <number>[suffix]\n", argv[0]);
922 exit(EXIT_FAILURE);
923 }
924
925 if (strtosize(argv[1], &size))
926 errx(EXIT_FAILURE, "invalid size '%s' value", argv[1]);
927
5d2a9849
FC
928 hum = size_to_human_string(SIZE_SUFFIX_1LETTER, size);
929 hum2 = size_to_human_string(SIZE_SUFFIX_3LETTER |
930 SIZE_SUFFIX_SPACE, size);
f9e05daf 931
5d2a9849 932 printf("%25s : %20ju : %8s : %12s\n", argv[1], size, hum, hum2);
f9e05daf 933 free(hum);
5d2a9849 934 free(hum2);
f9e05daf 935
958d2b71 936 return EXIT_SUCCESS;
013bff51
KZ
937}
938#endif /* TEST_PROGRAM */