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