Coverage Report

Created: 2025-07-11 06:18

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