]> git.ipfire.org Git - thirdparty/util-linux.git/blame - lib/strutils.c
libsmartcols: add sample-scols-continuous
[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
KZ
93 (errno != 0 && (x == UINTMAX_MAX || x == 0))) {
94 rc = errno ? -errno : -1;
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))) {
122 rc = errno ? -errno : -1;
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:
3643958f 167 return rc;
cf8de26a
KZ
168}
169
23106a29
KZ
170int strtosize(const char *str, uintmax_t *res)
171{
172 return parse_size(str, res, NULL);
173}
174
416c43a9
KZ
175int isdigit_string(const char *str)
176{
177 const char *p;
178
179 for (p = str; p && *p && isdigit((unsigned char) *p); p++);
180
181 return p && p > str && !*p;
182}
183
30b294c4
KZ
184/*
185 * parse_switch(argv[i], "on", "off", "yes", "no", NULL);
186 */
187int parse_switch(const char *arg, const char *errmesg, ...)
e5cf1476 188{
30b294c4
KZ
189 const char *a, *b;
190 va_list ap;
191
192 va_start(ap, errmesg);
193 do {
194 a = va_arg(ap, char *);
195 if (!a)
196 break;
197 b = va_arg(ap, char *);
198 if (!b)
199 break;
200
74819b5f
AH
201 if (strcmp(arg, a) == 0) {
202 va_end(ap);
30b294c4 203 return 1;
74819b5f
AH
204 } else if (strcmp(arg, b) == 0) {
205 va_end(ap);
30b294c4 206 return 0;
74819b5f 207 }
30b294c4
KZ
208 } while (1);
209 va_end(ap);
210
211 errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, arg);
e5cf1476 212}
416c43a9 213
02887b73
DT
214#ifndef HAVE_MEMPCPY
215void *mempcpy(void *restrict dest, const void *restrict src, size_t n)
216{
217 return ((char *)memcpy(dest, src, n)) + n;
218}
219#endif
220
8abcf290
DB
221#ifndef HAVE_STRNLEN
222size_t strnlen(const char *s, size_t maxlen)
223{
224 int i;
cf8de26a 225
8abcf290
DB
226 for (i = 0; i < maxlen; i++) {
227 if (s[i] == '\0')
228 return i + 1;
229 }
230 return maxlen;
231}
232#endif
cf8de26a 233
8abcf290
DB
234#ifndef HAVE_STRNCHR
235char *strnchr(const char *s, size_t maxlen, int c)
cf8de26a 236{
8abcf290
DB
237 for (; maxlen-- && *s != '\0'; ++s)
238 if (*s == (char)c)
239 return (char *)s;
240 return NULL;
241}
242#endif
cf8de26a 243
8abcf290
DB
244#ifndef HAVE_STRNDUP
245char *strndup(const char *s, size_t n)
246{
247 size_t len = strnlen(s, n);
248 char *new = (char *) malloc((len + 1) * sizeof(char));
249 if (!new)
250 return NULL;
251 new[len] = '\0';
252 return (char *) memcpy(new, s, len);
253}
254#endif
cf8de26a 255
551dae40 256int16_t strtos16_or_err(const char *str, const char *errmesg)
a9f97001 257{
551dae40
KZ
258 int32_t num = strtos32_or_err(str, errmesg);
259
260 if (num < INT16_MIN || num > INT16_MAX)
261 errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
262
263 return num;
264}
265
266uint16_t strtou16_or_err(const char *str, const char *errmesg)
267{
268 uint32_t num = strtou32_or_err(str, errmesg);
269
270 if (num > UINT16_MAX)
271 errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
272
273 return num;
274}
275
276int32_t strtos32_or_err(const char *str, const char *errmesg)
277{
278 int64_t num = strtos64_or_err(str, errmesg);
279
280 if (num < INT32_MIN || num > INT32_MAX)
281 errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
282
283 return num;
284}
285
286uint32_t strtou32_or_err(const char *str, const char *errmesg)
287{
288 uint64_t num = strtou64_or_err(str, errmesg);
289
290 if (num > UINT32_MAX)
291 errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
292
293 return num;
294}
295
296int64_t strtos64_or_err(const char *str, const char *errmesg)
297{
298 int64_t num;
a9f97001
SK
299 char *end = NULL;
300
301 if (str == NULL || *str == '\0')
302 goto err;
303 errno = 0;
551dae40 304 num = strtoimax(str, &end, 10);
a9f97001
SK
305
306 if (errno || str == end || (end && *end))
307 goto err;
308
309 return num;
a99c9130 310err:
a9f97001 311 if (errno)
a99c9130
KZ
312 err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
313
314 errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
a9f97001 315}
551dae40
KZ
316
317uint64_t strtou64_or_err(const char *str, const char *errmesg)
8abcf290 318{
551dae40 319 uintmax_t num;
a99c9130 320 char *end = NULL;
cf8de26a 321
a99c9130
KZ
322 if (str == NULL || *str == '\0')
323 goto err;
324 errno = 0;
551dae40 325 num = strtoumax(str, &end, 10);
cf8de26a 326
a99c9130
KZ
327 if (errno || str == end || (end && *end))
328 goto err;
8abcf290 329
a99c9130 330 return num;
8abcf290 331err:
a99c9130
KZ
332 if (errno)
333 err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
551dae40 334
a99c9130 335 errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
8abcf290 336}
551dae40
KZ
337
338
339double strtod_or_err(const char *str, const char *errmesg)
94d32126 340{
551dae40 341 double num;
a99c9130 342 char *end = NULL;
94d32126 343
a99c9130
KZ
344 if (str == NULL || *str == '\0')
345 goto err;
346 errno = 0;
551dae40 347 num = strtod(str, &end);
94d32126 348
a99c9130
KZ
349 if (errno || str == end || (end && *end))
350 goto err;
94d32126 351
a99c9130 352 return num;
94d32126 353err:
a99c9130
KZ
354 if (errno)
355 err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
356
357 errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
94d32126 358}
551dae40
KZ
359
360long strtol_or_err(const char *str, const char *errmesg)
361{
362 long num;
363 char *end = NULL;
364
365 if (str == NULL || *str == '\0')
366 goto err;
367 errno = 0;
368 num = strtol(str, &end, 10);
369
370 if (errno || str == end || (end && *end))
371 goto err;
372
373 return num;
374err:
375 if (errno)
376 err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
377 errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
378}
379
e53bc960
SK
380unsigned long strtoul_or_err(const char *str, const char *errmesg)
381{
382 unsigned long num;
383 char *end = NULL;
384
385 if (str == NULL || *str == '\0')
386 goto err;
387 errno = 0;
388 num = strtoul(str, &end, 10);
389
390 if (errno || str == end || (end && *end))
391 goto err;
392
393 return num;
394err:
395 if (errno)
a99c9130
KZ
396 err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
397
398 errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
399}
400
401uintmax_t strtosize_or_err(const char *str, const char *errmesg)
402{
403 uintmax_t num;
404
405 if (strtosize(str, &num) == 0)
406 return num;
407
408 if (errno)
409 err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
410
411 errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
e53bc960 412}
ce877f2d 413
477254da
KZ
414
415void strtotimeval_or_err(const char *str, struct timeval *tv, const char *errmesg)
416{
417 double user_input;
418
419 user_input = strtod_or_err(str, errmesg);
420 tv->tv_sec = (time_t) user_input;
421 tv->tv_usec = (long)((user_input - tv->tv_sec) * 1000000);
422}
423
ce877f2d
KZ
424/*
425 * Converts stat->st_mode to ls(1)-like mode string. The size of "str" must
d9554c97 426 * be 11 bytes.
ce877f2d 427 */
b0b24b11 428void xstrmode(mode_t mode, char *str)
ce877f2d 429{
7015df49
KZ
430 unsigned short i = 0;
431
ce877f2d 432 if (S_ISDIR(mode))
7015df49 433 str[i++] = 'd';
ce877f2d 434 else if (S_ISLNK(mode))
7015df49 435 str[i++] = 'l';
ce877f2d 436 else if (S_ISCHR(mode))
7015df49 437 str[i++] = 'c';
ce877f2d 438 else if (S_ISBLK(mode))
7015df49 439 str[i++] = 'b';
ce877f2d 440 else if (S_ISSOCK(mode))
7015df49 441 str[i++] = 's';
ce877f2d 442 else if (S_ISFIFO(mode))
7015df49 443 str[i++] = 'p';
ce877f2d 444 else if (S_ISREG(mode))
7015df49 445 str[i++] = '-';
ce877f2d 446
7015df49
KZ
447 str[i++] = mode & S_IRUSR ? 'r' : '-';
448 str[i++] = mode & S_IWUSR ? 'w' : '-';
449 str[i++] = (mode & S_ISUID
ce877f2d
KZ
450 ? (mode & S_IXUSR ? 's' : 'S')
451 : (mode & S_IXUSR ? 'x' : '-'));
7015df49
KZ
452 str[i++] = mode & S_IRGRP ? 'r' : '-';
453 str[i++] = mode & S_IWGRP ? 'w' : '-';
454 str[i++] = (mode & S_ISGID
ce877f2d
KZ
455 ? (mode & S_IXGRP ? 's' : 'S')
456 : (mode & S_IXGRP ? 'x' : '-'));
7015df49
KZ
457 str[i++] = mode & S_IROTH ? 'r' : '-';
458 str[i++] = mode & S_IWOTH ? 'w' : '-';
459 str[i++] = (mode & S_ISVTX
ce877f2d
KZ
460 ? (mode & S_IXOTH ? 't' : 'T')
461 : (mode & S_IXOTH ? 'x' : '-'));
7015df49 462 str[i] = '\0';
ce877f2d 463}
c4ecaf21
DB
464
465/*
466 * returns exponent (2^x=n) in range KiB..PiB
467 */
468static int get_exp(uint64_t n)
469{
470 int shft;
471
472 for (shft = 10; shft <= 60; shft += 10) {
473 if (n < (1ULL << shft))
474 break;
475 }
476 return shft - 10;
477}
478
5d2a9849 479char *size_to_human_string(int options, uint64_t bytes)
c4ecaf21
DB
480{
481 char buf[32];
f9e05daf
DR
482 int dec, exp;
483 uint64_t frac;
484 const char *letters = "BKMGTPE";
5d2a9849 485 char suffix[sizeof(" KiB")], *psuf = suffix;
c4ecaf21
DB
486 char c;
487
5d2a9849
FC
488 if (options & SIZE_SUFFIX_SPACE)
489 *psuf++ = ' ';
490
c4ecaf21
DB
491 exp = get_exp(bytes);
492 c = *(letters + (exp ? exp / 10 : 0));
493 dec = exp ? bytes / (1ULL << exp) : bytes;
494 frac = exp ? bytes % (1ULL << exp) : 0;
495
5d2a9849
FC
496 *psuf++ = c;
497
498 if ((options & SIZE_SUFFIX_3LETTER) && (c != 'B')) {
499 *psuf++ = 'i';
500 *psuf++ = 'B';
501 }
502
503 *psuf = '\0';
504
505 /* fprintf(stderr, "exp: %d, unit: %c, dec: %d, frac: %jd\n",
506 * exp, suffix[0], dec, frac);
f9e05daf
DR
507 */
508
c4ecaf21
DB
509 if (frac) {
510 /* round */
511 frac = (frac / (1ULL << (exp - 10)) + 50) / 100;
512 if (frac == 10)
513 dec++, frac = 0;
514 }
515
516 if (frac) {
517 struct lconv const *l = localeconv();
518 char *dp = l ? l->decimal_point : NULL;
519
520 if (!dp || !*dp)
521 dp = ".";
7231fb2a 522 snprintf(buf, sizeof(buf), "%d%s%" PRIu64 "%s", dec, dp, frac, suffix);
c4ecaf21 523 } else
5d2a9849 524 snprintf(buf, sizeof(buf), "%d%s", dec, suffix);
c4ecaf21
DB
525
526 return strdup(buf);
527}
013bff51 528
c87638ad
KZ
529/*
530 * Parses comma delimited list to array with IDs, for example:
531 *
532 * "aaa,bbb,ccc" --> ary[0] = FOO_AAA;
533 * ary[1] = FOO_BBB;
534 * ary[3] = FOO_CCC;
535 *
536 * The function name2id() provides conversion from string to ID.
537 *
538 * Returns: >= 0 : number of items added to ary[]
539 * -1 : parse error or unknown item
540 * -2 : arysz reached
541 */
542int string_to_idarray(const char *list, int ary[], size_t arysz,
543 int (name2id)(const char *, size_t))
544{
545 const char *begin = NULL, *p;
b28f2267 546 size_t n = 0;
c87638ad
KZ
547
548 if (!list || !*list || !ary || !arysz || !name2id)
549 return -1;
550
551 for (p = list; p && *p; p++) {
552 const char *end = NULL;
553 int id;
554
179b923a
CR
555 if (n >= arysz)
556 return -2;
c87638ad
KZ
557 if (!begin)
558 begin = p; /* begin of the column name */
559 if (*p == ',')
560 end = p; /* terminate the name */
561 if (*(p + 1) == '\0')
562 end = p + 1; /* end of string */
563 if (!begin || !end)
564 continue;
565 if (end <= begin)
566 return -1;
567
568 id = name2id(begin, end - begin);
569 if (id == -1)
570 return -1;
571 ary[ n++ ] = id;
c87638ad
KZ
572 begin = NULL;
573 if (end && !*end)
574 break;
575 }
576 return n;
577}
578
f5077b51
MB
579/*
580 * Parses the array like string_to_idarray but if format is "+aaa,bbb"
581 * it adds fields to array instead of replacing them.
582 */
583int string_add_to_idarray(const char *list, int ary[], size_t arysz,
40b17508 584 size_t *ary_pos, int (name2id)(const char *, size_t))
f5077b51
MB
585{
586 const char *list_add;
587 int r;
588
40b17508 589 if (!list || !*list || !ary_pos || *ary_pos > arysz)
f5077b51
MB
590 return -1;
591
592 if (list[0] == '+')
593 list_add = &list[1];
594 else {
595 list_add = list;
596 *ary_pos = 0;
597 }
598
599 r = string_to_idarray(list_add, &ary[*ary_pos], arysz - *ary_pos, name2id);
600 if (r > 0)
601 *ary_pos += r;
602 return r;
603}
604
c87638ad
KZ
605/*
606 * LIST ::= <item> [, <item>]
607 *
608 * The <item> is translated to 'id' by name2id() function and the 'id' is used
455fe9a0 609 * as a position in the 'ary' bit array. It means that the 'id' has to be in
c87638ad
KZ
610 * range <0..N> where N < sizeof(ary) * NBBY.
611 *
a883c634 612 * Returns: 0 on success, <0 on error.
c87638ad
KZ
613 */
614int string_to_bitarray(const char *list,
615 char *ary,
616 int (*name2bit)(const char *, size_t))
617{
618 const char *begin = NULL, *p;
619
620 if (!list || !name2bit || !ary)
621 return -EINVAL;
622
623 for (p = list; p && *p; p++) {
624 const char *end = NULL;
625 int bit;
626
627 if (!begin)
628 begin = p; /* begin of the level name */
629 if (*p == ',')
630 end = p; /* terminate the name */
631 if (*(p + 1) == '\0')
632 end = p + 1; /* end of string */
633 if (!begin || !end)
634 continue;
635 if (end <= begin)
636 return -1;
637
638 bit = name2bit(begin, end - begin);
639 if (bit < 0)
640 return bit;
641 setbit(ary, bit);
642 begin = NULL;
643 if (end && !*end)
644 break;
645 }
5ef16771
KZ
646 return 0;
647}
648
649/*
650 * LIST ::= <item> [, <item>]
651 *
652 * The <item> is translated to 'id' by name2flag() function and the flags is
653 * set to the 'mask'
654*
655 * Returns: 0 on success, <0 on error.
656 */
657int string_to_bitmask(const char *list,
658 unsigned long *mask,
659 long (*name2flag)(const char *, size_t))
660{
661 const char *begin = NULL, *p;
662
663 if (!list || !name2flag || !mask)
664 return -EINVAL;
665
666 for (p = list; p && *p; p++) {
667 const char *end = NULL;
668 long flag;
669
670 if (!begin)
671 begin = p; /* begin of the level name */
672 if (*p == ',')
673 end = p; /* terminate the name */
674 if (*(p + 1) == '\0')
675 end = p + 1; /* end of string */
676 if (!begin || !end)
677 continue;
678 if (end <= begin)
679 return -1;
680
681 flag = name2flag(begin, end - begin);
682 if (flag < 0)
683 return flag; /* error */
684 *mask |= flag;
685 begin = NULL;
686 if (end && !*end)
687 break;
688 }
c87638ad
KZ
689 return 0;
690}
013bff51 691
a883c634
DB
692/*
693 * Parse the lower and higher values in a string containing
694 * "lower:higher" or "lower-higher" format. Note that either
af7df9ee
DB
695 * the lower or the higher values may be missing, and the def
696 * value will be assigned to it by default.
a883c634
DB
697 *
698 * Returns: 0 on success, <0 on error.
699 */
af7df9ee 700int parse_range(const char *str, int *lower, int *upper, int def)
a883c634
DB
701{
702 char *end = NULL;
703
704 if (!str)
705 return 0;
706
af7df9ee 707 *upper = *lower = def;
a883c634
DB
708 errno = 0;
709
710 if (*str == ':') { /* <:N> */
711 str++;
712 *upper = strtol(str, &end, 10);
713 if (errno || !end || *end || end == str)
714 return -1;
715 } else {
716 *upper = *lower = strtol(str, &end, 10);
717 if (errno || !end || end == str)
718 return -1;
719
720 if (*end == ':' && !*(end + 1)) /* <M:> */
721 *upper = 0;
722 else if (*end == '-' || *end == ':') { /* <M:N> <M-N> */
723 str = end + 1;
724 end = NULL;
725 errno = 0;
726 *upper = strtol(str, &end, 10);
727
728 if (errno || !end || *end || end == str)
729 return -1;
730 }
731 }
732 return 0;
733}
734
b106d052
PU
735/*
736 * Compare two strings for equality, ignoring at most one trailing
737 * slash.
738 */
739int streq_except_trailing_slash(const char *s1, const char *s2)
740{
741 int equal;
742
743 if (!s1 && !s2)
744 return 1;
745 if (!s1 || !s2)
746 return 0;
747
748 equal = !strcmp(s1, s2);
749
750 if (!equal) {
751 size_t len1 = strlen(s1);
752 size_t len2 = strlen(s2);
753
754 if (len1 && *(s1 + len1 - 1) == '/')
755 len1--;
756 if (len2 && *(s2 + len2 - 1) == '/')
757 len2--;
758 if (len1 != len2)
759 return 0;
760
761 equal = !strncmp(s1, s2, len1);
762 }
763
764 return equal;
765}
766
a883c634 767
548b9714
KZ
768char *strnappend(const char *s, const char *suffix, size_t b)
769{
770 size_t a;
771 char *r;
772
773 if (!s && !suffix)
774 return strdup("");
775 if (!s)
776 return strndup(suffix, b);
777 if (!suffix)
778 return strdup(s);
779
780 assert(s);
781 assert(suffix);
782
783 a = strlen(s);
784 if (b > ((size_t) -1) - a)
785 return NULL;
786
787 r = malloc(a + b + 1);
788 if (!r)
789 return NULL;
790
791 memcpy(r, s, a);
792 memcpy(r + a, suffix, b);
793 r[a+b] = 0;
794
795 return r;
796}
797
798char *strappend(const char *s, const char *suffix)
799{
800 return strnappend(s, suffix, suffix ? strlen(suffix) : 0);
801}
802
803
804static size_t strcspn_escaped(const char *s, const char *reject)
805{
806 int escaped = 0;
807 int n;
808
809 for (n=0; s[n]; n++) {
810 if (escaped)
811 escaped = 0;
812 else if (s[n] == '\\')
813 escaped = 1;
814 else if (strchr(reject, s[n]))
815 break;
816 }
817
818 /* if s ends in \, return index of previous char */
819 return n - escaped;
820}
821
822/* Split a string into words. */
823const char *split(const char **state, size_t *l, const char *separator, int quoted)
824{
825 const char *current;
826
827 current = *state;
828
829 if (!*current) {
830 assert(**state == '\0');
831 return NULL;
832 }
833
834 current += strspn(current, separator);
835 if (!*current) {
836 *state = current;
837 return NULL;
838 }
839
840 if (quoted && strchr("\'\"", *current)) {
841 char quotechars[2] = {*current, '\0'};
842
843 *l = strcspn_escaped(current + 1, quotechars);
844 if (current[*l + 1] == '\0' || current[*l + 1] != quotechars[0] ||
845 (current[*l + 2] && !strchr(separator, current[*l + 2]))) {
846 /* right quote missing or garbage at the end */
847 *state = current;
848 return NULL;
849 }
850 *state = current++ + *l + 2;
851 } else if (quoted) {
852 *l = strcspn_escaped(current, separator);
853 if (current[*l] && !strchr(separator, current[*l])) {
854 /* unfinished escape */
855 *state = current;
856 return NULL;
857 }
858 *state = current + *l;
859 } else {
860 *l = strcspn(current, separator);
861 *state = current + *l;
862 }
863
864 return current;
865}
866
3e5a5455
SK
867/* Rewind file pointer forward to new line. */
868int skip_fline(FILE *fp)
869{
870 char ch;
871
872 do {
873 if ((ch = fgetc(fp)) == EOF)
874 return 1;
875 if (ch == '\n')
876 return 0;
877 } while (1);
878}
548b9714 879
013bff51
KZ
880#ifdef TEST_PROGRAM
881
013bff51
KZ
882int main(int argc, char *argv[])
883{
884 uintmax_t size = 0;
5d2a9849 885 char *hum, *hum2;
013bff51
KZ
886
887 if (argc < 2) {
888 fprintf(stderr, "usage: %s <number>[suffix]\n", argv[0]);
889 exit(EXIT_FAILURE);
890 }
891
892 if (strtosize(argv[1], &size))
893 errx(EXIT_FAILURE, "invalid size '%s' value", argv[1]);
894
5d2a9849
FC
895 hum = size_to_human_string(SIZE_SUFFIX_1LETTER, size);
896 hum2 = size_to_human_string(SIZE_SUFFIX_3LETTER |
897 SIZE_SUFFIX_SPACE, size);
f9e05daf 898
5d2a9849 899 printf("%25s : %20ju : %8s : %12s\n", argv[1], size, hum, hum2);
f9e05daf 900 free(hum);
5d2a9849 901 free(hum2);
f9e05daf 902
958d2b71 903 return EXIT_SUCCESS;
013bff51
KZ
904}
905#endif /* TEST_PROGRAM */