tomlib 0.2.0 → 0.3.0

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