1#include <enforce.h>
2#include <math/fixed.h>
3#include <math/units.h>
4#include <mem/alloc.h>
5#include <mem/nofail.h>
6#include <parse.h>
7#include <string.h>
8
9static bool parse_bool_internal(const char *str, bool *out) {
10 if (!str || *str == '\0') {
11 return false;
12 }
13
14 while (*str == ' ' || *str == '\t')
15 str++;
16
17 if (*str == '\0') {
18 return false;
19 }
20
21 const char *end = str + strlen(str);
22 while (end > str && (*(end - 1) == ' ' || *(end - 1) == '\t'))
23 end--;
24
25 size_t len = (size_t) (end - str);
26
27 const char *enabled_terms[] = {
28 "true", "enabled", "y", "yes", "yeah", "yup", "on",
29 "positive", "1", "active", "allow", "ok", "open"};
30
31 const char *disabled_terms[] = {
32 "false", "disabled", "n", "no", "nope", "off",
33 "negative", "0", "inactive", "deny", "blocked", "closed"};
34
35 for (size_t i = 0; i < sizeof(enabled_terms) / sizeof(enabled_terms[0]);
36 i++) {
37 if (len == strlen(str: enabled_terms[i]) &&
38 strncasecmp(s1: str, s2: enabled_terms[i], n: len) == 0) {
39 if (out)
40 *out = true;
41 return true;
42 }
43 }
44
45 for (size_t i = 0; i < sizeof(disabled_terms) / sizeof(disabled_terms[0]);
46 i++) {
47 if (len == strlen(str: disabled_terms[i]) &&
48 strncasecmp(s1: str, s2: disabled_terms[i], n: len) == 0) {
49 if (out)
50 *out = false;
51 return true;
52 }
53 }
54
55 return false;
56}
57
58static bool parse_data_size_internal(const char *str, uint64_t *out) {
59 if (!str || *str == '\0')
60 return false;
61
62 while (*str == ' ' || *str == '\t')
63 str++;
64
65 if (*str < '0' || *str > '9')
66 return false;
67
68 uint64_t value = 0;
69 while (*str >= '0' && *str <= '9') {
70 uint64_t digit = (uint64_t) (*str - '0');
71 if (value > (UINT64_MAX - digit) / 10)
72 return false; /* Overflow */
73
74 value = value * 10 + digit;
75 str++;
76 }
77
78 while (*str == ' ' || *str == '\t')
79 str++;
80
81 uint64_t multiplier = 1;
82 bool has_prefix = false;
83 switch (*str) {
84 case 'K':
85 case 'k':
86 multiplier = KIB(1);
87 has_prefix = true;
88 str++;
89 break;
90 case 'M':
91 case 'm':
92 multiplier = MIB(1);
93 has_prefix = true;
94 str++;
95 break;
96 case 'G':
97 case 'g':
98 multiplier = GIB(1);
99 has_prefix = true;
100 str++;
101 break;
102 case 'T':
103 case 't':
104 multiplier = TIB(1);
105 has_prefix = true;
106 str++;
107 break;
108 case 'B':
109 case 'b':
110 multiplier = 1ULL;
111 str++;
112 break;
113 case '\0': break;
114 default: return false;
115 }
116
117 if (has_prefix) {
118 if (*str == 'i' || *str == 'I') {
119 str++;
120 if (*str == 'b' || *str == 'B') {
121 str++;
122 } else {
123 return false;
124 }
125 } else if (*str == 'b' || *str == 'B') {
126 str++;
127 }
128 }
129
130 while (*str == ' ' || *str == '\t')
131 str++;
132
133 if (*str != '\0')
134 return false;
135
136 uint64_t result = value * multiplier;
137 if (multiplier > 1 && result / multiplier != value)
138 return false; /* Overflow */
139
140 if (out)
141 *out = result;
142 return true;
143}
144
145static bool parse_duration_internal(const char *str, time_ns_t *out) {
146 if (!str || *str == '\0')
147 return false;
148
149 while (*str == ' ' || *str == '\t')
150 str++;
151
152 if (*str < '0' || *str > '9')
153 return false;
154
155 time_ns_t value = 0;
156 while (*str >= '0' && *str <= '9') {
157 time_ns_t digit = (time_ns_t) (*str - '0');
158 if (value > (TIME_NS_MAX - digit) / 10)
159 return false;
160
161 value = value * 10 + digit;
162 str++;
163 }
164
165 while (*str == ' ' || *str == '\t')
166 str++;
167
168 const char *unit_start = str;
169 while (*str && *str != ' ' && *str != '\t')
170 str++;
171
172 size_t unit_len = str - unit_start;
173
174 while (*str == ' ' || *str == '\t')
175 str++;
176
177 if (*str != '\0')
178 return false;
179
180 time_ns_t multiplier = 0;
181 if (unit_len == 0) {
182 multiplier = 1ULL;
183 } else if (unit_len == 2 && strncasecmp(s1: unit_start, s2: "ns", n: 2) == 0) {
184 multiplier = 1ULL;
185 } else if (unit_len == 2 && strncasecmp(s1: unit_start, s2: "us", n: 2) == 0) {
186 multiplier = 1000ULL;
187 } else if (unit_len == 2 && strncasecmp(s1: unit_start, s2: "ms", n: 2) == 0) {
188 multiplier = 1000000ULL;
189 } else if ((unit_len == 1 && strncasecmp(s1: unit_start, s2: "s", n: 1) == 0) ||
190 (unit_len == 3 && strncasecmp(s1: unit_start, s2: "sec", n: 3) == 0) ||
191 (unit_len == 4 && strncasecmp(s1: unit_start, s2: "secs", n: 4) == 0) ||
192 (unit_len == 6 && strncasecmp(s1: unit_start, s2: "second", n: 6) == 0) ||
193 (unit_len == 7 && strncasecmp(s1: unit_start, s2: "seconds", n: 7) == 0)) {
194 multiplier = 1000000000ULL;
195 } else if ((unit_len == 1 && strncasecmp(s1: unit_start, s2: "m", n: 1) == 0) ||
196 (unit_len == 3 && strncasecmp(s1: unit_start, s2: "min", n: 3) == 0) ||
197 (unit_len == 4 && strncasecmp(s1: unit_start, s2: "mins", n: 4) == 0) ||
198 (unit_len == 6 && strncasecmp(s1: unit_start, s2: "minute", n: 6) == 0) ||
199 (unit_len == 7 && strncasecmp(s1: unit_start, s2: "minutes", n: 7) == 0)) {
200 multiplier = 60ULL * 1000000000ULL;
201 } else if ((unit_len == 1 && strncasecmp(s1: unit_start, s2: "h", n: 1) == 0) ||
202 (unit_len == 2 && strncasecmp(s1: unit_start, s2: "hr", n: 2) == 0) ||
203 (unit_len == 3 && strncasecmp(s1: unit_start, s2: "hrs", n: 3) == 0) ||
204 (unit_len == 4 && strncasecmp(s1: unit_start, s2: "hour", n: 4) == 0) ||
205 (unit_len == 5 && strncasecmp(s1: unit_start, s2: "hours", n: 5) == 0)) {
206 multiplier = 3600ULL * 1000000000ULL;
207 } else if ((unit_len == 1 && strncasecmp(s1: unit_start, s2: "d", n: 1) == 0) ||
208 (unit_len == 3 && strncasecmp(s1: unit_start, s2: "day", n: 3) == 0) ||
209 (unit_len == 4 && strncasecmp(s1: unit_start, s2: "days", n: 4) == 0)) {
210 multiplier = 86400ULL * 1000000000ULL;
211 } else {
212 return false;
213 }
214
215 if (multiplier > 0 && value > TIME_NS_MAX / multiplier)
216 return false;
217
218 if (out)
219 *out = (value * multiplier);
220 return true;
221}
222
223static bool parse_cpu_mask_internal(const char *str, struct cpu_mask *out,
224 size_t cpu_count) {
225 if (!str || *str == '\0' || cpu_count == 0)
226 return false;
227
228 /* validate syntax and range */
229 const char *p = str;
230 while (*p) {
231 if (*p < '0' || *p > '9')
232 return false;
233
234 size_t start = 0;
235 while (*p >= '0' && *p <= '9') {
236 size_t digit = (size_t) (*p - '0');
237 if (start > (SIZE_MAX - digit) / 10)
238 return false;
239
240 start = start * 10 + digit;
241 p++;
242 }
243
244 size_t end = start;
245 if (*p == '-') {
246 p++;
247 if (*p < '0' || *p > '9')
248 return false;
249
250 end = 0;
251 while (*p >= '0' && *p <= '9') {
252 size_t digit = (size_t) (*p - '0');
253 if (end > (SIZE_MAX - digit) / 10)
254 return false;
255
256 end = end * 10 + digit;
257 p++;
258 }
259 }
260
261 if (start > end || end >= cpu_count)
262 return false;
263
264 if (*p == ',') {
265 p++;
266 if (*p == '\0')
267 return false;
268
269 } else if (*p != '\0') {
270 return false;
271 }
272 }
273
274 if (!out)
275 return true;
276
277 /* populate mask if requested */
278 cpu_mask_init(m: out, nbits: cpu_count);
279
280 p = str;
281 while (*p) {
282 size_t start = 0;
283 while (*p >= '0' && *p <= '9') {
284 start = start * 10 + (size_t) (*p - '0');
285 p++;
286 }
287
288 size_t end = start;
289 if (*p == '-') {
290 p++;
291 end = 0;
292 while (*p >= '0' && *p <= '9') {
293 end = end * 10 + (size_t) (*p - '0');
294 p++;
295 }
296 }
297
298 for (size_t i = start; i <= end; i++)
299 cpu_mask_set(m: out, cpu: i);
300
301 if (*p == ',')
302 p++;
303 }
304
305 return true;
306}
307
308static bool parse_fx_internal(const char *str, fx32_32_t *out);
309
310static bool parse_range_internal(const char *str, uint64_t *start,
311 uint64_t *end) {
312 if (!str || *str == '\0')
313 return false;
314
315 while (*str == ' ' || *str == '\t')
316 str++;
317
318 if (*str == '-' || *str == '\0')
319 return false;
320
321 const char *sep = strstr(haystack: str, needle: "..");
322 size_t sep_len = 2;
323 if (!sep) {
324 sep = strchr(s: str + 1, c: '-');
325 sep_len = 1;
326 }
327
328 if (!sep)
329 return false;
330
331 size_t left_len = sep - str;
332 const char *right_str = sep + sep_len;
333
334 while (left_len > 0 &&
335 (str[left_len - 1] == ' ' || str[left_len - 1] == '\t'))
336 left_len--;
337
338 while (*right_str == ' ' || *right_str == '\t')
339 right_str++;
340
341 if (left_len == 0 || *right_str == '\0')
342 return false;
343
344 char left_buf[64];
345 if (left_len >= sizeof(left_buf))
346 return false;
347 memcpy(left_buf, str, left_len);
348 left_buf[left_len] = '\0';
349
350 uint64_t s = 0, f = 0;
351 bool parsed = false;
352
353 time_ns_t ds = 0, df = 0;
354 if (parse_duration_internal(str: left_buf, out: &ds) &&
355 parse_duration_internal(str: right_str, out: &df)) {
356 s = (uint64_t) ds;
357 f = (uint64_t) df;
358 parsed = true;
359 }
360
361 if (!parsed) {
362 uint64_t sz_s = 0, sz_f = 0;
363 if (parse_data_size_internal(str: left_buf, out: &sz_s) &&
364 parse_data_size_internal(str: right_str, out: &sz_f)) {
365 s = sz_s;
366 f = sz_f;
367 parsed = true;
368 }
369 }
370
371 if (!parsed) {
372 fx32_32_t fx_s = 0, fx_f = 0;
373 if (parse_fx_internal(str: left_buf, out: &fx_s) &&
374 parse_fx_internal(str: right_str, out: &fx_f)) {
375 s = (uint64_t) fx_s;
376 f = (uint64_t) fx_f;
377 parsed = true;
378 }
379 }
380
381 if (!parsed) {
382 char *end_l = NULL;
383 char *end_r = NULL;
384 s = strtoull(nptr: left_buf, endptr: &end_l, base: 0);
385 f = strtoull(nptr: right_str, endptr: &end_r, base: 0);
386 if (end_l != left_buf && *end_l == '\0' && end_r != right_str &&
387 *end_r == '\0') {
388 parsed = true;
389 }
390 }
391
392 if (!parsed || s > f)
393 return false;
394
395 if (start)
396 *start = s;
397 if (end)
398 *end = f;
399 return true;
400}
401
402static int hex_to_int(char c) {
403 if (c >= '0' && c <= '9')
404 return c - '0';
405 if (c >= 'a' && c <= 'f')
406 return c - 'a' + 10;
407 if (c >= 'A' && c <= 'F')
408 return c - 'A' + 10;
409 return -1;
410}
411
412static bool parse_mac_internal(const char *str, uint64_t *out) {
413 if (!str || *str == '\0')
414 return false;
415
416 uint64_t mac = 0;
417 const char *p = str;
418 for (int i = 0; i < 6; i++) {
419 int high = hex_to_int(c: *p++);
420 if (high < 0)
421 return false;
422
423 int low = hex_to_int(c: *p++);
424 if (low < 0)
425 return false;
426
427 mac = (mac << 8) | (high << 4) | low;
428
429 if (i < 5) {
430 if (*p != ':')
431 return false;
432
433 p++;
434 }
435 }
436
437 if (*p != '\0')
438 return false;
439
440 if (out)
441 *out = mac;
442 return true;
443}
444
445static bool parse_fx_internal(const char *str, fx32_32_t *out) {
446 if (!str || *str == '\0')
447 return false;
448
449 char *end = NULL;
450 fx32_32_t val = fx_parse(str, endptr: &end);
451 if (end == str || *end != '\0')
452 return false;
453
454 if (out)
455 *out = val;
456 return true;
457}
458
459static bool parse_int_internal(const char *str, int64_t *out) {
460 if (!str || *str == '\0')
461 return false;
462
463 char *end = NULL;
464 long val = strtol(nptr: str, endptr: &end, base: 0);
465 if (end == str || *end != '\0')
466 return false;
467
468 if (out)
469 *out = (int64_t) val;
470 return true;
471}
472
473static bool parse_uint_internal(const char *str, uint64_t *out) {
474 if (!str || *str == '\0')
475 return false;
476
477 const char *p = str;
478 while (*p == ' ' || *p == '\t')
479 p++;
480 if (*p == '-')
481 return false;
482
483 char *end = NULL;
484 unsigned long long val = strtoull(nptr: str, endptr: &end, base: 0);
485 if (end == str || *end != '\0')
486 return false;
487
488 if (out)
489 *out = (uint64_t) val;
490 return true;
491}
492
493static void parse_unescape(char *dst, const char *begin, const char *end) {
494 while (begin < end) {
495 if (*begin == '\\' && begin + 1 < end) {
496 begin++;
497 switch (*begin) {
498 case 'n': *dst++ = '\n'; break;
499 case 't': *dst++ = '\t'; break;
500 case 'r': *dst++ = '\r'; break;
501 default: *dst++ = *begin; break;
502 }
503 begin++;
504 } else {
505 *dst++ = *begin++;
506 }
507 }
508 *dst = '\0';
509}
510
511static bool parse_list_internal(const char *str, struct parse_list *out) {
512 if (!str || *str == '\0')
513 return false;
514
515 size_t count = 1;
516 bool quoted = false;
517 for (const char *p = str; *p; p++) {
518 if (*p == '\\' && p[1]) {
519 p++;
520 continue;
521 }
522 if (*p == '"')
523 quoted = !quoted;
524 else if (*p == ',' && !quoted)
525 count++;
526 }
527
528 if (quoted)
529 return false;
530
531 const char *item_start = str;
532 quoted = false;
533 for (const char *p = str;; p++) {
534 if (*p == '\\' && p[1]) {
535 p++;
536 continue;
537 }
538 if (*p == '"')
539 quoted = !quoted;
540 if ((*p == ',' && !quoted) || *p == '\0') {
541 const char *begin = item_start;
542 const char *end = p;
543 while (begin < end && (*begin == ' ' || *begin == '\t'))
544 begin++;
545 while (end > begin && (end[-1] == ' ' || end[-1] == '\t'))
546 end--;
547 if (begin == end)
548 return false;
549
550 if (*begin == '"') {
551 if (end - begin < 2 || end[-1] != '"')
552 return false;
553 }
554 if (*p == '\0')
555 break;
556 item_start = p + 1;
557 }
558 }
559
560 if (!out)
561 return true;
562
563 char **items = vnews(items, count, ALLOC_ZERO);
564 if (!items)
565 return false;
566
567 item_start = str;
568 size_t item = 0;
569 quoted = false;
570 for (const char *p = str;; p++) {
571 if (*p == '\\' && p[1]) {
572 p++;
573 continue;
574 }
575 if (*p == '"')
576 quoted = !quoted;
577 if ((*p == ',' && !quoted) || *p == '\0') {
578 const char *begin = item_start;
579 const char *end = p;
580 while (begin < end && (*begin == ' ' || *begin == '\t'))
581 begin++;
582 while (end > begin && (end[-1] == ' ' || end[-1] == '\t'))
583 end--;
584 if (*begin == '"') {
585 begin++;
586 end--;
587 }
588 char *text = valloc((size_t) (end - begin) + 1, ALLOC_ZERO);
589 if (!text) {
590 for (size_t k = 0; k < item; k++)
591 vfree(items[k]);
592 vfree(items);
593 return false;
594 }
595 parse_unescape(dst: text, begin, end);
596 items[item++] = text;
597 if (*p == '\0')
598 break;
599 item_start = p + 1;
600 }
601 }
602
603 out->count = count;
604 out->items = items;
605 return true;
606}
607
608bool parse_is_bool(const char *str, bool *out) {
609 return parse_bool_internal(str, out);
610}
611
612bool parse_is_data_size(const char *str, uint64_t *out) {
613 return parse_data_size_internal(str, out);
614}
615
616bool parse_is_duration(const char *str, time_ns_t *out) {
617 return parse_duration_internal(str, out);
618}
619
620bool parse_is_cpu_mask(const char *str, struct cpu_mask *out,
621 size_t cpu_count) {
622 return parse_cpu_mask_internal(str, out, cpu_count);
623}
624
625bool parse_is_range(const char *str, uint64_t *start, uint64_t *end) {
626 return parse_range_internal(str, start, end);
627}
628
629bool parse_is_mac(const char *str, uint64_t *out) {
630 return parse_mac_internal(str, out);
631}
632
633bool parse_is_fx(const char *str, fx32_32_t *out) {
634 return parse_fx_internal(str, out);
635}
636
637bool parse_is_int(const char *str, int64_t *out) {
638 return parse_int_internal(str, out);
639}
640
641bool parse_is_uint(const char *str, uint64_t *out) {
642 return parse_uint_internal(str, out);
643}
644
645bool parse_is_list(const char *str, struct parse_list *out) {
646 return parse_list_internal(str, out);
647}
648
649void parse_list_free(struct parse_list *list) {
650 if (!list || !list->items)
651 return;
652 for (size_t i = 0; i < list->count; i++) {
653 if (list->items[i])
654 vfree(list->items[i]);
655 }
656 vfree(list->items);
657 list->items = NULL;
658 list->count = 0;
659}
660