mysql-admutils/tomlc99/toml.c

2147 lines
46 KiB
C
Raw Permalink Normal View History

2023-03-14 15:11:51 +01:00
/*
MIT License
Copyright (c) 2017 - 2019 CK Tan
https://github.com/cktan/tomlc99
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <setjmp.h>
#include <stdlib.h>
#include <assert.h>
#include <errno.h>
#include <stdint.h>
#include <ctype.h>
#include <string.h>
#include "toml.h"
static void* (*ppmalloc)(size_t) = malloc;
static void (*ppfree)(void*) = free;
static void* (*ppcalloc)(size_t, size_t) = calloc;
static void* (*pprealloc)(void*, size_t) = realloc;
void toml_set_memutil(void* (*xxmalloc)(size_t),
void (*xxfree)(void*),
void* (*xxcalloc)(size_t, size_t),
void* (*xxrealloc)(void*, size_t))
{
ppmalloc = xxmalloc;
ppfree = xxfree;
ppcalloc = xxcalloc;
pprealloc = xxrealloc;
}
#define MALLOC(a) ppmalloc(a)
#define FREE(a) ppfree(a)
#define CALLOC(a,b) ppcalloc(a,b)
#define REALLOC(a,b) pprealloc(a,b)
static char* STRDUP(const char* s)
{
int len = strlen(s);
char* p = MALLOC(len+1);
if (p) {
memcpy(p, s, len);
p[len] = 0;
}
return p;
}
static char* STRNDUP(const char* s, size_t n)
{
size_t len = strnlen(s, n);
char* p = MALLOC(len+1);
if (p) {
memcpy(p, s, len);
p[len] = 0;
}
return p;
}
/**
* Convert a char in utf8 into UCS, and store it in *ret.
* Return #bytes consumed or -1 on failure.
*/
int toml_utf8_to_ucs(const char* orig, int len, int64_t* ret)
{
const unsigned char* buf = (const unsigned char*) orig;
unsigned i = *buf++;
int64_t v;
/* 0x00000000 - 0x0000007F:
0xxxxxxx
*/
if (0 == (i >> 7)) {
if (len < 1) return -1;
v = i;
return *ret = v, 1;
}
/* 0x00000080 - 0x000007FF:
110xxxxx 10xxxxxx
*/
if (0x6 == (i >> 5)) {
if (len < 2) return -1;
v = i & 0x1f;
for (int j = 0; j < 1; j++) {
i = *buf++;
if (0x2 != (i >> 6)) return -1;
v = (v << 6) | (i & 0x3f);
}
return *ret = v, (const char*) buf - orig;
}
/* 0x00000800 - 0x0000FFFF:
1110xxxx 10xxxxxx 10xxxxxx
*/
if (0xE == (i >> 4)) {
if (len < 3) return -1;
v = i & 0x0F;
for (int j = 0; j < 2; j++) {
i = *buf++;
if (0x2 != (i >> 6)) return -1;
v = (v << 6) | (i & 0x3f);
}
return *ret = v, (const char*) buf - orig;
}
/* 0x00010000 - 0x001FFFFF:
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
*/
if (0x1E == (i >> 3)) {
if (len < 4) return -1;
v = i & 0x07;
for (int j = 0; j < 3; j++) {
i = *buf++;
if (0x2 != (i >> 6)) return -1;
v = (v << 6) | (i & 0x3f);
}
return *ret = v, (const char*) buf - orig;
}
/* 0x00200000 - 0x03FFFFFF:
111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
*/
if (0x3E == (i >> 2)) {
if (len < 5) return -1;
v = i & 0x03;
for (int j = 0; j < 4; j++) {
i = *buf++;
if (0x2 != (i >> 6)) return -1;
v = (v << 6) | (i & 0x3f);
}
return *ret = v, (const char*) buf - orig;
}
/* 0x04000000 - 0x7FFFFFFF:
1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
*/
if (0x7e == (i >> 1)) {
if (len < 6) return -1;
v = i & 0x01;
for (int j = 0; j < 5; j++) {
i = *buf++;
if (0x2 != (i >> 6)) return -1;
v = (v << 6) | (i & 0x3f);
}
return *ret = v, (const char*) buf - orig;
}
return -1;
}
/**
* Convert a UCS char to utf8 code, and return it in buf.
* Return #bytes used in buf to encode the char, or
* -1 on error.
*/
int toml_ucs_to_utf8(int64_t code, char buf[6])
{
/* http://stackoverflow.com/questions/6240055/manually-converting-unicode-codepoints-into-utf-8-and-utf-16 */
/* The UCS code values 0xd800–0xdfff (UTF-16 surrogates) as well
* as 0xfffe and 0xffff (UCS noncharacters) should not appear in
* conforming UTF-8 streams.
*/
if (0xd800 <= code && code <= 0xdfff) return -1;
if (0xfffe <= code && code <= 0xffff) return -1;
/* 0x00000000 - 0x0000007F:
0xxxxxxx
*/
if (code < 0) return -1;
if (code <= 0x7F) {
buf[0] = (unsigned char) code;
return 1;
}
/* 0x00000080 - 0x000007FF:
110xxxxx 10xxxxxx
*/
if (code <= 0x000007FF) {
buf[0] = 0xc0 | (code >> 6);
buf[1] = 0x80 | (code & 0x3f);
return 2;
}
/* 0x00000800 - 0x0000FFFF:
1110xxxx 10xxxxxx 10xxxxxx
*/
if (code <= 0x0000FFFF) {
buf[0] = 0xe0 | (code >> 12);
buf[1] = 0x80 | ((code >> 6) & 0x3f);
buf[2] = 0x80 | (code & 0x3f);
return 3;
}
/* 0x00010000 - 0x001FFFFF:
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
*/
if (code <= 0x001FFFFF) {
buf[0] = 0xf0 | (code >> 18);
buf[1] = 0x80 | ((code >> 12) & 0x3f);
buf[2] = 0x80 | ((code >> 6) & 0x3f);
buf[3] = 0x80 | (code & 0x3f);
return 4;
}
/* 0x00200000 - 0x03FFFFFF:
111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
*/
if (code <= 0x03FFFFFF) {
buf[0] = 0xf8 | (code >> 24);
buf[1] = 0x80 | ((code >> 18) & 0x3f);
buf[2] = 0x80 | ((code >> 12) & 0x3f);
buf[3] = 0x80 | ((code >> 6) & 0x3f);
buf[4] = 0x80 | (code & 0x3f);
return 5;
}
/* 0x04000000 - 0x7FFFFFFF:
1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
*/
if (code <= 0x7FFFFFFF) {
buf[0] = 0xfc | (code >> 30);
buf[1] = 0x80 | ((code >> 24) & 0x3f);
buf[2] = 0x80 | ((code >> 18) & 0x3f);
buf[3] = 0x80 | ((code >> 12) & 0x3f);
buf[4] = 0x80 | ((code >> 6) & 0x3f);
buf[5] = 0x80 | (code & 0x3f);
return 6;
}
return -1;
}
/*
* TOML has 3 data structures: value, array, table.
* Each of them can have identification key.
*/
typedef struct toml_keyval_t toml_keyval_t;
struct toml_keyval_t {
const char* key; /* key to this value */
const char* val; /* the raw value */
};
struct toml_array_t {
const char* key; /* key to this array */
int kind; /* element kind: 'v'alue, 'a'rray, or 't'able */
int type; /* for value kind: 'i'nt, 'd'ouble, 'b'ool, 's'tring, 't'ime, 'D'ate, 'T'imestamp */
int nelem; /* number of elements */
union {
char** val;
toml_array_t** arr;
toml_table_t** tab;
} u;
};
struct toml_table_t {
const char* key; /* key to this table */
int implicit; /* table was created implicitly */
/* key-values in the table */
int nkval;
toml_keyval_t** kval;
/* arrays in the table */
int narr;
toml_array_t** arr;
/* tables in the table */
int ntab;
toml_table_t** tab;
};
static inline void xfree(const void* x) { if (x) FREE((void*)(intptr_t)x); }
enum tokentype_t {
INVALID,
DOT,
COMMA,
EQUAL,
LBRACE,
RBRACE,
NEWLINE,
LBRACKET,
RBRACKET,
STRING,
};
typedef enum tokentype_t tokentype_t;
typedef struct token_t token_t;
struct token_t {
tokentype_t tok;
int lineno;
char* ptr; /* points into context->start */
int len;
int eof;
};
typedef struct context_t context_t;
struct context_t {
char* start;
char* stop;
char* errbuf;
int errbufsz;
jmp_buf jmp;
token_t tok;
toml_table_t* root;
toml_table_t* curtab;
struct {
int top;
char* key[10];
token_t tok[10];
} tpath;
};
#define STRINGIFY(x) #x
#define TOSTRING(x) STRINGIFY(x)
#define FLINE __FILE__ ":" TOSTRING(__LINE__)
static tokentype_t next_token(context_t* ctx, int dotisspecial);
/* error routines. All these functions longjmp to ctx->jmp */
static int e_outofmemory(context_t* ctx, const char* fline)
{
snprintf(ctx->errbuf, ctx->errbufsz, "ERROR: out of memory (%s)", fline);
longjmp(ctx->jmp, 1);
return -1;
}
static int e_internal_error(context_t* ctx, const char* fline)
{
snprintf(ctx->errbuf, ctx->errbufsz, "internal error (%s)", fline);
longjmp(ctx->jmp, 1);
return -1;
}
static int e_syntax_error(context_t* ctx, int lineno, const char* msg)
{
snprintf(ctx->errbuf, ctx->errbufsz, "line %d: %s", lineno, msg);
longjmp(ctx->jmp, 1);
return -1;
}
static int e_bad_key_error(context_t* ctx, int lineno)
{
snprintf(ctx->errbuf, ctx->errbufsz, "line %d: bad key", lineno);
longjmp(ctx->jmp, 1);
return -1;
}
/*
static int e_noimpl(context_t* ctx, const char* feature)
{
snprintf(ctx->errbuf, ctx->errbufsz, "not implemented: %s", feature);
longjmp(ctx->jmp, 1);
return -1;
}
*/
static int e_key_exists_error(context_t* ctx, int lineno)
{
snprintf(ctx->errbuf, ctx->errbufsz,
"line %d: key exists", lineno);
longjmp(ctx->jmp, 1);
return -1;
}
static char* norm_lit_str(const char* src, int srclen,
int multiline,
char* errbuf, int errbufsz)
{
char* dst = 0; /* will write to dst[] and return it */
int max = 0; /* max size of dst[] */
int off = 0; /* cur offset in dst[] */
const char* sp = src;
const char* sq = src + srclen;
int ch;
/* scan forward on src */
for (;;) {
if (off >= max - 10) { /* have some slack for misc stuff */
char* x = REALLOC(dst, max += 50);
if (!x) {
xfree(dst);
snprintf(errbuf, errbufsz, "out of memory");
return 0;
}
dst = x;
}
/* finished? */
if (sp >= sq) break;
ch = *sp++;
/* control characters other than tab is not allowed */
if ((0 <= ch && ch <= 0x08)
|| (0x0a <= ch && ch <= 0x1f)
|| (ch == 0x7f)) {
if (! (multiline && (ch == '\r' || ch == '\n'))) {
xfree(dst);
snprintf(errbuf, errbufsz, "invalid char U+%04x", ch);
return 0;
}
}
// a plain copy suffice
dst[off++] = ch;
}
dst[off++] = 0;
return dst;
}
/*
* Convert src to raw unescaped utf-8 string.
* Returns NULL if error with errmsg in errbuf.
*/
static char* norm_basic_str(const char* src, int srclen,
int multiline,
char* errbuf, int errbufsz)
{
char* dst = 0; /* will write to dst[] and return it */
int max = 0; /* max size of dst[] */
int off = 0; /* cur offset in dst[] */
const char* sp = src;
const char* sq = src + srclen;
int ch;
/* scan forward on src */
for (;;) {
if (off >= max - 10) { /* have some slack for misc stuff */
char* x = REALLOC(dst, max += 50);
if (!x) {
xfree(dst);
snprintf(errbuf, errbufsz, "out of memory");
return 0;
}
dst = x;
}
/* finished? */
if (sp >= sq) break;
ch = *sp++;
if (ch != '\\') {
/* these chars must be escaped: U+0000 to U+0008, U+000A to U+001F, U+007F */
if ((0 <= ch && ch <= 0x08)
|| (0x0a <= ch && ch <= 0x1f)
|| (ch == 0x7f)) {
if (! (multiline && (ch == '\r' || ch == '\n'))) {
xfree(dst);
snprintf(errbuf, errbufsz, "invalid char U+%04x", ch);
return 0;
}
}
// a plain copy suffice
dst[off++] = ch;
continue;
}
/* ch was backslash. we expect the escape char. */
if (sp >= sq) {
snprintf(errbuf, errbufsz, "last backslash is invalid");
xfree(dst);
return 0;
}
/* for multi-line, we want to kill line-ending-backslash ... */
if (multiline) {
// if there is only whitespace after the backslash ...
if (sp[strspn(sp, " \t\r")] == '\n') {
/* skip all the following whitespaces */
sp += strspn(sp, " \t\r\n");
continue;
}
}
/* get the escaped char */
ch = *sp++;
switch (ch) {
case 'u': case 'U':
{
int64_t ucs = 0;
int nhex = (ch == 'u' ? 4 : 8);
for (int i = 0; i < nhex; i++) {
if (sp >= sq) {
snprintf(errbuf, errbufsz, "\\%c expects %d hex chars", ch, nhex);
xfree(dst);
return 0;
}
ch = *sp++;
int v = ('0' <= ch && ch <= '9')
? ch - '0'
: (('A' <= ch && ch <= 'F') ? ch - 'A' + 10 : -1);
if (-1 == v) {
snprintf(errbuf, errbufsz, "invalid hex chars for \\u or \\U");
xfree(dst);
return 0;
}
ucs = ucs * 16 + v;
}
int n = toml_ucs_to_utf8(ucs, &dst[off]);
if (-1 == n) {
snprintf(errbuf, errbufsz, "illegal ucs code in \\u or \\U");
xfree(dst);
return 0;
}
off += n;
}
continue;
case 'b': ch = '\b'; break;
case 't': ch = '\t'; break;
case 'n': ch = '\n'; break;
case 'f': ch = '\f'; break;
case 'r': ch = '\r'; break;
case '"': ch = '"'; break;
case '\\': ch = '\\'; break;
default:
snprintf(errbuf, errbufsz, "illegal escape char \\%c", ch);
xfree(dst);
return 0;
}
dst[off++] = ch;
}
// Cap with NUL and return it.
dst[off++] = 0;
return dst;
}
/* Normalize a key. Convert all special chars to raw unescaped utf-8 chars. */
static char* normalize_key(context_t* ctx, token_t strtok)
{
const char* sp = strtok.ptr;
const char* sq = strtok.ptr + strtok.len;
int lineno = strtok.lineno;
char* ret;
int ch = *sp;
char ebuf[80];
/* handle quoted string */
if (ch == '\'' || ch == '\"') {
/* if ''' or """, take 3 chars off front and back. Else, take 1 char off. */
int multiline = 0;
if (sp[1] == ch && sp[2] == ch) {
sp += 3, sq -= 3;
multiline = 1;
}
else
sp++, sq--;
if (ch == '\'') {
/* for single quote, take it verbatim. */
if (! (ret = STRNDUP(sp, sq - sp))) {
e_outofmemory(ctx, FLINE);
return 0; /* not reached */
}
} else {
/* for double quote, we need to normalize */
ret = norm_basic_str(sp, sq - sp, multiline, ebuf, sizeof(ebuf));
if (!ret) {
snprintf(ctx->errbuf, ctx->errbufsz, "line %d: %s", lineno, ebuf);
longjmp(ctx->jmp, 1);
}
}
/* newlines are not allowed in keys */
if (strchr(ret, '\n')) {
xfree(ret);
e_bad_key_error(ctx, lineno);
return 0; /* not reached */
}
return ret;
}
/* for bare-key allow only this regex: [A-Za-z0-9_-]+ */
const char* xp;
for (xp = sp; xp != sq; xp++) {
int k = *xp;
if (isalnum(k)) continue;
if (k == '_' || k == '-') continue;
e_bad_key_error(ctx, lineno);
return 0; /* not reached */
}
/* dup and return it */
if (! (ret = STRNDUP(sp, sq - sp))) {
e_outofmemory(ctx, FLINE);
return 0; /* not reached */
}
return ret;
}
/*
* Look up key in tab. Return 0 if not found, or
* 'v'alue, 'a'rray or 't'able depending on the element.
*/
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)
{
int i;
void* dummy;
if (!ret_tab) ret_tab = (toml_table_t**) &dummy;
if (!ret_arr) ret_arr = (toml_array_t**) &dummy;
if (!ret_val) ret_val = (toml_keyval_t**) &dummy;
*ret_tab = 0; *ret_arr = 0; *ret_val = 0;
for (i = 0; i < tab->nkval; i++) {
if (0 == strcmp(key, tab->kval[i]->key)) {
*ret_val = tab->kval[i];
return 'v';
}
}
for (i = 0; i < tab->narr; i++) {
if (0 == strcmp(key, tab->arr[i]->key)) {
*ret_arr = tab->arr[i];
return 'a';
}
}
for (i = 0; i < tab->ntab; i++) {
if (0 == strcmp(key, tab->tab[i]->key)) {
*ret_tab = tab->tab[i];
return 't';
}
}
return 0;
}
static int key_kind(toml_table_t* tab, const char* key)
{
return check_key(tab, key, 0, 0, 0);
}
/* Create a keyval in the table.
*/
static toml_keyval_t* create_keyval_in_table(context_t* ctx, toml_table_t* tab, token_t keytok)
{
/* first, normalize the key to be used for lookup.
* remember to free it if we error out.
*/
char* newkey = normalize_key(ctx, keytok);
/* if key exists: error out. */
toml_keyval_t* dest = 0;
if (key_kind(tab, newkey)) {
xfree(newkey);
e_key_exists_error(ctx, keytok.lineno);
return 0; /* not reached */
}
/* make a new entry */
int n = tab->nkval;
toml_keyval_t** base;
if (0 == (base = (toml_keyval_t**) REALLOC(tab->kval, (n+1) * sizeof(*base)))) {
xfree(newkey);
e_outofmemory(ctx, FLINE);
return 0; /* not reached */
}
tab->kval = base;
if (0 == (base[n] = (toml_keyval_t*) CALLOC(1, sizeof(*base[n])))) {
xfree(newkey);
e_outofmemory(ctx, FLINE);
return 0; /* not reached */
}
dest = tab->kval[tab->nkval++];
/* save the key in the new value struct */
dest->key = newkey;
return dest;
}
/* Create a table in the table.
*/
static toml_table_t* create_keytable_in_table(context_t* ctx, toml_table_t* tab, token_t keytok)
{
/* first, normalize the key to be used for lookup.
* remember to free it if we error out.
*/
char* newkey = normalize_key(ctx, keytok);
/* if key exists: error out */
toml_table_t* dest = 0;
if (check_key(tab, newkey, 0, 0, &dest)) {
xfree(newkey); /* don't need this anymore */
/* special case: if table exists, but was created implicitly ... */
if (dest && dest->implicit) {
/* we make it explicit now, and simply return it. */
dest->implicit = 0;
return dest;
}
e_key_exists_error(ctx, keytok.lineno);
return 0; /* not reached */
}
/* create a new table entry */
int n = tab->ntab;
toml_table_t** base;
if (0 == (base = (toml_table_t**) REALLOC(tab->tab, (n+1) * sizeof(*base)))) {
xfree(newkey);
e_outofmemory(ctx, FLINE);
return 0; /* not reached */
}
tab->tab = base;
if (0 == (base[n] = (toml_table_t*) CALLOC(1, sizeof(*base[n])))) {
xfree(newkey);
e_outofmemory(ctx, FLINE);
return 0; /* not reached */
}
dest = tab->tab[tab->ntab++];
/* save the key in the new table struct */
dest->key = newkey;
return dest;
}
/* Create an array in the table.
*/
static toml_array_t* create_keyarray_in_table(context_t* ctx,
toml_table_t* tab,
token_t keytok,
char kind)
{
/* first, normalize the key to be used for lookup.
* remember to free it if we error out.
*/
char* newkey = normalize_key(ctx, keytok);
/* if key exists: error out */
if (key_kind(tab, newkey)) {
xfree(newkey); /* don't need this anymore */
e_key_exists_error(ctx, keytok.lineno);
return 0; /* not reached */
}
/* make a new array entry */
int n = tab->narr;
toml_array_t** base;
if (0 == (base = (toml_array_t**) REALLOC(tab->arr, (n+1) * sizeof(*base)))) {
xfree(newkey);
e_outofmemory(ctx, FLINE);
return 0; /* not reached */
}
tab->arr = base;
if (0 == (base[n] = (toml_array_t*) CALLOC(1, sizeof(*base[n])))) {
xfree(newkey);
e_outofmemory(ctx, FLINE);
return 0; /* not reached */
}
toml_array_t* dest = tab->arr[tab->narr++];
/* save the key in the new array struct */
dest->key = newkey;
dest->kind = kind;
return dest;
}
/* Create an array in an array
*/
static toml_array_t* create_array_in_array(context_t* ctx,
toml_array_t* parent)
{
int n = parent->nelem;
toml_array_t** base;
if (0 == (base = (toml_array_t**) REALLOC(parent->u.arr, (n+1) * sizeof(*base)))) {
e_outofmemory(ctx, FLINE);
return 0; /* not reached */
}
parent->u.arr = base;
if (0 == (base[n] = (toml_array_t*) CALLOC(1, sizeof(*base[n])))) {
e_outofmemory(ctx, FLINE);
return 0; /* not reached */
}
return parent->u.arr[parent->nelem++];
}
/* Create a table in an array
*/
static toml_table_t* create_table_in_array(context_t* ctx,
toml_array_t* parent)
{
int n = parent->nelem;
toml_table_t** base;
if (0 == (base = (toml_table_t**) REALLOC(parent->u.tab, (n+1) * sizeof(*base)))) {
e_outofmemory(ctx, FLINE);
return 0; /* not reached */
}
parent->u.tab = base;
if (0 == (base[n] = (toml_table_t*) CALLOC(1, sizeof(*base[n])))) {
e_outofmemory(ctx, FLINE);
return 0; /* not reached */
}
return parent->u.tab[parent->nelem++];
}
static void skip_newlines(context_t* ctx, int isdotspecial)
{
while (ctx->tok.tok == NEWLINE) {
next_token(ctx, isdotspecial);
if (ctx->tok.eof) break;
}
}
#define EAT_TOKEN(ctx, typ, isdotspecial) \
if ((ctx)->tok.tok != typ) e_internal_error(ctx, FLINE); else next_token(ctx, isdotspecial)
static void parse_keyval(context_t* ctx, toml_table_t* tab);
/* We are at '{ ... }'.
* Parse the table.
*/
static void parse_table(context_t* ctx, toml_table_t* tab)
{
EAT_TOKEN(ctx, LBRACE, 1);
for (;;) {
if (ctx->tok.tok == NEWLINE) {
e_syntax_error(ctx, ctx->tok.lineno, "newline not allowed in inline table");
return; /* not reached */
}
/* until } */
if (ctx->tok.tok == RBRACE) break;
if (ctx->tok.tok != STRING) {
e_syntax_error(ctx, ctx->tok.lineno, "syntax error");
return; /* not reached */
}
parse_keyval(ctx, tab);
if (ctx->tok.tok == NEWLINE) {
e_syntax_error(ctx, ctx->tok.lineno, "newline not allowed in inline table");
return; /* not reached */
}
/* on comma, continue to scan for next keyval */
if (ctx->tok.tok == COMMA) {
EAT_TOKEN(ctx, COMMA, 1);
continue;
}
break;
}
if (ctx->tok.tok != RBRACE) {
e_syntax_error(ctx, ctx->tok.lineno, "syntax error");
return; /* not reached */
}
EAT_TOKEN(ctx, RBRACE, 1);
}
static int valtype(const char* val)
{
toml_timestamp_t ts;
if (*val == '\'' || *val == '"') return 's';
if (0 == toml_rtob(val, 0)) return 'b';
if (0 == toml_rtoi(val, 0)) return 'i';
if (0 == toml_rtod(val, 0)) return 'd';
if (0 == toml_rtots(val, &ts)) {
if (ts.year && ts.hour) return 'T'; /* timestamp */
if (ts.year) return 'D'; /* date */
return 't'; /* time */
}
return 'u'; /* unknown */
}
/* We are at '[...]' */
static void parse_array(context_t* ctx, toml_array_t* arr)
{
EAT_TOKEN(ctx, LBRACKET, 0);
for (;;) {
skip_newlines(ctx, 0);
/* until ] */
if (ctx->tok.tok == RBRACKET) break;
switch (ctx->tok.tok) {
case STRING:
{
char* val = ctx->tok.ptr;
int vlen = ctx->tok.len;
/* set array kind if this will be the first entry */
if (arr->kind == 0) arr->kind = 'v';
/* check array kind */
if (arr->kind != 'v') {
e_syntax_error(ctx, ctx->tok.lineno,
"a string array can only contain strings");
return; /* not reached */
}
/* make a new value in array */
char** tmp = (char**) REALLOC(arr->u.val, (arr->nelem+1) * sizeof(*tmp));
if (!tmp) {
e_outofmemory(ctx, FLINE);
return; /* not reached */
}
arr->u.val = tmp;
if (! (val = STRNDUP(val, vlen))) {
e_outofmemory(ctx, FLINE);
return; /* not reached */
}
arr->u.val[arr->nelem++] = val;
/* set array type if this is the first entry, or check that the types matched. */
if (arr->nelem == 1)
arr->type = valtype(arr->u.val[0]);
else if (arr->type != valtype(val)) {
e_syntax_error(ctx, ctx->tok.lineno,
"array type mismatch while processing array of values");
return; /* not reached */
}
EAT_TOKEN(ctx, STRING, 0);
break;
}
case LBRACKET:
{ /* [ [array], [array] ... ] */
/* set the array kind if this will be the first entry */
if (arr->kind == 0) arr->kind = 'a';
/* check array kind */
if (arr->kind != 'a') {
e_syntax_error(ctx, ctx->tok.lineno,
"array type mismatch while processing array of arrays");
return; /* not reached */
}
parse_array(ctx, create_array_in_array(ctx, arr));
break;
}
case LBRACE:
{ /* [ {table}, {table} ... ] */
/* set the array kind if this will be the first entry */
if (arr->kind == 0) arr->kind = 't';
/* check array kind */
if (arr->kind != 't') {
e_syntax_error(ctx, ctx->tok.lineno,
"array type mismatch while processing array of tables");
return; /* not reached */
}
parse_table(ctx, create_table_in_array(ctx, arr));
break;
}
default:
e_syntax_error(ctx, ctx->tok.lineno, "syntax error");
return; /* not reached */
}
skip_newlines(ctx, 0);
/* on comma, continue to scan for next element */
if (ctx->tok.tok == COMMA) {
EAT_TOKEN(ctx, COMMA, 0);
continue;
}
break;
}
if (ctx->tok.tok != RBRACKET) {
e_syntax_error(ctx, ctx->tok.lineno, "expect a right bracket");
return; /* not reached */
}
EAT_TOKEN(ctx, RBRACKET, 1);
}
/* handle lines like these:
key = "value"
key = [ array ]
key = { table }
*/
static void parse_keyval(context_t* ctx, toml_table_t* tab)
{
token_t key = ctx->tok;
EAT_TOKEN(ctx, STRING, 1);
if (ctx->tok.tok == DOT) {
/* handle inline dotted key.
e.g.
physical.color = "orange"
physical.shape = "round"
*/
toml_table_t* subtab = 0;
{
char* subtabstr = normalize_key(ctx, key);
subtab = toml_table_in(tab, subtabstr);
xfree(subtabstr);
}
if (!subtab) {
subtab = create_keytable_in_table(ctx, tab, key);
}
next_token(ctx, 1);
parse_keyval(ctx, subtab);
return;
}
if (ctx->tok.tok != EQUAL) {
e_syntax_error(ctx, ctx->tok.lineno, "missing =");
return; /* not reached */
}
next_token(ctx, 0);
switch (ctx->tok.tok) {
case STRING:
{ /* key = "value" */
toml_keyval_t* keyval = create_keyval_in_table(ctx, tab, key);
token_t val = ctx->tok;
assert(keyval->val == 0);
keyval->val = STRNDUP(val.ptr, val.len);
if (! keyval->val) {
e_outofmemory(ctx, FLINE);
return; /* not reached */
}
next_token(ctx, 1);
return;
}
case LBRACKET:
{ /* key = [ array ] */
toml_array_t* arr = create_keyarray_in_table(ctx, tab, key, 0);
parse_array(ctx, arr);
return;
}
case LBRACE: