skip to content
==> 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}