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