File Coverage

toml.c
Criterion Covered Total %
statement 685 1155 59.3
branch 421 900 46.7
condition n/a
subroutine n/a
pod n/a
total 1106 2055 53.8


line stmt bran cond sub pod time code
1             /*
2              
3             MIT License
4              
5             Copyright (c) 2017 - 2021 CK Tan
6             https://github.com/cktan/tomlc99
7              
8             Permission is hereby granted, free of charge, to any person obtaining a copy
9             of this software and associated documentation files (the "Software"), to deal
10             in the Software without restriction, including without limitation the rights
11             to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12             copies of the Software, and to permit persons to whom the Software is
13             furnished to do so, subject to the following conditions:
14              
15             The above copyright notice and this permission notice shall be included in all
16             copies or substantial portions of the Software.
17              
18             THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19             IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20             FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21             AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22             LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23             OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24             SOFTWARE.
25              
26             */
27             #define _POSIX_C_SOURCE 200809L
28             #include
29             #include
30             #include
31             #include
32             #include
33             #include
34             #include
35             #include
36             #include "toml.h"
37              
38              
39             static void* (*ppmalloc)(size_t) = malloc;
40             static void (*ppfree)(void*) = free;
41              
42 0           void toml_set_memutil(void* (*xxmalloc)(size_t),
43             void (*xxfree)(void*))
44             {
45 0 0         if (xxmalloc) ppmalloc = xxmalloc;
46 0 0         if (xxfree) ppfree = xxfree;
47 0           }
48              
49              
50             #define MALLOC(a) ppmalloc(a)
51             #define FREE(a) ppfree(a)
52              
53 40           static void* CALLOC(size_t nmemb, size_t sz)
54             {
55 40           int nb = sz * nmemb;
56 40           void* p = MALLOC(nb);
57 40 50         if (p) {
58 40           memset(p, 0, nb);
59             }
60 40           return p;
61             }
62              
63              
64 0           static char* STRDUP(const char* s)
65             {
66 0           int len = strlen(s);
67 0           char* p = MALLOC(len+1);
68 0 0         if (p) {
69 0           memcpy(p, s, len);
70 0           p[len] = 0;
71             }
72 0           return p;
73             }
74              
75 68           static char* STRNDUP(const char* s, size_t n)
76             {
77 68           size_t len = strnlen(s, n);
78 68           char* p = MALLOC(len+1);
79 68 50         if (p) {
80 68           memcpy(p, s, len);
81 68           p[len] = 0;
82             }
83 68           return p;
84             }
85              
86              
87              
88             /**
89             * Convert a char in utf8 into UCS, and store it in *ret.
90             * Return #bytes consumed or -1 on failure.
91             */
92 0           int toml_utf8_to_ucs(const char* orig, int len, int64_t* ret)
93             {
94 0           const unsigned char* buf = (const unsigned char*) orig;
95 0           unsigned i = *buf++;
96             int64_t v;
97              
98             /* 0x00000000 - 0x0000007F:
99             0xxxxxxx
100             */
101 0 0         if (0 == (i >> 7)) {
102 0 0         if (len < 1) return -1;
103 0           v = i;
104 0           return *ret = v, 1;
105             }
106             /* 0x00000080 - 0x000007FF:
107             110xxxxx 10xxxxxx
108             */
109 0 0         if (0x6 == (i >> 5)) {
110 0 0         if (len < 2) return -1;
111 0           v = i & 0x1f;
112 0 0         for (int j = 0; j < 1; j++) {
113 0           i = *buf++;
114 0 0         if (0x2 != (i >> 6)) return -1;
115 0           v = (v << 6) | (i & 0x3f);
116             }
117 0           return *ret = v, (const char*) buf - orig;
118             }
119              
120             /* 0x00000800 - 0x0000FFFF:
121             1110xxxx 10xxxxxx 10xxxxxx
122             */
123 0 0         if (0xE == (i >> 4)) {
124 0 0         if (len < 3) return -1;
125 0           v = i & 0x0F;
126 0 0         for (int j = 0; j < 2; j++) {
127 0           i = *buf++;
128 0 0         if (0x2 != (i >> 6)) return -1;
129 0           v = (v << 6) | (i & 0x3f);
130             }
131 0           return *ret = v, (const char*) buf - orig;
132             }
133              
134             /* 0x00010000 - 0x001FFFFF:
135             11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
136             */
137 0 0         if (0x1E == (i >> 3)) {
138 0 0         if (len < 4) return -1;
139 0           v = i & 0x07;
140 0 0         for (int j = 0; j < 3; j++) {
141 0           i = *buf++;
142 0 0         if (0x2 != (i >> 6)) return -1;
143 0           v = (v << 6) | (i & 0x3f);
144             }
145 0           return *ret = v, (const char*) buf - orig;
146             }
147              
148             /* 0x00200000 - 0x03FFFFFF:
149             111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
150             */
151 0 0         if (0x3E == (i >> 2)) {
152 0 0         if (len < 5) return -1;
153 0           v = i & 0x03;
154 0 0         for (int j = 0; j < 4; j++) {
155 0           i = *buf++;
156 0 0         if (0x2 != (i >> 6)) return -1;
157 0           v = (v << 6) | (i & 0x3f);
158             }
159 0           return *ret = v, (const char*) buf - orig;
160             }
161              
162             /* 0x04000000 - 0x7FFFFFFF:
163             1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
164             */
165 0 0         if (0x7e == (i >> 1)) {
166 0 0         if (len < 6) return -1;
167 0           v = i & 0x01;
168 0 0         for (int j = 0; j < 5; j++) {
169 0           i = *buf++;
170 0 0         if (0x2 != (i >> 6)) return -1;
171 0           v = (v << 6) | (i & 0x3f);
172             }
173 0           return *ret = v, (const char*) buf - orig;
174             }
175 0           return -1;
176             }
177              
178              
179             /**
180             * Convert a UCS char to utf8 code, and return it in buf.
181             * Return #bytes used in buf to encode the char, or
182             * -1 on error.
183             */
184 0           int toml_ucs_to_utf8(int64_t code, char buf[6])
185             {
186             /* http://stackoverflow.com/questions/6240055/manually-converting-unicode-codepoints-into-utf-8-and-utf-16 */
187             /* The UCS code values 0xd800–0xdfff (UTF-16 surrogates) as well
188             * as 0xfffe and 0xffff (UCS noncharacters) should not appear in
189             * conforming UTF-8 streams.
190             */
191 0 0         if (0xd800 <= code && code <= 0xdfff) return -1;
    0          
192 0 0         if (0xfffe <= code && code <= 0xffff) return -1;
    0          
193              
194             /* 0x00000000 - 0x0000007F:
195             0xxxxxxx
196             */
197 0 0         if (code < 0) return -1;
198 0 0         if (code <= 0x7F) {
199 0           buf[0] = (unsigned char) code;
200 0           return 1;
201             }
202              
203             /* 0x00000080 - 0x000007FF:
204             110xxxxx 10xxxxxx
205             */
206 0 0         if (code <= 0x000007FF) {
207 0           buf[0] = 0xc0 | (code >> 6);
208 0           buf[1] = 0x80 | (code & 0x3f);
209 0           return 2;
210             }
211              
212             /* 0x00000800 - 0x0000FFFF:
213             1110xxxx 10xxxxxx 10xxxxxx
214             */
215 0 0         if (code <= 0x0000FFFF) {
216 0           buf[0] = 0xe0 | (code >> 12);
217 0           buf[1] = 0x80 | ((code >> 6) & 0x3f);
218 0           buf[2] = 0x80 | (code & 0x3f);
219 0           return 3;
220             }
221              
222             /* 0x00010000 - 0x001FFFFF:
223             11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
224             */
225 0 0         if (code <= 0x001FFFFF) {
226 0           buf[0] = 0xf0 | (code >> 18);
227 0           buf[1] = 0x80 | ((code >> 12) & 0x3f);
228 0           buf[2] = 0x80 | ((code >> 6) & 0x3f);
229 0           buf[3] = 0x80 | (code & 0x3f);
230 0           return 4;
231             }
232              
233             /* 0x00200000 - 0x03FFFFFF:
234             111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
235             */
236 0 0         if (code <= 0x03FFFFFF) {
237 0           buf[0] = 0xf8 | (code >> 24);
238 0           buf[1] = 0x80 | ((code >> 18) & 0x3f);
239 0           buf[2] = 0x80 | ((code >> 12) & 0x3f);
240 0           buf[3] = 0x80 | ((code >> 6) & 0x3f);
241 0           buf[4] = 0x80 | (code & 0x3f);
242 0           return 5;
243             }
244              
245             /* 0x04000000 - 0x7FFFFFFF:
246             1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
247             */
248 0 0         if (code <= 0x7FFFFFFF) {
249 0           buf[0] = 0xfc | (code >> 30);
250 0           buf[1] = 0x80 | ((code >> 24) & 0x3f);
251 0           buf[2] = 0x80 | ((code >> 18) & 0x3f);
252 0           buf[3] = 0x80 | ((code >> 12) & 0x3f);
253 0           buf[4] = 0x80 | ((code >> 6) & 0x3f);
254 0           buf[5] = 0x80 | (code & 0x3f);
255 0           return 6;
256             }
257              
258 0           return -1;
259             }
260              
261             /*
262             * TOML has 3 data structures: value, array, table.
263             * Each of them can have identification key.
264             */
265             typedef struct toml_keyval_t toml_keyval_t;
266             struct toml_keyval_t {
267             const char* key; /* key to this value */
268             const char* val; /* the raw value */
269             };
270              
271             typedef struct toml_arritem_t toml_arritem_t;
272             struct toml_arritem_t {
273             int valtype; /* for value kind: 'i'nt, 'd'ouble, 'b'ool, 's'tring, 't'ime, 'D'ate, 'T'imestamp */
274             char* val;
275             toml_array_t* arr;
276             toml_table_t* tab;
277             };
278              
279              
280             struct toml_array_t {
281             const char* key; /* key to this array */
282             int kind; /* element kind: 'v'alue, 'a'rray, or 't'able, 'm'ixed */
283             int type; /* for value kind: 'i'nt, 'd'ouble, 'b'ool, 's'tring, 't'ime, 'D'ate, 'T'imestamp, 'm'ixed */
284              
285             int nitem; /* number of elements */
286             toml_arritem_t* item;
287             };
288              
289              
290             struct toml_table_t {
291             const char* key; /* key to this table */
292             bool implicit; /* table was created implicitly */
293             bool readonly; /* no more modification allowed */
294              
295             /* key-values in the table */
296             int nkval;
297             toml_keyval_t** kval;
298              
299             /* arrays in the table */
300             int narr;
301             toml_array_t** arr;
302              
303             /* tables in the table */
304             int ntab;
305             toml_table_t** tab;
306             };
307              
308              
309 348 100         static inline void xfree(const void* x) { if (x) FREE((void*)(intptr_t)x); }
310              
311              
312             enum tokentype_t {
313             INVALID,
314             DOT,
315             COMMA,
316             EQUAL,
317             LBRACE,
318             RBRACE,
319             NEWLINE,
320             LBRACKET,
321             RBRACKET,
322             STRING,
323             };
324             typedef enum tokentype_t tokentype_t;
325              
326             typedef struct token_t token_t;
327             struct token_t {
328             tokentype_t tok;
329             int lineno;
330             char* ptr; /* points into context->start */
331             int len;
332             int eof;
333             };
334              
335              
336             typedef struct context_t context_t;
337             struct context_t {
338             char* start;
339             char* stop;
340             char* errbuf;
341             int errbufsz;
342              
343             token_t tok;
344             toml_table_t* root;
345             toml_table_t* curtab;
346              
347             struct {
348             int top;
349             char* key[10];
350             token_t tok[10];
351             } tpath;
352              
353             };
354              
355             #define STRINGIFY(x) #x
356             #define TOSTRING(x) STRINGIFY(x)
357             #define FLINE __FILE__ ":" TOSTRING(__LINE__)
358              
359             static int next_token(context_t* ctx, int dotisspecial);
360              
361             /*
362             Error reporting. Call when an error is detected. Always return -1.
363             */
364 0           static int e_outofmemory(context_t* ctx, const char* fline)
365             {
366 0           snprintf(ctx->errbuf, ctx->errbufsz, "ERROR: out of memory (%s)", fline);
367 0           return -1;
368             }
369              
370              
371 0           static int e_internal(context_t* ctx, const char* fline)
372             {
373 0           snprintf(ctx->errbuf, ctx->errbufsz, "internal error (%s)", fline);
374 0           return -1;
375             }
376              
377 2           static int e_syntax(context_t* ctx, int lineno, const char* msg)
378             {
379 2           snprintf(ctx->errbuf, ctx->errbufsz, "line %d: %s", lineno, msg);
380 2           return -1;
381             }
382              
383 0           static int e_badkey(context_t* ctx, int lineno)
384             {
385 0           snprintf(ctx->errbuf, ctx->errbufsz, "line %d: bad key", lineno);
386 0           return -1;
387             }
388              
389 0           static int e_keyexists(context_t* ctx, int lineno)
390             {
391 0           snprintf(ctx->errbuf, ctx->errbufsz, "line %d: key exists", lineno);
392 0           return -1;
393             }
394              
395 0           static int e_forbid(context_t* ctx, int lineno, const char* msg)
396             {
397 0           snprintf(ctx->errbuf, ctx->errbufsz, "line %d: %s", lineno, msg);
398 0           return -1;
399             }
400              
401 34           static void* expand(void* p, int sz, int newsz)
402             {
403 34           void* s = MALLOC(newsz);
404 34 50         if (!s) return 0;
405              
406 34           memcpy(s, p, sz);
407 34           FREE(p);
408 34           return s;
409             }
410              
411 30           static void** expand_ptrarr(void** p, int n)
412             {
413 30           void** s = MALLOC((n+1) * sizeof(void*));
414 30 50         if (!s) return 0;
415              
416 30           s[n] = 0;
417 30           memcpy(s, p, n * sizeof(void*));
418 30           FREE(p);
419 30           return s;
420             }
421              
422 17           static toml_arritem_t* expand_arritem(toml_arritem_t* p, int n)
423             {
424 17           toml_arritem_t* pp = expand(p, n*sizeof(*p), (n+1)*sizeof(*p));
425 17 50         if (!pp) return 0;
426              
427 17           memset(&pp[n], 0, sizeof(pp[n]));
428 17           return pp;
429             }
430              
431              
432 0           static char* norm_lit_str(const char* src, int srclen,
433             int multiline,
434             char* errbuf, int errbufsz)
435             {
436 0           char* dst = 0; /* will write to dst[] and return it */
437 0           int max = 0; /* max size of dst[] */
438 0           int off = 0; /* cur offset in dst[] */
439 0           const char* sp = src;
440 0           const char* sq = src + srclen;
441             int ch;
442              
443             /* scan forward on src */
444             for (;;) {
445 0 0         if (off >= max - 10) { /* have some slack for misc stuff */
446 0           int newmax = max + 50;
447 0           char* x = expand(dst, max, newmax);
448 0 0         if (!x) {
449 0           xfree(dst);
450 0           snprintf(errbuf, errbufsz, "out of memory");
451 0           return 0;
452             }
453 0           dst = x;
454 0           max = newmax;
455             }
456              
457             /* finished? */
458 0 0         if (sp >= sq) break;
459              
460 0           ch = *sp++;
461             /* control characters other than tab is not allowed */
462 0 0         if ((0 <= ch && ch <= 0x08)
    0          
463 0 0         || (0x0a <= ch && ch <= 0x1f)
    0          
464 0 0         || (ch == 0x7f)) {
465 0 0         if (! (multiline && (ch == '\r' || ch == '\n'))) {
    0          
    0          
466 0           xfree(dst);
467 0           snprintf(errbuf, errbufsz, "invalid char U+%04x", ch);
468 0           return 0;
469             }
470             }
471              
472             // a plain copy suffice
473 0           dst[off++] = ch;
474 0           }
475              
476 0           dst[off++] = 0;
477 0           return dst;
478             }
479              
480              
481              
482              
483             /*
484             * Convert src to raw unescaped utf-8 string.
485             * Returns NULL if error with errmsg in errbuf.
486             */
487 17           static char* norm_basic_str(const char* src, int srclen,
488             int multiline,
489             char* errbuf, int errbufsz)
490             {
491 17           char* dst = 0; /* will write to dst[] and return it */
492 17           int max = 0; /* max size of dst[] */
493 17           int off = 0; /* cur offset in dst[] */
494 17           const char* sp = src;
495 17           const char* sq = src + srclen;
496             int ch;
497              
498             /* scan forward on src */
499             for (;;) {
500 151 100         if (off >= max - 10) { /* have some slack for misc stuff */
501 17           int newmax = max + 50;
502 17           char* x = expand(dst, max, newmax);
503 17 50         if (!x) {
504 0           xfree(dst);
505 0           snprintf(errbuf, errbufsz, "out of memory");
506 0           return 0;
507             }
508 17           dst = x;
509 17           max = newmax;
510             }
511              
512             /* finished? */
513 151 100         if (sp >= sq) break;
514              
515 134           ch = *sp++;
516 134 50         if (ch != '\\') {
517             /* these chars must be escaped: U+0000 to U+0008, U+000A to U+001F, U+007F */
518 134 100         if ((0 <= ch && ch <= 0x08)
    50          
519 134 100         || (0x0a <= ch && ch <= 0x1f)
    50          
520 134 50         || (ch == 0x7f)) {
521 0 0         if (! (multiline && (ch == '\r' || ch == '\n'))) {
    0          
    0          
522 0           xfree(dst);
523 0           snprintf(errbuf, errbufsz, "invalid char U+%04x", ch);
524 0           return 0;
525             }
526             }
527              
528             // a plain copy suffice
529 134           dst[off++] = ch;
530 134           continue;
531             }
532              
533             /* ch was backslash. we expect the escape char. */
534 0 0         if (sp >= sq) {
535 0           snprintf(errbuf, errbufsz, "last backslash is invalid");
536 0           xfree(dst);
537 0           return 0;
538             }
539              
540             /* for multi-line, we want to kill line-ending-backslash ... */
541 0 0         if (multiline) {
542              
543             // if there is only whitespace after the backslash ...
544 0 0         if (sp[strspn(sp, " \t\r")] == '\n') {
545             /* skip all the following whitespaces */
546 0           sp += strspn(sp, " \t\r\n");
547 0           continue;
548             }
549             }
550              
551             /* get the escaped char */
552 0           ch = *sp++;
553 0           switch (ch) {
554             case 'u': case 'U':
555             {
556 0           int64_t ucs = 0;
557 0 0         int nhex = (ch == 'u' ? 4 : 8);
558 0 0         for (int i = 0; i < nhex; i++) {
559 0 0         if (sp >= sq) {
560 0           snprintf(errbuf, errbufsz, "\\%c expects %d hex chars", ch, nhex);
561 0           xfree(dst);
562 0           return 0;
563             }
564 0           ch = *sp++;
565 0 0         int v = ('0' <= ch && ch <= '9')
566             ? ch - '0'
567 0 0         : (('A' <= ch && ch <= 'F') ? ch - 'A' + 10 : -1);
    0          
    0          
568 0 0         if (-1 == v) {
569 0           snprintf(errbuf, errbufsz, "invalid hex chars for \\u or \\U");
570 0           xfree(dst);
571 0           return 0;
572             }
573 0           ucs = ucs * 16 + v;
574             }
575 0           int n = toml_ucs_to_utf8(ucs, &dst[off]);
576 0 0         if (-1 == n) {
577 0           snprintf(errbuf, errbufsz, "illegal ucs code in \\u or \\U");
578 0           xfree(dst);
579 0           return 0;
580             }
581 0           off += n;
582             }
583 0           continue;
584              
585 0           case 'b': ch = '\b'; break;
586 0           case 't': ch = '\t'; break;
587 0           case 'n': ch = '\n'; break;
588 0           case 'f': ch = '\f'; break;
589 0           case 'r': ch = '\r'; break;
590 0           case '"': ch = '"'; break;
591 0           case '\\': ch = '\\'; break;
592             default:
593 0           snprintf(errbuf, errbufsz, "illegal escape char \\%c", ch);
594 0           xfree(dst);
595 0           return 0;
596             }
597              
598 0           dst[off++] = ch;
599 134           }
600              
601             // Cap with NUL and return it.
602 17           dst[off++] = 0;
603 17           return dst;
604             }
605              
606              
607             /* Normalize a key. Convert all special chars to raw unescaped utf-8 chars. */
608 39           static char* normalize_key(context_t* ctx, token_t strtok)
609             {
610 39           const char* sp = strtok.ptr;
611 39           const char* sq = strtok.ptr + strtok.len;
612 39           int lineno = strtok.lineno;
613             char* ret;
614 39           int ch = *sp;
615             char ebuf[80];
616              
617             /* handle quoted string */
618 39 50         if (ch == '\'' || ch == '\"') {
    100          
619             /* if ''' or """, take 3 chars off front and back. Else, take 1 char off. */
620 1           int multiline = 0;
621 1 50         if (sp[1] == ch && sp[2] == ch) {
    0          
622 0           sp += 3, sq -= 3;
623 0           multiline = 1;
624             }
625             else
626 1           sp++, sq--;
627              
628 1 50         if (ch == '\'') {
629             /* for single quote, take it verbatim. */
630 0 0         if (! (ret = STRNDUP(sp, sq - sp))) {
631 0           e_outofmemory(ctx, FLINE);
632 0           return 0;
633             }
634             } else {
635             /* for double quote, we need to normalize */
636 1           ret = norm_basic_str(sp, sq - sp, multiline, ebuf, sizeof(ebuf));
637 1 50         if (!ret) {
638 0           e_syntax(ctx, lineno, ebuf);
639 0           return 0;
640             }
641             }
642              
643             /* newlines are not allowed in keys */
644 1 50         if (strchr(ret, '\n')) {
645 0           xfree(ret);
646 0           e_badkey(ctx, lineno);
647 0           return 0;
648             }
649 1           return ret;
650             }
651              
652             /* for bare-key allow only this regex: [A-Za-z0-9_-]+ */
653             const char* xp;
654 256 100         for (xp = sp; xp != sq; xp++) {
655 218           int k = *xp;
656 218 100         if (isalnum(k)) continue;
657 1 50         if (k == '_' || k == '-') continue;
    0          
658 0           e_badkey(ctx, lineno);
659 0           return 0;
660             }
661              
662             /* dup and return it */
663 38 50         if (! (ret = STRNDUP(sp, sq - sp))) {
664 0           e_outofmemory(ctx, FLINE);
665 0           return 0;
666             }
667 39           return ret;
668             }
669              
670              
671             /*
672             * Look up key in tab. Return 0 if not found, or
673             * 'v'alue, 'a'rray or 't'able depending on the element.
674             */
675 32           static int check_key(toml_table_t* tab, const char* key,
676             toml_keyval_t** ret_val,
677             toml_array_t** ret_arr,
678             toml_table_t** ret_tab)
679             {
680             int i;
681             void* dummy;
682              
683 32 100         if (!ret_tab) ret_tab = (toml_table_t**) &dummy;
684 32 100         if (!ret_arr) ret_arr = (toml_array_t**) &dummy;
685 32 100         if (!ret_val) ret_val = (toml_keyval_t**) &dummy;
686              
687 32           *ret_tab = 0; *ret_arr = 0; *ret_val = 0;
688              
689 56 100         for (i = 0; i < tab->nkval; i++) {
690 24 50         if (0 == strcmp(key, tab->kval[i]->key)) {
691 0           *ret_val = tab->kval[i];
692 0           return 'v';
693             }
694             }
695 39 100         for (i = 0; i < tab->narr; i++) {
696 7 50         if (0 == strcmp(key, tab->arr[i]->key)) {
697 0           *ret_arr = tab->arr[i];
698 0           return 'a';
699             }
700             }
701 43 100         for (i = 0; i < tab->ntab; i++) {
702 13 100         if (0 == strcmp(key, tab->tab[i]->key)) {
703 2           *ret_tab = tab->tab[i];
704 2           return 't';
705             }
706             }
707 32           return 0;
708             }
709              
710              
711 22           static int key_kind(toml_table_t* tab, const char* key)
712             {
713 22           return check_key(tab, key, 0, 0, 0);
714             }
715              
716             /* Create a keyval in the table.
717             */
718 17           static toml_keyval_t* create_keyval_in_table(context_t* ctx, toml_table_t* tab, token_t keytok)
719             {
720             /* first, normalize the key to be used for lookup.
721             * remember to free it if we error out.
722             */
723 17           char* newkey = normalize_key(ctx, keytok);
724 17 50         if (!newkey) return 0;
725              
726             /* if key exists: error out. */
727 17           toml_keyval_t* dest = 0;
728 17 50         if (key_kind(tab, newkey)) {
729 0           xfree(newkey);
730 0           e_keyexists(ctx, keytok.lineno);
731 0           return 0;
732             }
733              
734             /* make a new entry */
735 17           int n = tab->nkval;
736             toml_keyval_t** base;
737 17 50         if (0 == (base = (toml_keyval_t**) expand_ptrarr((void**)tab->kval, n))) {
738 0           xfree(newkey);
739 0           e_outofmemory(ctx, FLINE);
740 0           return 0;
741             }
742 17           tab->kval = base;
743              
744 17 50         if (0 == (base[n] = (toml_keyval_t*) CALLOC(1, sizeof(*base[n])))) {
745 0           xfree(newkey);
746 0           e_outofmemory(ctx, FLINE);
747 0           return 0;
748             }
749 17           dest = tab->kval[tab->nkval++];
750              
751             /* save the key in the new value struct */
752 17           dest->key = newkey;
753 17           return dest;
754             }
755              
756              
757             /* Create a table in the table.
758             */
759 8           static toml_table_t* create_keytable_in_table(context_t* ctx, toml_table_t* tab, token_t keytok)
760             {
761             /* first, normalize the key to be used for lookup.
762             * remember to free it if we error out.
763             */
764 8           char* newkey = normalize_key(ctx, keytok);
765 8 50         if (!newkey) return 0;
766              
767             /* if key exists: error out */
768 8           toml_table_t* dest = 0;
769 8 50         if (check_key(tab, newkey, 0, 0, &dest)) {
770 0           xfree(newkey); /* don't need this anymore */
771              
772             /* special case: if table exists, but was created implicitly ... */
773 0 0         if (dest && dest->implicit) {
    0          
774             /* we make it explicit now, and simply return it. */
775 0           dest->implicit = false;
776 0           return dest;
777             }
778 0           e_keyexists(ctx, keytok.lineno);
779 0           return 0;
780             }
781              
782             /* create a new table entry */
783 8           int n = tab->ntab;
784             toml_table_t** base;
785 8 50         if (0 == (base = (toml_table_t**) expand_ptrarr((void**)tab->tab, n))) {
786 0           xfree(newkey);
787 0           e_outofmemory(ctx, FLINE);
788 0           return 0;
789             }
790 8           tab->tab = base;
791              
792 8 50         if (0 == (base[n] = (toml_table_t*) CALLOC(1, sizeof(*base[n])))) {
793 0           xfree(newkey);
794 0           e_outofmemory(ctx, FLINE);
795 0           return 0;
796             }
797 8           dest = tab->tab[tab->ntab++];
798              
799             /* save the key in the new table struct */
800 8           dest->key = newkey;
801 8           return dest;
802             }
803              
804              
805             /* Create an array in the table.
806             */
807 5           static toml_array_t* create_keyarray_in_table(context_t* ctx,
808             toml_table_t* tab,
809             token_t keytok,
810             char kind)
811             {
812             /* first, normalize the key to be used for lookup.
813             * remember to free it if we error out.
814             */
815 5           char* newkey = normalize_key(ctx, keytok);
816 5 50         if (!newkey) return 0;
817              
818             /* if key exists: error out */
819 5 50         if (key_kind(tab, newkey)) {
820 0           xfree(newkey); /* don't need this anymore */
821 0           e_keyexists(ctx, keytok.lineno);
822 0           return 0;
823             }
824              
825             /* make a new array entry */
826 5           int n = tab->narr;
827             toml_array_t** base;
828 5 50         if (0 == (base = (toml_array_t**) expand_ptrarr((void**)tab->arr, n))) {
829 0           xfree(newkey);
830 0           e_outofmemory(ctx, FLINE);
831 0           return 0;
832             }
833 5           tab->arr = base;
834              
835 5 50         if (0 == (base[n] = (toml_array_t*) CALLOC(1, sizeof(*base[n])))) {
836 0           xfree(newkey);
837 0           e_outofmemory(ctx, FLINE);
838 0           return 0;
839             }
840 5           toml_array_t* dest = tab->arr[tab->narr++];
841              
842             /* save the key in the new array struct */
843 5           dest->key = newkey;
844 5           dest->kind = kind;
845 5           return dest;
846             }
847              
848              
849 13           static toml_arritem_t* create_value_in_array(context_t* ctx,
850             toml_array_t* parent)
851             {
852 13           const int n = parent->nitem;
853 13           toml_arritem_t* base = expand_arritem(parent->item, n);
854 13 50         if (!base) {
855 0           e_outofmemory(ctx, FLINE);
856 0           return 0;
857             }
858 13           parent->item = base;
859 13           parent->nitem++;
860 13           return &parent->item[n];
861             }
862              
863             /* Create an array in an array
864             */
865 2           static toml_array_t* create_array_in_array(context_t* ctx,
866             toml_array_t* parent)
867             {
868 2           const int n = parent->nitem;
869 2           toml_arritem_t* base = expand_arritem(parent->item, n);
870 2 50         if (!base) {
871 0           e_outofmemory(ctx, FLINE);
872 0           return 0;
873             }
874 2           toml_array_t* ret = (toml_array_t*) CALLOC(1, sizeof(toml_array_t));
875 2 50         if (!ret) {
876 0           e_outofmemory(ctx, FLINE);
877 0           return 0;
878             }
879 2           base[n].arr = ret;
880 2           parent->item = base;
881 2           parent->nitem++;
882 2           return ret;
883             }
884              
885             /* Create a table in an array
886             */
887 2           static toml_table_t* create_table_in_array(context_t* ctx,
888             toml_array_t* parent)
889             {
890 2           int n = parent->nitem;
891 2           toml_arritem_t* base = expand_arritem(parent->item, n);
892 2 50         if (!base) {
893 0           e_outofmemory(ctx, FLINE);
894 0           return 0;
895             }
896 2           toml_table_t* ret = (toml_table_t*) CALLOC(1, sizeof(toml_table_t));
897 2 50         if (!ret) {
898 0           e_outofmemory(ctx, FLINE);
899 0           return 0;
900             }
901 2           base[n].tab = ret;
902 2           parent->item = base;
903 2           parent->nitem++;
904 2           return ret;
905             }
906              
907              
908 34           static int skip_newlines(context_t* ctx, int isdotspecial)
909             {
910 34 50         while (ctx->tok.tok == NEWLINE) {
911 0 0         if (next_token(ctx, isdotspecial)) return -1;
912 0 0         if (ctx->tok.eof) break;
913             }
914 34           return 0;
915             }
916              
917              
918             static int parse_keyval(context_t* ctx, toml_table_t* tab);
919              
920 103           static inline int eat_token(context_t* ctx, tokentype_t typ, int isdotspecial, const char* fline)
921             {
922 103 50         if (ctx->tok.tok != typ)
923 0           return e_internal(ctx, fline);
924              
925 103 50         if (next_token(ctx, isdotspecial))
926 0           return -1;
927              
928 103           return 0;
929             }
930              
931              
932              
933             /* We are at '{ ... }'.
934             * Parse the table.
935             */
936 3           static int parse_inline_table(context_t* ctx, toml_table_t* tab)
937             {
938 3 50         if (eat_token(ctx, LBRACE, 1, FLINE))
939 0           return -1;
940              
941             for (;;) {
942 4 50         if (ctx->tok.tok == NEWLINE)
943 0           return e_syntax(ctx, ctx->tok.lineno, "newline not allowed in inline table");
944              
945             /* until } */
946 4 100         if (ctx->tok.tok == RBRACE)
947 1           break;
948              
949 3 50         if (ctx->tok.tok != STRING)
950 0           return e_syntax(ctx, ctx->tok.lineno, "expect a string");
951              
952 3 50         if (parse_keyval(ctx, tab))
953 0           return -1;
954              
955 3 50         if (ctx->tok.tok == NEWLINE)
956 0           return e_syntax(ctx, ctx->tok.lineno, "newline not allowed in inline table");
957              
958             /* on comma, continue to scan for next keyval */
959 3 100         if (ctx->tok.tok == COMMA) {
960 1 50         if (eat_token(ctx, COMMA, 1, FLINE))
961 0           return -1;
962 1           continue;
963             }
964 2           break;
965 1           }
966              
967 3 50         if (eat_token(ctx, RBRACE, 1, FLINE))
968 0           return -1;
969              
970 3           tab->readonly = 1;
971              
972 3           return 0;
973             }
974              
975 13           static int valtype(const char* val)
976             {
977             toml_timestamp_t ts;
978 13 50         if (*val == '\'' || *val == '"') return 's';
    100          
979 10 100         if (0 == toml_rtob(val, 0)) return 'b';
980 8 100         if (0 == toml_rtoi(val, 0)) return 'i';
981 4 100         if (0 == toml_rtod(val, 0)) return 'd';
982 2 100         if (0 == toml_rtots(val, &ts)) {
983 1 50         if (ts.year && ts.hour) return 'T'; /* timestamp */
    50          
984 0 0         if (ts.year) return 'D'; /* date */
985 0           return 't'; /* time */
986             }
987 13           return 'u'; /* unknown */
988             }
989              
990              
991             /* We are at '[...]' */
992 7           static int parse_array(context_t* ctx, toml_array_t* arr)
993             {
994 7 50         if (eat_token(ctx, LBRACKET, 0, FLINE)) return -1;
995              
996             for (;;) {
997 17 50         if (skip_newlines(ctx, 0)) return -1;
998              
999             /* until ] */
1000 17 50         if (ctx->tok.tok == RBRACKET) break;
1001              
1002 17           switch (ctx->tok.tok) {
1003             case STRING:
1004             {
1005             /* set array kind if this will be the first entry */
1006 13 100         if (arr->kind == 0)
1007 5           arr->kind = 'v';
1008 8 50         else if (arr->kind != 'v')
1009 0           arr->kind = 'm';
1010              
1011 13           char* val = ctx->tok.ptr;
1012 13           int vlen = ctx->tok.len;
1013              
1014             /* make a new value in array */
1015 13           toml_arritem_t* newval = create_value_in_array(ctx, arr);
1016 13 50         if (!newval)
1017 0           return e_outofmemory(ctx, FLINE);
1018              
1019 13 50         if (! (newval->val = STRNDUP(val, vlen)))
1020 0           return e_outofmemory(ctx, FLINE);
1021              
1022 13           newval->valtype = valtype(newval->val);
1023              
1024             /* set array type if this is the first entry */
1025 13 100         if (arr->nitem == 1)
1026 5           arr->type = newval->valtype;
1027 8 100         else if (arr->type != newval->valtype)
1028 5           arr->type = 'm'; /* mixed */
1029              
1030 13 50         if (eat_token(ctx, STRING, 0, FLINE)) return -1;
1031 13           break;
1032             }
1033              
1034             case LBRACKET:
1035             { /* [ [array], [array] ... ] */
1036             /* set the array kind if this will be the first entry */
1037 2 100         if (arr->kind == 0)
1038 1           arr->kind = 'a';
1039 1 50         else if (arr->kind != 'a')
1040 0           arr->kind = 'm';
1041              
1042 2           toml_array_t* subarr = create_array_in_array(ctx, arr);
1043 2 50         if (!subarr) return -1;
1044 2 50         if (parse_array(ctx, subarr)) return -1;
1045 2           break;
1046             }
1047              
1048             case LBRACE:
1049             { /* [ {table}, {table} ... ] */
1050             /* set the array kind if this will be the first entry */
1051 2 100         if (arr->kind == 0)
1052 1           arr->kind = 't';
1053 1 50         else if (arr->kind != 't')
1054 1           arr->kind = 'm';
1055              
1056 2           toml_table_t* subtab = create_table_in_array(ctx, arr);
1057 2 50         if (!subtab) return -1;
1058 2 50         if (parse_inline_table(ctx, subtab)) return -1;
1059 2           break;
1060             }
1061              
1062             default:
1063 0           return e_syntax(ctx, ctx->tok.lineno, "syntax error");
1064             }
1065              
1066 17 50         if (skip_newlines(ctx, 0)) return -1;
1067              
1068             /* on comma, continue to scan for next element */
1069 17 100         if (ctx->tok.tok == COMMA) {
1070 10 50         if (eat_token(ctx, COMMA, 0, FLINE)) return -1;
1071 10           continue;
1072             }
1073 7           break;
1074 10           }
1075              
1076 7 50         if (eat_token(ctx, RBRACKET, 1, FLINE)) return -1;
1077 7           return 0;
1078             }
1079              
1080              
1081             /* handle lines like these:
1082             key = "value"
1083             key = [ array ]
1084             key = { table }
1085             */
1086 25           static int parse_keyval(context_t* ctx, toml_table_t* tab)
1087             {
1088 25 50         if (tab->readonly) {
1089 0           return e_forbid(ctx, ctx->tok.lineno, "cannot insert new entry into existing table");
1090             }
1091              
1092 25           token_t key = ctx->tok;
1093 25 50         if (eat_token(ctx, STRING, 1, FLINE)) return -1;
1094              
1095 25 50         if (ctx->tok.tok == DOT) {
1096             /* handle inline dotted key.
1097             e.g.
1098             physical.color = "orange"
1099             physical.shape = "round"
1100             */
1101 0           toml_table_t* subtab = 0;
1102             {
1103 0           char* subtabstr = normalize_key(ctx, key);
1104 0 0         if (!subtabstr) return -1;
1105              
1106 0           subtab = toml_table_in(tab, subtabstr);
1107 0           xfree(subtabstr);
1108             }
1109 0 0         if (!subtab) {
1110 0           subtab = create_keytable_in_table(ctx, tab, key);
1111 0 0         if (!subtab) return -1;
1112             }
1113 0 0         if (next_token(ctx, 1)) return -1;
1114 0 0         if (parse_keyval(ctx, subtab)) return -1;
1115 0           return 0;
1116             }
1117              
1118 25 100         if (ctx->tok.tok != EQUAL) {
1119 1           return e_syntax(ctx, ctx->tok.lineno, "missing =");
1120             }
1121              
1122 24 100         if (next_token(ctx, 0)) return -1;
1123              
1124 23           switch (ctx->tok.tok) {
1125             case STRING:
1126             { /* key = "value" */
1127 17           toml_keyval_t* keyval = create_keyval_in_table(ctx, tab, key);
1128 17 50         if (!keyval) return -1;
1129 17           token_t val = ctx->tok;
1130              
1131 17 50         assert(keyval->val == 0);
1132 17 50         if (! (keyval->val = STRNDUP(val.ptr, val.len)))
1133 0           return e_outofmemory(ctx, FLINE);
1134              
1135 17 50         if (next_token(ctx, 1)) return -1;
1136              
1137 17           return 0;
1138             }
1139              
1140             case LBRACKET:
1141             { /* key = [ array ] */
1142 5           toml_array_t* arr = create_keyarray_in_table(ctx, tab, key, 0);
1143 5 50         if (!arr) return -1;
1144 5 50         if (parse_array(ctx, arr)) return -1;
1145 5           return 0;
1146             }
1147              
1148             case LBRACE:
1149             { /* key = { table } */
1150 1           toml_table_t* nxttab = create_keytable_in_table(ctx, tab, key);
1151 1 50         if (!nxttab) return -1;
1152 1 50         if (parse_inline_table(ctx, nxttab)) return -1;
1153 1           return 0;
1154             }
1155              
1156             default:
1157 25           return e_syntax(ctx, ctx->tok.lineno, "syntax error");
1158             }
1159             return 0;
1160             }
1161              
1162              
1163             typedef struct tabpath_t tabpath_t;
1164             struct tabpath_t {
1165             int cnt;
1166             token_t key[10];
1167             };
1168              
1169             /* at [x.y.z] or [[x.y.z]]
1170             * Scan forward and fill tabpath until it enters ] or ]]
1171             * There will be at least one entry on return.
1172             */
1173 7           static int fill_tabpath(context_t* ctx)
1174             {
1175 7           int lineno = ctx->tok.lineno;
1176             int i;
1177              
1178             /* clear tpath */
1179 9 100         for (i = 0; i < ctx->tpath.top; i++) {
1180 2           char** p = &ctx->tpath.key[i];
1181 2           xfree(*p);
1182 2           *p = 0;
1183             }
1184 7           ctx->tpath.top = 0;
1185              
1186             for (;;) {
1187 9 50         if (ctx->tpath.top >= 10)
1188 0           return e_syntax(ctx, lineno, "table path is too deep; max allowed is 10.");
1189              
1190 9 50         if (ctx->tok.tok != STRING)
1191 0           return e_syntax(ctx, lineno, "invalid or missing key");
1192              
1193 9           char* key = normalize_key(ctx, ctx->tok);
1194 9 50         if (!key) return -1;
1195 9           ctx->tpath.tok[ctx->tpath.top] = ctx->tok;
1196 9           ctx->tpath.key[ctx->tpath.top] = key;
1197 9           ctx->tpath.top++;
1198              
1199 9 50         if (next_token(ctx, 1)) return -1;
1200              
1201 9 100         if (ctx->tok.tok == RBRACKET) break;
1202              
1203 2 50         if (ctx->tok.tok != DOT)
1204 0           return e_syntax(ctx, lineno, "invalid key");
1205              
1206 2 50         if (next_token(ctx, 1)) return -1;
1207 2           }
1208              
1209 7 50         if (ctx->tpath.top <= 0)
1210 0           return e_syntax(ctx, lineno, "empty table selector");
1211              
1212 7           return 0;
1213             }
1214              
1215              
1216             /* Walk tabpath from the root, and create new tables on the way.
1217             * Sets ctx->curtab to the final table.
1218             */
1219 7           static int walk_tabpath(context_t* ctx)
1220             {
1221             /* start from root */
1222 7           toml_table_t* curtab = ctx->root;
1223              
1224 9 100         for (int i = 0; i < ctx->tpath.top; i++) {
1225 2           const char* key = ctx->tpath.key[i];
1226              
1227 2           toml_keyval_t* nextval = 0;
1228 2           toml_array_t* nextarr = 0;
1229 2           toml_table_t* nexttab = 0;
1230 2           switch (check_key(curtab, key, &nextval, &nextarr, &nexttab)) {
1231             case 't':
1232             /* found a table. nexttab is where we will go next. */
1233 2           break;
1234              
1235             case 'a':
1236             /* found an array. nexttab is the last table in the array. */
1237 0 0         if (nextarr->kind != 't')
1238 0           return e_internal(ctx, FLINE);
1239              
1240 0 0         if (nextarr->nitem == 0)
1241 0           return e_internal(ctx, FLINE);
1242              
1243 0           nexttab = nextarr->item[nextarr->nitem-1].tab;
1244 0           break;
1245              
1246             case 'v':
1247 0           return e_keyexists(ctx, ctx->tpath.tok[i].lineno);
1248              
1249             default:
1250             { /* Not found. Let's create an implicit table. */
1251 0           int n = curtab->ntab;
1252 0           toml_table_t** base = (toml_table_t**) expand_ptrarr((void**)curtab->tab, n);
1253 0 0         if (0 == base)
1254 0           return e_outofmemory(ctx, FLINE);
1255              
1256 0           curtab->tab = base;
1257              
1258 0 0         if (0 == (base[n] = (toml_table_t*) CALLOC(1, sizeof(*base[n]))))
1259 0           return e_outofmemory(ctx, FLINE);
1260              
1261 0 0         if (0 == (base[n]->key = STRDUP(key)))
1262 0           return e_outofmemory(ctx, FLINE);
1263              
1264 0           nexttab = curtab->tab[curtab->ntab++];
1265              
1266             /* tabs created by walk_tabpath are considered implicit */
1267 0           nexttab->implicit = true;
1268             }
1269 0           break;
1270             }
1271              
1272             /* switch to next tab */
1273 2           curtab = nexttab;
1274             }
1275              
1276             /* save it */
1277 7           ctx->curtab = curtab;
1278              
1279 7           return 0;
1280             }
1281              
1282              
1283             /* handle lines like [x.y.z] or [[x.y.z]] */
1284 7           static int parse_select(context_t* ctx)
1285             {
1286 7 50         assert(ctx->tok.tok == LBRACKET);
1287              
1288             /* true if [[ */
1289 7 50         int llb = (ctx->tok.ptr + 1 < ctx->stop && ctx->tok.ptr[1] == '[');
    50          
1290             /* need to detect '[[' on our own because next_token() will skip whitespace,
1291             and '[ [' would be taken as '[[', which is wrong. */
1292              
1293             /* eat [ or [[ */
1294 7 50         if (eat_token(ctx, LBRACKET, 1, FLINE)) return -1;
1295 7 50         if (llb) {
1296 0 0         assert(ctx->tok.tok == LBRACKET);
1297 0 0         if (eat_token(ctx, LBRACKET, 1, FLINE)) return -1;
1298             }
1299              
1300 7 50         if (fill_tabpath(ctx)) return -1;
1301              
1302             /* For [x.y.z] or [[x.y.z]], remove z from tpath.
1303             */
1304 7           token_t z = ctx->tpath.tok[ctx->tpath.top-1];
1305 7           xfree(ctx->tpath.key[ctx->tpath.top-1]);
1306 7           ctx->tpath.top--;
1307              
1308             /* set up ctx->curtab */
1309 7 50         if (walk_tabpath(ctx)) return -1;
1310              
1311 7 50         if (! llb) {
1312             /* [x.y.z] -> create z = {} in x.y */
1313 7           toml_table_t* curtab = create_keytable_in_table(ctx, ctx->curtab, z);
1314 7 50         if (!curtab) return -1;
1315 7           ctx->curtab = curtab;
1316             } else {
1317             /* [[x.y.z]] -> create z = [] in x.y */
1318 0           toml_array_t* arr = 0;
1319             {
1320 0           char* zstr = normalize_key(ctx, z);
1321 0 0         if (!zstr) return -1;
1322 0           arr = toml_array_in(ctx->curtab, zstr);
1323 0           xfree(zstr);
1324             }
1325 0 0         if (!arr) {
1326 0           arr = create_keyarray_in_table(ctx, ctx->curtab, z, 't');
1327 0 0         if (!arr) return -1;
1328             }
1329 0 0         if (arr->kind != 't')
1330 0           return e_syntax(ctx, z.lineno, "array mismatch");
1331              
1332             /* add to z[] */
1333             toml_table_t* dest;
1334             {
1335 0           toml_table_t* t = create_table_in_array(ctx, arr);
1336 0 0         if (!t) return -1;
1337              
1338 0 0         if (0 == (t->key = STRDUP("__anon__")))
1339 0           return e_outofmemory(ctx, FLINE);
1340              
1341 0           dest = t;
1342             }
1343              
1344 0           ctx->curtab = dest;
1345             }
1346              
1347 7 50         if (ctx->tok.tok != RBRACKET) {
1348 0           return e_syntax(ctx, ctx->tok.lineno, "expects ]");
1349             }
1350 7 50         if (llb) {
1351 0 0         if (! (ctx->tok.ptr + 1 < ctx->stop && ctx->tok.ptr[1] == ']')) {
    0          
1352 0           return e_syntax(ctx, ctx->tok.lineno, "expects ]]");
1353             }
1354 0 0         if (eat_token(ctx, RBRACKET, 1, FLINE)) return -1;
1355             }
1356              
1357 7 50         if (eat_token(ctx, RBRACKET, 1, FLINE))
1358 0           return -1;
1359              
1360 7 50         if (ctx->tok.tok != NEWLINE)
1361 0           return e_syntax(ctx, ctx->tok.lineno, "extra chars after ] or ]]");
1362              
1363 7           return 0;
1364             }
1365              
1366              
1367              
1368              
1369 6           toml_table_t* toml_parse(char* conf,
1370             char* errbuf,
1371             int errbufsz)
1372             {
1373             context_t ctx;
1374              
1375             // clear errbuf
1376 6 50         if (errbufsz <= 0) errbufsz = 0;
1377 6 50         if (errbufsz > 0) errbuf[0] = 0;
1378              
1379             // init context
1380 6           memset(&ctx, 0, sizeof(ctx));
1381 6           ctx.start = conf;
1382 6           ctx.stop = ctx.start + strlen(conf);
1383 6           ctx.errbuf = errbuf;
1384 6           ctx.errbufsz = errbufsz;
1385              
1386             // start with an artificial newline of length 0
1387 6           ctx.tok.tok = NEWLINE;
1388 6           ctx.tok.lineno = 1;
1389 6           ctx.tok.ptr = conf;
1390 6           ctx.tok.len = 0;
1391              
1392             // make a root table
1393 6 50         if (0 == (ctx.root = CALLOC(1, sizeof(*ctx.root)))) {
1394 0           e_outofmemory(&ctx, FLINE);
1395             // Do not goto fail, root table not set up yet
1396 0           return 0;
1397             }
1398              
1399             // set root as default table
1400 6           ctx.curtab = ctx.root;
1401              
1402             /* Scan forward until EOF */
1403 58 100         for (token_t tok = ctx.tok; ! tok.eof ; tok = ctx.tok) {
1404 54           switch (tok.tok) {
1405              
1406             case NEWLINE:
1407 27 50         if (next_token(&ctx, 1)) goto fail;
1408 25           break;
1409              
1410             case STRING:
1411 22 100         if (parse_keyval(&ctx, ctx.curtab)) goto fail;
1412              
1413 20 50         if (ctx.tok.tok != NEWLINE) {
1414 0           e_syntax(&ctx, ctx.tok.lineno, "extra chars after value");
1415 0           goto fail;
1416             }
1417              
1418 20 50         if (eat_token(&ctx, NEWLINE, 1, FLINE)) goto fail;
1419 20           break;
1420              
1421             case LBRACKET: /* [ x.y.z ] or [[ x.y.z ]] */
1422 7 50         if (parse_select(&ctx)) goto fail;
1423 7           break;
1424              
1425             default:
1426 0           e_syntax(&ctx, tok.lineno, "syntax error");
1427 0           goto fail;
1428             }
1429             }
1430              
1431             /* success */
1432 4 50         for (int i = 0; i < ctx.tpath.top; i++) xfree(ctx.tpath.key[i]);
1433 4           return ctx.root;
1434              
1435             fail:
1436             // Something bad has happened. Free resources and return error.
1437 2 50         for (int i = 0; i < ctx.tpath.top; i++) xfree(ctx.tpath.key[i]);
1438 2           toml_free(ctx.root);
1439 6           return 0;
1440             }
1441              
1442              
1443 0           toml_table_t* toml_parse_file(FILE* fp,
1444             char* errbuf,
1445             int errbufsz)
1446             {
1447 0           int bufsz = 0;
1448 0           char* buf = 0;
1449 0           int off = 0;
1450              
1451             /* read from fp into buf */
1452 0 0         while (! feof(fp)) {
1453              
1454 0 0         if (off == bufsz) {
1455 0           int xsz = bufsz + 1000;
1456 0           char* x = expand(buf, bufsz, xsz);
1457 0 0         if (!x) {
1458 0           snprintf(errbuf, errbufsz, "out of memory");
1459 0           xfree(buf);
1460 0           return 0;
1461             }
1462 0           buf = x;
1463 0           bufsz = xsz;
1464             }
1465              
1466 0           errno = 0;
1467 0           int n = fread(buf + off, 1, bufsz - off, fp);
1468 0 0         if (ferror(fp)) {
1469 0 0         snprintf(errbuf, errbufsz, "%s",
1470 0           errno ? strerror(errno) : "Error reading file");
1471 0           xfree(buf);
1472 0           return 0;
1473             }
1474 0           off += n;
1475             }
1476              
1477             /* tag on a NUL to cap the string */
1478 0 0         if (off == bufsz) {
1479 0           int xsz = bufsz + 1;
1480 0           char* x = expand(buf, bufsz, xsz);
1481 0 0         if (!x) {
1482 0           snprintf(errbuf, errbufsz, "out of memory");
1483 0           xfree(buf);
1484 0           return 0;
1485             }
1486 0           buf = x;
1487 0           bufsz = xsz;
1488             }
1489 0           buf[off] = 0;
1490              
1491             /* parse it, cleanup and finish */
1492 0           toml_table_t* ret = toml_parse(buf, errbuf, errbufsz);
1493 0           xfree(buf);
1494 0           return ret;
1495             }
1496              
1497              
1498 17           static void xfree_kval(toml_keyval_t* p)
1499             {
1500 17 50         if (!p) return;
1501 17           xfree(p->key);
1502 17           xfree(p->val);
1503 17           xfree(p);
1504             }
1505              
1506             static void xfree_tab(toml_table_t* p);
1507              
1508 7           static void xfree_arr(toml_array_t* p)
1509             {
1510 7 50         if (!p) return;
1511              
1512 7           xfree(p->key);
1513 7           const int n = p->nitem;
1514 24 100         for (int i = 0; i < n; i++) {
1515 17           toml_arritem_t* a = &p->item[i];
1516 17 100         if (a->val)
1517 13           xfree(a->val);
1518 4 100         else if (a->arr)
1519 2           xfree_arr(a->arr);
1520 2 50         else if (a->tab)
1521 2           xfree_tab(a->tab);
1522             }
1523 7           xfree(p->item);
1524 7           xfree(p);
1525             }
1526              
1527              
1528 16           static void xfree_tab(toml_table_t* p)
1529             {
1530             int i;
1531              
1532 16 50         if (!p) return;
1533              
1534 16           xfree(p->key);
1535              
1536 33 100         for (i = 0; i < p->nkval; i++) xfree_kval(p->kval[i]);
1537 16           xfree(p->kval);
1538              
1539 21 100         for (i = 0; i < p->narr; i++) xfree_arr(p->arr[i]);
1540 16           xfree(p->arr);
1541              
1542 24 100         for (i = 0; i < p->ntab; i++) xfree_tab(p->tab[i]);
1543 16           xfree(p->tab);
1544              
1545 16           xfree(p);
1546             }
1547              
1548              
1549 6           void toml_free(toml_table_t* tab)
1550             {
1551 6           xfree_tab(tab);
1552 6           }
1553              
1554              
1555 179           static void set_token(context_t* ctx, tokentype_t tok, int lineno, char* ptr, int len)
1556             {
1557             token_t t;
1558 179           t.tok = tok;
1559 179           t.lineno = lineno;
1560 179           t.ptr = ptr;
1561 179           t.len = len;
1562 179           t.eof = 0;
1563 179           ctx->tok = t;
1564 179           }
1565              
1566 5           static void set_eof(context_t* ctx, int lineno)
1567             {
1568 5           set_token(ctx, NEWLINE, lineno, ctx->stop, 0);
1569 5           ctx->tok.eof = 1;
1570 5           }
1571              
1572              
1573             /* Scan p for n digits compositing entirely of [0-9] */
1574 182           static int scan_digits(const char* p, int n)
1575             {
1576 182           int ret = 0;
1577 444 100         for ( ; n > 0 && isdigit(*p); n--, p++) {
    100          
1578 262           ret = 10 * ret + (*p - '0');
1579             }
1580 182 100         return n ? -1 : ret;
1581             }
1582              
1583 67           static int scan_date(const char* p, int* YY, int* MM, int* DD)
1584             {
1585             int year, month, day;
1586 67           year = scan_digits(p, 4);
1587 67 100         month = (year >= 0 && p[4] == '-') ? scan_digits(p+5, 2) : -1;
    100          
1588 67 100         day = (month >= 0 && p[7] == '-') ? scan_digits(p+8, 2) : -1;
    50          
1589 67 100         if (YY) *YY = year;
1590 67 100         if (MM) *MM = month;
1591 67 100         if (DD) *DD = day;
1592 67 100         return (year >= 0 && month >= 0 && day >= 0) ? 0 : -1;
    100          
    50          
1593             }
1594              
1595 63           static int scan_time(const char* p, int* hh, int* mm, int* ss)
1596             {
1597             int hour, minute, second;
1598 63           hour = scan_digits(p, 2);
1599 63 100         minute = (hour >= 0 && p[2] == ':') ? scan_digits(p+3, 2) : -1;
    100          
1600 63 100         second = (minute >= 0 && p[5] == ':') ? scan_digits(p+6, 2) : -1;
    50          
1601 63 100         if (hh) *hh = hour;
1602 63 100         if (mm) *mm = minute;
1603 63 100         if (ss) *ss = second;
1604 63 100         return (hour >= 0 && minute >= 0 && second >= 0) ? 0 : -1;
    100          
    50          
1605             }
1606              
1607              
1608 65           static int scan_string(context_t* ctx, char* p, int lineno, int dotisspecial)
1609             {
1610 65           char* orig = p;
1611 65 50         if (0 == strncmp(p, "'''", 3)) {
1612 0           char* q = p + 3;
1613              
1614             while (1) {
1615 0           q = strstr(q, "'''");
1616 0 0         if (0 == q) {
1617 0           return e_syntax(ctx, lineno, "unterminated triple-s-quote");
1618             }
1619 0 0         while (q[3] == '\'') q++;
1620 0           break;
1621             }
1622              
1623 0           set_token(ctx, STRING, lineno, orig, q + 3 - orig);
1624 0           return 0;
1625             }
1626              
1627 65 50         if (0 == strncmp(p, "\"\"\"", 3)) {
1628 0           char* q = p + 3;
1629              
1630             while (1) {
1631 0           q = strstr(q, "\"\"\"");
1632 0 0         if (0 == q) {
1633 0           return e_syntax(ctx, lineno, "unterminated triple-d-quote");
1634             }
1635 0 0         if (q[-1] == '\\') {
1636 0           q++;
1637 0           continue;
1638             }
1639 0 0         while (q[3] == '\"') q++;
1640 0           break;
1641 0           }
1642              
1643             // the string is [p+3, q-1]
1644              
1645 0           int hexreq = 0; /* #hex required */
1646 0           int escape = 0;
1647 0 0         for (p += 3; p < q; p++) {
1648 0 0         if (escape) {
1649 0           escape = 0;
1650 0 0         if (strchr("btnfr\"\\", *p)) continue;
1651 0 0         if (*p == 'u') { hexreq = 4; continue; }
1652 0 0         if (*p == 'U') { hexreq = 8; continue; }
1653 0 0         if (p[strspn(p, " \t\r")] == '\n') continue; /* allow for line ending backslash */
1654 0           return e_syntax(ctx, lineno, "bad escape char");
1655             }
1656 0 0         if (hexreq) {
1657 0           hexreq--;
1658 0 0         if (strchr("0123456789ABCDEF", *p)) continue;
1659 0           return e_syntax(ctx, lineno, "expect hex char");
1660             }
1661 0 0         if (*p == '\\') { escape = 1; continue; }
1662             }
1663 0 0         if (escape)
1664 0           return e_syntax(ctx, lineno, "expect an escape char");
1665 0 0         if (hexreq)
1666 0           return e_syntax(ctx, lineno, "expected more hex char");
1667              
1668 0           set_token(ctx, STRING, lineno, orig, q + 3 - orig);
1669 0           return 0;
1670             }
1671              
1672 65 50         if ('\'' == *p) {
1673 0 0         for (p++; *p && *p != '\n' && *p != '\''; p++);
    0          
    0          
1674 0 0         if (*p != '\'') {
1675 0           return e_syntax(ctx, lineno, "unterminated s-quote");
1676             }
1677              
1678 0           set_token(ctx, STRING, lineno, orig, p + 1 - orig);
1679 0           return 0;
1680             }
1681              
1682 65 100         if ('\"' == *p) {
1683 12           int hexreq = 0; /* #hex required */
1684 12           int escape = 0;
1685 12           int sqcnt = 0; /* count single-quote */
1686 134 50         for (p++; *p; p++) {
1687 134 50         if (escape) {
1688 0           escape = 0;
1689 0 0         if (strchr("btnfr\"\\", *p)) continue;
1690 0 0         if (*p == 'u') { hexreq = 4; continue; }
1691 0 0         if (*p == 'U') { hexreq = 8; continue; }
1692 0           return e_syntax(ctx, lineno, "bad escape char");
1693             }
1694 134 50         if (hexreq) {
1695 0           hexreq--;
1696 0 0         if (strchr("0123456789ABCDEF", *p)) continue;
1697 0           return e_syntax(ctx, lineno, "expect hex char");
1698             }
1699 134 100         if (sqcnt) {
1700 2 50         if (*p == '\'') {
1701 2 100         if (++sqcnt < 3) continue;
1702 1           return e_syntax(ctx, lineno, "triple-s-quote inside string lit");
1703             }
1704 0           sqcnt = 0;
1705             }
1706              
1707 132 50         if (*p == '\\') { escape = 1; continue; }
1708 132 100         if (*p == '\'') { sqcnt = 1; continue; }
1709 131 50         if (*p == '\n') break;
1710 131 100         if (*p == '"') break;
1711             }
1712 11 50         if (*p != '"') {
1713 0           return e_syntax(ctx, lineno, "unterminated quote");
1714             }
1715              
1716 11           set_token(ctx, STRING, lineno, orig, p + 1 - orig);
1717 11           return 0;
1718             }
1719              
1720             /* check for timestamp without quotes */
1721 53 100         if (0 == scan_date(p, 0, 0, 0) || 0 == scan_time(p, 0, 0, 0)) {
    50          
1722             // forward thru the timestamp
1723 108 100         for ( ; strchr("0123456789.:+-T Z", toupper(*p)); p++);
1724             // squeeze out any spaces at end of string
1725 4 50         for ( ; p[-1] == ' '; p--);
1726             // tokenize
1727 4           set_token(ctx, STRING, lineno, orig, p - orig);
1728 4           return 0;
1729             }
1730              
1731             /* literals */
1732 304 100         for ( ; *p && *p != '\n'; p++) {
    100          
1733 298           int ch = *p;
1734 298 100         if (ch == '.' && dotisspecial) break;
    100          
1735 296 100         if ('A' <= ch && ch <= 'Z') continue;
    50          
1736 296 100         if ('a' <= ch && ch <= 'z') continue;
    50          
1737 90 100         if (strchr("0123456789+-_.", ch)) continue;
1738 41           break;
1739             }
1740              
1741 49           set_token(ctx, STRING, lineno, orig, p - orig);
1742 49           return 0;
1743             }
1744              
1745              
1746 180           static int next_token(context_t* ctx, int dotisspecial)
1747             {
1748 180           int lineno = ctx->tok.lineno;
1749 180           char* p = ctx->tok.ptr;
1750             int i;
1751              
1752             /* eat this tok */
1753 761 100         for (i = 0; i < ctx->tok.len; i++) {
1754 581 100         if (*p++ == '\n')
1755 39           lineno++;
1756             }
1757              
1758             /* make next tok */
1759 254 100         while (p < ctx->stop) {
1760             /* skip comment. stop just before the \n. */
1761 249 100         if (*p == '#') {
1762 75 50         for (p++; p < ctx->stop && *p != '\n'; p++);
    100          
1763 3           continue;
1764             }
1765              
1766 246 100         if (dotisspecial && *p == '.') {
    100          
1767 2           set_token(ctx, DOT, lineno, p, 1);
1768 2           return 0;
1769             }
1770              
1771 244           switch (*p) {
1772 11           case ',': set_token(ctx, COMMA, lineno, p, 1); return 0;
1773 24           case '=': set_token(ctx, EQUAL, lineno, p, 1); return 0;
1774 3           case '{': set_token(ctx, LBRACE, lineno, p, 1); return 0;
1775 3           case '}': set_token(ctx, RBRACE, lineno, p, 1); return 0;
1776 14           case '[': set_token(ctx, LBRACKET, lineno, p, 1); return 0;
1777 14           case ']': set_token(ctx, RBRACKET, lineno, p, 1); return 0;
1778 39           case '\n': set_token(ctx, NEWLINE, lineno, p, 1); return 0;
1779             case '\r': case ' ': case '\t':
1780             /* ignore white spaces */
1781 71           p++;
1782 71           continue;
1783             }
1784              
1785 65           return scan_string(ctx, p, lineno, dotisspecial);
1786             }
1787              
1788 5           set_eof(ctx, lineno);
1789 5           return 0;
1790             }
1791              
1792              
1793 52           const char* toml_key_in(const toml_table_t* tab, int keyidx)
1794             {
1795 52 100         if (keyidx < tab->nkval) return tab->kval[keyidx]->key;
1796              
1797 30           keyidx -= tab->nkval;
1798 30 100         if (keyidx < tab->narr) return tab->arr[keyidx]->key;
1799              
1800 24           keyidx -= tab->narr;
1801 24 100         if (keyidx < tab->ntab) return tab->tab[keyidx]->key;
1802              
1803 16           return 0;
1804             }
1805              
1806 76           toml_raw_t toml_raw_in(const toml_table_t* tab, const char* key)
1807             {
1808             int i;
1809 207 50         for (i = 0; i < tab->nkval; i++) {
1810 207 100         if (0 == strcmp(key, tab->kval[i]->key))
1811 76           return tab->kval[i]->val;
1812             }
1813 0           return 0;
1814             }
1815              
1816 56           toml_array_t* toml_array_in(const toml_table_t* tab, const char* key)
1817             {
1818             int i;
1819 76 100         for (i = 0; i < tab->narr; i++) {
1820 41 100         if (0 == strcmp(key, tab->arr[i]->key))
1821 21           return tab->arr[i];
1822             }
1823 35           return 0;
1824             }
1825              
1826              
1827 71           toml_table_t* toml_table_in(const toml_table_t* tab, const char* key)
1828             {
1829             int i;
1830 146 100         for (i = 0; i < tab->ntab; i++) {
1831 104 100         if (0 == strcmp(key, tab->tab[i]->key))
1832 29           return tab->tab[i];
1833             }
1834 42           return 0;
1835             }
1836              
1837 105           toml_raw_t toml_raw_at(const toml_array_t* arr, int idx)
1838             {
1839 105 50         return (0 <= idx && idx < arr->nitem) ? arr->item[idx].val : 0;
    50          
1840             }
1841              
1842 0           char toml_array_kind(const toml_array_t* arr)
1843             {
1844 0           return arr->kind;
1845             }
1846              
1847 0           char toml_array_type(const toml_array_t* arr)
1848             {
1849 0 0         if (arr->kind != 'v')
1850 0           return 0;
1851              
1852 0 0         if (arr->nitem == 0)
1853 0           return 0;
1854              
1855 0           return arr->type;
1856             }
1857              
1858              
1859 9           int toml_array_nelem(const toml_array_t* arr)
1860             {
1861 9           return arr->nitem;
1862             }
1863              
1864 0           const char* toml_array_key(const toml_array_t* arr)
1865             {
1866 0 0         return arr ? arr->key : (const char*) NULL;
1867             }
1868              
1869 0           int toml_table_nkval(const toml_table_t* tab)
1870             {
1871 0           return tab->nkval;
1872             }
1873              
1874 0           int toml_table_narr(const toml_table_t* tab)
1875             {
1876 0           return tab->narr;
1877             }
1878              
1879 0           int toml_table_ntab(const toml_table_t* tab)
1880             {
1881 0           return tab->ntab;
1882             }
1883              
1884 0           const char* toml_table_key(const toml_table_t* tab)
1885             {
1886 0 0         return tab ? tab->key : (const char*) NULL;
1887             }
1888              
1889 43           toml_array_t* toml_array_at(const toml_array_t* arr, int idx)
1890             {
1891 43 50         return (0 <= idx && idx < arr->nitem) ? arr->item[idx].arr : 0;
    50          
1892             }
1893              
1894 43           toml_table_t* toml_table_at(const toml_array_t* arr, int idx)
1895             {
1896 43 50         return (0 <= idx && idx < arr->nitem) ? arr->item[idx].tab : 0;
    50          
1897             }
1898              
1899              
1900 14           int toml_rtots(toml_raw_t src_, toml_timestamp_t* ret)
1901             {
1902 14 50         if (! src_) return -1;
1903              
1904 14           const char* p = src_;
1905 14           int must_parse_time = 0;
1906              
1907 14           memset(ret, 0, sizeof(*ret));
1908              
1909 14           int* year = &ret->__buffer.year;
1910 14           int* month = &ret->__buffer.month;
1911 14           int* day = &ret->__buffer.day;
1912 14           int* hour = &ret->__buffer.hour;
1913 14           int* minute = &ret->__buffer.minute;
1914 14           int* second = &ret->__buffer.second;
1915 14           int* millisec = &ret->__buffer.millisec;
1916              
1917             /* parse date YYYY-MM-DD */
1918 14 100         if (0 == scan_date(p, year, month, day)) {
1919 11           ret->year = year;
1920 11           ret->month = month;
1921 11           ret->day = day;
1922              
1923 11           p += 10;
1924 11 50         if (*p) {
1925             // parse the T or space separator
1926 11 50         if (*p != 'T' && *p != ' ') return -1;
    0          
1927 11           must_parse_time = 1;
1928 11           p++;
1929             }
1930             }
1931              
1932             /* parse time HH:MM:SS */
1933 14 100         if (0 == scan_time(p, hour, minute, second)) {
1934 11           ret->hour = hour;
1935 11           ret->minute = minute;
1936 11           ret->second = second;
1937              
1938             /* optionally, parse millisec */
1939 11           p += 8;
1940 11 100         if (*p == '.') {
1941             char* qq;
1942 1           p++;
1943 1           errno = 0;
1944 1           *millisec = strtol(p, &qq, 0);
1945 1 50         if (errno) {
1946 0           return -1;
1947             }
1948 1 50         while (*millisec > 999) {
1949 0           *millisec /= 10;
1950             }
1951              
1952 1           ret->millisec = millisec;
1953 1           p = qq;
1954             }
1955              
1956 11 50         if (*p) {
1957             /* parse and copy Z */
1958 11           char* z = ret->__buffer.z;
1959 11           ret->z = z;
1960 11 50         if (*p == 'Z' || *p == 'z') {
    50          
1961 0           *z++ = 'Z'; p++;
1962 0           *z = 0;
1963              
1964 11 50         } else if (*p == '+' || *p == '-') {
    50          
1965 11           *z++ = *p++;
1966              
1967 11 50         if (! (isdigit(p[0]) && isdigit(p[1]))) return -1;
    50          
1968 11           *z++ = *p++;
1969 11           *z++ = *p++;
1970              
1971 11 50         if (*p == ':') {
1972 11           *z++ = *p++;
1973              
1974 11 50         if (! (isdigit(p[0]) && isdigit(p[1]))) return -1;
    50          
1975 11           *z++ = *p++;
1976 11           *z++ = *p++;
1977             }
1978              
1979 11           *z = 0;
1980             }
1981             }
1982             }
1983 14 100         if (*p != 0)
1984 3           return -1;
1985              
1986 11 50         if (must_parse_time && !ret->hour)
    50          
1987 0           return -1;
1988              
1989 11           return 0;
1990             }
1991              
1992              
1993             /* Raw to boolean */
1994 58           int toml_rtob(toml_raw_t src, int* ret_)
1995             {
1996 58 50         if (!src) return -1;
1997             int dummy;
1998 58 100         int* ret = ret_ ? ret_ : &dummy;
1999              
2000 58 100         if (0 == strcmp(src, "true")) {
2001 7           *ret = 1;
2002 7           return 0;
2003             }
2004 51 100         if (0 == strcmp(src, "false")) {
2005 9           *ret = 0;
2006 9           return 0;
2007             }
2008 58           return -1;
2009             }
2010              
2011              
2012             /* Raw to integer */
2013 42           int toml_rtoi(toml_raw_t src, int64_t* ret_)
2014             {
2015 42 50         if (!src) return -1;
2016              
2017             char buf[100];
2018 42           char* p = buf;
2019 42           char* q = p + sizeof(buf);
2020 42           const char* s = src;
2021 42           int base = 0;
2022             int64_t dummy;
2023 42 100         int64_t* ret = ret_ ? ret_ : &dummy;
2024              
2025              
2026             /* allow +/- */
2027 42 50         if (s[0] == '+' || s[0] == '-')
    50          
2028 0           *p++ = *s++;
2029              
2030             /* disallow +_100 */
2031 42 50         if (s[0] == '_')
2032 0           return -1;
2033              
2034             /* if 0 ... */
2035 42 100         if ('0' == s[0]) {
2036 3           switch (s[1]) {
2037 0           case 'x': base = 16; s += 2; break;
2038 0           case 'o': base = 8; s += 2; break;
2039 0           case 'b': base = 2; s += 2; break;
2040 0           case '\0': return *ret = 0, 0;
2041             default:
2042             /* ensure no other digits after it */
2043 3 50         if (s[1]) return -1;
2044             }
2045             }
2046              
2047             /* just strip underscores and pass to strtoll */
2048 421 100         while (*s && p < q) {
    50          
2049 382           int ch = *s++;
2050 382 50         switch (ch) {
2051             case '_':
2052             // disallow '__'
2053 0 0         if (s[0] == '_') return -1;
2054 0           continue; /* skip _ */
2055             default:
2056 382           break;
2057             }
2058 382           *p++ = ch;
2059             }
2060 39 50         if (*s || p == q) return -1;
    50          
2061              
2062             /* last char cannot be '_' */
2063 39 50         if (s[-1] == '_') return -1;
2064              
2065             /* cap with NUL */
2066 39           *p = 0;
2067              
2068             /* Run strtoll on buf to get the integer */
2069             char* endp;
2070 39           errno = 0;
2071 39           *ret = strtoll(buf, &endp, base);
2072 42 50         return (errno || *endp) ? -1 : 0;
    100          
2073             }
2074              
2075              
2076 27           int toml_rtod_ex(toml_raw_t src, double* ret_, char* buf, int buflen)
2077             {
2078 27 50         if (!src) return -1;
2079              
2080 27           char* p = buf;
2081 27           char* q = p + buflen;
2082 27           const char* s = src;
2083             double dummy;
2084 27 100         double* ret = ret_ ? ret_ : &dummy;
2085              
2086              
2087             /* allow +/- */
2088 27 50         if (s[0] == '+' || s[0] == '-')
    50          
2089 0           *p++ = *s++;
2090              
2091             /* disallow +_1.00 */
2092 27 50         if (s[0] == '_')
2093 0           return -1;
2094              
2095             /* decimal point, if used, must be surrounded by at least one digit on each side */
2096             {
2097 27           char* dot = strchr(s, '.');
2098 27 100         if (dot) {
2099 14 50         if (dot == s || !isdigit(dot[-1]) || !isdigit(dot[1]))
    50          
    50          
2100 0           return -1;
2101             }
2102             }
2103              
2104             /* zero must be followed by . or 'e', or NUL */
2105 27 100         if (s[0] == '0' && s[1] && !strchr("eE.", s[1]))
    50          
    50          
2106 3           return -1;
2107              
2108             /* just strip underscores and pass to strtod */
2109 355 100         while (*s && p < q) {
    50          
2110 331           int ch = *s++;
2111 331 50         if (ch == '_') {
2112             // disallow '__'
2113 0 0         if (s[0] == '_') return -1;
2114             // disallow last char '_'
2115 0 0         if (s[0] == 0) return -1;
2116 0           continue; /* skip _ */
2117             }
2118 331           *p++ = ch;
2119             }
2120 24 50         if (*s || p == q) return -1; /* reached end of string or buffer is full? */
    50          
2121              
2122             /* cap with NUL */
2123 24           *p = 0;
2124              
2125             /* Run strtod on buf to get the value */
2126             char* endp;
2127 24           errno = 0;
2128 24           *ret = strtod(buf, &endp);
2129 27 50         return (errno || *endp) ? -1 : 0;
    100          
2130             }
2131              
2132 27           int toml_rtod(toml_raw_t src, double* ret_)
2133             {
2134             char buf[100];
2135 27           return toml_rtod_ex(src, ret_, buf, sizeof(buf));
2136             }
2137              
2138              
2139              
2140              
2141 64           int toml_rtos(toml_raw_t src, char** ret)
2142             {
2143 64           int multiline = 0;
2144             const char* sp;
2145             const char* sq;
2146              
2147 64           *ret = 0;
2148 64 50         if (!src) return -1;
2149              
2150 64           int qchar = src[0];
2151 64           int srclen = strlen(src);
2152 64 50         if (! (qchar == '\'' || qchar == '"')) {
    100          
2153 48           return -1;
2154             }
2155              
2156             // triple quotes?
2157 16 50         if (qchar == src[1] && qchar == src[2]) {
    0          
2158 0           multiline = 1;
2159 0           sp = src + 3;
2160 0           sq = src + srclen - 3;
2161             /* last 3 chars in src must be qchar */
2162 0 0         if (! (sp <= sq && sq[0] == qchar && sq[1] == qchar && sq[2] == qchar))
    0          
    0          
    0          
2163 0           return -1;
2164              
2165             /* skip new line immediate after qchar */
2166 0 0         if (sp[0] == '\n')
2167 0           sp++;
2168 0 0         else if (sp[0] == '\r' && sp[1] == '\n')
    0          
2169 0           sp += 2;
2170              
2171             } else {
2172 16           sp = src + 1;
2173 16           sq = src + srclen - 1;
2174             /* last char in src must be qchar */
2175 16 50         if (! (sp <= sq && *sq == qchar))
    50          
2176 0           return -1;
2177             }
2178              
2179 16 50         if (qchar == '\'') {
2180 0           *ret = norm_lit_str(sp, sq - sp,
2181             multiline,
2182             0, 0);
2183             } else {
2184 16           *ret = norm_basic_str(sp, sq - sp,
2185             multiline,
2186             0, 0);
2187             }
2188              
2189 16 50         return *ret ? 0 : -1;
2190             }
2191              
2192              
2193 37           toml_datum_t toml_string_at(const toml_array_t* arr, int idx)
2194             {
2195             toml_datum_t ret;
2196 37           memset(&ret, 0, sizeof(ret));
2197 37           ret.ok = (0 == toml_rtos(toml_raw_at(arr, idx), &ret.u.s));
2198 37           return ret;
2199             }
2200              
2201 30           toml_datum_t toml_bool_at(const toml_array_t* arr, int idx)
2202             {
2203             toml_datum_t ret;
2204 30           memset(&ret, 0, sizeof(ret));
2205 30           ret.ok = (0 == toml_rtob(toml_raw_at(arr, idx), &ret.u.b));
2206 30           return ret;
2207             }
2208              
2209 20           toml_datum_t toml_int_at(const toml_array_t* arr, int idx)
2210             {
2211             toml_datum_t ret;
2212 20           memset(&ret, 0, sizeof(ret));
2213 20           ret.ok = (0 == toml_rtoi(toml_raw_at(arr, idx), &ret.u.i));
2214 20           return ret;
2215             }
2216              
2217 12           toml_datum_t toml_double_at(const toml_array_t* arr, int idx)
2218             {
2219             toml_datum_t ret;
2220 12           memset(&ret, 0, sizeof(ret));
2221 12           ret.ok = (0 == toml_rtod(toml_raw_at(arr, idx), &ret.u.d));
2222 12           return ret;
2223             }
2224              
2225 6           toml_datum_t toml_timestamp_at(const toml_array_t* arr, int idx)
2226             {
2227             toml_timestamp_t ts;
2228             toml_datum_t ret;
2229 6           memset(&ret, 0, sizeof(ret));
2230 6           ret.ok = (0 == toml_rtots(toml_raw_at(arr, idx), &ts));
2231 6 100         if (ret.ok) {
2232 5           ret.ok = !!(ret.u.ts = malloc(sizeof(*ret.u.ts)));
2233 5 50         if (ret.ok) {
2234 5           *ret.u.ts = ts;
2235 5 50         if (ret.u.ts->year) ret.u.ts->year = &ret.u.ts->__buffer.year;
2236 5 50         if (ret.u.ts->month) ret.u.ts->month = &ret.u.ts->__buffer.month;
2237 5 50         if (ret.u.ts->day) ret.u.ts->day = &ret.u.ts->__buffer.day;
2238 5 50         if (ret.u.ts->hour) ret.u.ts->hour = &ret.u.ts->__buffer.hour;
2239 5 50         if (ret.u.ts->minute) ret.u.ts->minute = &ret.u.ts->__buffer.minute;
2240 5 50         if (ret.u.ts->second) ret.u.ts->second = &ret.u.ts->__buffer.second;
2241 5 50         if (ret.u.ts->millisec) ret.u.ts->millisec = &ret.u.ts->__buffer.millisec;
2242 5 50         if (ret.u.ts->z) ret.u.ts->z = ret.u.ts->__buffer.z;
2243             }
2244             }
2245 6           return ret;
2246             }
2247              
2248 27           toml_datum_t toml_string_in(const toml_table_t* arr, const char* key)
2249             {
2250             toml_datum_t ret;
2251 27           memset(&ret, 0, sizeof(ret));
2252 27           toml_raw_t raw = toml_raw_in(arr, key);
2253 27 50         if (raw) {
2254 27           ret.ok = (0 == toml_rtos(raw, &ret.u.s));
2255             }
2256 27           return ret;
2257             }
2258              
2259 18           toml_datum_t toml_bool_in(const toml_table_t* arr, const char* key)
2260             {
2261             toml_datum_t ret;
2262 18           memset(&ret, 0, sizeof(ret));
2263 18           ret.ok = (0 == toml_rtob(toml_raw_in(arr, key), &ret.u.b));
2264 18           return ret;
2265             }
2266              
2267 14           toml_datum_t toml_int_in(const toml_table_t* arr, const char* key)
2268             {
2269             toml_datum_t ret;
2270 14           memset(&ret, 0, sizeof(ret));
2271 14           ret.ok = (0 == toml_rtoi(toml_raw_in(arr, key), &ret.u.i));
2272 14           return ret;
2273             }
2274              
2275 11           toml_datum_t toml_double_in(const toml_table_t* arr, const char* key)
2276             {
2277             toml_datum_t ret;
2278 11           memset(&ret, 0, sizeof(ret));
2279 11           ret.ok = (0 == toml_rtod(toml_raw_in(arr, key), &ret.u.d));
2280 11           return ret;
2281             }
2282              
2283 6           toml_datum_t toml_timestamp_in(const toml_table_t* arr, const char* key)
2284             {
2285             toml_timestamp_t ts;
2286             toml_datum_t ret;
2287 6           memset(&ret, 0, sizeof(ret));
2288 6           ret.ok = (0 == toml_rtots(toml_raw_in(arr, key), &ts));
2289 6 100         if (ret.ok) {
2290 5           ret.ok = !!(ret.u.ts = malloc(sizeof(*ret.u.ts)));
2291 5 50         if (ret.ok) {
2292 5           *ret.u.ts = ts;
2293 5 50         if (ret.u.ts->year) ret.u.ts->year = &ret.u.ts->__buffer.year;
2294 5 50         if (ret.u.ts->month) ret.u.ts->month = &ret.u.ts->__buffer.month;
2295 5 50         if (ret.u.ts->day) ret.u.ts->day = &ret.u.ts->__buffer.day;
2296 5 50         if (ret.u.ts->hour) ret.u.ts->hour = &ret.u.ts->__buffer.hour;
2297 5 50         if (ret.u.ts->minute) ret.u.ts->minute = &ret.u.ts->__buffer.minute;
2298 5 50         if (ret.u.ts->second) ret.u.ts->second = &ret.u.ts->__buffer.second;
2299 5 100         if (ret.u.ts->millisec) ret.u.ts->millisec = &ret.u.ts->__buffer.millisec;
2300 5 50         if (ret.u.ts->z) ret.u.ts->z = ret.u.ts->__buffer.z;
2301             }
2302             }
2303 6           return ret;
2304             }