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