1 // TOML config file parsing library
2 // https://github.com/arp242/toml-c
6 #ifndef _POSIX_C_SOURCE
7 #define _POSIX_C_SOURCE 200809L
10 #pragma warning(disable : 4996)
13 #define TOML_EXTERN extern "C"
15 #define TOML_EXTERN extern
22 typedef struct toml_table_t toml_table_t
;
23 typedef struct toml_array_t toml_array_t
;
24 typedef struct toml_value_t toml_value_t
;
25 typedef struct toml_timestamp_t toml_timestamp_t
;
26 typedef struct toml_keyval_t toml_keyval_t
;
27 typedef struct toml_arritem_t toml_arritem_t
;
31 const char *key
; // Key for this table
32 int keylen
; // length of key.
33 bool implicit
; // Table was created implicitly
34 bool readonly
; // No more modification allowed
36 int nkval
; // key-values in the table
38 int narr
; // arrays in the table
40 int ntab
; // tables in the table
46 const char *key
; // key to this array
47 int keylen
; // length of key.
48 int kind
; // element kind: 'v'alue, 'a'rray, or 't'able, 'm'ixed
49 int type
; // for value kind: 'i'nt, 'd'ouble, 'b'ool, 's'tring, 't'ime, 'D'ate, 'T'imestamp, 'm'ixed
50 int nitem
; // number of elements
53 struct toml_arritem_t
{
54 int valtype
; // for value kind: 'i'nt, 'd'ouble, 'b'ool, 's'tring, 't'ime, 'D'ate, 'T'imestamp
60 // TOML key/value pair.
61 struct toml_keyval_t
{
62 const char *key
; // key to this value
63 int keylen
; // length of key.
64 const char *val
; // the raw value
69 // The string value s is a regular NULL-terminated C string, but the string
70 // length is also given in sl since TOML values may contain NULL bytes. The
71 // value is guaranteed to be correct UTF-8.
73 bool ok
; // Was this value present?
75 toml_timestamp_t
*ts
; // datetime; must be freed after use.
76 char *s
; // string value; must be freed after use
77 int sl
; // string length, excluding NULL.
79 int64_t i
; // int value
80 double d
; // double value
84 // Timestamp type; some values may be empty depending on the value of kind.
85 struct toml_timestamp_t
{
92 int hour
, minute
, second
, millisec
;
96 // toml_parse() parses a TOML document from a string. Returns 0 on error, with
97 // the error message stored in errbuf.
99 // toml_parse_file() is identical, but reads from a file descriptor.
101 // Use toml_free() to free the return value; this will invalidate all handles
103 TOML_EXTERN toml_table_t
*toml_parse (char *toml
, char *errbuf
, int errbufsz
);
104 TOML_EXTERN toml_table_t
*toml_parse_file (FILE *fp
, char *errbuf
, int errbufsz
);
105 TOML_EXTERN
void toml_free (toml_table_t
*table
);
109 // toml_table_len() gets the number of direct keys for this table;
110 // toml_table_key() gets the nth direct key in this table.
111 TOML_EXTERN
int toml_table_len (const toml_table_t
*table
);
112 TOML_EXTERN
const char *toml_table_key (const toml_table_t
*table
, int keyidx
, int *keylen
);
113 TOML_EXTERN toml_value_t
toml_table_string (const toml_table_t
*table
, const char *key
);
114 TOML_EXTERN toml_value_t
toml_table_bool (const toml_table_t
*table
, const char *key
);
115 TOML_EXTERN toml_value_t
toml_table_int (const toml_table_t
*table
, const char *key
);
116 TOML_EXTERN toml_value_t
toml_table_double (const toml_table_t
*table
, const char *key
);
117 TOML_EXTERN toml_value_t
toml_table_timestamp (const toml_table_t
*table
, const char *key
);
118 TOML_EXTERN toml_array_t
*toml_table_array (const toml_table_t
*table
, const char *key
);
119 TOML_EXTERN toml_table_t
*toml_table_table (const toml_table_t
*table
, const char *key
);
122 TOML_EXTERN
int toml_array_len (const toml_array_t
*array
);
123 TOML_EXTERN toml_value_t
toml_array_string (const toml_array_t
*array
, int idx
);
124 TOML_EXTERN toml_value_t
toml_array_bool (const toml_array_t
*array
, int idx
);
125 TOML_EXTERN toml_value_t
toml_array_int (const toml_array_t
*array
, int idx
);
126 TOML_EXTERN toml_value_t
toml_array_double (const toml_array_t
*array
, int idx
);
127 TOML_EXTERN toml_value_t
toml_array_timestamp (const toml_array_t
*array
, int idx
);
128 TOML_EXTERN toml_array_t
*toml_array_array (const toml_array_t
*array
, int idx
);
129 TOML_EXTERN toml_table_t
*toml_array_table (const toml_array_t
*array
, int idx
);
142 #define ALIGN8(sz) (((sz) + 7) & ~7)
143 #define calloc(x, y) error - forbidden - use CALLOC instead
144 static void *CALLOC(size_t nmemb
, size_t sz
) {
145 int nb
= ALIGN8(sz
) * nmemb
;
146 void *p
= malloc(nb
);
153 // some old platforms define strdup macro -- drop it.
155 #define strdup(x) error - forbidden - use STRDUP instead
156 static char *STRDUP(const char *s
) {
158 char *p
= malloc(len
+ 1);
166 // some old platforms define strndup macro -- drop it.
168 #define strndup(x) error - forbiden - use STRNDUP instead
169 static char *STRNDUP(const char *s
, size_t n
) {
170 size_t len
= strnlen(s
, n
);
171 char *p
= malloc(len
+ 1);
180 typedef const char *toml_unparsed_t
;
181 toml_unparsed_t
toml_table_unparsed (const toml_table_t
*table
, const char *key
);
182 toml_unparsed_t
toml_array_unparsed (const toml_array_t
*array
, int idx
);
183 int toml_value_string (toml_unparsed_t s
, char **ret
, int *len
);
184 int toml_value_bool (toml_unparsed_t s
, bool *ret
);
185 int toml_value_int (toml_unparsed_t s
, int64_t *ret
);
186 int toml_value_double (toml_unparsed_t s
, double *ret
);
187 int toml_value_timestamp (toml_unparsed_t s
, toml_timestamp_t
*ret
);
189 // Convert escape to UTF-8; return #bytes used in buf to encode the char, or -1
191 // http://stackoverflow.com/questions/6240055/manually-converting-unicode-codepoints-into-utf-8-and-utf-16
192 int read_unicode_escape(int64_t code
, char buf
[6]) {
193 if (0xd800 <= code
&& code
<= 0xdfff) /// UTF-16 surrogates
199 if (code
<= 0x7F) { /// 0x00000000 - 0x0000007F: 0xxxxxxx
200 buf
[0] = (unsigned char)code
;
203 if (code
<= 0x000007FF) { /// 0x00000080 - 0x000007FF: 110xxxxx 10xxxxxx
204 buf
[0] = (unsigned char)(0xc0 | (code
>> 6));
205 buf
[1] = (unsigned char)(0x80 | (code
& 0x3f));
208 if (code
<= 0x0000FFFF) { /// 0x00000800 - 0x0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx
209 buf
[0] = (unsigned char)(0xe0 | (code
>> 12));
210 buf
[1] = (unsigned char)(0x80 | ((code
>> 6) & 0x3f));
211 buf
[2] = (unsigned char)(0x80 | (code
& 0x3f));
214 if (code
<= 0x001FFFFF) { /// 0x00010000 - 0x001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
215 buf
[0] = (unsigned char)(0xf0 | (code
>> 18));
216 buf
[1] = (unsigned char)(0x80 | ((code
>> 12) & 0x3f));
217 buf
[2] = (unsigned char)(0x80 | ((code
>> 6) & 0x3f));
218 buf
[3] = (unsigned char)(0x80 | (code
& 0x3f));
221 if (code
<= 0x03FFFFFF) { /// 0x00200000 - 0x03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
222 buf
[0] = (unsigned char)(0xf8 | (code
>> 24));
223 buf
[1] = (unsigned char)(0x80 | ((code
>> 18) & 0x3f));
224 buf
[2] = (unsigned char)(0x80 | ((code
>> 12) & 0x3f));
225 buf
[3] = (unsigned char)(0x80 | ((code
>> 6) & 0x3f));
226 buf
[4] = (unsigned char)(0x80 | (code
& 0x3f));
229 if (code
<= 0x7FFFFFFF) { /// 0x04000000 - 0x7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
230 buf
[0] = (unsigned char)(0xfc | (code
>> 30));
231 buf
[1] = (unsigned char)(0x80 | ((code
>> 24) & 0x3f));
232 buf
[2] = (unsigned char)(0x80 | ((code
>> 18) & 0x3f));
233 buf
[3] = (unsigned char)(0x80 | ((code
>> 12) & 0x3f));
234 buf
[4] = (unsigned char)(0x80 | ((code
>> 6) & 0x3f));
235 buf
[5] = (unsigned char)(0x80 | (code
& 0x3f));
241 static inline void xfree(const void *x
) {
243 free((void *)(intptr_t)x
);
258 typedef enum tokentype_t tokentype_t
;
260 typedef struct token_t token_t
;
264 char *ptr
; // points into context->start
269 typedef struct context_t context_t
;
278 toml_table_t
*curtab
;
288 #define STRINGIFY(x) #x
289 #define TOSTRING(x) STRINGIFY(x)
290 #define FLINE __FILE__ ":" TOSTRING(__LINE__)
292 static int next_token(context_t
*ctx
, bool dotisspecial
);
294 // Error reporting. Call when an error is detected. Always return -1.
295 static int e_outofmemory(context_t
*ctx
, const char *fline
) {
296 snprintf(ctx
->errbuf
, ctx
->errbufsz
, "ERROR: out of memory (%s)", fline
);
300 static int e_internal(context_t
*ctx
, const char *fline
) {
301 snprintf(ctx
->errbuf
, ctx
->errbufsz
, "internal error (%s)", fline
);
305 static int e_syntax(context_t
*ctx
, int lineno
, const char *msg
) {
306 snprintf(ctx
->errbuf
, ctx
->errbufsz
, "line %d: %s", lineno
, msg
);
310 static int e_badkey(context_t
*ctx
, int lineno
) {
311 snprintf(ctx
->errbuf
, ctx
->errbufsz
, "line %d: bad key", lineno
);
315 static int e_keyexists(context_t
*ctx
, int lineno
) {
316 snprintf(ctx
->errbuf
, ctx
->errbufsz
, "line %d: key exists", lineno
);
320 static int e_forbid(context_t
*ctx
, int lineno
, const char *msg
) {
321 snprintf(ctx
->errbuf
, ctx
->errbufsz
, "line %d: %s", lineno
, msg
);
325 static void *expand(void *p
, int sz
, int newsz
) {
326 void *s
= malloc(newsz
);
337 static void **expand_ptrarr(void **p
, int n
) {
338 void **s
= malloc((n
+ 1) * sizeof(void *));
344 memcpy(s
, p
, n
* sizeof(void *));
350 static toml_arritem_t
*expand_arritem(toml_arritem_t
*p
, int n
) {
351 toml_arritem_t
*pp
= expand(p
, n
* sizeof(*p
), (n
+ 1) * sizeof(*p
));
355 memset(&pp
[n
], 0, sizeof(pp
[n
]));
359 static uint8_t const u8_length
[] = {1,1,1,1,1,1,1,1,0,0,0,0,2,2,3,4};
360 #define u8length(s) u8_length[(((uint8_t *)(s))[0] & 0xFF) >> 4];
362 static char *norm_lit_str(const char *src
, int srclen
, int *len
, bool multiline
, bool is_key
, char *errbuf
, int errbufsz
) {
363 const char *sp
= src
;
364 const char *sq
= src
+ srclen
;
365 char *dst
= 0; /// will write to dst[] and return it
366 int max
= 0; /// max size of dst[]
367 int off
= 0; /// cur offset in dst[]
369 for (;;) { /// scan forward on src
370 if (off
>= max
- 10) { /// have some slack for misc stuff
371 int newmax
= max
+ 50;
372 char *x
= expand(dst
, max
, newmax
);
375 snprintf(errbuf
, errbufsz
, "out of memory");
382 if (sp
>= sq
) /// finished?
385 uint8_t l
= u8length(sp
);
388 snprintf(errbuf
, errbufsz
, "invalid UTF-8 at byte pos %d", off
);
392 for (int i
= 0; i
< l
; i
++) {
394 if ((ch
& 0x80) != 0x80) {
396 snprintf(errbuf
, errbufsz
, "invalid UTF-8 at byte pos %d", off
);
405 if (is_key
&& ch
== '\n') {
407 snprintf(errbuf
, errbufsz
, "literal newlines not allowed in key");
410 /// control characters other than tab is not allowed
411 if ((0 <= ch
&& ch
<= 0x08) || (0x0a <= ch
&& ch
<= 0x1f) || ch
== 0x7f) {
412 if (!(multiline
&& (ch
== '\r' || ch
== '\n'))) {
414 snprintf(errbuf
, errbufsz
, "invalid char U+%04x", ch
);
419 dst
[off
++] = ch
; /// a plain copy suffice
427 /* Convert src to raw unescaped utf-8 string.
428 * Returns NULL if error with errmsg in errbuf. */
429 static char *norm_basic_str(const char *src
, int srclen
, int *len
, bool multiline
, bool is_key
, char *errbuf
, int errbufsz
) {
430 const char *sp
= src
;
431 const char *sq
= src
+ srclen
;
432 char *dst
= 0; /// will write to dst[] and return it
433 int max
= 0; /// max size of dst[]
434 int off
= 0; /// cur offset in dst[]
436 /// scan forward on src
438 if (off
>= max
- 10) { /// have some slack for misc stuff
439 int newmax
= max
+ 50;
440 char *x
= expand(dst
, max
, newmax
);
443 snprintf(errbuf
, errbufsz
, "out of memory");
450 if (sp
>= sq
) /// finished?
453 uint8_t l
= u8length(sp
);
456 snprintf(errbuf
, errbufsz
, "invalid UTF-8 at byte pos %d", off
);
460 for (int i
= 0; i
< l
; i
++) {
462 if ((ch
& 0x80) != 0x80) {
464 snprintf(errbuf
, errbufsz
, "invalid UTF-8 at byte pos %d", off
);
473 if (is_key
&& ch
== '\n') {
475 snprintf(errbuf
, errbufsz
, "literal newlines not allowed in key");
479 /// must be escaped: U+0000 to U+0008, U+000A to U+001F, U+007F
480 if ((ch
>= 0 && ch
<= 0x08) || (ch
>= 0x0a && ch
<= 0x1f) || ch
== 0x7f) {
481 if (!(multiline
&& (ch
== '\r' || ch
== '\n'))) {
483 snprintf(errbuf
, errbufsz
, "invalid char U+%04x", ch
);
488 dst
[off
++] = ch
; /// a plain copy suffice
492 if (sp
>= sq
) { /// ch was backslash. we expect the escape char.
493 snprintf(errbuf
, errbufsz
, "last backslash is invalid");
498 if (multiline
) { /// for multi-line, we want to kill line-ending-backslash.
499 if (sp
[strspn(sp
, " \t\r")] == '\n') { /// if there is only whitespace after the backslash ...
500 sp
+= strspn(sp
, " \t\r\n"); /// skip all the following whitespaces
505 ch
= *sp
++; /// get the escaped char
510 int nhex
= (ch
== 'u' ? 4 : 8);
511 for (int i
= 0; i
< nhex
; i
++) {
513 snprintf(errbuf
, errbufsz
, "\\%c expects %d hex chars", ch
, nhex
);
519 if ('0' <= ch
&& ch
<= '9')
521 else if ('A' <= ch
&& ch
<= 'F')
523 else if ('a' <= ch
&& ch
<= 'f')
524 v
= (ch
^ 0x20) - 'A' + 10;
526 snprintf(errbuf
, errbufsz
, "invalid hex chars for \\u or \\U");
532 int n
= read_unicode_escape(ucs
, &dst
[off
]);
534 snprintf(errbuf
, errbufsz
, "illegal ucs code in \\u or \\U");
540 case 'b': ch
= '\b'; break;
541 case 't': ch
= '\t'; break;
542 case 'n': ch
= '\n'; break;
543 case 'f': ch
= '\f'; break;
544 case 'r': ch
= '\r'; break;
545 case '"': ch
= '"'; break;
546 case '\\': ch
= '\\'; break;
548 snprintf(errbuf
, errbufsz
, "illegal escape char \\%c", ch
);
557 dst
[off
++] = 0; /// Cap with NUL and return it.
561 // Normalize a key. Convert all special chars to raw unescaped utf-8 chars.
562 static char *normalize_key(context_t
*ctx
, token_t strtok
, int *keylen
) {
563 const char *sp
= strtok
.ptr
;
564 const char *sq
= strtok
.ptr
+ strtok
.len
;
565 int lineno
= strtok
.lineno
;
570 if (ch
== '\'' || ch
== '\"') {
571 /// if ''' or """, take 3 chars off front and back. Else, take 1 char off.
572 bool multiline
= (sp
[1] == ch
&& sp
[2] == ch
);
580 ret
= norm_lit_str(sp
, sq
- sp
, keylen
, multiline
, true, ebuf
, sizeof(ebuf
));
582 ret
= norm_basic_str(sp
, sq
- sp
, keylen
, multiline
, true, ebuf
, sizeof(ebuf
));
584 e_syntax(ctx
, lineno
, ebuf
);
591 for (const char *c
= sp
; c
!= sq
; c
++) { /// Bare key: allow: [A-Za-z0-9_-]+
592 *keylen
= *keylen
+ 1;
593 if (isalnum(*c
) || *c
== '_' || *c
== '-')
595 e_badkey(ctx
, lineno
);
599 if (!(ret
= STRNDUP(sp
, sq
- sp
))) { /// dup and return
600 e_outofmemory(ctx
, FLINE
);
606 /* Look up key in tab. Return 0 if not found, or
607 * 'v'alue, 'a'rray or 't'able depending on the element. */
608 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
) {
613 ret_tab
= (toml_table_t
**)&dummy
;
615 ret_arr
= (toml_array_t
**)&dummy
;
617 ret_val
= (toml_keyval_t
**)&dummy
;
623 for (i
= 0; i
< tab
->nkval
; i
++) {
624 if (strcmp(key
, tab
->kval
[i
]->key
) == 0) {
625 *ret_val
= tab
->kval
[i
];
629 for (i
= 0; i
< tab
->narr
; i
++) {
630 if (strcmp(key
, tab
->arr
[i
]->key
) == 0) {
631 *ret_arr
= tab
->arr
[i
];
635 for (i
= 0; i
< tab
->ntab
; i
++) {
636 if (strcmp(key
, tab
->tab
[i
]->key
) == 0) {
637 *ret_tab
= tab
->tab
[i
];
644 static int key_kind(toml_table_t
*tab
, const char *key
) {
645 return check_key(tab
, key
, 0, 0, 0);
648 /* Create a keyval in the table. */
649 static toml_keyval_t
*create_keyval_in_table(context_t
*ctx
, toml_table_t
*tab
, token_t keytok
) {
651 char *newkey
= normalize_key(ctx
, keytok
, &keylen
);
655 toml_keyval_t
*dest
= 0;
656 if (key_kind(tab
, newkey
)) {
658 e_keyexists(ctx
, keytok
.lineno
);
663 toml_keyval_t
**base
;
664 if ((base
= (toml_keyval_t
**)expand_ptrarr((void **)tab
->kval
, n
)) == 0) {
666 e_outofmemory(ctx
, FLINE
);
671 if ((base
[n
] = (toml_keyval_t
*)CALLOC(1, sizeof(*base
[n
]))) == 0) {
673 e_outofmemory(ctx
, FLINE
);
677 dest
= tab
->kval
[tab
->nkval
++];
679 dest
->keylen
= keylen
;
683 // Create a table in the table.
684 static toml_table_t
*create_keytable_in_table(context_t
*ctx
, toml_table_t
*tab
, token_t keytok
) {
686 char *newkey
= normalize_key(ctx
, keytok
, &keylen
);
690 toml_table_t
*dest
= 0;
691 if (check_key(tab
, newkey
, 0, 0, &dest
)) {
694 /// Special case: make explicit if table exists and was created
696 if (dest
&& dest
->implicit
) {
697 dest
->implicit
= false;
700 e_keyexists(ctx
, keytok
.lineno
);
706 if ((base
= (toml_table_t
**)expand_ptrarr((void **)tab
->tab
, n
)) == 0) {
708 e_outofmemory(ctx
, FLINE
);
713 if ((base
[n
] = (toml_table_t
*)CALLOC(1, sizeof(*base
[n
]))) == 0) {
715 e_outofmemory(ctx
, FLINE
);
719 dest
= tab
->tab
[tab
->ntab
++];
721 dest
->keylen
= keylen
;
725 // Create an array in the table.
726 static toml_array_t
*create_keyarray_in_table(context_t
*ctx
, toml_table_t
*tab
, token_t keytok
, char kind
) {
728 char *newkey
= normalize_key(ctx
, keytok
, &keylen
);
732 if (key_kind(tab
, newkey
)) {
734 e_keyexists(ctx
, keytok
.lineno
);
740 if ((base
= (toml_array_t
**)expand_ptrarr((void **)tab
->arr
, n
)) == 0) {
742 e_outofmemory(ctx
, FLINE
);
747 if ((base
[n
] = (toml_array_t
*)CALLOC(1, sizeof(*base
[n
]))) == 0) {
749 e_outofmemory(ctx
, FLINE
);
752 toml_array_t
*dest
= tab
->arr
[tab
->narr
++];
754 dest
->keylen
= keylen
;
760 static toml_arritem_t
*create_value_in_array(context_t
*ctx
, toml_array_t
*parent
) {
761 const int n
= parent
->nitem
;
762 toml_arritem_t
*base
= expand_arritem(parent
->item
, n
);
764 e_outofmemory(ctx
, FLINE
);
769 return &parent
->item
[n
];
772 /* Create an array in an array */
773 static toml_array_t
*create_array_in_array(context_t
*ctx
,
774 toml_array_t
*parent
) {
775 const int n
= parent
->nitem
;
776 toml_arritem_t
*base
= expand_arritem(parent
->item
, n
);
778 e_outofmemory(ctx
, FLINE
);
781 toml_array_t
*ret
= (toml_array_t
*)CALLOC(1, sizeof(toml_array_t
));
783 e_outofmemory(ctx
, FLINE
);
792 /* Create a table in an array */
793 static toml_table_t
*create_table_in_array(context_t
*ctx
, toml_array_t
*parent
) {
794 int n
= parent
->nitem
;
795 toml_arritem_t
*base
= expand_arritem(parent
->item
, n
);
797 e_outofmemory(ctx
, FLINE
);
800 toml_table_t
*ret
= (toml_table_t
*)CALLOC(1, sizeof(toml_table_t
));
802 e_outofmemory(ctx
, FLINE
);
811 static int skip_newlines(context_t
*ctx
, bool isdotspecial
) {
812 while (ctx
->tok
.tok
== NEWLINE
) {
813 if (next_token(ctx
, isdotspecial
))
821 static int parse_keyval(context_t
*ctx
, toml_table_t
*tab
);
823 static inline int eat_token(context_t
*ctx
, tokentype_t typ
, bool isdotspecial
, const char *fline
) {
824 if (ctx
->tok
.tok
!= typ
)
825 return e_internal(ctx
, fline
);
827 if (next_token(ctx
, isdotspecial
))
833 /* We are at '{ ... }'; parse the table. */
834 static int parse_inline_table(context_t
*ctx
, toml_table_t
*tab
) {
835 if (eat_token(ctx
, LBRACE
, 1, FLINE
))
839 if (ctx
->tok
.tok
== NEWLINE
)
840 return e_syntax(ctx
, ctx
->tok
.lineno
, "newline not allowed in inline table");
842 if (ctx
->tok
.tok
== RBRACE
) // until closing brace
845 if (ctx
->tok
.tok
!= STRING
)
846 return e_syntax(ctx
, ctx
->tok
.lineno
, "expect a string");
848 if (parse_keyval(ctx
, tab
))
851 if (ctx
->tok
.tok
== NEWLINE
)
852 return e_syntax(ctx
, ctx
->tok
.lineno
, "newline not allowed in inline table");
854 /* on comma, continue to scan for next keyval */
855 if (ctx
->tok
.tok
== COMMA
) {
856 if (eat_token(ctx
, COMMA
, 1, FLINE
))
863 if (eat_token(ctx
, RBRACE
, 1, FLINE
))
869 static int valtype(const char *val
) {
871 if (*val
== '\'' || *val
== '"')
873 if (toml_value_bool(val
, false) == 0)
875 if (toml_value_int(val
, 0) == 0)
877 if (toml_value_double(val
, 0) == 0)
879 if (toml_value_timestamp(val
, &ts
) == 0) {
880 if (ts
.year
&& ts
.hour
)
881 return 'T'; /// timestamp
886 return 'u'; /// unknown
889 /* We are at '[...]' */
890 static int parse_array(context_t
*ctx
, toml_array_t
*arr
) {
891 if (eat_token(ctx
, LBRACKET
, 0, FLINE
))
895 if (skip_newlines(ctx
, 0))
898 if (ctx
->tok
.tok
== RBRACKET
) /// until ]
901 switch (ctx
->tok
.tok
) {
903 /// set array kind if this will be the first entry
906 else if (arr
->kind
!= 'v')
909 char *val
= ctx
->tok
.ptr
;
910 int vlen
= ctx
->tok
.len
;
912 /// make a new value in array
913 toml_arritem_t
*newval
= create_value_in_array(ctx
, arr
);
915 return e_outofmemory(ctx
, FLINE
);
917 if (!(newval
->val
= STRNDUP(val
, vlen
)))
918 return e_outofmemory(ctx
, FLINE
);
920 newval
->valtype
= valtype(newval
->val
);
922 /// set array type if this is the first entry
924 arr
->type
= newval
->valtype
;
925 else if (arr
->type
!= newval
->valtype
)
926 arr
->type
= 'm'; /// mixed
928 if (eat_token(ctx
, ctx
->tok
.tok
, 0, FLINE
))
932 case LBRACKET
: { /* [ [array], [array] ... ] */
933 /* set the array kind if this will be the first entry */
936 else if (arr
->kind
!= 'a')
939 toml_array_t
*subarr
= create_array_in_array(ctx
, arr
);
942 if (parse_array(ctx
, subarr
))
946 case LBRACE
: { /* [ {table}, {table} ... ] */
947 /* set the array kind if this will be the first entry */
950 else if (arr
->kind
!= 't')
953 toml_table_t
*subtab
= create_table_in_array(ctx
, arr
);
956 if (parse_inline_table(ctx
, subtab
))
961 return e_syntax(ctx
, ctx
->tok
.lineno
, "syntax error");
964 if (skip_newlines(ctx
, 0))
967 /* on comma, continue to scan for next element */
968 if (ctx
->tok
.tok
== COMMA
) {
969 if (eat_token(ctx
, COMMA
, 0, FLINE
))
976 if (eat_token(ctx
, RBRACKET
, 1, FLINE
))
981 /* handle lines like these:
985 static int parse_keyval(context_t
*ctx
, toml_table_t
*tab
) {
987 return e_forbid(ctx
, ctx
->tok
.lineno
, "cannot insert new entry into existing table");
990 token_t key
= ctx
->tok
;
991 if (eat_token(ctx
, STRING
, 1, FLINE
))
994 if (ctx
->tok
.tok
== DOT
) {
995 /* handle inline dotted key. e.g.
996 physical.color = "orange"
997 physical.shape = "round" */
998 toml_table_t
*subtab
= 0;
1001 char *subtabstr
= normalize_key(ctx
, key
, &keylen
);
1005 subtab
= toml_table_table(tab
, subtabstr
);
1007 subtab
->keylen
= keylen
;
1011 subtab
= create_keytable_in_table(ctx
, tab
, key
);
1015 if (next_token(ctx
, true))
1017 if (parse_keyval(ctx
, subtab
))
1022 if (ctx
->tok
.tok
!= EQUAL
)
1023 return e_syntax(ctx
, ctx
->tok
.lineno
, "missing =");
1025 if (next_token(ctx
, false))
1028 switch (ctx
->tok
.tok
) {
1029 case STRING
: { // key = "value"
1030 toml_keyval_t
*keyval
= create_keyval_in_table(ctx
, tab
, key
);
1033 token_t val
= ctx
->tok
;
1035 assert(keyval
->val
== 0);
1036 if (!(keyval
->val
= STRNDUP(val
.ptr
, val
.len
)))
1037 return e_outofmemory(ctx
, FLINE
);
1039 if (next_token(ctx
, true))
1044 case LBRACKET
: { /* key = [ array ] */
1045 toml_array_t
*arr
= create_keyarray_in_table(ctx
, tab
, key
, 0);
1048 if (parse_array(ctx
, arr
))
1052 case LBRACE
: { /* key = { table } */
1053 toml_table_t
*nxttab
= create_keytable_in_table(ctx
, tab
, key
);
1056 if (parse_inline_table(ctx
, nxttab
))
1061 return e_syntax(ctx
, ctx
->tok
.lineno
, "syntax error");
1066 typedef struct tabpath_t tabpath_t
;
1072 /* at [x.y.z] or [[x.y.z]]
1073 * Scan forward and fill tabpath until it enters ] or ]]
1074 * There will be at least one entry on return. */
1075 static int fill_tabpath(context_t
*ctx
) {
1077 for (int i
= 0; i
< ctx
->tpath
.top
; i
++) {
1078 char **p
= &ctx
->tpath
.key
[i
];
1085 if (ctx
->tpath
.top
>= 10)
1086 return e_syntax(ctx
, ctx
->tok
.lineno
, "table path is too deep; max allowed is 10.");
1087 if (ctx
->tok
.tok
!= STRING
)
1088 return e_syntax(ctx
, ctx
->tok
.lineno
, "invalid or missing key");
1091 char *key
= normalize_key(ctx
, ctx
->tok
, &keylen
);
1094 ctx
->tpath
.tok
[ctx
->tpath
.top
] = ctx
->tok
;
1095 ctx
->tpath
.key
[ctx
->tpath
.top
] = key
;
1096 ctx
->tpath
.keylen
[ctx
->tpath
.top
] = keylen
;
1099 if (next_token(ctx
, true))
1102 if (ctx
->tok
.tok
== RBRACKET
)
1104 if (ctx
->tok
.tok
!= DOT
)
1105 return e_syntax(ctx
, ctx
->tok
.lineno
, "invalid key");
1106 if (next_token(ctx
, true))
1110 if (ctx
->tpath
.top
<= 0)
1111 return e_syntax(ctx
, ctx
->tok
.lineno
, "empty table selector");
1115 /* Walk tabpath from the root, and create new tables on the way.
1116 * Sets ctx->curtab to the final table. */
1117 static int walk_tabpath(context_t
*ctx
) {
1118 toml_table_t
*curtab
= ctx
->root
; /// start from root
1120 for (int i
= 0; i
< ctx
->tpath
.top
; i
++) {
1121 const char *key
= ctx
->tpath
.key
[i
];
1122 int keylen
= ctx
->tpath
.keylen
[i
];
1124 toml_keyval_t
*nextval
= 0;
1125 toml_array_t
*nextarr
= 0;
1126 toml_table_t
*nexttab
= 0;
1127 switch (check_key(curtab
, key
, &nextval
, &nextarr
, &nexttab
)) {
1128 case 't': /// found a table. nexttab is where we will go next.
1130 case 'a': /// found an array. nexttab is the last table in the array.
1131 if (nextarr
->kind
!= 't')
1132 return e_internal(ctx
, FLINE
);
1134 if (nextarr
->nitem
== 0)
1135 return e_internal(ctx
, FLINE
);
1137 nexttab
= nextarr
->item
[nextarr
->nitem
- 1].tab
;
1140 return e_keyexists(ctx
, ctx
->tpath
.tok
[i
].lineno
);
1141 default: { /// Not found. Let's create an implicit table.
1142 int n
= curtab
->ntab
;
1143 toml_table_t
**base
= (toml_table_t
**)expand_ptrarr((void **)curtab
->tab
, n
);
1145 return e_outofmemory(ctx
, FLINE
);
1149 if ((base
[n
] = (toml_table_t
*)CALLOC(1, sizeof(*base
[n
]))) == 0)
1150 return e_outofmemory(ctx
, FLINE
);
1152 if ((base
[n
]->key
= STRDUP(key
)) == 0)
1153 return e_outofmemory(ctx
, FLINE
);
1154 base
[n
]->keylen
= keylen
;
1156 nexttab
= curtab
->tab
[curtab
->ntab
++];
1158 /// tabs created by walk_tabpath are considered implicit
1159 nexttab
->implicit
= true;
1162 curtab
= nexttab
; /// switch to next tab
1165 ctx
->curtab
= curtab
; /// save it
1169 /* handle lines like [x.y.z] or [[x.y.z]] */
1170 static int parse_select(context_t
*ctx
) {
1171 assert(ctx
->tok
.tok
== LBRACKET
);
1174 int llb
= (ctx
->tok
.ptr
+ 1 < ctx
->stop
&& ctx
->tok
.ptr
[1] == '[');
1175 /* need to detect '[[' on our own because next_token() will skip whitespace,
1176 and '[ [' would be taken as '[[', which is wrong. */
1179 if (eat_token(ctx
, LBRACKET
, 1, FLINE
))
1182 assert(ctx
->tok
.tok
== LBRACKET
);
1183 if (eat_token(ctx
, LBRACKET
, 1, FLINE
))
1187 if (fill_tabpath(ctx
))
1190 /* For [x.y.z] or [[x.y.z]], remove z from tpath. */
1191 token_t z
= ctx
->tpath
.tok
[ctx
->tpath
.top
- 1];
1192 xfree(ctx
->tpath
.key
[ctx
->tpath
.top
- 1]);
1195 /* set up ctx->curtab */
1196 if (walk_tabpath(ctx
))
1200 /* [x.y.z] -> create z = {} in x.y */
1201 toml_table_t
*curtab
= create_keytable_in_table(ctx
, ctx
->curtab
, z
);
1204 ctx
->curtab
= curtab
;
1206 /* [[x.y.z]] -> create z = [] in x.y */
1207 toml_array_t
*arr
= 0;
1210 char *zstr
= normalize_key(ctx
, z
, &keylen
);
1213 arr
= toml_table_array(ctx
->curtab
, zstr
);
1215 arr
->keylen
= keylen
;
1219 arr
= create_keyarray_in_table(ctx
, ctx
->curtab
, z
, 't');
1223 if (arr
->kind
!= 't')
1224 return e_syntax(ctx
, z
.lineno
, "array mismatch");
1229 toml_table_t
*t
= create_table_in_array(ctx
, arr
);
1233 if ((t
->key
= STRDUP("__anon__")) == 0)
1234 return e_outofmemory(ctx
, FLINE
);
1242 if (ctx
->tok
.tok
!= RBRACKET
) {
1243 return e_syntax(ctx
, ctx
->tok
.lineno
, "expects ]");
1246 if (!(ctx
->tok
.ptr
+ 1 < ctx
->stop
&& ctx
->tok
.ptr
[1] == ']')) {
1247 return e_syntax(ctx
, ctx
->tok
.lineno
, "expects ]]");
1249 if (eat_token(ctx
, RBRACKET
, 1, FLINE
))
1253 if (eat_token(ctx
, RBRACKET
, 1, FLINE
))
1255 if (ctx
->tok
.tok
!= NEWLINE
)
1256 return e_syntax(ctx
, ctx
->tok
.lineno
, "extra chars after ] or ]]");
1260 toml_table_t
*toml_parse(char *toml
, char *errbuf
, int errbufsz
) {
1270 memset(&ctx
, 0, sizeof(ctx
));
1272 ctx
.stop
= ctx
.start
+ strlen(toml
);
1273 ctx
.errbuf
= errbuf
;
1274 ctx
.errbufsz
= errbufsz
;
1276 // start with an artificial newline of length 0
1277 ctx
.tok
.tok
= NEWLINE
;
1282 // make a root table
1283 if ((ctx
.root
= CALLOC(1, sizeof(*ctx
.root
))) == 0) {
1284 e_outofmemory(&ctx
, FLINE
);
1285 return 0; // Do not goto fail, root table not set up yet
1288 // set root as default table
1289 ctx
.curtab
= ctx
.root
;
1291 // Scan forward until EOF
1292 for (token_t tok
= ctx
.tok
; !tok
.eof
; tok
= ctx
.tok
) {
1295 if (next_token(&ctx
, true))
1300 if (parse_keyval(&ctx
, ctx
.curtab
))
1303 if (ctx
.tok
.tok
!= NEWLINE
) {
1304 e_syntax(&ctx
, ctx
.tok
.lineno
, "extra chars after value");
1308 if (eat_token(&ctx
, NEWLINE
, 1, FLINE
))
1312 case LBRACKET
: /* [ x.y.z ] or [[ x.y.z ]] */
1313 if (parse_select(&ctx
))
1318 e_syntax(&ctx
, tok
.lineno
, "syntax error");
1324 for (int i
= 0; i
< ctx
.tpath
.top
; i
++)
1325 xfree(ctx
.tpath
.key
[i
]);
1329 // Something bad has happened. Free resources and return error.
1330 for (int i
= 0; i
< ctx
.tpath
.top
; i
++)
1331 xfree(ctx
.tpath
.key
[i
]);
1332 toml_free(ctx
.root
);
1336 toml_table_t
*toml_parse_file(FILE *fp
, char *errbuf
, int errbufsz
) {
1343 if (bufsz
== 1024 * 20) /// Increment buffer by 20k after 20k.
1346 int xsz
= bufsz
+ inc
;
1347 char *x
= expand(buf
, bufsz
, xsz
);
1349 snprintf(errbuf
, errbufsz
, "out of memory");
1358 int n
= fread(buf
+ off
, 1, bufsz
- off
, fp
);
1360 snprintf(errbuf
, errbufsz
, "%s", (errno
? strerror(errno
) : "Error reading file"));
1367 /// tag on a NUL to cap the string
1369 int xsz
= bufsz
+ 1;
1370 char *x
= expand(buf
, bufsz
, xsz
);
1372 snprintf(errbuf
, errbufsz
, "out of memory");
1381 /// parse it, cleanup and finish.
1382 toml_table_t
*ret
= toml_parse(buf
, errbuf
, errbufsz
);
1387 static void xfree_kval(toml_keyval_t
*p
) {
1395 static void xfree_tab(toml_table_t
*p
);
1397 static void xfree_arr(toml_array_t
*p
) {
1402 const int n
= p
->nitem
;
1403 for (int i
= 0; i
< n
; i
++) {
1404 toml_arritem_t
*a
= &p
->item
[i
];
1416 static void xfree_tab(toml_table_t
*p
) {
1422 for (int i
= 0; i
< p
->nkval
; i
++)
1423 xfree_kval(p
->kval
[i
]);
1426 for (int i
= 0; i
< p
->narr
; i
++)
1427 xfree_arr(p
->arr
[i
]);
1430 for (int i
= 0; i
< p
->ntab
; i
++)
1431 xfree_tab(p
->tab
[i
]);
1437 void toml_free(toml_table_t
*tab
) { xfree_tab(tab
); }
1439 static void set_token(context_t
*ctx
, tokentype_t tok
, int lineno
, char *ptr
, int len
) {
1449 static void set_eof(context_t
*ctx
, int lineno
) {
1450 set_token(ctx
, NEWLINE
, lineno
, ctx
->stop
, 0);
1454 /* Scan p for n digits compositing entirely of [0-9] */
1455 static int scan_digits(const char *p
, int n
) {
1457 for (; n
> 0 && isdigit(*p
); n
--, p
++) {
1458 ret
= 10 * ret
+ (*p
- '0');
1460 return n
? -1 : ret
;
1463 static int scan_date(const char *p
, int *YY
, int *MM
, int *DD
) {
1464 int year
, month
, day
;
1465 year
= scan_digits(p
, 4);
1466 month
= (year
>= 0 && p
[4] == '-') ? scan_digits(p
+ 5, 2) : -1;
1467 day
= (month
>= 0 && p
[7] == '-') ? scan_digits(p
+ 8, 2) : -1;
1474 return (year
>= 0 && month
>= 0 && day
>= 0) ? 0 : -1;
1477 static int scan_time(const char *p
, int *hh
, int *mm
, int *ss
) {
1478 int hour
= scan_digits(p
, 2);
1479 int minute
= (hour
>= 0 && p
[2] == ':') ? scan_digits(p
+ 3, 2) : -1;
1480 int second
= (minute
>= 0 && p
[5] == ':') ? scan_digits(p
+ 6, 2) : -1;
1487 return (hour
>= 0 && minute
>= 0 && second
>= 0) ? 0 : -1;
1490 static int scan_string(context_t
*ctx
, char *p
, int lineno
, bool dotisspecial
) {
1493 // Literal multiline.
1494 if (strncmp(p
, "'''", 3) == 0) {
1497 q
= strstr(q
, "'''");
1499 return e_syntax(ctx
, lineno
, "unterminated triple-s-quote");
1501 while (q
[3] == '\'') {
1504 return e_syntax(ctx
, lineno
, "too many ''' in triple-s-quote");
1509 set_token(ctx
, STRING
, lineno
, orig
, q
+ 3 - orig
);
1514 if (strncmp(p
, "\"\"\"", 3) == 0) {
1517 q
= strstr(q
, "\"\"\"");
1519 return e_syntax(ctx
, lineno
, "unterminated triple-d-quote");
1520 if (q
[-1] == '\\') {
1525 while (q
[3] == '\"') {
1528 return e_syntax(ctx
, lineno
, "too many \"\"\" in triple-d-quote");
1534 /// the string is [p+3, q-1]
1535 int hexreq
= 0; /// #hex required
1536 bool escape
= false;
1537 for (p
+= 3; p
< q
; p
++) {
1540 if (strchr("btnfr\"\\", *p
))
1550 if (p
[strspn(p
, " \t\r")] == '\n')
1551 continue; /* allow for line ending backslash */
1552 return e_syntax(ctx
, lineno
, "bad escape char");
1556 if (strchr("0123456789ABCDEFabcdef", *p
))
1558 return e_syntax(ctx
, lineno
, "expect hex char");
1566 return e_syntax(ctx
, lineno
, "expect an escape char");
1568 return e_syntax(ctx
, lineno
, "expected more hex char");
1570 set_token(ctx
, STRING
, lineno
, orig
, q
+ 3 - orig
);
1576 for (p
++; *p
&& *p
!= '\n' && *p
!= '\''; p
++)
1579 return e_syntax(ctx
, lineno
, "unterminated s-quote");
1581 set_token(ctx
, STRING
, lineno
, orig
, p
+ 1 - orig
);
1587 int hexreq
= 0; /// #hex required
1588 bool escape
= false;
1589 for (p
++; *p
; p
++) {
1592 if (strchr("btnfr\"\\", *p
))
1602 return e_syntax(ctx
, lineno
, "bad escape char");
1606 if (strchr("0123456789ABCDEFabcdef", *p
))
1608 return e_syntax(ctx
, lineno
, "expect hex char");
1620 return e_syntax(ctx
, lineno
, "unterminated quote");
1622 set_token(ctx
, STRING
, lineno
, orig
, p
+ 1 - orig
);
1627 if (scan_date(p
, 0, 0, 0) == 0 || scan_time(p
, 0, 0, 0) == 0) {
1628 p
+= strspn(p
, "0123456789.:+-Tt Zz"); /// forward thru the timestamp
1629 for (; p
[-1] == ' '; p
--) /// squeeze out any spaces at end of string
1631 set_token(ctx
, STRING
, lineno
, orig
, p
- orig
); /// tokenize
1636 for (; *p
&& *p
!= '\n'; p
++) {
1638 if (ch
== '.' && dotisspecial
)
1640 if ('A' <= ch
&& ch
<= 'Z')
1642 if ('a' <= ch
&& ch
<= 'z')
1644 if (strchr("0123456789+-_.", ch
))
1649 set_token(ctx
, STRING
, lineno
, orig
, p
- orig
);
1653 static int next_token(context_t
*ctx
, bool dotisspecial
) {
1655 char *p
= ctx
->tok
.ptr
;
1656 int lineno
= ctx
->tok
.lineno
;
1657 for (int i
= 0; i
< ctx
->tok
.len
; i
++)
1662 while (p
< ctx
->stop
) {
1663 if (*p
== '#') { /// Skip comment. stop just before the \n.
1664 for (p
++; p
< ctx
->stop
&& *p
!= '\n'; p
++)
1669 if (dotisspecial
&& *p
== '.') {
1670 set_token(ctx
, DOT
, lineno
, p
, 1);
1676 set_token(ctx
, COMMA
, lineno
, p
, 1);
1679 set_token(ctx
, EQUAL
, lineno
, p
, 1);
1682 set_token(ctx
, LBRACE
, lineno
, p
, 1);
1685 set_token(ctx
, RBRACE
, lineno
, p
, 1);
1688 set_token(ctx
, LBRACKET
, lineno
, p
, 1);
1691 set_token(ctx
, RBRACKET
, lineno
, p
, 1);
1694 set_token(ctx
, NEWLINE
, lineno
, p
, 1);
1696 case '\r': case ' ': case '\t': /// ignore white spaces
1701 return scan_string(ctx
, p
, lineno
, dotisspecial
);
1704 set_eof(ctx
, lineno
);
1708 const char *toml_table_key(const toml_table_t
*tab
, int keyidx
, int *keylen
) {
1709 if (keyidx
< tab
->nkval
) {
1710 *keylen
= tab
->kval
[keyidx
]->keylen
;
1711 return tab
->kval
[keyidx
]->key
;
1713 if ((keyidx
-= tab
->nkval
) < tab
->narr
) {
1714 *keylen
= tab
->arr
[keyidx
]->keylen
;
1715 return tab
->arr
[keyidx
]->key
;
1717 if ((keyidx
-= tab
->narr
) < tab
->ntab
) {
1718 *keylen
= tab
->tab
[keyidx
]->keylen
;
1719 return tab
->tab
[keyidx
]->key
;
1725 toml_unparsed_t
toml_table_unparsed(const toml_table_t
*tab
, const char *key
) {
1726 for (int i
= 0; i
< tab
->nkval
; i
++)
1727 if (strcmp(key
, tab
->kval
[i
]->key
) == 0)
1728 return tab
->kval
[i
]->val
;
1732 toml_array_t
*toml_table_array(const toml_table_t
*tab
, const char *key
) {
1733 for (int i
= 0; i
< tab
->narr
; i
++)
1734 if (strcmp(key
, tab
->arr
[i
]->key
) == 0)
1739 toml_table_t
*toml_table_table(const toml_table_t
*tab
, const char *key
) {
1740 for (int i
= 0; i
< tab
->ntab
; i
++)
1741 if (strcmp(key
, tab
->tab
[i
]->key
) == 0)
1746 toml_unparsed_t
toml_array_unparsed(const toml_array_t
*arr
, int idx
) {
1747 return (0 <= idx
&& idx
< arr
->nitem
) ? arr
->item
[idx
].val
: 0;
1750 int toml_table_len(const toml_table_t
*tbl
) {
1751 return tbl
->nkval
+ tbl
->narr
+ tbl
->ntab
;
1754 int toml_array_len(const toml_array_t
*arr
) {
1758 toml_array_t
*toml_array_array(const toml_array_t
*arr
, int idx
) {
1759 return (0 <= idx
&& idx
< arr
->nitem
) ? arr
->item
[idx
].arr
: 0;
1762 toml_table_t
*toml_array_table(const toml_array_t
*arr
, int idx
) {
1763 return (0 <= idx
&& idx
< arr
->nitem
) ? arr
->item
[idx
].tab
: 0;
1766 static int parse_millisec(const char *p
, const char **endp
);
1768 bool is_leap(int y
) { return y
% 4 == 0 && (y
% 100 != 0 || y
% 400 == 0); }
1770 int toml_value_timestamp(toml_unparsed_t src_
, toml_timestamp_t
*ret
) {
1774 const char *p
= src_
;
1775 bool must_parse_time
= false;
1777 memset(ret
, 0, sizeof(*ret
));
1780 if (scan_date(p
, &ret
->year
, &ret
->month
, &ret
->day
) == 0) {
1781 if (ret
->month
< 1 || ret
->day
< 1 || ret
->month
> 12 || ret
->day
> 31)
1783 if (ret
->month
== 2 && ret
->day
> (is_leap(ret
->year
) ? 29 : 28))
1789 if (*p
!= 'T' && *p
!= 't' && *p
!= ' ') /// T or space
1791 must_parse_time
= true;
1797 if (scan_time(p
, &ret
->hour
, &ret
->minute
, &ret
->second
) == 0) {
1798 if (ret
->second
< 0 || ret
->minute
< 0 || ret
->hour
< 0 || ret
->hour
> 23 || ret
->minute
> 59 || ret
->second
> 60)
1800 ret
->kind
= (ret
->kind
== 'D' ? 'l' : 't');
1803 if (*p
== '.') { /// optionally, parse millisec
1806 ret
->millisec
= parse_millisec(p
, &qq
);
1810 if (*p
) { /// parse and copy Z
1812 char *z
= malloc(10);
1814 if (*p
== 'Z' || *p
== 'z') {
1818 } else if (*p
== '+' || *p
== '-') {
1821 if (!(isdigit(p
[0]) && isdigit(p
[1])))
1828 if (!(isdigit(p
[0]) && isdigit(p
[1])))
1840 if (must_parse_time
&& ret
->kind
== 'D')
1845 /* Raw to boolean */
1846 int toml_value_bool(toml_unparsed_t src
, bool *ret_
) {
1850 bool *ret
= ret_
? ret_
: &dummy
;
1852 if (strcmp(src
, "true") == 0) {
1856 if (strcmp(src
, "false") == 0) {
1863 /* Raw to integer */
1864 int toml_value_int(toml_unparsed_t src
, int64_t *ret_
) {
1870 char *q
= p
+ sizeof(buf
);
1871 const char *s
= src
;
1874 int64_t *ret
= ret_
? ret_
: &dummy
;
1875 bool have_sign
= false;
1877 if (s
[0] == '+' || s
[0] == '-') { /// allow +/-
1882 if (s
[0] == '_') /// disallow +_100
1885 if (s
[0] == '0') { /// if 0* ...
1887 case 'x': base
= 16; s
+= 2; break;
1888 case 'o': base
= 8; s
+= 2; break;
1889 case 'b': base
= 2; s
+= 2; break;
1893 if (s
[1]) /// ensure no other digits after it
1898 if (have_sign
) /// disallow +0xff, -0xff
1900 if (s
[0] == '_') /// disallow 0x_, 0o_, 0b_
1904 while (*s
&& p
< q
) { /// just strip underscores and pass to strtoll
1907 if (s
[0] == '_') /// disallow '__'
1909 if (s
[0] == '\0') /// numbers cannot end with '_'
1911 continue; /// skip _
1916 if (*s
|| p
== q
) /// if not at end-of-string or we ran out of buffer ...
1919 *p
= 0; /// cap with NUL
1921 /// Run strtoll on buf to get the integer
1924 *ret
= strtoll(buf
, &endp
, base
);
1925 return (errno
|| *endp
) ? -1 : 0;
1928 int toml_value_double(toml_unparsed_t src
, double *ret_
) {
1934 char *q
= p
+ sizeof(buf
);
1935 const char *s
= src
;
1937 double *ret
= ret_
? ret_
: &dummy
;
1938 bool have_us
= false;
1940 if (s
[0] == '+' || s
[0] == '-') /// allow +/-
1943 if (s
[0] == '_') /// disallow +_1.00
1946 { /// decimal point, if used, must be surrounded by at least one digit on each side
1947 char *dot
= strchr(s
, '.');
1949 if (dot
== s
|| !isdigit(dot
[-1]) || !isdigit(dot
[1]))
1954 /// zero must be followed by . or 'e', or NUL
1955 if (s
[0] == '0' && s
[1] && !strchr("eE.", s
[1]))
1958 /// just strip underscores and pass to strtod
1959 while (*s
&& p
< q
) {
1963 if (s
[0] == '_') /// disallow '__'
1965 if (s
[0] == 'e') /// disallow _e
1967 if (s
[0] == 0) /// disallow last char '_'
1969 continue; /// skip _
1971 if (ch
== 'I' || ch
== 'N' || ch
== 'F' || ch
== 'A') /// inf and nan are case-sensitive.
1973 if (ch
== 'e' && s
[0] == '_') /// disallow e_
1978 return -1; /// reached end of string or buffer is full?
1980 *p
= 0; /// cap with NUL
1982 /// Run strtod on buf to get the value
1985 *ret
= strtod(buf
, &endp
);
1988 if (have_us
&& (isnan(*ret
) || isinf(*ret
)))
1993 int toml_value_string(toml_unparsed_t src
, char **ret
, int *len
) {
1994 bool multiline
= false;
2002 /// First char must be a s-quote or d-quote
2004 int srclen
= strlen(src
);
2005 if (!(qchar
== '\'' || qchar
== '"')) {
2010 if (qchar
== src
[1] && qchar
== src
[2]) {
2011 multiline
= true; /// triple-quote implies multiline
2012 sp
= src
+ 3; /// first char after quote
2013 sq
= src
+ srclen
- 3; /// first char of ending quote
2015 if (!(sp
<= sq
&& sq
[0] == qchar
&& sq
[1] == qchar
&& sq
[2] == qchar
))
2016 return -1; /// last 3 chars in src must be qchar
2018 if (sp
[0] == '\n') /// skip new line immediate after qchar
2020 else if (sp
[0] == '\r' && sp
[1] == '\n')
2023 sp
= src
+ 1; /// first char after quote
2024 sq
= src
+ srclen
- 1; /// ending quote
2025 if (!(sp
<= sq
&& *sq
== qchar
)) /// last char in src must be qchar
2030 /// sp points to first valid char after quote.
2031 /// sq points to one char beyond last valid char.
2032 /// string len is (sq - sp).
2034 *ret
= norm_lit_str(sp
, sq
- sp
, len
, multiline
, false, 0, 0);
2036 *ret
= norm_basic_str(sp
, sq
- sp
, len
, multiline
, false, 0, 0);
2037 return *ret
? 0 : -1;
2040 toml_value_t
toml_array_string(const toml_array_t
*arr
, int idx
) {
2042 memset(&ret
, 0, sizeof(ret
));
2043 ret
.ok
= (toml_value_string(toml_array_unparsed(arr
, idx
), &ret
.u
.s
, &ret
.u
.sl
) == 0);
2047 toml_value_t
toml_array_bool(const toml_array_t
*arr
, int idx
) {
2049 memset(&ret
, 0, sizeof(ret
));
2050 ret
.ok
= (toml_value_bool(toml_array_unparsed(arr
, idx
), &ret
.u
.b
) == 0);
2054 toml_value_t
toml_array_int(const toml_array_t
*arr
, int idx
) {
2056 memset(&ret
, 0, sizeof(ret
));
2057 ret
.ok
= (toml_value_int(toml_array_unparsed(arr
, idx
), &ret
.u
.i
) == 0);
2061 toml_value_t
toml_array_double(const toml_array_t
*arr
, int idx
) {
2063 memset(&ret
, 0, sizeof(ret
));
2064 ret
.ok
= (toml_value_double(toml_array_unparsed(arr
, idx
), &ret
.u
.d
) == 0);
2068 toml_value_t
toml_array_timestamp(const toml_array_t
*arr
, int idx
) {
2069 toml_timestamp_t ts
;
2071 memset(&ret
, 0, sizeof(ret
));
2072 ret
.ok
= (toml_value_timestamp(toml_array_unparsed(arr
, idx
), &ts
) == 0);
2074 ret
.ok
= !!(ret
.u
.ts
= malloc(sizeof(*ret
.u
.ts
)));
2081 toml_value_t
toml_table_string(const toml_table_t
*tbl
, const char *key
) {
2083 memset(&ret
, 0, sizeof(ret
));
2084 toml_unparsed_t raw
= toml_table_unparsed(tbl
, key
);
2086 ret
.ok
= (toml_value_string(raw
, &ret
.u
.s
, &ret
.u
.sl
) == 0);
2090 toml_value_t
toml_table_bool(const toml_table_t
*tbl
, const char *key
) {
2092 memset(&ret
, 0, sizeof(ret
));
2093 ret
.ok
= (toml_value_bool(toml_table_unparsed(tbl
, key
), &ret
.u
.b
) == 0);
2097 toml_value_t
toml_table_int(const toml_table_t
*tbl
, const char *key
) {
2099 memset(&ret
, 0, sizeof(ret
));
2100 ret
.ok
= (toml_value_int(toml_table_unparsed(tbl
, key
), &ret
.u
.i
) == 0);
2104 toml_value_t
toml_table_double(const toml_table_t
*tbl
, const char *key
) {
2106 memset(&ret
, 0, sizeof(ret
));
2107 ret
.ok
= (toml_value_double(toml_table_unparsed(tbl
, key
), &ret
.u
.d
) == 0);
2111 toml_value_t
toml_table_timestamp(const toml_table_t
*tbl
, const char *key
) {
2112 toml_timestamp_t ts
;
2114 memset(&ret
, 0, sizeof(ret
));
2115 ret
.ok
= (toml_value_timestamp(toml_table_unparsed(tbl
, key
), &ts
) == 0);
2117 ret
.ok
= !!(ret
.u
.ts
= malloc(sizeof(*ret
.u
.ts
)));
2124 static int parse_millisec(const char *p
, const char **endp
) {
2126 int unit
= 100; /// unit in millisec
2127 for (; '0' <= *p
&& *p
<= '9'; p
++, unit
/= 10)
2128 ret
+= (*p
- '0') * unit
;