1#include <kassert.h>
2#include <math/fixed.h>
3#include <mem/alloc.h>
4#include <mem/alloc_or_die.h>
5#include <parse.h>
6#include <string.h>
7
8static bool parse_bool_internal(const char *str, bool *out) {
9 if (!str || *str == '\0') {
10 return false;
11 }
12
13 while (*str == ' ' || *str == '\t')
14 str++;
15
16 if (*str == '\0') {
17 return false;
18 }
19
20 const char *end = str + strlen(str);
21 while (end > str && (*(end - 1) == ' ' || *(end - 1) == '\t'))
22 end--;
23
24 size_t len = (size_t) (end - str);
25
26 const char *enabled_terms[] = {
27 "true", "enabled", "y", "yes", "yeah", "yup", "on",
28 "positive", "1", "active", "allow", "ok", "open"};
29
30 const char *disabled_terms[] = {
31 "false", "disabled", "n", "no", "nope", "off",
32 "negative", "0", "inactive", "deny", "blocked", "closed"};
33
34 for (size_t i = 0; i < sizeof(enabled_terms) / sizeof(enabled_terms[0]);
35 i++) {
36 if (len == strlen(str: enabled_terms[i]) &&
37 strncasecmp(s1: str, s2: enabled_terms[i], n: len) == 0) {
38 if (out)
39 *out = true;
40 return true;
41 }
42 }
43
44 for (size_t i = 0; i < sizeof(disabled_terms) / sizeof(disabled_terms[0]);
45 i++) {
46 if (len == strlen(str: disabled_terms[i]) &&
47 strncasecmp(s1: str, s2: disabled_terms[i], n: len) == 0) {
48 if (out)
49 *out = false;
50 return true;
51 }
52 }
53
54 return false;
55}
56
57static bool parse_data_size_internal(const char *str, uint64_t *out) {
58 if (!str || *str == '\0')
59 return false;
60
61 while (*str == ' ' || *str == '\t')
62 str++;
63
64 if (*str < '0' || *str > '9')
65 return false;
66
67 uint64_t value = 0;
68 while (*str >= '0' && *str <= '9') {
69 uint64_t digit = (uint64_t) (*str - '0');
70 if (value > (UINT64_MAX - digit) / 10)
71 return false; /* Overflow */
72
73 value = value * 10 + digit;
74 str++;
75 }
76
77 while (*str == ' ' || *str == '\t')
78 str++;
79
80 uint64_t multiplier = 1;
81 bool has_prefix = false;
82 switch (*str) {
83 case 'K':
84 case 'k':
85 multiplier = 1024ULL;
86 has_prefix = true;
87 str++;
88 break;
89 case 'M':
90 case 'm':
91 multiplier = 1024ULL * 1024ULL;
92 has_prefix = true;
93 str++;
94 break;
95 case 'G':
96 case 'g':
97 multiplier = 1024ULL * 1024ULL * 1024ULL;
98 has_prefix = true;
99 str++;
100 break;
101 case 'T':
102 case 't':
103 multiplier = 1024ULL * 1024ULL * 1024ULL * 1024ULL;
104 has_prefix = true;
105 str++;
106 break;
107 case 'B':
108 case 'b':
109 multiplier = 1ULL;
110 str++;
111 break;
112 case '\0': break;
113 default: return false;
114 }
115
116 if (has_prefix) {
117 if (*str == 'i' || *str == 'I') {
118 str++;
119 if (*str == 'b' || *str == 'B') {
120 str++;
121 } else {
122 return false;
123 }
124 } else if (*str == 'b' || *str == 'B') {
125 str++;
126 }
127 }
128
129 while (*str == ' ' || *str == '\t')
130 str++;
131
132 if (*str != '\0')
133 return false;
134
135 uint64_t result = value * multiplier;
136 if (multiplier > 1 && result / multiplier != value)
137 return false; /* Overflow */
138
139 if (out)
140 *out = result;
141 return true;
142}
143
144static bool parse_duration_internal(const char *str, time_ns_t *out) {
145 if (!str || *str == '\0')
146 return false;
147
148 while (*str == ' ' || *str == '\t')
149 str++;
150
151 if (*str < '0' || *str > '9')
152 return false;
153
154 time_ns_t value = 0;
155 while (*str >= '0' && *str <= '9') {
156 time_ns_t digit = (time_ns_t) (*str - '0');
157 if (value > (TIME_NS_MAX - digit) / 10)
158 return false;
159
160 value = value * 10 + digit;
161 str++;
162 }
163
164 while (*str == ' ' || *str == '\t')
165 str++;
166
167 const char *unit_start = str;
168 while (*str && *str != ' ' && *str != '\t')
169 str++;
170
171 size_t unit_len = str - unit_start;
172
173 while (*str == ' ' || *str == '\t')
174 str++;
175
176 if (*str != '\0')
177 return false;
178
179 time_ns_t multiplier = 0;
180 if (unit_len == 0) {
181 multiplier = 1ULL;
182 } else if (unit_len == 2 && strncasecmp(s1: unit_start, s2: "ns", n: 2) == 0) {
183 multiplier = 1ULL;
184 } else if (unit_len == 2 && strncasecmp(s1: unit_start, s2: "us", n: 2) == 0) {
185 multiplier = 1000ULL;
186 } else if (unit_len == 2 && strncasecmp(s1: unit_start, s2: "ms", n: 2) == 0) {
187 multiplier = 1000000ULL;
188 } else if ((unit_len == 1 && strncasecmp(s1: unit_start, s2: "s", n: 1) == 0) ||
189 (unit_len == 3 && strncasecmp(s1: unit_start, s2: "sec", n: 3) == 0) ||
190 (unit_len == 4 && strncasecmp(s1: unit_start, s2: "secs", n: 4) == 0) ||
191 (unit_len == 6 && strncasecmp(s1: unit_start, s2: "second", n: 6) == 0) ||
192 (unit_len == 7 && strncasecmp(s1: unit_start, s2: "seconds", n: 7) == 0)) {
193 multiplier = 1000000000ULL;
194 } else if ((unit_len == 1 && strncasecmp(s1: unit_start, s2: "m", n: 1) == 0) ||
195 (unit_len == 3 && strncasecmp(s1: unit_start, s2: "min", n: 3) == 0) ||
196 (unit_len == 4 && strncasecmp(s1: unit_start, s2: "mins", n: 4) == 0) ||
197 (unit_len == 6 && strncasecmp(s1: unit_start, s2: "minute", n: 6) == 0) ||
198 (unit_len == 7 && strncasecmp(s1: unit_start, s2: "minutes", n: 7) == 0)) {
199 multiplier = 60ULL * 1000000000ULL;
200 } else if ((unit_len == 1 && strncasecmp(s1: unit_start, s2: "h", n: 1) == 0) ||
201 (unit_len == 2 && strncasecmp(s1: unit_start, s2: "hr", n: 2) == 0) ||
202 (unit_len == 3 && strncasecmp(s1: unit_start, s2: "hrs", n: 3) == 0) ||
203 (unit_len == 4 && strncasecmp(s1: unit_start, s2: "hour", n: 4) == 0) ||
204 (unit_len == 5 && strncasecmp(s1: unit_start, s2: "hours", n: 5) == 0)) {
205 multiplier = 3600ULL * 1000000000ULL;
206 } else if ((unit_len == 1 && strncasecmp(s1: unit_start, s2: "d", n: 1) == 0) ||
207 (unit_len == 3 && strncasecmp(s1: unit_start, s2: "day", n: 3) == 0) ||
208 (unit_len == 4 && strncasecmp(s1: unit_start, s2: "days", n: 4) == 0)) {
209 multiplier = 86400ULL * 1000000000ULL;
210 } else {
211 return false;
212 }
213
214 if (multiplier > 0 && value > TIME_NS_MAX / multiplier)
215 return false;
216
217 if (out)
218 *out = (value * multiplier);
219 return true;
220}
221
222static bool parse_cpu_mask_internal(const char *str, struct cpu_mask *out,
223 size_t n_cpus) {
224 if (!str || *str == '\0' || n_cpus == 0)
225 return false;
226
227 /* validate syntax and range */
228 const char *p = str;
229 while (*p) {
230 if (*p < '0' || *p > '9')
231 return false;
232
233 size_t start = 0;
234 while (*p >= '0' && *p <= '9') {
235 size_t digit = (size_t) (*p - '0');
236 if (start > (SIZE_MAX - digit) / 10)
237 return false;
238
239 start = start * 10 + digit;
240 p++;
241 }
242
243 size_t end = start;
244 if (*p == '-') {
245 p++;
246 if (*p < '0' || *p > '9')
247 return false;
248
249 end = 0;
250 while (*p >= '0' && *p <= '9') {
251 size_t digit = (size_t) (*p - '0');
252 if (end > (SIZE_MAX - digit) / 10)
253 return false;
254
255 end = end * 10 + digit;
256 p++;
257 }
258 }
259
260 if (start > end || end >= n_cpus)
261 return false;
262
263 if (*p == ',') {
264 p++;
265 if (*p == '\0')
266 return false;
267
268 } else if (*p != '\0') {
269 return false;
270 }
271 }
272
273 if (!out)
274 return true;
275
276 /* populate mask if requested */
277 if (!cpu_mask_init(m: out, nbits: n_cpus))
278 return false;
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 = kmalloc(count * sizeof(char *), ALLOC_FLAGS_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 = kmalloc((size_t) (end - begin) + 1, ALLOC_FLAGS_ZERO);
589 if (!text) {
590 for (size_t k = 0; k < item; k++)
591 kfree(items[k]);
592 kfree(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, size_t n_cpus) {
621 return parse_cpu_mask_internal(str, out, n_cpus);
622}
623
624bool parse_is_range(const char *str, uint64_t *start, uint64_t *end) {
625 return parse_range_internal(str, start, end);
626}
627
628bool parse_is_mac(const char *str, uint64_t *out) {
629 return parse_mac_internal(str, out);
630}
631
632bool parse_is_fx(const char *str, fx32_32_t *out) {
633 return parse_fx_internal(str, out);
634}
635
636bool parse_is_int(const char *str, int64_t *out) {
637 return parse_int_internal(str, out);
638}
639
640bool parse_is_uint(const char *str, uint64_t *out) {
641 return parse_uint_internal(str, out);
642}
643
644bool parse_is_list(const char *str, struct parse_list *out) {
645 return parse_list_internal(str, out);
646}
647
648void parse_list_free(struct parse_list *list) {
649 if (!list || !list->items)
650 return;
651 for (size_t i = 0; i < list->count; i++) {
652 if (list->items[i])
653 kfree(list->items[i]);
654 }
655 kfree(list->items);
656 list->items = NULL;
657 list->count = 0;
658}
659