GVM User Suite
User tools for the GVM open source project.
toml.c
Go to the documentation of this file.
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 
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 
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;
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;
289  int valtype; /* for value kind: 'i'nt, 'd'ouble, 'b'ool, 's'tring, 't'ime,
290  'D'ate, 'T'imestamp */
291  char *val;
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 */
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;
314 
315  /* arrays in the table */
316  int narr;
318 
319  /* tables in the table */
320  int ntab;
322 };
323 
324 static inline void xfree(const void *x) {
325  if (x)
326  FREE((void *)(intptr_t)x);
327 }
328 
340 };
341 typedef enum tokentype_t tokentype_t;
342 
343 typedef struct token_t token_t;
344 struct token_t {
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 
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 
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  */
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  */
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  */
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 
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  */
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  */
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;
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 
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 
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 
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 
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 
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 }
char * stop
Definition: toml.c:355
char * errbuf
Definition: toml.c:356
token_t tok
Definition: toml.c:359
char * key[10]
Definition: toml.c:365
struct context_t::@2 tpath
int top
Definition: toml.c:364
char * start
Definition: toml.c:354
toml_table_t * root
Definition: toml.c:360
toml_table_t * curtab
Definition: toml.c:361
int errbufsz
Definition: toml.c:357
int cnt
Definition: toml.c:1188
token_t key[10]
Definition: toml.c:1189
Definition: toml.c:344
int eof
Definition: toml.c:349
int len
Definition: toml.c:348
tokentype_t tok
Definition: toml.c:345
int lineno
Definition: toml.c:346
char * ptr
Definition: toml.c:347
int nitem
Definition: toml.c:302
toml_arritem_t * item
Definition: toml.c:303
int kind
Definition: toml.c:298
int type
Definition: toml.c:299
const char * key
Definition: toml.c:297
int valtype
Definition: toml.c:289
toml_array_t * arr
Definition: toml.c:292
char * val
Definition: toml.c:291
toml_table_t * tab
Definition: toml.c:293
union toml_datum_t::@1 u
int b
Definition: toml.h:87
double d
Definition: toml.h:89
char * s
Definition: toml.h:86
int ok
Definition: toml.h:83
int64_t i
Definition: toml.h:88
toml_timestamp_t * ts
Definition: toml.h:85
const char * val
Definition: toml.c:284
const char * key
Definition: toml.c:283
const char * key
Definition: toml.c:307
int narr
Definition: toml.c:316
bool readonly
Definition: toml.c:309
bool implicit
Definition: toml.c:308
int nkval
Definition: toml.c:312
int ntab
Definition: toml.c:320
toml_array_t ** arr
Definition: toml.c:317
toml_keyval_t ** kval
Definition: toml.c:313
toml_table_t ** tab
Definition: toml.c:321
struct toml_timestamp_t::@0 __buffer
int millisec
Definition: toml.h:71
char z[10]
Definition: toml.h:72
int minute
Definition: toml.h:71
int second
Definition: toml.h:71
static int e_forbid(context_t *ctx, int lineno, const char *msg)
Definition: toml.c:404
int toml_table_ntab(const toml_table_t *tab)
Definition: toml.c:1945
toml_datum_t toml_bool_at(const toml_array_t *arr, int idx)
Definition: toml.c:2259
static void xfree_arr(toml_array_t *p)
Definition: toml.c:1536
static void(* ppfree)(void *)
Definition: toml.c:39
static void * CALLOC(size_t nmemb, size_t sz)
Definition: toml.c:55
static int valtype(const char *val)
Definition: toml.c:980
tokentype_t
Definition: toml.c:329
@ LBRACE
Definition: toml.c:334
@ LBRACKET
Definition: toml.c:337
@ EQUAL
Definition: toml.c:333
@ NEWLINE
Definition: toml.c:336
@ DOT
Definition: toml.c:331
@ RBRACE
Definition: toml.c:335
@ RBRACKET
Definition: toml.c:338
@ STRING
Definition: toml.c:339
@ INVALID
Definition: toml.c:330
@ COMMA
Definition: toml.c:332
static int e_keyexists(context_t *ctx, int lineno)
Definition: toml.c:399
static int parse_array(context_t *ctx, toml_array_t *arr)
Definition: toml.c:1001
toml_table_t * toml_parse_file(FILE *fp, char *errbuf, int errbufsz)
Definition: toml.c:1475
static int skip_newlines(context_t *ctx, int isdotspecial)
Definition: toml.c:914
static int parse_select(context_t *ctx)
Definition: toml.c:1307
static toml_table_t * create_table_in_array(context_t *ctx, toml_array_t *parent)
Definition: toml.c:895
static void xfree(const void *x)
Definition: toml.c:324
static int check_key(toml_table_t *tab, const char *key, toml_keyval_t **ret_val, toml_array_t **ret_arr, toml_table_t **ret_tab)
Definition: toml.c:687
static toml_array_t * create_array_in_array(context_t *ctx, toml_array_t *parent)
Definition: toml.c:874
char toml_array_type(const toml_array_t *arr)
Definition: toml.c:1925
void toml_set_memutil(void *(*xxmalloc)(size_t), void(*xxfree)(void *))
Definition: toml.c:41
toml_datum_t toml_string_in(const toml_table_t *arr, const char *key)
Definition: toml.c:2310
#define malloc(x)
Definition: toml.c:51
toml_datum_t toml_int_at(const toml_array_t *arr, int idx)
Definition: toml.c:2266
#define MALLOC(a)
Definition: toml.c:48
toml_array_t * toml_array_in(const toml_table_t *tab, const char *key)
Definition: toml.c:1901
static int walk_tabpath(context_t *ctx)
Definition: toml.c:1245
static int key_kind(toml_table_t *tab, const char *key)
Definition: toml.c:725
toml_datum_t toml_double_at(const toml_array_t *arr, int idx)
Definition: toml.c:2273
int toml_table_nkval(const toml_table_t *tab)
Definition: toml.c:1941
const char * toml_array_key(const toml_array_t *arr)
Definition: toml.c:1937
char toml_array_kind(const toml_array_t *arr)
Definition: toml.c:1923
int toml_rtob(toml_raw_t src, int *ret_)
Definition: toml.c:2050
static int next_token(context_t *ctx, int dotisspecial)
Definition: toml.c:1798
static int scan_string(context_t *ctx, char *p, int lineno, int dotisspecial)
Definition: toml.c:1633
static int scan_digits(const char *p, int n)
Definition: toml.c:1597
int toml_rtos(toml_raw_t src, char **ret)
Definition: toml.c:2205
static char * STRDUP(const char *s)
Definition: toml.c:68
int toml_array_nelem(const toml_array_t *arr)
Definition: toml.c:1935
static int eat_token(context_t *ctx, tokentype_t typ, int isdotspecial, const char *fline)
Definition: toml.c:926
toml_raw_t toml_raw_at(const toml_array_t *arr, int idx)
Definition: toml.c:1919
#define FREE(a)
Definition: toml.c:49
static toml_array_t * create_keyarray_in_table(context_t *ctx, toml_table_t *tab, token_t keytok, char kind)
Definition: toml.c:820
static void *(* ppmalloc)(size_t)
Definition: toml.c:38
toml_datum_t toml_double_in(const toml_table_t *arr, const char *key)
Definition: toml.c:2334
toml_datum_t toml_int_in(const toml_table_t *arr, const char *key)
Definition: toml.c:2327
static int parse_keyval(context_t *ctx, toml_table_t *tab)
Definition: toml.c:1101
static void set_eof(context_t *ctx, int lineno)
Definition: toml.c:1591
static void xfree_kval(toml_keyval_t *p)
Definition: toml.c:1526
int toml_key_exists(const toml_table_t *tab, const char *key)
Definition: toml.c:1875
static char * STRNDUP(const char *s, size_t n)
Definition: toml.c:82
#define FLINE
Definition: toml.c:372
static int scan_date(const char *p, int *YY, int *MM, int *DD)
Definition: toml.c:1605
const char * toml_table_key(const toml_table_t *tab)
Definition: toml.c:1947
int toml_ucs_to_utf8(int64_t code, char buf[6])
Definition: toml.c:197
static int e_internal(context_t *ctx, const char *fline)
Definition: toml.c:384
toml_datum_t toml_bool_in(const toml_table_t *arr, const char *key)
Definition: toml.c:2320
int toml_rtoi(toml_raw_t src, int64_t *ret_)
Definition: toml.c:2068
static char * norm_lit_str(const char *src, int srclen, int multiline, char *errbuf, int errbufsz)
Definition: toml.c:439
static char * normalize_key(context_t *ctx, token_t strtok)
Definition: toml.c:621
int toml_table_narr(const toml_table_t *tab)
Definition: toml.c:1943
static int fill_tabpath(context_t *ctx)
Definition: toml.c:1196
static int parse_millisec(const char *p, const char **endp)
Definition: toml.c:2371
static void * expand(void *p, int sz, int newsz)
Definition: toml.c:409
toml_raw_t toml_raw_in(const toml_table_t *tab, const char *key)
Definition: toml.c:1892
toml_table_t * toml_parse(char *conf, char *errbuf, int errbufsz)
Definition: toml.c:1397
static int scan_time(const char *p, int *hh, int *mm, int *ss)
Definition: toml.c:1619
const char * toml_key_in(const toml_table_t *tab, int keyidx)
Definition: toml.c:1860
void toml_free(toml_table_t *tab)
Definition: toml.c:1578
static int e_badkey(context_t *ctx, int lineno)
Definition: toml.c:394
static int parse_inline_table(context_t *ctx, toml_table_t *tab)
Definition: toml.c:940
toml_table_t * toml_table_in(const toml_table_t *tab, const char *key)
Definition: toml.c:1910
static toml_arritem_t * expand_arritem(toml_arritem_t *p, int n)
Definition: toml.c:430
int toml_rtots(toml_raw_t src_, toml_timestamp_t *ret)
Definition: toml.c:1961
static void set_token(context_t *ctx, tokentype_t tok, int lineno, char *ptr, int len)
Definition: toml.c:1580
static toml_table_t * create_keytable_in_table(context_t *ctx, toml_table_t *tab, token_t keytok)
Definition: toml.c:772
toml_datum_t toml_timestamp_in(const toml_table_t *arr, const char *key)
Definition: toml.c:2341
int toml_rtod_ex(toml_raw_t src, double *ret_, char *buf, int buflen)
Definition: toml.c:2141
static int e_syntax(context_t *ctx, int lineno, const char *msg)
Definition: toml.c:389
static toml_arritem_t * create_value_in_array(context_t *ctx, toml_array_t *parent)
Definition: toml.c:859
int toml_rtod(toml_raw_t src, double *ret_)
Definition: toml.c:2200
int toml_utf8_to_ucs(const char *orig, int len, int64_t *ret)
Definition: toml.c:96
static char * norm_basic_str(const char *src, int srclen, int multiline, char *errbuf, int errbufsz)
Definition: toml.c:488
static toml_keyval_t * create_keyval_in_table(context_t *ctx, toml_table_t *tab, token_t keytok)
Definition: toml.c:731
toml_table_t * toml_table_at(const toml_array_t *arr, int idx)
Definition: toml.c:1955
static int e_outofmemory(context_t *ctx, const char *fline)
Definition: toml.c:379
toml_datum_t toml_string_at(const toml_array_t *arr, int idx)
Definition: toml.c:2252
toml_array_t * toml_array_at(const toml_array_t *arr, int idx)
Definition: toml.c:1951
static void ** expand_ptrarr(void **p, int n)
Definition: toml.c:419
static void xfree_tab(toml_table_t *p)
Definition: toml.c:1555
toml_datum_t toml_timestamp_at(const toml_array_t *arr, int idx)
Definition: toml.c:2280
#define free(x)
Definition: toml.c:52
const char * toml_raw_t
Definition: toml.h:165