chiark / gitweb /
parse-util: similar to safe_atou16_full() add safe_atou_full()
[elogind.git] / src / basic / parse-util.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3   This file is part of systemd.
4
5   Copyright 2010 Lennart Poettering
6
7   systemd is free software; you can redistribute it and/or modify it
8   under the terms of the GNU Lesser General Public License as published by
9   the Free Software Foundation; either version 2.1 of the License, or
10   (at your option) any later version.
11
12   systemd is distributed in the hope that it will be useful, but
13   WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15   Lesser General Public License for more details.
16
17   You should have received a copy of the GNU Lesser General Public License
18   along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <errno.h>
22 #include <inttypes.h>
23 #include <locale.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27
28 #include "alloc-util.h"
29 #include "errno-list.h"
30 //#include "extract-word.h"
31 #include "locale-util.h"
32 #include "macro.h"
33 #include "parse-util.h"
34 #include "process-util.h"
35 #include "string-util.h"
36
37 /// Additional includes needed by elogind
38 #include "musl_missing.h"
39
40 int parse_boolean(const char *v) {
41         assert(v);
42
43         if (streq(v, "1") || strcaseeq(v, "yes") || strcaseeq(v, "y") || strcaseeq(v, "true") || strcaseeq(v, "t") || strcaseeq(v, "on"))
44                 return 1;
45         else if (streq(v, "0") || strcaseeq(v, "no") || strcaseeq(v, "n") || strcaseeq(v, "false") || strcaseeq(v, "f") || strcaseeq(v, "off"))
46                 return 0;
47
48         return -EINVAL;
49 }
50
51 int parse_pid(const char *s, pid_t* ret_pid) {
52         unsigned long ul = 0;
53         pid_t pid;
54         int r;
55
56         assert(s);
57         assert(ret_pid);
58
59         r = safe_atolu(s, &ul);
60         if (r < 0)
61                 return r;
62
63         pid = (pid_t) ul;
64
65         if ((unsigned long) pid != ul)
66                 return -ERANGE;
67
68         if (!pid_is_valid(pid))
69                 return -ERANGE;
70
71         *ret_pid = pid;
72         return 0;
73 }
74
75 int parse_mode(const char *s, mode_t *ret) {
76         char *x;
77         long l;
78
79         assert(s);
80         assert(ret);
81
82         s += strspn(s, WHITESPACE);
83         if (s[0] == '-')
84                 return -ERANGE;
85
86         errno = 0;
87         l = strtol(s, &x, 8);
88         if (errno > 0)
89                 return -errno;
90         if (!x || x == s || *x != 0)
91                 return -EINVAL;
92         if (l < 0 || l  > 07777)
93                 return -ERANGE;
94
95         *ret = (mode_t) l;
96         return 0;
97 }
98
99 int parse_ifindex(const char *s, int *ret) {
100         int ifi, r;
101
102         r = safe_atoi(s, &ifi);
103         if (r < 0)
104                 return r;
105         if (ifi <= 0)
106                 return -EINVAL;
107
108         *ret = ifi;
109         return 0;
110 }
111
112 int parse_size(const char *t, uint64_t base, uint64_t *size) {
113
114         /* Soo, sometimes we want to parse IEC binary suffixes, and
115          * sometimes SI decimal suffixes. This function can parse
116          * both. Which one is the right way depends on the
117          * context. Wikipedia suggests that SI is customary for
118          * hardware metrics and network speeds, while IEC is
119          * customary for most data sizes used by software and volatile
120          * (RAM) memory. Hence be careful which one you pick!
121          *
122          * In either case we use just K, M, G as suffix, and not Ki,
123          * Mi, Gi or so (as IEC would suggest). That's because that's
124          * frickin' ugly. But this means you really need to make sure
125          * to document which base you are parsing when you use this
126          * call. */
127
128         struct table {
129                 const char *suffix;
130                 unsigned long long factor;
131         };
132
133         static const struct table iec[] = {
134                 { "E", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL*1024ULL },
135                 { "P", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL },
136                 { "T", 1024ULL*1024ULL*1024ULL*1024ULL },
137                 { "G", 1024ULL*1024ULL*1024ULL },
138                 { "M", 1024ULL*1024ULL },
139                 { "K", 1024ULL },
140                 { "B", 1ULL },
141                 { "",  1ULL },
142         };
143
144         static const struct table si[] = {
145                 { "E", 1000ULL*1000ULL*1000ULL*1000ULL*1000ULL*1000ULL },
146                 { "P", 1000ULL*1000ULL*1000ULL*1000ULL*1000ULL },
147                 { "T", 1000ULL*1000ULL*1000ULL*1000ULL },
148                 { "G", 1000ULL*1000ULL*1000ULL },
149                 { "M", 1000ULL*1000ULL },
150                 { "K", 1000ULL },
151                 { "B", 1ULL },
152                 { "",  1ULL },
153         };
154
155         const struct table *table;
156         const char *p;
157         unsigned long long r = 0;
158         unsigned n_entries, start_pos = 0;
159
160         assert(t);
161         assert(IN_SET(base, 1000, 1024));
162         assert(size);
163
164         if (base == 1000) {
165                 table = si;
166                 n_entries = ELEMENTSOF(si);
167         } else {
168                 table = iec;
169                 n_entries = ELEMENTSOF(iec);
170         }
171
172         p = t;
173         do {
174                 unsigned long long l, tmp;
175                 double frac = 0;
176                 char *e;
177                 unsigned i;
178
179                 p += strspn(p, WHITESPACE);
180
181                 errno = 0;
182                 l = strtoull(p, &e, 10);
183                 if (errno > 0)
184                         return -errno;
185                 if (e == p)
186                         return -EINVAL;
187                 if (*p == '-')
188                         return -ERANGE;
189
190                 if (*e == '.') {
191                         e++;
192
193                         /* strtoull() itself would accept space/+/- */
194                         if (*e >= '0' && *e <= '9') {
195                                 unsigned long long l2;
196                                 char *e2;
197
198                                 l2 = strtoull(e, &e2, 10);
199                                 if (errno > 0)
200                                         return -errno;
201
202                                 /* Ignore failure. E.g. 10.M is valid */
203                                 frac = l2;
204                                 for (; e < e2; e++)
205                                         frac /= 10;
206                         }
207                 }
208
209                 e += strspn(e, WHITESPACE);
210
211                 for (i = start_pos; i < n_entries; i++)
212                         if (startswith(e, table[i].suffix))
213                                 break;
214
215                 if (i >= n_entries)
216                         return -EINVAL;
217
218                 if (l + (frac > 0) > ULLONG_MAX / table[i].factor)
219                         return -ERANGE;
220
221                 tmp = l * table[i].factor + (unsigned long long) (frac * table[i].factor);
222                 if (tmp > ULLONG_MAX - r)
223                         return -ERANGE;
224
225                 r += tmp;
226                 if ((unsigned long long) (uint64_t) r != r)
227                         return -ERANGE;
228
229                 p = e + strlen(table[i].suffix);
230
231                 start_pos = i + 1;
232
233         } while (*p);
234
235         *size = r;
236
237         return 0;
238 }
239
240 #if 0 /// UNNEEDED by elogind
241 int parse_range(const char *t, unsigned *lower, unsigned *upper) {
242         _cleanup_free_ char *word = NULL;
243         unsigned l, u;
244         int r;
245
246         assert(lower);
247         assert(upper);
248
249         /* Extract the lower bound. */
250         r = extract_first_word(&t, &word, "-", EXTRACT_DONT_COALESCE_SEPARATORS);
251         if (r < 0)
252                 return r;
253         if (r == 0)
254                 return -EINVAL;
255
256         r = safe_atou(word, &l);
257         if (r < 0)
258                 return r;
259
260         /* Check for the upper bound and extract it if needed */
261         if (!t)
262                 /* Single number with no dashes. */
263                 u = l;
264         else if (!*t)
265                 /* Trailing dash is an error. */
266                 return -EINVAL;
267         else {
268                 r = safe_atou(t, &u);
269                 if (r < 0)
270                         return r;
271         }
272
273         *lower = l;
274         *upper = u;
275         return 0;
276 }
277 #endif // 0
278
279 int parse_errno(const char *t) {
280         int r, e;
281
282         assert(t);
283
284         r = errno_from_name(t);
285         if (r > 0)
286                 return r;
287
288         r = safe_atoi(t, &e);
289         if (r < 0)
290                 return r;
291
292         /* 0 is also allowed here */
293         if (!errno_is_valid(e) && e != 0)
294                 return -ERANGE;
295
296         return e;
297 }
298
299 int parse_syscall_and_errno(const char *in, char **name, int *error) {
300         _cleanup_free_ char *n = NULL;
301         char *p;
302         int e = -1;
303
304         assert(in);
305         assert(name);
306         assert(error);
307
308         /*
309          * This parse "syscall:errno" like "uname:EILSEQ", "@sync:255".
310          * If errno is omitted, then error is set to -1.
311          * Empty syscall name is not allowed.
312          * Here, we do not check that the syscall name is valid or not.
313          */
314
315         p = strchr(in, ':');
316         if (p) {
317                 e = parse_errno(p + 1);
318                 if (e < 0)
319                         return e;
320
321                 n = strndup(in, p - in);
322         } else
323                 n = strdup(in);
324
325         if (!n)
326                 return -ENOMEM;
327
328         if (isempty(n))
329                 return -EINVAL;
330
331         *error = e;
332         *name = n;
333         n = NULL;
334
335         return 0;
336 }
337
338 char *format_bytes(char *buf, size_t l, uint64_t t) {
339         unsigned i;
340
341         /* This only does IEC units so far */
342
343         static const struct {
344                 const char *suffix;
345                 uint64_t factor;
346         } table[] = {
347                 { "E", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) },
348                 { "P", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) },
349                 { "T", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) },
350                 { "G", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) },
351                 { "M", UINT64_C(1024)*UINT64_C(1024) },
352                 { "K", UINT64_C(1024) },
353         };
354
355         if (t == (uint64_t) -1)
356                 return NULL;
357
358         for (i = 0; i < ELEMENTSOF(table); i++) {
359
360                 if (t >= table[i].factor) {
361                         snprintf(buf, l,
362                                  "%" PRIu64 ".%" PRIu64 "%s",
363                                  t / table[i].factor,
364                                  ((t*UINT64_C(10)) / table[i].factor) % UINT64_C(10),
365                                  table[i].suffix);
366
367                         goto finish;
368                 }
369         }
370
371         snprintf(buf, l, "%" PRIu64 "B", t);
372
373 finish:
374         buf[l-1] = 0;
375         return buf;
376
377 }
378
379 int safe_atou_full(const char *s, unsigned base, unsigned *ret_u) {
380         char *x = NULL;
381         unsigned long l;
382
383         assert(s);
384         assert(ret_u);
385         assert(base <= 16);
386
387         /* strtoul() is happy to parse negative values, and silently
388          * converts them to unsigned values without generating an
389          * error. We want a clean error, hence let's look for the "-"
390          * prefix on our own, and generate an error. But let's do so
391          * only after strtoul() validated that the string is clean
392          * otherwise, so that we return EINVAL preferably over
393          * ERANGE. */
394
395         s += strspn(s, WHITESPACE);
396
397         errno = 0;
398         l = strtoul(s, &x, base);
399         if (errno > 0)
400                 return -errno;
401         if (!x || x == s || *x != 0)
402                 return -EINVAL;
403         if (s[0] == '-')
404                 return -ERANGE;
405         if ((unsigned long) (unsigned) l != l)
406                 return -ERANGE;
407
408         *ret_u = (unsigned) l;
409         return 0;
410 }
411
412 int safe_atoi(const char *s, int *ret_i) {
413         char *x = NULL;
414         long l;
415
416         assert(s);
417         assert(ret_i);
418
419         errno = 0;
420         l = strtol(s, &x, 0);
421         if (errno > 0)
422                 return -errno;
423         if (!x || x == s || *x != 0)
424                 return -EINVAL;
425         if ((long) (int) l != l)
426                 return -ERANGE;
427
428         *ret_i = (int) l;
429         return 0;
430 }
431
432 int safe_atollu(const char *s, long long unsigned *ret_llu) {
433         char *x = NULL;
434         unsigned long long l;
435
436         assert(s);
437         assert(ret_llu);
438
439         s += strspn(s, WHITESPACE);
440
441         errno = 0;
442         l = strtoull(s, &x, 0);
443         if (errno > 0)
444                 return -errno;
445         if (!x || x == s || *x != 0)
446                 return -EINVAL;
447         if (*s == '-')
448                 return -ERANGE;
449
450         *ret_llu = l;
451         return 0;
452 }
453
454 int safe_atolli(const char *s, long long int *ret_lli) {
455         char *x = NULL;
456         long long l;
457
458         assert(s);
459         assert(ret_lli);
460
461         errno = 0;
462         l = strtoll(s, &x, 0);
463         if (errno > 0)
464                 return -errno;
465         if (!x || x == s || *x != 0)
466                 return -EINVAL;
467
468         *ret_lli = l;
469         return 0;
470 }
471
472 int safe_atou8(const char *s, uint8_t *ret) {
473         char *x = NULL;
474         unsigned long l;
475
476         assert(s);
477         assert(ret);
478
479         s += strspn(s, WHITESPACE);
480
481         errno = 0;
482         l = strtoul(s, &x, 0);
483         if (errno > 0)
484                 return -errno;
485         if (!x || x == s || *x != 0)
486                 return -EINVAL;
487         if (s[0] == '-')
488                 return -ERANGE;
489         if ((unsigned long) (uint8_t) l != l)
490                 return -ERANGE;
491
492         *ret = (uint8_t) l;
493         return 0;
494 }
495
496 int safe_atou16_full(const char *s, unsigned base, uint16_t *ret) {
497         char *x = NULL;
498         unsigned long l;
499
500         assert(s);
501         assert(ret);
502         assert(base <= 16);
503
504         s += strspn(s, WHITESPACE);
505
506         errno = 0;
507         l = strtoul(s, &x, base);
508         if (errno > 0)
509                 return -errno;
510         if (!x || x == s || *x != 0)
511                 return -EINVAL;
512         if (s[0] == '-')
513                 return -ERANGE;
514         if ((unsigned long) (uint16_t) l != l)
515                 return -ERANGE;
516
517         *ret = (uint16_t) l;
518         return 0;
519 }
520
521 int safe_atoi16(const char *s, int16_t *ret) {
522         char *x = NULL;
523         long l;
524
525         assert(s);
526         assert(ret);
527
528         errno = 0;
529         l = strtol(s, &x, 0);
530         if (errno > 0)
531                 return -errno;
532         if (!x || x == s || *x != 0)
533                 return -EINVAL;
534         if ((long) (int16_t) l != l)
535                 return -ERANGE;
536
537         *ret = (int16_t) l;
538         return 0;
539 }
540
541 int safe_atod(const char *s, double *ret_d) {
542         _cleanup_(freelocalep) locale_t loc = (locale_t) 0;
543         char *x = NULL;
544         double d = 0;
545
546         assert(s);
547         assert(ret_d);
548
549         loc = newlocale(LC_NUMERIC_MASK, "C", (locale_t) 0);
550         if (loc == (locale_t) 0)
551                 return -errno;
552
553         errno = 0;
554         d = strtod_l(s, &x, loc);
555         if (errno > 0)
556                 return -errno;
557         if (!x || x == s || *x != 0)
558                 return -EINVAL;
559
560         *ret_d = (double) d;
561         return 0;
562 }
563
564 int parse_fractional_part_u(const char **p, size_t digits, unsigned *res) {
565         size_t i;
566         unsigned val = 0;
567         const char *s;
568
569         s = *p;
570
571         /* accept any number of digits, strtoull is limted to 19 */
572         for (i=0; i < digits; i++,s++) {
573                 if (*s < '0' || *s > '9') {
574                         if (i == 0)
575                                 return -EINVAL;
576
577                         /* too few digits, pad with 0 */
578                         for (; i < digits; i++)
579                                 val *= 10;
580
581                         break;
582                 }
583
584                 val *= 10;
585                 val += *s - '0';
586         }
587
588         /* maybe round up */
589         if (*s >= '5' && *s <= '9')
590                 val++;
591
592         s += strspn(s, DIGITS);
593
594         *p = s;
595         *res = val;
596
597         return 0;
598 }
599
600 int parse_percent_unbounded(const char *p) {
601         const char *pc, *n;
602         int r, v;
603
604         pc = endswith(p, "%");
605         if (!pc)
606                 return -EINVAL;
607
608         n = strndupa(p, pc - p);
609         r = safe_atoi(n, &v);
610         if (r < 0)
611                 return r;
612         if (v < 0)
613                 return -ERANGE;
614
615         return v;
616 }
617
618 int parse_percent(const char *p) {
619         int v;
620
621         v = parse_percent_unbounded(p);
622         if (v > 100)
623                 return -ERANGE;
624
625         return v;
626 }
627
628 #if 0 /// UNNEEDED by elogind
629 int parse_nice(const char *p, int *ret) {
630         int n, r;
631
632         r = safe_atoi(p, &n);
633         if (r < 0)
634                 return r;
635
636         if (!nice_is_valid(n))
637                 return -ERANGE;
638
639         *ret = n;
640         return 0;
641 }
642
643 int parse_ip_port(const char *s, uint16_t *ret) {
644         uint16_t l;
645         int r;
646
647         r = safe_atou16(s, &l);
648         if (r < 0)
649                 return r;
650
651         if (l == 0)
652                 return -EINVAL;
653
654         *ret = (uint16_t) l;
655
656         return 0;
657 }
658 #endif // 0
659
660 int parse_dev(const char *s, dev_t *ret) {
661         unsigned x, y;
662         dev_t d;
663
664         if (sscanf(s, "%u:%u", &x, &y) != 2)
665                 return -EINVAL;
666
667         d = makedev(x, y);
668         if ((unsigned) major(d) != x || (unsigned) minor(d) != y)
669                 return -EINVAL;
670
671         *ret = d;
672         return 0;
673 }