==> src/toml.c <==1/* The manifest value parser, as manifest.rs's `value`, `quoted`, `int`, `pair` and `strip_comment`. */
2#include "mesh/toml.h"
3
4#include <stdlib.h>
5#include <string.h>
6
7bool mesh_toml_int(const char *s, size_t n, uint32_t *out) {
8 char *b = mesh_alloc(n + 1);
9 size_t k = 0;
10 for (size_t i = 0; i < n; i++)
11 if (s[i] != '_') b[k++] = s[i];
12 bool ok = k >= 2 && b[0] == '0' && b[1] == 'x' ? mesh_parse_u32(b + 2, k - 2, 16, out) : mesh_parse_u32(b, k, 10, out);
13 free(b);
14 return ok;
15}
16
17bool mesh_toml_pair(const char *s, size_t n, uint32_t *a, uint32_t *b) {
18 const char *colon = memchr(s, ':', n);
19 if (!colon) return false;
20 const char *x = s, *y = colon + 1;
21 size_t xn = (size_t)(colon - s), yn = n - xn - 1;
22 mesh_trim(&x, &xn);
23 mesh_trim(&y, &yn);
24 return mesh_toml_int(x, xn, a) && mesh_toml_int(y, yn, b);
25}
26
27/* One "..." at the start of s: its contents and where the rest starts. */
28static bool quoted(const char *s, size_t n, const char **body, size_t *bn, const char **rest, size_t *rn) {
29 if (n == 0 || s[0] != '"') return false;
30 const char *end = memchr(s + 1, '"', n - 1);
31 if (!end) return false;
32 *body = s + 1;
33 *bn = (size_t)(end - s - 1);
34 *rest = end + 1;
35 *rn = n - (size_t)(end + 1 - s);
36 return true;
37}
38
39static void free_items(char **items, size_t n) {
40 for (size_t i = 0; i < n; i++) free(items[i]);
41 free(items);
42}
43
44bool mesh_toml_value(const char *v, size_t n, mesh_toml_val *out, char **err) {
45 *out = (mesh_toml_val){0};
46 mesh_trim(&v, &n);
47 if ((n == 4 && memcmp(v, "true", 4) == 0) || (n == 5 && memcmp(v, "false", 5) == 0)) {
48 out->kind = MESH_TOML_BOOL;
49 out->b = n == 4;
50 return true;
51 }
52 if (n > 0 && v[0] == '"') {
53 const char *body, *rest;
54 size_t bn, rn;
55 if (!quoted(v, n, &body, &bn, &rest, &rn)) return mesh_errf(err, "unterminated string");
56 const char *r = rest;
57 size_t rr = rn;
58 mesh_trim(&r, &rr);
59 if (rr != 0) {
60 mesh_text t = {0};
61 mesh_text_puts(&t, "junk after string: ");
62 mesh_text_debug_str(&t, rest, rn);
63 if (err) { free(*err); *err = mesh_text_take(&t); } else mesh_text_free(&t);
64 return false;
65 }
66 out->kind = MESH_TOML_STR;
67 out->s = mesh_strndup(body, bn);
68 return true;
69 }
70 if (n > 0 && v[0] == '[') {
71 const char *inner = v + 1;
72 size_t in = n - 1;
73 if (in == 0 || inner[in - 1] != ']') return mesh_errf(err, "a list must close on its line");
74 in--;
75 const char *rest = inner;
76 size_t rn = in;
77 mesh_trim(&rest, &rn);
78 char **items = NULL;
79 size_t count = 0;
80 while (rn > 0) {
81 const char *body, *r;
82 size_t bn, rr;
83 if (!quoted(rest, rn, &body, &bn, &r, &rr)) {
84 mesh_text t = {0};
85 mesh_text_puts(&t, "list items are strings: ");
86 mesh_text_debug_str(&t, rest, rn);
87 if (err) { free(*err); *err = mesh_text_take(&t); } else mesh_text_free(&t);
88 free_items(items, count);
89 return false;
90 }
91 items = mesh_realloc(items, (count + 1) * sizeof *items);
92 items[count++] = mesh_strndup(body, bn);
93 rest = r;
94 rn = rr;
95 mesh_trim(&rest, &rn);
96 if (rn > 0 && rest[0] == ',') {
97 rest++;
98 rn--;
99 mesh_trim(&rest, &rn);
100 } else if (rn != 0) {
101 mesh_text t = {0};
102 mesh_text_puts(&t, "expected ',' in list: ");
103 mesh_text_debug_str(&t, rest, rn);
104 if (err) { free(*err); *err = mesh_text_take(&t); } else mesh_text_free(&t);
105 free_items(items, count);
106 return false;
107 }
108 }
109 out->kind = MESH_TOML_LIST;
110 out->items = items;
111 out->nitems = count;
112 return true;
113 }
114 uint32_t x;
115 if (mesh_toml_int(v, n, &x)) {
116 out->kind = MESH_TOML_INT;
117 out->n = x;
118 return true;
119 }
120 if (n > 0 && v[0] == '-' && mesh_toml_int(v + 1, n - 1, &x)) {
121 out->kind = MESH_TOML_NEG;
122 out->n = x;
123 return true;
124 }
125 double f;
126 if (mesh_parse_f64(v, n, &f) && memchr(v, '.', n) && f - f == 0) {
127 out->kind = MESH_TOML_FLOAT;
128 out->f = f;
129 return true;
130 }
131 mesh_text t = {0};
132 mesh_text_puts(&t, "bad value ");
133 mesh_text_debug_str(&t, v, n);
134 if (err) { free(*err); *err = mesh_text_take(&t); } else mesh_text_free(&t);
135 return false;
136}
137
138size_t mesh_toml_strip_comment(const char *line, size_t n) {
139 bool in_str = false;
140 for (size_t i = 0; i < n; i++) {
141 if (line[i] == '"') in_str = !in_str;
142 else if (line[i] == '#' && !in_str) return i;
143 }
144 return n;
145}
146
147void mesh_toml_val_debug(mesh_text *t, const mesh_toml_val *v) {
148 switch (v->kind) {
149 case MESH_TOML_INT: mesh_text_printf(t, "Int(%u)", (unsigned)v->n); break;
150 case MESH_TOML_NEG: mesh_text_printf(t, "Neg(%u)", (unsigned)v->n); break;
151 case MESH_TOML_FLOAT:
152 mesh_text_puts(t, "Float(");
153 mesh_text_debug_f64(t, v->f);
154 mesh_text_putc(t, ')');
155 break;
156 case MESH_TOML_STR:
157 mesh_text_puts(t, "Str(");
158 mesh_text_debug_str(t, v->s, strlen(v->s));
159 mesh_text_putc(t, ')');
160 break;
161 case MESH_TOML_BOOL: mesh_text_puts(t, v->b ? "Bool(true)" : "Bool(false)"); break;
162 case MESH_TOML_LIST:
163 mesh_text_puts(t, "List([");
164 for (size_t i = 0; i < v->nitems; i++) {
165 if (i) mesh_text_puts(t, ", ");
166 mesh_text_debug_str(t, v->items[i], strlen(v->items[i]));
167 }
168 mesh_text_puts(t, "])");
169 break;
170 }
171}
172
173char *mesh_toml_val_debug_str(const mesh_toml_val *v) {
174 mesh_text t = {0};
175 mesh_toml_val_debug(&t, v);
176 return mesh_text_take(&t);
177}
178
179void mesh_toml_val_free(mesh_toml_val *v) {
180 free(v->s);
181 free_items(v->items, v->nitems);
182 *v = (mesh_toml_val){0};
183}