Coverage Report

Created: 2026-10-17 07:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/util-linux/lib/strutils.c
Line
Count
Source
1
/*
2
 * No copyright is claimed.  This code is in the public domain; do with
3
 * it what you wish.
4
 *
5
 * Authors: Karel Zak <kzak@redhat.com> [2010]
6
 *          Davidlohr Bueso <dave@gnu.org> [2010]
7
 */
8
#include <stdio.h>
9
#include <stdlib.h>
10
#include <inttypes.h>
11
#include <ctype.h>
12
#include <errno.h>
13
#include <sys/stat.h>
14
#include <string.h>
15
#include <strings.h>
16
#include <assert.h>
17
18
#include "c.h"
19
#include "cctype.h"
20
#include "nls.h"
21
#include "strutils.h"
22
#include "timeutils.h"
23
#include "bitops.h"
24
#include "pathnames.h"
25
26
static int STRTOXX_EXIT_CODE = EXIT_FAILURE;
27
28
void strutils_set_exitcode(int ex)
29
0
{
30
0
  STRTOXX_EXIT_CODE = ex;
31
0
}
32
33
static int do_scale_by_power (uintmax_t *x, int base, int power)
34
1.10k
{
35
6.19k
  while (power--) {
36
5.11k
    if (UINTMAX_MAX / base < *x)
37
22
      return -ERANGE;
38
5.09k
    *x *= base;
39
5.09k
  }
40
1.08k
  return 0;
41
1.10k
}
42
43
/*
44
 * strtosize() - convert string to size (uintmax_t).
45
 *
46
 * Supported suffixes:
47
 *
48
 * XiB or X for 2^N
49
 *     where X = {K,M,G,T,P,E,Z,Y}
50
 *        or X = {k,m,g,t,p,e}  (undocumented for backward compatibility only)
51
 * for example:
52
 *    10KiB = 10240
53
 *    10K = 10240
54
 *
55
 * XB for 10^N
56
 *     where X = {K,M,G,T,P,E,Z,Y}
57
 * for example:
58
 *    10KB  = 10000
59
 *
60
 * The optional 'power' variable returns number associated with used suffix
61
 * {K,M,G,T,P,E,Z,Y}  = {1,2,3,4,5,6,7,8}.
62
 *
63
 * The function also supports decimal point, for example:
64
 *              0.5MB   = 500000
65
 *              0.5MiB  = 524288
66
 *
67
 * Note that the function does not accept numbers with '-' (negative sign)
68
 * prefix.
69
 */
70
int ul_parse_size(const char *str, uintmax_t *res, int *power)
71
9.74k
{
72
9.74k
  const char *p;
73
9.74k
  char *end;
74
9.74k
  uintmax_t x, frac = 0;
75
9.74k
  int base = 1024, rc = 0, pwr = 0, frac_zeros = 0;
76
77
9.74k
  static const char *suf  = "KMGTPEZY";
78
9.74k
  static const char *suf2 = "kmgtpezy";
79
9.74k
  const char *sp;
80
81
9.74k
  *res = 0;
82
83
9.74k
  if (!str || !*str) {
84
7
    rc = -EINVAL;
85
7
    goto err;
86
7
  }
87
88
  /* Only positive numbers are acceptable
89
   *
90
   * Note that this check is not perfect, it would be better to
91
   * use lconv->negative_sign. But coreutils use the same solution,
92
   * so it's probably good enough...
93
   */
94
9.74k
  p = str;
95
9.74k
  while (isspace((unsigned char) *p))
96
588
    p++;
97
9.74k
  if (*p == '-') {
98
3
    rc = -EINVAL;
99
3
    goto err;
100
3
  }
101
102
9.74k
  errno = 0, end = NULL;
103
9.73k
  x = strtoumax(str, &end, 0);
104
105
9.73k
  if (end == str ||
106
9.59k
      (errno != 0 && (x == UINTMAX_MAX || x == 0))) {
107
152
    rc = errno ? -errno : -EINVAL;
108
152
    goto err;
109
152
  }
110
9.58k
  if (!end || !*end)
111
8.53k
    goto done;      /* without suffix */
112
1.05k
  p = end;
113
114
  /*
115
   * Check size suffixes
116
   */
117
2.03k
check_suffix:
118
2.03k
  if (*(p + 1) == 'i' && (*(p + 2) == 'B' || *(p + 2) == 'b') && !*(p + 3))
119
196
    base = 1024;      /* XiB, 2^N */
120
1.84k
  else if ((*(p + 1) == 'B' || *(p + 1) == 'b') && !*(p + 2))
121
5
    base = 1000;      /* XB, 10^N */
122
1.83k
  else if (*(p + 1)) {
123
1.18k
    struct lconv const *l = localeconv();
124
1.18k
    const char *dp = l ? l->decimal_point : NULL;
125
1.18k
    size_t dpsz = dp ? strlen(dp) : 0;
126
127
1.18k
    if (frac == 0 && *p && dp && strncmp(dp, p, dpsz) == 0) {
128
1.00k
      const char *fstr = p + dpsz;
129
130
9.51k
      for (p = fstr; *p == '0'; p++)
131
8.50k
        frac_zeros++;
132
1.00k
      fstr = p;
133
1.00k
      if (isdigit(*fstr)) {
134
401
        errno = 0, end = NULL;
135
401
        frac = strtoumax(fstr, &end, 0);
136
401
        if (end == fstr ||
137
401
            (errno != 0 && (frac == UINTMAX_MAX || frac == 0))) {
138
1
          rc = errno ? -errno : -EINVAL;
139
1
          goto err;
140
1
        }
141
401
      } else
142
604
        end = (char *) p;
143
144
1.00k
      if (!end || !*end) {
145
18
        rc = -EINVAL;
146
18
        goto err; /* without suffix, but with fractions */
147
18
      }
148
986
      p = end;
149
986
      goto check_suffix;
150
1.00k
    }
151
176
    rc = -EINVAL;
152
176
    goto err;     /* unexpected suffix */
153
1.18k
  }
154
155
856
  sp = strchr(suf, *p);
156
856
  if (sp)
157
581
    pwr = (sp - suf) + 1;
158
275
  else {
159
275
    sp = strchr(suf2, *p);
160
275
    if (sp)
161
243
      pwr = (sp - suf2) + 1;
162
32
    else {
163
32
      rc = -EINVAL;
164
32
      goto err;
165
32
    }
166
275
  }
167
168
824
  if (power)
169
298
    *power = pwr;
170
171
824
  rc = do_scale_by_power(&x, base, pwr);
172
824
  if (rc)
173
12
    goto err;
174
175
812
  if (frac && pwr) {
176
280
    int i;
177
280
    uintmax_t frac_base = 1, frac_val = 0;
178
179
    /* mega, giga, ... */
180
280
    rc = do_scale_by_power(&frac_base, base, pwr);
181
280
    if (rc)
182
10
      goto err;
183
184
    /*
185
     * Go backwardly from last digit and compute frac_base * 0.<frac>
186
     * rounded down. For example 0.25G is (5G / 10 + 2G) / 10.
187
     * The intermediate value is always smaller than 10 * frac_base.
188
     */
189
2.19k
    do {
190
2.19k
      frac_val = ((frac % 10) * frac_base + frac_val) / 10;
191
2.19k
      frac /= 10;
192
2.19k
    } while (frac);
193
194
    /* 'frac' is without zeros (5 means 0.5 as well as 0.05) */
195
488
    for (i = 0; i < frac_zeros && frac_val; i++)
196
218
      frac_val /= 10;
197
198
270
    if (UINTMAX_MAX - x < frac_val) {
199
1
      rc = -ERANGE;
200
1
      goto err;
201
1
    }
202
269
    x += frac_val;
203
269
  }
204
9.33k
done:
205
9.33k
  *res = x;
206
9.74k
err:
207
9.74k
  if (rc < 0)
208
9.74k
    errno = -rc;
209
9.74k
  return rc;
210
9.33k
}
211
212
int strtosize(const char *str, uintmax_t *res)
213
544
{
214
544
  return ul_parse_size(str, res, NULL);
215
544
}
216
217
int isdigit_strend(const char *str, const char **end)
218
0
{
219
0
  const char *p;
220
221
0
  for (p = str; p && *p && isdigit((unsigned char) *p); p++);
222
223
0
  if (end)
224
0
    *end = p;
225
0
  return p && p > str && !*p;
226
0
}
227
228
int isxdigit_strend(const char *str, const char **end)
229
46
{
230
46
  const char *p;
231
232
46
  for (p = str; p && *p && isxdigit((unsigned char) *p); p++);
233
234
46
  if (end)
235
0
    *end = p;
236
237
46
  return p && p > str && !*p;
238
46
}
239
240
/*
241
 *  For example: ul_parse_switch(argv[i], "on", "off",  "yes", "no",  NULL);
242
 */
243
int ul_strtobool(const char *str, bool *result)
244
0
{
245
0
  static const char *bool_true[]  = { "1", "y", "t", "yes", "true", "on", "enable" };
246
0
  static const char *bool_false[] = { "0", "n", "f", "no", "false", "off", "disable", "not", };
247
0
  size_t i;
248
249
0
  if (!str || !result)
250
0
    return -(errno = EINVAL);
251
252
0
  errno = 0;
253
254
0
  for (i = 0; i < ARRAY_SIZE(bool_true); i++) {
255
0
    if (c_strcasecmp(str, bool_true[i]) == 0) {
256
0
      *result = true;
257
0
      return 0;
258
0
    }
259
0
  }
260
261
0
  for (i = 0; i < ARRAY_SIZE(bool_false); i++) {
262
0
    if (c_strcasecmp(str, bool_false[i]) == 0) {
263
0
      *result = false;
264
0
      return 0;
265
0
    }
266
0
  }
267
268
0
  return -(errno = EINVAL);
269
0
}
270
271
int ul_parse_switch(const char *arg, ...)
272
0
{
273
0
  const char *a, *b;
274
0
  va_list ap;
275
276
0
  va_start(ap, arg);
277
0
  do {
278
0
    a = va_arg(ap, char *);
279
0
    if (!a)
280
0
      break;
281
0
    b = va_arg(ap, char *);
282
0
    if (!b)
283
0
      break;
284
285
0
    if (strcmp(arg, a) == 0) {
286
0
      va_end(ap);
287
0
      return 1;
288
0
    }
289
290
0
    if (strcmp(arg, b) == 0) {
291
0
      va_end(ap);
292
0
      return 0;
293
0
    }
294
0
  } while (1);
295
0
  va_end(ap);
296
297
0
  errx(STRTOXX_EXIT_CODE, _("unsupported argument: %s"), arg);
298
0
}
299
300
#ifndef HAVE_MEMPCPY
301
void *mempcpy(void *restrict dest, const void *restrict src, size_t n)
302
{
303
  return ((char *)memcpy(dest, src, n)) + n;
304
}
305
#endif
306
307
#ifndef HAVE_STRNLEN
308
size_t strnlen(const char *s, size_t maxlen)
309
{
310
  size_t i;
311
312
  for (i = 0; i < maxlen; i++) {
313
    if (s[i] == '\0')
314
      return i;
315
  }
316
  return maxlen;
317
}
318
#endif
319
320
#ifndef HAVE_STRNCHR
321
char *strnchr(const char *s, size_t maxlen, int c)
322
0
{
323
0
  for (; maxlen-- && *s != '\0'; ++s)
324
0
    if (*s == (char)c)
325
0
      return (char *)s;
326
0
  return NULL;
327
0
}
328
#endif
329
330
#ifndef HAVE_STRNDUP
331
char *strndup(const char *s, size_t n)
332
{
333
  size_t len = strnlen(s, n);
334
  char *new = malloc((len + 1) * sizeof(char));
335
  if (!new)
336
    return NULL;
337
  new[len] = '\0';
338
  return (char *) memcpy(new, s, len);
339
}
340
#endif
341
342
/*
343
 * convert strings to numbers; returns <0 on error, and 0 on success
344
 */
345
int ul_strtos64(const char *str, int64_t *num, int base)
346
0
{
347
0
  char *end = NULL;
348
349
0
  if (str == NULL || *str == '\0')
350
0
    return -(errno = EINVAL);
351
352
0
  errno = 0;
353
0
  *num = (int64_t) strtoimax(str, &end, base);
354
355
0
  if (errno != 0)
356
0
    return -errno;
357
0
  if (str == end || (end && *end))
358
0
    return -(errno = EINVAL);
359
0
  return 0;
360
0
}
361
362
int ul_strtou64(const char *str, uint64_t *num, int base)
363
888
{
364
888
  char *end = NULL;
365
888
  int64_t tmp;
366
367
888
  if (str == NULL || *str == '\0')
368
0
    return -(errno = EINVAL);
369
370
  /* we need to ignore negative numbers, note that for invalid negative
371
   * number strtoimax() returns negative number too, so we do not
372
   * need to check errno here */
373
888
  errno = 0;
374
888
  tmp = (int64_t) strtoimax(str, &end, base);
375
888
  if (tmp < 0)
376
888
    errno = ERANGE;
377
805
  else {
378
805
    errno = 0;
379
805
    *num = strtoumax(str, &end, base);
380
805
  }
381
382
888
  if (errno != 0)
383
83
    return -errno;
384
805
  if (str == end || (end && *end))
385
14
    return -(errno = EINVAL);
386
791
  return 0;
387
805
}
388
389
int ul_strtos32(const char *str, int32_t *num, int base)
390
0
{
391
0
  int64_t tmp;
392
0
  int rc;
393
394
0
  rc = ul_strtos64(str, &tmp, base);
395
0
  if (rc == 0 && (tmp < INT32_MIN || tmp > INT32_MAX))
396
0
    rc = -(errno = ERANGE);
397
0
  if (rc == 0)
398
0
    *num = (int32_t) tmp;
399
0
  return rc;
400
0
}
401
402
int ul_strtou32(const char *str, uint32_t *num, int base)
403
0
{
404
0
  uint64_t tmp;
405
0
  int rc;
406
407
0
  rc = ul_strtou64(str, &tmp, base);
408
0
  if (rc == 0 && tmp > UINT32_MAX)
409
0
    rc = -(errno = ERANGE);
410
0
  if (rc == 0)
411
0
    *num = (uint32_t) tmp;
412
0
  return rc;
413
0
}
414
415
int ul_strtou16(const char *str, uint16_t *num, int base)
416
0
{
417
0
  uint64_t tmp;
418
0
  int rc;
419
420
0
  rc = ul_strtou64(str, &tmp, base);
421
0
  if (rc == 0 && tmp > UINT16_MAX)
422
0
    rc = -(errno = ERANGE);
423
0
  if (rc == 0)
424
0
    *num = (uint16_t) tmp;
425
0
  return rc;
426
0
}
427
428
/*
429
 * Convert strings to numbers in defined range and print message on error.
430
 *
431
 * These functions are used when we read input from users (getopt() etc.). It's
432
 * better to consolidate the code and keep it all based on 64-bit numbers than
433
 * implement it for 32 and 16-bit numbers too.
434
 */
435
int64_t str2num_or_err(const char *str, int base, const char *errmesg,
436
           int64_t low, int64_t up)
437
0
{
438
0
  int64_t num = 0;
439
0
  int rc;
440
441
0
  rc = ul_strtos64(str, &num, base);
442
0
  if (rc == 0 && ((low && num < low) || (up && num > up)))
443
0
    rc = -(errno = ERANGE);
444
445
0
  if (rc) {
446
0
    if (errno == ERANGE)
447
0
      err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
448
0
    errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
449
0
  }
450
0
  return num;
451
0
}
452
453
uint64_t str2unum_or_err(const char *str, int base, const char *errmesg, uint64_t up)
454
0
{
455
0
  uint64_t num = 0;
456
0
  int rc;
457
458
0
  rc = ul_strtou64(str, &num, base);
459
0
  if (rc == 0 && (up && num > up))
460
0
    rc = -(errno = ERANGE);
461
462
0
  if (rc) {
463
0
    if (errno == ERANGE)
464
0
      err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
465
0
    errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
466
0
  }
467
0
  return num;
468
0
}
469
470
double strtod_or_err(const char *str, const char *errmesg)
471
0
{
472
0
  double num;
473
0
  char *end = NULL;
474
475
0
  errno = 0;
476
0
  if (str == NULL || *str == '\0')
477
0
    goto err;
478
0
  num = strtod(str, &end);
479
480
0
  if (errno || str == end || (end && *end))
481
0
    goto err;
482
483
0
  return num;
484
0
err:
485
0
  if (errno == ERANGE)
486
0
    err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
487
488
0
  errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
489
0
}
490
491
int ul_strtold(const char *str, long double *num)
492
0
{
493
0
  char *end = NULL;
494
495
0
  errno = 0;
496
0
  if (str == NULL || *str == '\0')
497
0
    return -(errno = EINVAL);
498
0
  *num = strtold(str, &end);
499
500
0
  if (errno != 0)
501
0
    return -errno;
502
0
  if (str == end || (end && *end))
503
0
    return -(errno = EINVAL);
504
0
  return 0;
505
0
}
506
507
long double strtold_or_err(const char *str, const char *errmesg)
508
0
{
509
0
  long double num = 0;
510
511
0
  if (ul_strtold(str, &num) == 0)
512
0
    return num;
513
0
  if (errno == ERANGE)
514
0
    err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
515
516
0
  errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
517
0
}
518
519
uintmax_t strtosize_or_err(const char *str, const char *errmesg)
520
0
{
521
0
  uintmax_t num;
522
523
0
  if (strtosize(str, &num) == 0)
524
0
    return num;
525
526
0
  if (errno)
527
0
    err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
528
529
0
  errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
530
0
}
531
532
533
void strtotimeval_or_err(const char *str, struct timeval *tv, const char *errmesg)
534
0
{
535
0
  long double user_input;
536
537
0
  user_input = strtold_or_err(str, errmesg);
538
0
  tv->tv_sec = (time_t) user_input;
539
0
  tv->tv_usec = (suseconds_t)((user_input - tv->tv_sec) * USEC_PER_SEC);
540
0
}
541
542
void strtotimespec_or_err(const char *str, struct timespec *ts, const char *errmesg)
543
0
{
544
0
  long double user_input;
545
546
0
  user_input = strtold_or_err(str, errmesg);
547
0
  ts->tv_sec = (time_t) user_input;
548
0
  ts->tv_nsec = (long)((user_input - ts->tv_sec) * NSEC_PER_SEC);
549
0
}
550
551
time_t strtotime_or_err(const char *str, const char *errmesg)
552
0
{
553
0
  int64_t user_input;
554
555
0
  user_input = strtos64_or_err(str, errmesg);
556
0
  return (time_t) user_input;
557
0
}
558
559
bool hyperlinkwanted(const char *mode)
560
0
{
561
0
  if (mode && strcmp(mode, "never") == 0)
562
0
    return false;
563
564
0
  if (mode && strcmp(mode, "always") == 0)
565
0
    return true;
566
567
0
  if (!mode || strcmp(mode, "auto") == 0)
568
0
    return isatty(STDOUT_FILENO) ? true : false;
569
570
0
  errx(EXIT_FAILURE, _("invalid argument of --hyperlink: %s"), mode);
571
0
}
572
573
bool annotationwanted(const char *mode)
574
0
{
575
0
  if (mode && strcmp(mode, "never") == 0)
576
0
    return false;
577
578
0
  if (mode && strcmp(mode, "always") == 0)
579
0
    return true;
580
581
0
  if (!mode || strcmp(mode, "auto") == 0)
582
0
    return isatty(STDOUT_FILENO) ? true : false;
583
584
0
  errx(EXIT_FAILURE, _("invalid argument of --annotate: %s"), mode);
585
0
}
586
587
/*
588
 * Converts stat->st_mode to ls(1)-like mode string. The size of "str" must
589
 * be 11 bytes.
590
 */
591
char *xstrmode(mode_t mode, char *str)
592
0
{
593
0
  unsigned short i = 0;
594
595
0
  if (S_ISDIR(mode))
596
0
    str[i++] = 'd';
597
0
  else if (S_ISLNK(mode))
598
0
    str[i++] = 'l';
599
0
  else if (S_ISCHR(mode))
600
0
    str[i++] = 'c';
601
0
  else if (S_ISBLK(mode))
602
0
    str[i++] = 'b';
603
0
  else if (S_ISSOCK(mode))
604
0
    str[i++] = 's';
605
0
  else if (S_ISFIFO(mode))
606
0
    str[i++] = 'p';
607
0
  else if (S_ISREG(mode))
608
0
    str[i++] = '-';
609
610
0
  str[i++] = mode & S_IRUSR ? 'r' : '-';
611
0
  str[i++] = mode & S_IWUSR ? 'w' : '-';
612
0
  str[i++] = (mode & S_ISUID
613
0
    ? (mode & S_IXUSR ? 's' : 'S')
614
0
    : (mode & S_IXUSR ? 'x' : '-'));
615
0
  str[i++] = mode & S_IRGRP ? 'r' : '-';
616
0
  str[i++] = mode & S_IWGRP ? 'w' : '-';
617
0
  str[i++] = (mode & S_ISGID
618
0
    ? (mode & S_IXGRP ? 's' : 'S')
619
0
    : (mode & S_IXGRP ? 'x' : '-'));
620
0
  str[i++] = mode & S_IROTH ? 'r' : '-';
621
0
  str[i++] = mode & S_IWOTH ? 'w' : '-';
622
0
  str[i++] = (mode & S_ISVTX
623
0
    ? (mode & S_IXOTH ? 't' : 'T')
624
0
    : (mode & S_IXOTH ? 'x' : '-'));
625
0
  str[i] = '\0';
626
627
0
  return str;
628
0
}
629
630
/*
631
 * returns exponent (2^x=n) in range KiB..EiB (2^10..2^60)
632
 */
633
static int get_exp(uint64_t n)
634
0
{
635
0
  int shft;
636
637
0
  for (shft = 10; shft <= 60; shft += 10) {
638
0
    if (n < (1ULL << shft))
639
0
      break;
640
0
  }
641
0
  return shft - 10;
642
0
}
643
644
char *size_to_human_string(int options, uint64_t bytes)
645
0
{
646
0
  char buf[32];
647
0
  int dec, exp;
648
0
  uint64_t frac;
649
0
  const char *letters = "BKMGTPE";
650
0
  char suffix[sizeof(" KiB")], *psuf = suffix;
651
0
  char c;
652
653
0
  if (options & SIZE_SUFFIX_SPACE)
654
0
    *psuf++ = ' ';
655
656
657
0
  exp  = get_exp(bytes);
658
0
  c    = *(letters + (exp ? exp / 10 : 0));
659
0
  dec  = exp ? bytes / (1ULL << exp) : bytes;
660
0
  frac = exp ? bytes % (1ULL << exp) : 0;
661
662
0
  *psuf++ = c;
663
664
0
  if ((options & SIZE_SUFFIX_3LETTER) && (c != 'B')) {
665
0
    *psuf++ = 'i';
666
0
    *psuf++ = 'B';
667
0
  }
668
669
0
  *psuf = '\0';
670
671
  /* fprintf(stderr, "exp: %d, unit: %c, dec: %d, frac: %jd\n",
672
   *                 exp, suffix[0], dec, frac);
673
   */
674
675
  /* round */
676
0
  if (frac) {
677
    /* get 3 digits after decimal point */
678
0
    if (frac >= UINT64_MAX / 1000)
679
0
      frac = ((frac / 1024) * 1000) / (1ULL << (exp - 10)) ;
680
0
    else
681
0
      frac = (frac * 1000) / (1ULL << (exp)) ;
682
683
0
    if (options & SIZE_DECIMAL_2DIGITS) {
684
      /* round 4/5 and keep 2 digits after decimal point */
685
0
      frac = (frac + 5) / 10 ;
686
0
    } else {
687
      /* round 4/5 and keep 1 digit after decimal point */
688
0
      frac = ((frac + 50) / 100) * 10 ;
689
0
    }
690
691
    /* rounding could have overflowed */
692
0
    if (frac == 100) {
693
0
      dec++;
694
0
      frac = 0;
695
0
    }
696
0
  }
697
698
0
  if (frac) {
699
0
    struct lconv const *l = localeconv();
700
0
    char *dp = l ? l->decimal_point : NULL;
701
0
    int len;
702
703
0
    if (!dp || !*dp)
704
0
      dp = ".";
705
706
0
    len = snprintf(buf, sizeof(buf), "%d%s%02" PRIu64, dec, dp, frac);
707
0
    if (len > 0 && (size_t) len < sizeof(buf)) {
708
      /* remove potential extraneous zero */
709
0
      if (buf[len - 1] == '0')
710
0
        buf[len--] = '\0';
711
      /* append suffix */
712
0
      xstrncpy(buf+len, suffix, sizeof(buf) - len);
713
0
    } else
714
0
      *buf = '\0'; /* snprintf error */
715
0
  } else
716
0
    snprintf(buf, sizeof(buf), "%d%s", dec, suffix);
717
718
0
  return strdup(buf);
719
0
}
720
721
/*
722
 * Parses comma delimited list to array with IDs, for example:
723
 *
724
 * "aaa,bbb,ccc" --> ary[0] = FOO_AAA;
725
 *                   ary[1] = FOO_BBB;
726
 *                   ary[3] = FOO_CCC;
727
 *
728
 * The function name2id() provides conversion from string to ID.
729
 *
730
 * Returns: >= 0  : number of items added to ary[]
731
 *            -1  : parse error or unknown item
732
 *            -2  : arysz reached
733
 */
734
int string_to_idarray(const char *list, int ary[], size_t arysz,
735
      int (name2id)(const char *, size_t))
736
0
{
737
0
  const char *begin = NULL, *p;
738
0
  size_t n = 0;
739
740
0
  if (!list || !*list || !ary || !arysz || !name2id)
741
0
    return -1;
742
743
0
  for (p = list; p && *p; p++) {
744
0
    const char *end = NULL;
745
0
    int id;
746
747
0
    if (n >= arysz)
748
0
      return -2;
749
0
    if (!begin)
750
0
      begin = p;   /* begin of the column name */
751
0
    if (*p == ',')
752
0
      end = p;   /* terminate the name */
753
0
    if (*(p + 1) == '\0')
754
0
      end = p + 1;   /* end of string */
755
0
    if (!begin || !end)
756
0
      continue;
757
0
    if (end <= begin)
758
0
      return -1;
759
760
0
    id = name2id(begin, end - begin);
761
0
    if (id == -1)
762
0
      return -1;
763
0
    ary[ n++ ] = id;
764
0
    begin = NULL;
765
0
    if (end && !*end)
766
0
      break;
767
0
  }
768
0
  return n;
769
0
}
770
771
/*
772
 * Parses the array like string_to_idarray but if format is "+aaa,bbb"
773
 * it adds fields to array instead of replacing them.
774
 */
775
int string_add_to_idarray(const char *list, int ary[], size_t arysz,
776
      size_t *ary_pos, int (name2id)(const char *, size_t))
777
0
{
778
0
  const char *list_add;
779
0
  int r;
780
781
0
  if (!list || !*list || !ary_pos || *ary_pos > arysz)
782
0
    return -1;
783
784
0
  if (list[0] == '+')
785
0
    list_add = &list[1];
786
0
  else {
787
0
    list_add = list;
788
0
    *ary_pos = 0;
789
0
  }
790
791
0
  r = string_to_idarray(list_add, &ary[*ary_pos], arysz - *ary_pos, name2id);
792
0
  if (r > 0)
793
0
    *ary_pos += r;
794
0
  return r;
795
0
}
796
797
/*
798
 * LIST ::= <item> [, <item>]
799
 *
800
 * The <item> is translated to 'id' by name2id() function and the 'id' is used
801
 * as a position in the 'ary' bit array. It means that the 'id' has to be in
802
 * range <0..N> where N < sizeof(ary) * NBBY.
803
 *
804
 * If allow_range is enabled:
805
 * An item ending in '+' also sets all bits in <0..N>.
806
 * An item beginning with '+' also sets all bits in <N..allow_minus>.
807
 *
808
 * Returns: 0 on success, <0 on error.
809
 */
810
int string_to_bitarray(const char *list,
811
         char *ary,
812
         int (*name2bit)(const char *, size_t),
813
         size_t allow_range)
814
0
{
815
0
  const char *begin = NULL, *p;
816
817
0
  if (!list || !name2bit || !ary)
818
0
    return -EINVAL;
819
820
0
  for (p = list; p && *p; p++) {
821
0
    const char *end = NULL;
822
0
    int bit, set_lower = 0, set_higher = 0;
823
824
0
    if (!begin)
825
0
      begin = p;   /* begin of the level name */
826
0
    if (*p == ',')
827
0
      end = p;   /* terminate the name */
828
0
    if (*(p + 1) == '\0')
829
0
      end = p + 1;   /* end of string */
830
0
    if (!begin || !end)
831
0
      continue;
832
0
    if (end <= begin)
833
0
      return -1;
834
0
    if (allow_range) {
835
0
      if (*(end - 1) == '+') {
836
0
        end--;
837
0
        set_lower = 1;
838
0
      } else if (*begin == '+') {
839
0
        begin++;
840
0
        set_higher = 1;
841
0
      }
842
0
    }
843
844
0
    bit = name2bit(begin, end - begin);
845
0
    if (bit < 0)
846
0
      return bit;
847
0
    setbit(ary, bit);
848
0
    if (set_lower)
849
0
      while (--bit >= 0)
850
0
        setbit(ary, bit);
851
0
    else if (set_higher)
852
0
      while (++bit < (int) allow_range)
853
0
        setbit(ary, bit);
854
0
    begin = NULL;
855
0
    if (end && !*end)
856
0
      break;
857
0
  }
858
0
  return 0;
859
0
}
860
861
/*
862
 * LIST ::= <item> [, <item>]
863
 *
864
 * The <item> is translated to 'id' by name2flag() function and the flags is
865
 * set to the 'mask'
866
*
867
 * Returns: 0 on success, <0 on error.
868
 */
869
int string_to_bitmask(const char *list,
870
         unsigned long *mask,
871
         long (*name2flag)(const char *, size_t))
872
0
{
873
0
  const char *begin = NULL, *p;
874
875
0
  if (!list || !name2flag || !mask)
876
0
    return -EINVAL;
877
878
0
  for (p = list; p && *p; p++) {
879
0
    const char *end = NULL;
880
0
    long flag;
881
882
0
    if (!begin)
883
0
      begin = p;   /* begin of the level name */
884
0
    if (*p == ',')
885
0
      end = p;   /* terminate the name */
886
0
    if (*(p + 1) == '\0')
887
0
      end = p + 1;   /* end of string */
888
0
    if (!begin || !end)
889
0
      continue;
890
0
    if (end <= begin)
891
0
      return -1;
892
893
0
    flag = name2flag(begin, end - begin);
894
0
    if (flag < 0)
895
0
      return flag; /* error */
896
0
    *mask |= flag;
897
0
    begin = NULL;
898
0
    if (end && !*end)
899
0
      break;
900
0
  }
901
0
  return 0;
902
0
}
903
904
/*
905
 * Parse the lower and higher values in a string containing
906
 * "lower:higher" or "lower-higher" format. Note that either
907
 * the lower or the higher values may be missing, and the def
908
 * value will be assigned to it by default.
909
 *
910
 * Returns: 0 on success, <0 on error.
911
 */
912
int ul_parse_range(const char *str, int *lower, int *upper, int def)
913
0
{
914
0
  char *end = NULL;
915
916
0
  if (!str)
917
0
    return 0;
918
919
0
  *upper = *lower = def;
920
0
  errno = 0;
921
922
0
  if (*str == ':') {       /* <:N> */
923
0
    str++;
924
0
    *upper = strtol(str, &end, 10);
925
0
    if (errno || !end || *end || end == str)
926
0
      return -1;
927
0
  } else {
928
0
    *upper = *lower = strtol(str, &end, 10);
929
0
    if (errno || !end || end == str)
930
0
      return -1;
931
932
0
    if (*end == ':' && !*(end + 1))   /* <M:> */
933
0
      *upper = def;
934
0
    else if (*end == '-' || *end == ':') { /* <M:N> <M-N> */
935
0
      str = end + 1;
936
0
      end = NULL;
937
0
      errno = 0;
938
0
      *upper = strtol(str, &end, 10);
939
940
0
      if (errno || !end || *end || end == str)
941
0
        return -1;
942
0
    }
943
0
  }
944
0
  return 0;
945
0
}
946
947
static const char *next_path_segment(const char *str, size_t *sz)
948
0
{
949
0
  const char *start, *p;
950
951
0
  start = str;
952
0
  *sz = 0;
953
0
  while (start && *start == '/' && *(start + 1) == '/')
954
0
    start++;
955
956
0
  if (!start || !*start)
957
0
    return NULL;
958
959
0
  for (*sz = 1, p = start + 1; *p && *p != '/'; p++) {
960
0
    (*sz)++;
961
0
  }
962
963
0
  return start;
964
0
}
965
966
int streq_paths(const char *a, const char *b)
967
0
{
968
0
  while (a && b) {
969
0
    size_t a_sz, b_sz;
970
0
    const char *a_seg = next_path_segment(a, &a_sz);
971
0
    const char *b_seg = next_path_segment(b, &b_sz);
972
973
    /*
974
    fprintf(stderr, "A>>>(%zu) '%s'\n", a_sz, a_seg);
975
    fprintf(stderr, "B>>>(%zu) '%s'\n", b_sz, b_seg);
976
    */
977
978
    /* end of the path */
979
0
    if (a_sz + b_sz == 0)
980
0
      return 1;
981
982
    /* ignore trailing slash */
983
0
    if (a_sz + b_sz == 1 &&
984
0
        ((a_seg && *a_seg == '/') || (b_seg && *b_seg == '/')))
985
0
      return 1;
986
987
0
    if (!a_seg || !b_seg)
988
0
      break;
989
0
    if (a_sz != b_sz || strncmp(a_seg, b_seg, a_sz) != 0)
990
0
      break;
991
992
0
    a = a_seg + a_sz;
993
0
    b = b_seg + b_sz;
994
0
  };
995
996
0
  return 0;
997
0
}
998
999
/* Returns 1 if @path is @dir or a path below @dir, compared like streq_paths() */
1000
int ul_path_is_within(const char *path, const char *dir)
1001
0
{
1002
0
  if (!path || !dir || !*path || !*dir)
1003
0
    return 0;
1004
1005
0
  while (path && dir) {
1006
0
    size_t p_sz, d_sz;
1007
0
    const char *p_seg = next_path_segment(path, &p_sz);
1008
0
    const char *d_seg = next_path_segment(dir, &d_sz);
1009
1010
    /* all @dir segments matched */
1011
0
    if (d_sz == 0 || (d_sz == 1 && d_seg && *d_seg == '/'))
1012
0
      return 1;
1013
1014
0
    if (!p_seg || !d_seg)
1015
0
      break;
1016
0
    if (p_sz != d_sz || strncmp(p_seg, d_seg, p_sz) != 0)
1017
0
      break;
1018
1019
0
    path = p_seg + p_sz;
1020
0
    dir = d_seg + d_sz;
1021
0
  }
1022
1023
0
  return 0;
1024
0
}
1025
1026
/* concatenate two strings to a new string, the size of the second string is limited by @b */
1027
char *ul_strnconcat(const char *s, const char *suffix, size_t b)
1028
0
{
1029
0
  size_t a;
1030
0
  char *r;
1031
1032
0
  if (!s && !suffix)
1033
0
    return strdup("");
1034
0
  if (!s)
1035
0
    return strndup(suffix, b);
1036
0
  if (!suffix)
1037
0
    return strdup(s);
1038
1039
0
  assert(s);
1040
0
  assert(suffix);
1041
1042
0
  a = strlen(s);
1043
0
  if (b > ((size_t) -1) - a)
1044
0
    return NULL;
1045
1046
0
  r = malloc(a + b + 1);
1047
0
  if (!r)
1048
0
    return NULL;
1049
1050
0
  memcpy(r, s, a);
1051
0
  memcpy(r + a, suffix, b);
1052
0
  r[a+b] = 0;
1053
1054
0
  return r;
1055
0
}
1056
1057
/* concatenate two strings to a new string */
1058
char *ul_strconcat(const char *s, const char *suffix)
1059
0
{
1060
0
  return ul_strnconcat(s, suffix, suffix ? strlen(suffix) : 0);
1061
0
}
1062
1063
/* concatenate @s and string defined by @format to a new string */
1064
char *ul_strfconcat(const char *s, const char *format, ...)
1065
0
{
1066
0
  va_list ap;
1067
0
  char *val, *res;
1068
0
  int sz;
1069
1070
0
  va_start(ap, format);
1071
0
  sz = vasprintf(&val, format, ap);
1072
0
  va_end(ap);
1073
1074
0
  if (sz < 0)
1075
0
    return NULL;
1076
1077
0
  res = ul_strnconcat(s, val, sz);
1078
0
  free(val);
1079
0
  return res;
1080
0
}
1081
1082
int ul_strappend(char **a, const char *b)
1083
15.3k
{
1084
15.3k
  size_t al, bl;
1085
15.3k
  char *tmp;
1086
1087
15.3k
  if (!a)
1088
0
    return -EINVAL;
1089
15.3k
  if (!b || !*b)
1090
206
    return 0;
1091
15.1k
  if (!*a) {
1092
7.50k
    *a = strdup(b);
1093
7.50k
    return !*a ? -ENOMEM : 0;
1094
7.50k
  }
1095
1096
7.67k
  al = strlen(*a);
1097
7.67k
  bl = strlen(b);
1098
1099
7.67k
  tmp = realloc(*a, al + bl + 1);
1100
7.67k
  if (!tmp)
1101
0
    return -ENOMEM;
1102
7.67k
  *a = tmp;
1103
7.67k
  memcpy((*a) + al, b, bl + 1);
1104
7.67k
  return 0;
1105
7.67k
}
1106
1107
/* the hybrid version of strfconcat and strappend. */
1108
int strfappend(char **a, const char *format, ...)
1109
0
{
1110
0
  va_list ap;
1111
0
  int res;
1112
1113
0
  va_start(ap, format);
1114
0
  res = ul_strvfappend(a, format, ap);
1115
0
  va_end(ap);
1116
1117
0
  return res;
1118
0
}
1119
1120
extern int ul_strvfappend(char **a, const char *format, va_list ap)
1121
0
{
1122
0
  char *val;
1123
0
  int sz;
1124
0
  int res;
1125
1126
0
  sz = vasprintf(&val, format, ap);
1127
0
  if (sz < 0)
1128
0
    return -errno;
1129
1130
0
  res = ul_strappend(a, val);
1131
0
  free(val);
1132
0
  return res;
1133
0
}
1134
1135
static size_t strcspn_escaped(const char *s, const char *reject)
1136
0
{
1137
0
  int escaped = 0;
1138
0
  int n;
1139
1140
0
  for (n=0; s[n]; n++) {
1141
0
    if (escaped)
1142
0
      escaped = 0;
1143
0
    else if (s[n] == '\\')
1144
0
      escaped = 1;
1145
0
    else if (strchr(reject, s[n]))
1146
0
      break;
1147
0
  }
1148
1149
  /* if s ends in \, return index of previous char */
1150
0
  return n - escaped;
1151
0
}
1152
1153
/*
1154
 * Like strchr() but ignores @c if escaped by '\', '\\' is interpreted like '\'.
1155
 *
1156
 * For example for @c='X':
1157
 *
1158
 *      "abcdXefgXh"    --> "XefgXh"
1159
 *  "abcd\XefgXh"   --> "Xh"
1160
 *  "abcd\\XefgXh"  --> "XefgXh"
1161
 *  "abcd\\\XefgXh" --> "Xh"
1162
 *  "abcd\Xefg\Xh"  --> (null)
1163
 *
1164
 *  "abcd\\XefgXh"  --> "\XefgXh"   for @c='\\'
1165
 */
1166
char *ul_strchr_escaped(const char *s, int c)
1167
0
{
1168
0
  char *p;
1169
0
  int esc = 0;
1170
1171
0
  for (p = (char *) s; p && *p; p++) {
1172
0
    if (!esc && *p == '\\') {
1173
0
      esc = 1;
1174
0
      continue;
1175
0
    }
1176
0
    if (*p == c && (!esc || c == '\\'))
1177
0
      return p;
1178
0
    esc = 0;
1179
0
  }
1180
1181
0
  return NULL;
1182
0
}
1183
1184
/* Split a string into words. */
1185
const char *ul_split(const char **state, size_t *l, const char *separator, int quoted)
1186
0
{
1187
0
  const char *current;
1188
1189
0
  current = *state;
1190
1191
0
  if (!*current) {
1192
0
    assert(**state == '\0');
1193
0
    return NULL;
1194
0
  }
1195
1196
0
  current += strspn(current, separator);
1197
0
  if (!*current) {
1198
0
    *state = current;
1199
0
    return NULL;
1200
0
  }
1201
1202
0
  if (quoted && strchr("\'\"", *current)) {
1203
0
    char quotechars[2] = {*current, '\0'};
1204
1205
0
    *l = strcspn_escaped(current + 1, quotechars);
1206
0
    if (current[*l + 1] == '\0' || current[*l + 1] != quotechars[0] ||
1207
0
        (current[*l + 2] && !strchr(separator, current[*l + 2]))) {
1208
      /* right quote missing or garbage at the end */
1209
0
      *state = current;
1210
0
      return NULL;
1211
0
    }
1212
0
    *state = current++ + *l + 2;
1213
0
  } else if (quoted) {
1214
0
    *l = strcspn_escaped(current, separator);
1215
0
    if (current[*l] && !strchr(separator, current[*l])) {
1216
      /* unfinished escape */
1217
0
      *state = current;
1218
0
      return NULL;
1219
0
    }
1220
0
    *state = current + *l;
1221
0
  } else {
1222
0
    *l = strcspn(current, separator);
1223
0
    *state = current + *l;
1224
0
  }
1225
1226
0
  return current;
1227
0
}
1228
1229
/* Rewind file pointer forward to new line.  */
1230
int skip_fline(FILE *fp)
1231
0
{
1232
0
  int ch;
1233
1234
0
  do {
1235
0
    if ((ch = fgetc(fp)) == EOF)
1236
0
      return 1;
1237
0
    if (ch == '\n')
1238
0
      return 0;
1239
0
  } while (1);
1240
0
}
1241
1242
1243
/* compare two strings, but ignoring non-alnum and case of the characters, for example
1244
 * "Hello (123)!" is the same as "hello123".
1245
 */
1246
int ul_stralnumcmp(const char *p1, const char *p2)
1247
1.02M
{
1248
1.02M
  const unsigned char *s1 = (const unsigned char *) p1;
1249
1.02M
  const unsigned char *s2 = (const unsigned char *) p2;
1250
1.02M
  unsigned char c1, c2;
1251
1252
1.12M
  do {
1253
1.13M
    do {
1254
1.13M
      c1 = (unsigned char) *s1++;
1255
1.13M
    } while (c1 != '\0' && !isalnum((unsigned int) c1));
1256
1257
1.27M
    do {
1258
1.27M
      c2 = (unsigned char) *s2++;
1259
1.27M
    } while (c2 != '\0' && !isalnum((unsigned int) c2));
1260
1261
1.12M
    if (c1 != '\0')
1262
1.11M
      c1 = tolower(c1);
1263
1.12M
    if (c2 != '\0')
1264
775k
      c2 = tolower(c2);
1265
1.12M
    if (c1 == '\0')
1266
5.13k
      return c1 - c2;
1267
1.12M
  } while (c1 == c2);
1268
1269
1.01M
  return c1 - c2;
1270
1.02M
}
1271
1272
/*
1273
 * Parses the first option from @optstr. The @optstr pointer is set to the beginning
1274
 * of the next option. The options string looks like 'aaa,bbb=data,foo,bar="xxx"'.
1275
 *
1276
 * Note this function is used by libmount to parse mount options. Be careful when modify.
1277
 *
1278
 * Returns -EINVAL on parse error, 1 at the end of optstr and 0 on success.
1279
 */
1280
int ul_optstr_next(char **optstr, char **name, size_t *namesz,
1281
       char **value, size_t *valsz)
1282
55.2k
{
1283
55.2k
  int open_quote = 0;
1284
55.2k
  char *start = NULL, *stop = NULL, *p, *sep = NULL;
1285
55.2k
  char *optstr0;
1286
1287
55.2k
  assert(optstr);
1288
55.2k
  assert(*optstr);
1289
1290
55.2k
  optstr0 = *optstr;
1291
1292
55.2k
  if (name)
1293
55.2k
    *name = NULL;
1294
55.2k
  if (namesz)
1295
55.2k
    *namesz = 0;
1296
55.2k
  if (value)
1297
55.2k
    *value = NULL;
1298
55.2k
  if (valsz)
1299
55.2k
    *valsz = 0;
1300
1301
  /* trim leading commas as to not invalidate option
1302
   * strings with multiple consecutive commas */
1303
56.4k
  while (optstr0 && *optstr0 == ',')
1304
1.26k
    optstr0++;
1305
1306
2.29M
  for (p = optstr0; p && *p; p++) {
1307
2.27M
    if (!start && *p == '=')
1308
301
      return -EINVAL;
1309
2.27M
    if (!start)
1310
35.6k
      start = p;   /* beginning of the option item */
1311
2.27M
    if (*p == '"' && (p == optstr0 || *(p - 1) != '\\'))
1312
2.08k
      open_quote ^= 1; /* reverse the status */
1313
2.27M
    if (open_quote)
1314
272k
      continue;    /* still in quoted block */
1315
1.99M
    if (!sep && p > start && *p == '=')
1316
4.71k
      sep = p;   /* name and value separator */
1317
1.99M
    if (*p == ',' && (p == optstr0 || *(p - 1) != '\\'))
1318
15.7k
      stop = p;    /* terminate the option item */
1319
1.98M
    else if (*(p + 1) == '\0')
1320
19.2k
      stop = p + 1;   /* end of optstr */
1321
1.99M
    if (!start || !stop)
1322
1.96M
      continue;
1323
35.0k
    if (stop <= start)
1324
0
      return -EINVAL;
1325
1326
35.0k
    if (name)
1327
35.0k
      *name = start;
1328
35.0k
    if (namesz)
1329
35.0k
      *namesz = sep ? sep - start : stop - start;
1330
35.0k
    *optstr = *stop ? stop + 1 : stop;
1331
1332
35.0k
    if (sep) {
1333
4.71k
      if (value)
1334
4.71k
        *value = sep + 1;
1335
4.71k
      if (valsz)
1336
4.71k
        *valsz = stop - sep - 1;
1337
4.71k
    }
1338
35.0k
    return 0;
1339
35.0k
  }
1340
1341
19.8k
  return 1;       /* end of optstr */
1342
55.2k
}
1343
1344
int ul_optstr_is_valid(const char *optstr)
1345
0
{
1346
0
  int rc;
1347
0
  char *p = (char *) optstr;
1348
1349
0
  while ((rc = ul_optstr_next(&p, NULL, NULL, NULL, NULL)) == 0);
1350
0
  return rc < 0 ? 0 : 1;
1351
0
}
1352
1353
char *ul_optstr_get_value(const char *optstr, const char *key)
1354
0
{
1355
0
  size_t sz, namesz = 0, valsz = 0;
1356
0
  char *name = NULL, *value = NULL;
1357
0
  char *p = (char *) optstr;
1358
1359
0
  if (!optstr || !key || !*key)
1360
0
    return NULL;
1361
1362
0
  sz = strlen(key);
1363
0
  while (ul_optstr_next(&p, &name, &namesz, &value, &valsz) == 0) {
1364
0
    if (namesz != sz || !valsz)
1365
0
      continue;
1366
0
    if (strncmp(name, key, namesz) == 0)
1367
0
      return strndup(value, valsz);
1368
0
  }
1369
0
  return NULL;
1370
0
}
1371
1372
/* Removes one pair of matching surrounding quotes ('' or "") from @str.
1373
 *
1374
 * Returns size of the new string (without \0).
1375
 */
1376
size_t ul_unquote(char *str)
1377
0
{
1378
0
  size_t len;
1379
0
  char q;
1380
1381
0
  if (!str || !*str)
1382
0
    return 0;
1383
1384
0
  len = strlen(str);
1385
0
  q = *str;
1386
1387
0
  if ((q != '"' && q != '\'') || len < 2 || str[len - 1] != q)
1388
0
    return len;
1389
1390
0
  memmove(str, str + 1, len - 2);
1391
0
  str[len - 2] = '\0';
1392
0
  return len - 2;
1393
0
}
1394
1395
#ifdef TEST_PROGRAM_STRUTILS
1396
1397
struct testS {
1398
  char *name;
1399
  char *value;
1400
};
1401
1402
static int test_strdup_to_member(int argc, char *argv[])
1403
{
1404
  struct testS *xx;
1405
1406
  if (argc < 3)
1407
    return EXIT_FAILURE;
1408
1409
  xx = calloc(1, sizeof(*xx));
1410
  if (!xx)
1411
    err(EXIT_FAILURE, "calloc() failed");
1412
1413
  strdup_to_struct_member(xx, name, argv[1]);
1414
  strdup_to_struct_member(xx, value, argv[2]);
1415
1416
  if (strcmp(xx->name, argv[1]) != 0 &&
1417
      strcmp(xx->value, argv[2]) != 0)
1418
    errx(EXIT_FAILURE, "strdup_to_struct_member() failed");
1419
1420
  printf("1: '%s', 2: '%s'\n", xx->name, xx->value);
1421
1422
  free(xx->name);
1423
  free(xx->value);
1424
  free(xx);
1425
  return EXIT_SUCCESS;
1426
}
1427
1428
static int test_strutils_sizes(int argc, char *argv[])
1429
{
1430
  uintmax_t size = 0;
1431
  char *hum1, *hum2, *hum3;
1432
1433
  if (argc < 2)
1434
    return EXIT_FAILURE;
1435
1436
  if (strtosize(argv[1], &size))
1437
    errx(EXIT_FAILURE, "invalid size '%s' value", argv[1]);
1438
1439
  hum1 = size_to_human_string(SIZE_SUFFIX_1LETTER, size);
1440
  hum2 = size_to_human_string(SIZE_SUFFIX_3LETTER |
1441
            SIZE_SUFFIX_SPACE, size);
1442
  hum3 = size_to_human_string(SIZE_SUFFIX_3LETTER |
1443
            SIZE_SUFFIX_SPACE |
1444
            SIZE_DECIMAL_2DIGITS, size);
1445
1446
  printf("%25s : %20ju : %8s : %12s : %13s\n", argv[1], size, hum1, hum2, hum3);
1447
  free(hum1);
1448
  free(hum2);
1449
  free(hum3);
1450
1451
  return EXIT_SUCCESS;
1452
}
1453
1454
static int test_strutils_cmp_paths(int argc, char *argv[])
1455
{
1456
  int rc = streq_paths(argv[1], argv[2]);
1457
1458
  if (argc < 3)
1459
    return EXIT_FAILURE;
1460
1461
  printf("%s: '%s' '%s'\n", rc == 1 ? "YES" : "NOT", argv[1], argv[2]);
1462
  return EXIT_SUCCESS;
1463
}
1464
1465
static int test_strutils_path_is_within(int argc, char *argv[])
1466
{
1467
  int rc;
1468
1469
  if (argc < 3)
1470
    return EXIT_FAILURE;
1471
1472
  rc = ul_path_is_within(argv[1], argv[2]);
1473
1474
  printf("%s: '%s' within '%s'\n", rc == 1 ? "YES" : "NOT", argv[1], argv[2]);
1475
  return EXIT_SUCCESS;
1476
}
1477
1478
static int test_strutils_normalize(int argc, char *argv[])
1479
{
1480
  unsigned char *src, *dst, *org;
1481
  size_t sz, len;
1482
1483
  if (argc < 2)
1484
    return EXIT_FAILURE;
1485
1486
  org = (unsigned char *) strdup(argv[1]);
1487
  src = (unsigned char *) strdup((char *) org);
1488
  len = strlen((char *) src);
1489
  dst = malloc(len + 1);
1490
1491
  if (!org || !src || !dst)
1492
    goto done;
1493
1494
  /* two buffers */
1495
  sz = __normalize_whitespace(src, len, dst, len + 1);
1496
  printf("1: '%s' --> '%s' [sz=%zu]\n", src, dst, sz);
1497
1498
  /* one buffer */
1499
  sz = normalize_whitespace(src);
1500
  printf("2: '%s' --> '%s' [sz=%zu]\n", org, src, sz);
1501
1502
done:
1503
  free(src);
1504
  free(dst);
1505
  free(org);
1506
1507
  return EXIT_SUCCESS;
1508
}
1509
1510
static int test_strutils_cstrcasecmp(int argc, char *argv[])
1511
{
1512
  char *a, *b;
1513
1514
  if (argc < 3)
1515
    return EXIT_FAILURE;
1516
1517
  a = argv[1];
1518
  b = argv[2];
1519
1520
  if (!a || !b)
1521
    return EXIT_FAILURE;
1522
1523
  printf("cmp    '%s' '%s' = %d\n", a, b, strcasecmp(a, b));
1524
  printf("c_cmp  '%s' '%s' = %d\n", a, b, c_strcasecmp(a, b));
1525
  printf("c_ncmp '%s' '%s' = %d\n", a, b, c_strncasecmp(a, b, strlen(a)));
1526
1527
  return EXIT_SUCCESS;
1528
}
1529
1530
int main(int argc, char *argv[])
1531
{
1532
  if (argc == 3 && strcmp(argv[1], "--size") == 0) {
1533
    return test_strutils_sizes(argc - 1, argv + 1);
1534
1535
  } else if (argc == 3 && strcmp(argv[1], "--parse-switch") == 0) {
1536
    printf("'%s'-->%d\n", argv[2], ul_parse_switch(argv[2],
1537
            "on", "off",
1538
            "enable", "disable",
1539
            "yes", "no",
1540
            "1", "0",
1541
            NULL));
1542
    return EXIT_SUCCESS;
1543
  } else if (argc == 3 && strcmp(argv[1], "--strtobool") == 0) {
1544
    bool val;
1545
    if (ul_strtobool(argv[2], &val))
1546
      err(EXIT_FAILURE, "could not parse '%s'", argv[2]);
1547
    printf("'%s'-->%s\n", argv[2], val ? "true" : "false");
1548
    return EXIT_SUCCESS;
1549
1550
  } else if (argc == 4 && strcmp(argv[1], "--cmp-paths") == 0) {
1551
    return test_strutils_cmp_paths(argc - 1, argv + 1);
1552
1553
  } else if (argc == 4 && strcmp(argv[1], "--path-is-within") == 0) {
1554
    return test_strutils_path_is_within(argc - 1, argv + 1);
1555
1556
  } else if (argc == 4 && strcmp(argv[1], "--strdup-member") == 0) {
1557
    return test_strdup_to_member(argc - 1, argv + 1);
1558
1559
  } else if  (argc == 4 && strcmp(argv[1], "--stralnumcmp") == 0) {
1560
    printf("%s\n", ul_stralnumcmp(argv[2], argv[3]) == 0 ?
1561
        "match" : "mismatch");
1562
    return EXIT_SUCCESS;
1563
1564
  } else if (argc == 4 && strcmp(argv[1], "--cstrcasecmp") == 0) {
1565
    return test_strutils_cstrcasecmp(argc - 1, argv + 1);
1566
1567
  } else if (argc == 3 && strcmp(argv[1], "--normalize") == 0) {
1568
    return test_strutils_normalize(argc - 1, argv + 1);
1569
1570
  } else if (argc == 3 && strcmp(argv[1], "--strtos64") == 0) {
1571
    printf("'%s'-->%"PRId64"\n", argv[2], strtos64_or_err(argv[2], "strtos64 failed"));
1572
    return EXIT_SUCCESS;
1573
  } else if (argc == 3 && strcmp(argv[1], "--strtou64") == 0) {
1574
    printf("'%s'-->%"PRIu64"\n", argv[2], strtou64_or_err(argv[2], "strtou64 failed"));
1575
    return EXIT_SUCCESS;
1576
  } else if (argc == 3 && strcmp(argv[1], "--strtos32") == 0) {
1577
    printf("'%s'-->%d\n", argv[2], strtos32_or_err(argv[2], "strtos32 failed"));
1578
    return EXIT_SUCCESS;
1579
  } else if (argc == 3 && strcmp(argv[1], "--strtou32") == 0) {
1580
    printf("'%s'-->%u\n", argv[2], strtou32_or_err(argv[2], "strtou32 failed"));
1581
    return EXIT_SUCCESS;
1582
  } else if (argc == 3 && strcmp(argv[1], "--strtos16") == 0) {
1583
    printf("'%s'-->%hd\n", argv[2], strtos16_or_err(argv[2], "strtos16 failed"));
1584
    return EXIT_SUCCESS;
1585
  } else if (argc == 3 && strcmp(argv[1], "--strtou16") == 0) {
1586
    printf("'%s'-->%hu\n", argv[2], strtou16_or_err(argv[2], "strtou16 failed"));
1587
    return EXIT_SUCCESS;
1588
1589
  } else if (argc == 4 && strcmp(argv[1], "--strchr-escaped") == 0) {
1590
    printf("\"%s\" --> \"%s\"\n", argv[2], ul_strchr_escaped(argv[2], *argv[3]));
1591
    return EXIT_SUCCESS;
1592
1593
  } else if (argc == 2 && strcmp(argv[1], "--next-string") == 0) {
1594
    char *buf = "abc\0Y\0\0xyz\0X";
1595
    char *end = buf + 12;
1596
    char *p = buf;
1597
1598
    do {
1599
      printf("str: '%s'\n", p);
1600
    } while ((p = ul_next_string(p, end)));
1601
1602
    return EXIT_SUCCESS;
1603
1604
  } else if (argc == 3 && strcmp(argv[1], "--optstr") == 0) {
1605
1606
    size_t namesz, valsz;
1607
    char *name = NULL, *val = NULL;
1608
    char *p = argv[2];
1609
    int rc;
1610
1611
    if (!ul_optstr_is_valid(p))
1612
      errx(EXIT_FAILURE, _("unsupported option format: %s"), p);
1613
1614
    while ((rc = ul_optstr_next(&p, &name, &namesz, &val, &valsz)) == 0) {
1615
      printf("'%.*s' : '%.*s'\n", (int) namesz, name,
1616
               (int) valsz, val);
1617
    }
1618
    if (rc == 1)
1619
      return EXIT_SUCCESS;
1620
  } else if (argc == 3 && strcmp(argv[1], "--unquote") == 0) {
1621
1622
    char *unquoted = strdup(argv[2]);
1623
1624
    if (!unquoted)
1625
      err(EXIT_FAILURE, "strdup() failed");
1626
1627
    ul_unquote(unquoted);
1628
    printf("%s-->%s\n", argv[2], unquoted);
1629
    free(unquoted);
1630
    return EXIT_SUCCESS;
1631
1632
  } else {
1633
    fprintf(stderr, "usage: %1$s --size <number>[suffix]\n"
1634
        "       %1$s --strtobool <str>\n"
1635
        "       %1$s --parse-switch <str>\n"
1636
        "       %1$s --cmp-paths <path> <path>\n"
1637
        "       %1$s --path-is-within <path> <dir>\n"
1638
        "       %1$s --strdup-member <str> <str>\n"
1639
        "       %1$s --stralnumcmp <str> <str>\n"
1640
        "       %1$s --cstrcasecmp <str> <str>\n"
1641
        "       %1$s --normalize <str>\n"
1642
        "       %1$s --strto{s,u}{16,32,64} <str>\n"
1643
        "       %1$s --optstr <str>\n"
1644
        "       %1$s --unquote <str>\n",
1645
        argv[0]);
1646
    exit(EXIT_FAILURE);
1647
  }
1648
1649
  return EXIT_FAILURE;
1650
}
1651
#endif /* TEST_PROGRAM_STRUTILS */