Fixes as suggested by mytchel
1. A critical bug in toml_parse_file when the file is empty 2. Add toml_cat.c to makefile 3. Other misc fixes related to error handling
This commit is contained in:
parent
e6a6db8bda
commit
a0b45225e2
3 changed files with 213 additions and 98 deletions
4
Makefile
4
Makefile
|
@ -5,7 +5,7 @@ CFLAGS = -std=c99 -Wall -Wextra
|
||||||
CFLAGS += -O2 -DNDEBUG
|
CFLAGS += -O2 -DNDEBUG
|
||||||
#CFLAGS += -O0 -g
|
#CFLAGS += -O0 -g
|
||||||
|
|
||||||
EXEC = toml_json
|
EXEC = toml_json toml_cat
|
||||||
|
|
||||||
LIB = libtoml.a
|
LIB = libtoml.a
|
||||||
|
|
||||||
|
@ -17,6 +17,8 @@ libtoml.a: toml.o
|
||||||
|
|
||||||
toml_json: toml_json.c $(LIB)
|
toml_json: toml_json.c $(LIB)
|
||||||
|
|
||||||
|
toml_cat: toml_cat.c $(LIB)
|
||||||
|
|
||||||
prefix ?= /usr/local
|
prefix ?= /usr/local
|
||||||
|
|
||||||
install: all
|
install: all
|
||||||
|
|
299
toml.c
299
toml.c
|
@ -311,24 +311,33 @@ struct context_t {
|
||||||
#define STRINGIFY(x) #x
|
#define STRINGIFY(x) #x
|
||||||
#define TOSTRING(x) STRINGIFY(x)
|
#define TOSTRING(x) STRINGIFY(x)
|
||||||
#define FLINE __FILE__ ":" TOSTRING(__LINE__)
|
#define FLINE __FILE__ ":" TOSTRING(__LINE__)
|
||||||
static int outofmemory(context_t* ctx, const char* fline);
|
|
||||||
static tokentype_t next_token(context_t* ctx, int dotisspecial);
|
static tokentype_t next_token(context_t* ctx, int dotisspecial);
|
||||||
|
|
||||||
static int internal_error(context_t* ctx, const char* fline)
|
/* 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);
|
snprintf(ctx->errbuf, ctx->errbufsz, "internal error (%s)", fline);
|
||||||
longjmp(ctx->jmp, 1);
|
longjmp(ctx->jmp, 1);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int syntax_error(context_t* ctx, int lineno, const char* msg)
|
static int e_syntax_error(context_t* ctx, int lineno, const char* msg)
|
||||||
{
|
{
|
||||||
snprintf(ctx->errbuf, ctx->errbufsz, "line %d: %s", lineno, msg);
|
snprintf(ctx->errbuf, ctx->errbufsz, "line %d: %s", lineno, msg);
|
||||||
longjmp(ctx->jmp, 1);
|
longjmp(ctx->jmp, 1);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int bad_key_error(context_t* ctx, int lineno)
|
static int e_bad_key_error(context_t* ctx, int lineno)
|
||||||
{
|
{
|
||||||
snprintf(ctx->errbuf, ctx->errbufsz, "line %d: bad key", lineno);
|
snprintf(ctx->errbuf, ctx->errbufsz, "line %d: bad key", lineno);
|
||||||
longjmp(ctx->jmp, 1);
|
longjmp(ctx->jmp, 1);
|
||||||
|
@ -336,7 +345,7 @@ static int bad_key_error(context_t* ctx, int lineno)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
static int noimpl(context_t* ctx, const char* feature)
|
static int e_noimpl(context_t* ctx, const char* feature)
|
||||||
{
|
{
|
||||||
snprintf(ctx->errbuf, ctx->errbufsz, "not implemented: %s", feature);
|
snprintf(ctx->errbuf, ctx->errbufsz, "not implemented: %s", feature);
|
||||||
longjmp(ctx->jmp, 1);
|
longjmp(ctx->jmp, 1);
|
||||||
|
@ -344,7 +353,7 @@ static int noimpl(context_t* ctx, const char* feature)
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int key_exists_error(context_t* ctx, token_t keytok)
|
static int e_key_exists_error(context_t* ctx, token_t keytok)
|
||||||
{
|
{
|
||||||
char buf[100];
|
char buf[100];
|
||||||
int i;
|
int i;
|
||||||
|
@ -491,7 +500,10 @@ static char* normalize_key(context_t* ctx, token_t strtok)
|
||||||
|
|
||||||
if (ch == '\'') {
|
if (ch == '\'') {
|
||||||
/* for single quote, take it verbatim. */
|
/* for single quote, take it verbatim. */
|
||||||
if (! (ret = strndup(sp, sq - sp))) outofmemory(ctx, FLINE);
|
if (! (ret = strndup(sp, sq - sp))) {
|
||||||
|
e_outofmemory(ctx, FLINE);
|
||||||
|
return 0; /* not reached */
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
/* for double quote, we need to normalize */
|
/* for double quote, we need to normalize */
|
||||||
ret = normalize_string(sp, sq - sp, 0, ebuf, sizeof(ebuf));
|
ret = normalize_string(sp, sq - sp, 0, ebuf, sizeof(ebuf));
|
||||||
|
@ -504,7 +516,8 @@ static char* normalize_key(context_t* ctx, token_t strtok)
|
||||||
/* newlines are not allowed in keys */
|
/* newlines are not allowed in keys */
|
||||||
if (strchr(ret, '\n')) {
|
if (strchr(ret, '\n')) {
|
||||||
free(ret);
|
free(ret);
|
||||||
bad_key_error(ctx, lineno);
|
e_bad_key_error(ctx, lineno);
|
||||||
|
return 0; /* not reached */
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -515,11 +528,15 @@ static char* normalize_key(context_t* ctx, token_t strtok)
|
||||||
int k = *xp;
|
int k = *xp;
|
||||||
if (isalnum(k)) continue;
|
if (isalnum(k)) continue;
|
||||||
if (k == '_' || k == '-') continue;
|
if (k == '_' || k == '-') continue;
|
||||||
bad_key_error(ctx, lineno);
|
e_bad_key_error(ctx, lineno);
|
||||||
|
return 0; /* not reached */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* dup and return it */
|
/* dup and return it */
|
||||||
if (! (ret = strndup(sp, sq - sp))) outofmemory(ctx, FLINE);
|
if (! (ret = strndup(sp, sq - sp))) {
|
||||||
|
e_outofmemory(ctx, FLINE);
|
||||||
|
return 0; /* not reached */
|
||||||
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -576,7 +593,8 @@ static toml_keyval_t* create_keyval_in_table(context_t* ctx, toml_table_t* tab,
|
||||||
toml_keyval_t* dest = 0;
|
toml_keyval_t* dest = 0;
|
||||||
if (check_key(tab, newkey, 0, 0, 0)) {
|
if (check_key(tab, newkey, 0, 0, 0)) {
|
||||||
free(newkey);
|
free(newkey);
|
||||||
key_exists_error(ctx, keytok);
|
e_key_exists_error(ctx, keytok);
|
||||||
|
return 0; /* not reached */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* make a new entry */
|
/* make a new entry */
|
||||||
|
@ -584,13 +602,15 @@ static toml_keyval_t* create_keyval_in_table(context_t* ctx, toml_table_t* tab,
|
||||||
toml_keyval_t** base;
|
toml_keyval_t** base;
|
||||||
if (0 == (base = (toml_keyval_t**) realloc(tab->kval, (n+1) * sizeof(*base)))) {
|
if (0 == (base = (toml_keyval_t**) realloc(tab->kval, (n+1) * sizeof(*base)))) {
|
||||||
free(newkey);
|
free(newkey);
|
||||||
outofmemory(ctx, FLINE);
|
e_outofmemory(ctx, FLINE);
|
||||||
|
return 0; /* not reached */
|
||||||
}
|
}
|
||||||
tab->kval = base;
|
tab->kval = base;
|
||||||
|
|
||||||
if (0 == (base[n] = (toml_keyval_t*) calloc(1, sizeof(*base[n])))) {
|
if (0 == (base[n] = (toml_keyval_t*) calloc(1, sizeof(*base[n])))) {
|
||||||
free(newkey);
|
free(newkey);
|
||||||
outofmemory(ctx, FLINE);
|
e_outofmemory(ctx, FLINE);
|
||||||
|
return 0; /* not reached */
|
||||||
}
|
}
|
||||||
dest = tab->kval[tab->nkval++];
|
dest = tab->kval[tab->nkval++];
|
||||||
|
|
||||||
|
@ -620,7 +640,8 @@ static toml_table_t* create_keytable_in_table(context_t* ctx, toml_table_t* tab,
|
||||||
dest->implicit = 0;
|
dest->implicit = 0;
|
||||||
return dest;
|
return dest;
|
||||||
}
|
}
|
||||||
key_exists_error(ctx, keytok);
|
e_key_exists_error(ctx, keytok);
|
||||||
|
return 0; /* not reached */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* create a new table entry */
|
/* create a new table entry */
|
||||||
|
@ -628,13 +649,15 @@ static toml_table_t* create_keytable_in_table(context_t* ctx, toml_table_t* tab,
|
||||||
toml_table_t** base;
|
toml_table_t** base;
|
||||||
if (0 == (base = (toml_table_t**) realloc(tab->tab, (n+1) * sizeof(*base)))) {
|
if (0 == (base = (toml_table_t**) realloc(tab->tab, (n+1) * sizeof(*base)))) {
|
||||||
free(newkey);
|
free(newkey);
|
||||||
outofmemory(ctx, FLINE);
|
e_outofmemory(ctx, FLINE);
|
||||||
|
return 0; /* not reached */
|
||||||
}
|
}
|
||||||
tab->tab = base;
|
tab->tab = base;
|
||||||
|
|
||||||
if (0 == (base[n] = (toml_table_t*) calloc(1, sizeof(*base[n])))) {
|
if (0 == (base[n] = (toml_table_t*) calloc(1, sizeof(*base[n])))) {
|
||||||
free(newkey);
|
free(newkey);
|
||||||
outofmemory(ctx, FLINE);
|
e_outofmemory(ctx, FLINE);
|
||||||
|
return 0; /* not reached */
|
||||||
}
|
}
|
||||||
dest = tab->tab[tab->ntab++];
|
dest = tab->tab[tab->ntab++];
|
||||||
|
|
||||||
|
@ -664,7 +687,8 @@ static toml_array_t* create_keyarray_in_table(context_t* ctx,
|
||||||
/* special case skip if exists? */
|
/* special case skip if exists? */
|
||||||
if (skip_if_exist) return dest;
|
if (skip_if_exist) return dest;
|
||||||
|
|
||||||
key_exists_error(ctx, keytok);
|
e_key_exists_error(ctx, keytok);
|
||||||
|
return 0; /* not reached */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* make a new array entry */
|
/* make a new array entry */
|
||||||
|
@ -672,13 +696,15 @@ static toml_array_t* create_keyarray_in_table(context_t* ctx,
|
||||||
toml_array_t** base;
|
toml_array_t** base;
|
||||||
if (0 == (base = (toml_array_t**) realloc(tab->arr, (n+1) * sizeof(*base)))) {
|
if (0 == (base = (toml_array_t**) realloc(tab->arr, (n+1) * sizeof(*base)))) {
|
||||||
free(newkey);
|
free(newkey);
|
||||||
outofmemory(ctx, FLINE);
|
e_outofmemory(ctx, FLINE);
|
||||||
|
return 0; /* not reached */
|
||||||
}
|
}
|
||||||
tab->arr = base;
|
tab->arr = base;
|
||||||
|
|
||||||
if (0 == (base[n] = (toml_array_t*) calloc(1, sizeof(*base[n])))) {
|
if (0 == (base[n] = (toml_array_t*) calloc(1, sizeof(*base[n])))) {
|
||||||
free(newkey);
|
free(newkey);
|
||||||
outofmemory(ctx, FLINE);
|
e_outofmemory(ctx, FLINE);
|
||||||
|
return 0; /* not reached */
|
||||||
}
|
}
|
||||||
dest = tab->arr[tab->narr++];
|
dest = tab->arr[tab->narr++];
|
||||||
|
|
||||||
|
@ -695,12 +721,14 @@ static toml_array_t* create_array_in_array(context_t* ctx,
|
||||||
int n = parent->nelem;
|
int n = parent->nelem;
|
||||||
toml_array_t** base;
|
toml_array_t** base;
|
||||||
if (0 == (base = (toml_array_t**) realloc(parent->u.arr, (n+1) * sizeof(*base)))) {
|
if (0 == (base = (toml_array_t**) realloc(parent->u.arr, (n+1) * sizeof(*base)))) {
|
||||||
outofmemory(ctx, FLINE);
|
e_outofmemory(ctx, FLINE);
|
||||||
|
return 0; /* not reached */
|
||||||
}
|
}
|
||||||
parent->u.arr = base;
|
parent->u.arr = base;
|
||||||
|
|
||||||
if (0 == (base[n] = (toml_array_t*) calloc(1, sizeof(*base[n])))) {
|
if (0 == (base[n] = (toml_array_t*) calloc(1, sizeof(*base[n])))) {
|
||||||
outofmemory(ctx, FLINE);
|
e_outofmemory(ctx, FLINE);
|
||||||
|
return 0; /* not reached */
|
||||||
}
|
}
|
||||||
|
|
||||||
return parent->u.arr[parent->nelem++];
|
return parent->u.arr[parent->nelem++];
|
||||||
|
@ -714,12 +742,14 @@ static toml_table_t* create_table_in_array(context_t* ctx,
|
||||||
int n = parent->nelem;
|
int n = parent->nelem;
|
||||||
toml_table_t** base;
|
toml_table_t** base;
|
||||||
if (0 == (base = (toml_table_t**) realloc(parent->u.tab, (n+1) * sizeof(*base)))) {
|
if (0 == (base = (toml_table_t**) realloc(parent->u.tab, (n+1) * sizeof(*base)))) {
|
||||||
outofmemory(ctx, FLINE);
|
e_outofmemory(ctx, FLINE);
|
||||||
|
return 0; /* not reached */
|
||||||
}
|
}
|
||||||
parent->u.tab = base;
|
parent->u.tab = base;
|
||||||
|
|
||||||
if (0 == (base[n] = (toml_table_t*) calloc(1, sizeof(*base[n])))) {
|
if (0 == (base[n] = (toml_table_t*) calloc(1, sizeof(*base[n])))) {
|
||||||
outofmemory(ctx, FLINE);
|
e_outofmemory(ctx, FLINE);
|
||||||
|
return 0; /* not reached */
|
||||||
}
|
}
|
||||||
|
|
||||||
return parent->u.tab[parent->nelem++];
|
return parent->u.tab[parent->nelem++];
|
||||||
|
@ -728,7 +758,7 @@ static toml_table_t* create_table_in_array(context_t* ctx,
|
||||||
|
|
||||||
#define SKIP_NEWLINES(ctx) while (ctx->tok.tok == NEWLINE) next_token(ctx, 0)
|
#define SKIP_NEWLINES(ctx) while (ctx->tok.tok == NEWLINE) next_token(ctx, 0)
|
||||||
#define EAT_TOKEN(ctx, typ) \
|
#define EAT_TOKEN(ctx, typ) \
|
||||||
if ((ctx)->tok.tok != typ) internal_error(ctx, FLINE); else next_token(ctx, 0)
|
if ((ctx)->tok.tok != typ) e_internal_error(ctx, FLINE); else next_token(ctx, 0)
|
||||||
|
|
||||||
|
|
||||||
static void parse_keyval(context_t* ctx, toml_table_t* tab);
|
static void parse_keyval(context_t* ctx, toml_table_t* tab);
|
||||||
|
@ -747,11 +777,11 @@ static void parse_table(context_t* ctx, toml_table_t* tab)
|
||||||
/* until } */
|
/* until } */
|
||||||
if (ctx->tok.tok == RBRACE) break;
|
if (ctx->tok.tok == RBRACE) break;
|
||||||
|
|
||||||
switch (ctx->tok.tok) {
|
if (ctx->tok.tok != STRING) {
|
||||||
case STRING: parse_keyval(ctx, tab); break;
|
e_syntax_error(ctx, ctx->tok.lineno, "syntax error");
|
||||||
default: syntax_error(ctx, ctx->tok.lineno, "syntax error");
|
return; /* not reached */
|
||||||
}
|
}
|
||||||
|
parse_keyval(ctx, tab);
|
||||||
SKIP_NEWLINES(ctx);
|
SKIP_NEWLINES(ctx);
|
||||||
|
|
||||||
/* on comma, continue to scan for next keyval */
|
/* on comma, continue to scan for next keyval */
|
||||||
|
@ -762,8 +792,10 @@ static void parse_table(context_t* ctx, toml_table_t* tab)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ctx->tok.tok != RBRACE)
|
if (ctx->tok.tok != RBRACE) {
|
||||||
syntax_error(ctx, ctx->tok.lineno, "syntax error");
|
e_syntax_error(ctx, ctx->tok.lineno, "syntax error");
|
||||||
|
return; /* not reached */
|
||||||
|
}
|
||||||
|
|
||||||
EAT_TOKEN(ctx, RBRACE);
|
EAT_TOKEN(ctx, RBRACE);
|
||||||
}
|
}
|
||||||
|
@ -805,22 +837,31 @@ static void parse_array(context_t* ctx, toml_array_t* arr)
|
||||||
if (arr->kind == 0) arr->kind = 'v';
|
if (arr->kind == 0) arr->kind = 'v';
|
||||||
/* check array kind */
|
/* check array kind */
|
||||||
if (arr->kind != 'v') {
|
if (arr->kind != 'v') {
|
||||||
syntax_error(ctx, ctx->tok.lineno,
|
e_syntax_error(ctx, ctx->tok.lineno,
|
||||||
"a string array can only contain strings");
|
"a string array can only contain strings");
|
||||||
|
return; /* not reached */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* make a new value in array */
|
/* make a new value in array */
|
||||||
char** tmp = (char**) realloc(arr->u.val, (arr->nelem+1) * sizeof(*tmp));
|
char** tmp = (char**) realloc(arr->u.val, (arr->nelem+1) * sizeof(*tmp));
|
||||||
if (!tmp) outofmemory(ctx, FLINE);
|
if (!tmp) {
|
||||||
|
e_outofmemory(ctx, FLINE);
|
||||||
|
return; /* not reached */
|
||||||
|
}
|
||||||
arr->u.val = tmp;
|
arr->u.val = tmp;
|
||||||
if (! (val = strndup(val, vlen))) outofmemory(ctx, FLINE);
|
if (! (val = strndup(val, vlen))) {
|
||||||
|
e_outofmemory(ctx, FLINE);
|
||||||
|
return; /* not reached */
|
||||||
|
}
|
||||||
arr->u.val[arr->nelem++] = val;
|
arr->u.val[arr->nelem++] = val;
|
||||||
|
|
||||||
/* set array type if this is the first entry, or check that the types matched. */
|
/* set array type if this is the first entry, or check that the types matched. */
|
||||||
if (arr->nelem == 1)
|
if (arr->nelem == 1)
|
||||||
arr->type = valtype(arr->u.val[0]);
|
arr->type = valtype(arr->u.val[0]);
|
||||||
else if (arr->type != valtype(val))
|
else if (arr->type != valtype(val)) {
|
||||||
syntax_error(ctx, ctx->tok.lineno, "array type mismatch");
|
e_syntax_error(ctx, ctx->tok.lineno, "array type mismatch");
|
||||||
|
return; /* not reached */
|
||||||
|
}
|
||||||
|
|
||||||
EAT_TOKEN(ctx, STRING);
|
EAT_TOKEN(ctx, STRING);
|
||||||
break;
|
break;
|
||||||
|
@ -832,7 +873,8 @@ static void parse_array(context_t* ctx, toml_array_t* arr)
|
||||||
if (arr->kind == 0) arr->kind = 'a';
|
if (arr->kind == 0) arr->kind = 'a';
|
||||||
/* check array kind */
|
/* check array kind */
|
||||||
if (arr->kind != 'a') {
|
if (arr->kind != 'a') {
|
||||||
syntax_error(ctx, ctx->tok.lineno, "array type mismatch");
|
e_syntax_error(ctx, ctx->tok.lineno, "array type mismatch");
|
||||||
|
return; /* not reached */
|
||||||
}
|
}
|
||||||
parse_array(ctx, create_array_in_array(ctx, arr));
|
parse_array(ctx, create_array_in_array(ctx, arr));
|
||||||
break;
|
break;
|
||||||
|
@ -844,14 +886,16 @@ static void parse_array(context_t* ctx, toml_array_t* arr)
|
||||||
if (arr->kind == 0) arr->kind = 't';
|
if (arr->kind == 0) arr->kind = 't';
|
||||||
/* check array kind */
|
/* check array kind */
|
||||||
if (arr->kind != 't') {
|
if (arr->kind != 't') {
|
||||||
syntax_error(ctx, ctx->tok.lineno, "array type mismatch");
|
e_syntax_error(ctx, ctx->tok.lineno, "array type mismatch");
|
||||||
|
return; /* not reached */
|
||||||
}
|
}
|
||||||
parse_table(ctx, create_table_in_array(ctx, arr));
|
parse_table(ctx, create_table_in_array(ctx, arr));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
syntax_error(ctx, ctx->tok.lineno, "syntax error");
|
e_syntax_error(ctx, ctx->tok.lineno, "syntax error");
|
||||||
|
return; /* not reached */
|
||||||
}
|
}
|
||||||
|
|
||||||
SKIP_NEWLINES(ctx);
|
SKIP_NEWLINES(ctx);
|
||||||
|
@ -864,8 +908,10 @@ static void parse_array(context_t* ctx, toml_array_t* arr)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ctx->tok.tok != RBRACKET)
|
if (ctx->tok.tok != RBRACKET) {
|
||||||
syntax_error(ctx, ctx->tok.lineno, "syntax error");
|
e_syntax_error(ctx, ctx->tok.lineno, "syntax error");
|
||||||
|
return; /* not reached */
|
||||||
|
}
|
||||||
|
|
||||||
EAT_TOKEN(ctx, RBRACKET);
|
EAT_TOKEN(ctx, RBRACKET);
|
||||||
}
|
}
|
||||||
|
@ -879,14 +925,18 @@ static void parse_array(context_t* ctx, toml_array_t* arr)
|
||||||
*/
|
*/
|
||||||
static void parse_keyval(context_t* ctx, toml_table_t* tab)
|
static void parse_keyval(context_t* ctx, toml_table_t* tab)
|
||||||
{
|
{
|
||||||
if (ctx->tok.tok != STRING)
|
if (ctx->tok.tok != STRING) {
|
||||||
internal_error(ctx, FLINE);
|
e_internal_error(ctx, FLINE);
|
||||||
|
return; /* not reached */
|
||||||
|
}
|
||||||
|
|
||||||
token_t key = ctx->tok;
|
token_t key = ctx->tok;
|
||||||
|
|
||||||
EAT_TOKEN(ctx, STRING);
|
EAT_TOKEN(ctx, STRING);
|
||||||
if (ctx->tok.tok != EQUAL)
|
if (ctx->tok.tok != EQUAL) {
|
||||||
syntax_error(ctx, ctx->tok.lineno, "missing =");
|
e_syntax_error(ctx, ctx->tok.lineno, "missing =");
|
||||||
|
return; /* not reached */
|
||||||
|
}
|
||||||
|
|
||||||
EAT_TOKEN(ctx, EQUAL);
|
EAT_TOKEN(ctx, EQUAL);
|
||||||
|
|
||||||
|
@ -897,7 +947,10 @@ static void parse_keyval(context_t* ctx, toml_table_t* tab)
|
||||||
token_t val = ctx->tok;
|
token_t val = ctx->tok;
|
||||||
assert(keyval->val == 0);
|
assert(keyval->val == 0);
|
||||||
keyval->val = strndup(val.ptr, val.len);
|
keyval->val = strndup(val.ptr, val.len);
|
||||||
if (! keyval->val) outofmemory(ctx, FLINE);
|
if (! keyval->val) {
|
||||||
|
e_outofmemory(ctx, FLINE);
|
||||||
|
return; /* not reached */
|
||||||
|
}
|
||||||
|
|
||||||
EAT_TOKEN(ctx, STRING);
|
EAT_TOKEN(ctx, STRING);
|
||||||
|
|
||||||
|
@ -919,7 +972,8 @@ static void parse_keyval(context_t* ctx, toml_table_t* tab)
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
syntax_error(ctx, ctx->tok.lineno, "syntax error");
|
e_syntax_error(ctx, ctx->tok.lineno, "syntax error");
|
||||||
|
return; /* not reached */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -948,11 +1002,15 @@ static void fill_tabpath(context_t* ctx)
|
||||||
ctx->tpath.top = 0;
|
ctx->tpath.top = 0;
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if (ctx->tpath.top >= 10)
|
if (ctx->tpath.top >= 10) {
|
||||||
syntax_error(ctx, lineno, "table path is too deep; max allowed is 10.");
|
e_syntax_error(ctx, lineno, "table path is too deep; max allowed is 10.");
|
||||||
|
return; /* not reached */
|
||||||
|
}
|
||||||
|
|
||||||
if (ctx->tok.tok != STRING)
|
if (ctx->tok.tok != STRING) {
|
||||||
syntax_error(ctx, lineno, "invalid or missing key");
|
e_syntax_error(ctx, lineno, "invalid or missing key");
|
||||||
|
return; /* not reached */
|
||||||
|
}
|
||||||
|
|
||||||
ctx->tpath.tok[ctx->tpath.top] = ctx->tok;
|
ctx->tpath.tok[ctx->tpath.top] = ctx->tok;
|
||||||
ctx->tpath.key[ctx->tpath.top] = normalize_key(ctx, ctx->tok);
|
ctx->tpath.key[ctx->tpath.top] = normalize_key(ctx, ctx->tok);
|
||||||
|
@ -962,14 +1020,17 @@ static void fill_tabpath(context_t* ctx)
|
||||||
|
|
||||||
if (ctx->tok.tok == RBRACKET) break;
|
if (ctx->tok.tok == RBRACKET) break;
|
||||||
|
|
||||||
if (ctx->tok.tok != DOT)
|
if (ctx->tok.tok != DOT) {
|
||||||
syntax_error(ctx, lineno, "invalid key");
|
e_syntax_error(ctx, lineno, "invalid key");
|
||||||
|
return; /* not reached */
|
||||||
|
}
|
||||||
|
|
||||||
next_token(ctx, 1);
|
next_token(ctx, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ctx->tpath.top <= 0) {
|
if (ctx->tpath.top <= 0) {
|
||||||
syntax_error(ctx, lineno, "empty table selector");
|
e_syntax_error(ctx, lineno, "empty table selector");
|
||||||
|
return; /* not reached */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -995,27 +1056,40 @@ static void walk_tabpath(context_t* ctx)
|
||||||
|
|
||||||
case 'a':
|
case 'a':
|
||||||
/* found an array. nexttab is the last table in the array. */
|
/* found an array. nexttab is the last table in the array. */
|
||||||
if (nextarr->kind != 't') internal_error(ctx, FLINE);
|
if (nextarr->kind != 't') {
|
||||||
if (nextarr->nelem == 0) internal_error(ctx, FLINE);
|
e_internal_error(ctx, FLINE);
|
||||||
|
return; /* not reached */
|
||||||
|
}
|
||||||
|
if (nextarr->nelem == 0) {
|
||||||
|
e_internal_error(ctx, FLINE);
|
||||||
|
return; /* not reached */
|
||||||
|
}
|
||||||
nexttab = nextarr->u.tab[nextarr->nelem-1];
|
nexttab = nextarr->u.tab[nextarr->nelem-1];
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'v':
|
case 'v':
|
||||||
key_exists_error(ctx, ctx->tpath.tok[i]);
|
e_key_exists_error(ctx, ctx->tpath.tok[i]);
|
||||||
break;
|
return; /* not reached */
|
||||||
|
|
||||||
default:
|
default:
|
||||||
{ /* Not found. Let's create an implicit table. */
|
{ /* Not found. Let's create an implicit table. */
|
||||||
int n = curtab->ntab;
|
int n = curtab->ntab;
|
||||||
toml_table_t** base = (toml_table_t**) realloc(curtab->tab, (n+1) * sizeof(*base));
|
toml_table_t** base = (toml_table_t**) realloc(curtab->tab, (n+1) * sizeof(*base));
|
||||||
if (0 == base) outofmemory(ctx, FLINE);
|
if (0 == base) {
|
||||||
|
e_outofmemory(ctx, FLINE);
|
||||||
|
return; /* not reached */
|
||||||
|
}
|
||||||
curtab->tab = base;
|
curtab->tab = base;
|
||||||
|
|
||||||
if (0 == (base[n] = (toml_table_t*) calloc(1, sizeof(*base[n]))))
|
if (0 == (base[n] = (toml_table_t*) calloc(1, sizeof(*base[n])))) {
|
||||||
outofmemory(ctx, FLINE);
|
e_outofmemory(ctx, FLINE);
|
||||||
|
return; /* not reached */
|
||||||
|
}
|
||||||
|
|
||||||
if (0 == (base[n]->key = strdup(key)))
|
if (0 == (base[n]->key = strdup(key))) {
|
||||||
outofmemory(ctx, FLINE);
|
e_outofmemory(ctx, FLINE);
|
||||||
|
return; /* not reached */
|
||||||
|
}
|
||||||
|
|
||||||
nexttab = curtab->tab[curtab->ntab++];
|
nexttab = curtab->tab[curtab->ntab++];
|
||||||
|
|
||||||
|
@ -1038,7 +1112,10 @@ static void walk_tabpath(context_t* ctx)
|
||||||
static void parse_select(context_t* ctx)
|
static void parse_select(context_t* ctx)
|
||||||
{
|
{
|
||||||
int count_lbracket = 0;
|
int count_lbracket = 0;
|
||||||
if (ctx->tok.tok != LBRACKET) internal_error(ctx, FLINE);
|
if (ctx->tok.tok != LBRACKET) {
|
||||||
|
e_internal_error(ctx, FLINE);
|
||||||
|
return; /* not reached */
|
||||||
|
}
|
||||||
count_lbracket++;
|
count_lbracket++;
|
||||||
next_token(ctx, 1 /* DOT IS SPECIAL */);
|
next_token(ctx, 1 /* DOT IS SPECIAL */);
|
||||||
if (ctx->tok.tok == LBRACKET) {
|
if (ctx->tok.tok == LBRACKET) {
|
||||||
|
@ -1064,21 +1141,31 @@ static void parse_select(context_t* ctx)
|
||||||
toml_array_t* arr = create_keyarray_in_table(ctx, ctx->curtab, z,
|
toml_array_t* arr = create_keyarray_in_table(ctx, ctx->curtab, z,
|
||||||
1 /*skip_if_exist*/);
|
1 /*skip_if_exist*/);
|
||||||
if (arr->kind == 0) arr->kind = 't';
|
if (arr->kind == 0) arr->kind = 't';
|
||||||
if (arr->kind != 't') syntax_error(ctx, z.lineno, "array mismatch");
|
if (arr->kind != 't') {
|
||||||
|
e_syntax_error(ctx, z.lineno, "array mismatch");
|
||||||
|
return; /* not reached */
|
||||||
|
}
|
||||||
|
|
||||||
/* add to z[] */
|
/* add to z[] */
|
||||||
toml_table_t* dest;
|
toml_table_t* dest;
|
||||||
{
|
{
|
||||||
int n = arr->nelem;
|
int n = arr->nelem;
|
||||||
toml_table_t** base = realloc(arr->u.tab, (n+1) * sizeof(*base));
|
toml_table_t** base = realloc(arr->u.tab, (n+1) * sizeof(*base));
|
||||||
if (0 == base) outofmemory(ctx, FLINE);
|
if (0 == base) {
|
||||||
|
e_outofmemory(ctx, FLINE);
|
||||||
|
return; /* not reached */
|
||||||
|
}
|
||||||
arr->u.tab = base;
|
arr->u.tab = base;
|
||||||
|
|
||||||
if (0 == (base[n] = calloc(1, sizeof(*base[n]))))
|
if (0 == (base[n] = calloc(1, sizeof(*base[n])))) {
|
||||||
outofmemory(ctx, FLINE);
|
e_outofmemory(ctx, FLINE);
|
||||||
|
return; /* not reached */
|
||||||
|
}
|
||||||
|
|
||||||
if (0 == (base[n]->key = strdup("__anon__")))
|
if (0 == (base[n]->key = strdup("__anon__"))) {
|
||||||
outofmemory(ctx, FLINE);
|
e_outofmemory(ctx, FLINE);
|
||||||
|
return; /* not reached */
|
||||||
|
}
|
||||||
|
|
||||||
dest = arr->u.tab[arr->nelem++];
|
dest = arr->u.tab[arr->nelem++];
|
||||||
}
|
}
|
||||||
|
@ -1086,14 +1173,23 @@ static void parse_select(context_t* ctx)
|
||||||
ctx->curtab = dest;
|
ctx->curtab = dest;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ctx->tok.tok != RBRACKET) syntax_error(ctx, ctx->tok.lineno, "expects ]");
|
if (ctx->tok.tok != RBRACKET) {
|
||||||
|
e_syntax_error(ctx, ctx->tok.lineno, "expects ]");
|
||||||
|
return; /* not reached */
|
||||||
|
}
|
||||||
EAT_TOKEN(ctx, RBRACKET);
|
EAT_TOKEN(ctx, RBRACKET);
|
||||||
|
|
||||||
if (count_lbracket == 2) {
|
if (count_lbracket == 2) {
|
||||||
if (ctx->tok.tok != RBRACKET) syntax_error(ctx, ctx->tok.lineno, "expects ]]");
|
if (ctx->tok.tok != RBRACKET) {
|
||||||
|
e_syntax_error(ctx, ctx->tok.lineno, "expects ]]");
|
||||||
|
return; /* not reached */
|
||||||
|
}
|
||||||
EAT_TOKEN(ctx, RBRACKET);
|
EAT_TOKEN(ctx, RBRACKET);
|
||||||
}
|
}
|
||||||
if (ctx->tok.tok != NEWLINE) syntax_error(ctx, ctx->tok.lineno, "extra chars after ] or ]]");
|
if (ctx->tok.tok != NEWLINE) {
|
||||||
|
e_syntax_error(ctx, ctx->tok.lineno, "extra chars after ] or ]]");
|
||||||
|
return; /* not reached */
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1124,7 +1220,8 @@ toml_table_t* toml_parse(char* conf,
|
||||||
|
|
||||||
// make a root table
|
// make a root table
|
||||||
if (0 == (ctx.root = calloc(1, sizeof(*ctx.root)))) {
|
if (0 == (ctx.root = calloc(1, sizeof(*ctx.root)))) {
|
||||||
outofmemory(&ctx, FLINE);
|
/* do not call outofmemory() here... setjmp not done yet */
|
||||||
|
snprintf(ctx.errbuf, ctx.errbufsz, "ERROR: out of memory (%s)", FLINE);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1149,8 +1246,10 @@ toml_table_t* toml_parse(char* conf,
|
||||||
|
|
||||||
case STRING:
|
case STRING:
|
||||||
parse_keyval(&ctx, ctx.curtab);
|
parse_keyval(&ctx, ctx.curtab);
|
||||||
if (ctx.tok.tok != NEWLINE)
|
if (ctx.tok.tok != NEWLINE) {
|
||||||
syntax_error(&ctx, ctx.tok.lineno, "extra chars after value");
|
e_syntax_error(&ctx, ctx.tok.lineno, "extra chars after value");
|
||||||
|
return 0; /* not reached */
|
||||||
|
}
|
||||||
|
|
||||||
EAT_TOKEN(&ctx, NEWLINE);
|
EAT_TOKEN(&ctx, NEWLINE);
|
||||||
break;
|
break;
|
||||||
|
@ -1179,9 +1278,16 @@ toml_table_t* toml_parse_file(FILE* fp,
|
||||||
char* buf = 0;
|
char* buf = 0;
|
||||||
int off = 0;
|
int off = 0;
|
||||||
|
|
||||||
|
/* prime the buf[] */
|
||||||
|
bufsz = 1000;
|
||||||
|
if (! (buf = malloc(bufsz + 1))) {
|
||||||
|
snprintf(errbuf, errbufsz, "out of memory");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* read from fp into buf */
|
/* read from fp into buf */
|
||||||
while (! feof(fp)) {
|
while (! feof(fp)) {
|
||||||
bufsz += 100;
|
bufsz += 1000;
|
||||||
|
|
||||||
/* Allocate 1 extra byte because we will tag on a NUL */
|
/* Allocate 1 extra byte because we will tag on a NUL */
|
||||||
char* x = realloc(buf, bufsz + 1);
|
char* x = realloc(buf, bufsz + 1);
|
||||||
|
@ -1301,8 +1407,10 @@ static tokentype_t scan_string(context_t* ctx, char* p, int lineno, int dotisspe
|
||||||
char* orig = p;
|
char* orig = p;
|
||||||
if (0 == strncmp(p, "'''", 3)) {
|
if (0 == strncmp(p, "'''", 3)) {
|
||||||
p = strstr(p + 3, "'''");
|
p = strstr(p + 3, "'''");
|
||||||
if (0 == p)
|
if (0 == p) {
|
||||||
syntax_error(ctx, lineno, "unterminated triple-s-quote");
|
e_syntax_error(ctx, lineno, "unterminated triple-s-quote");
|
||||||
|
return 0; /* not reached */
|
||||||
|
}
|
||||||
|
|
||||||
return ret_token(ctx, STRING, lineno, orig, p + 3 - orig);
|
return ret_token(ctx, STRING, lineno, orig, p + 3 - orig);
|
||||||
}
|
}
|
||||||
|
@ -1318,26 +1426,32 @@ static tokentype_t scan_string(context_t* ctx, char* p, int lineno, int dotisspe
|
||||||
if (*p == 'u') { hexreq = 4; continue; }
|
if (*p == 'u') { hexreq = 4; continue; }
|
||||||
if (*p == 'U') { hexreq = 8; continue; }
|
if (*p == 'U') { hexreq = 8; continue; }
|
||||||
if (*p == '\n') continue; /* allow for line ending backslash */
|
if (*p == '\n') continue; /* allow for line ending backslash */
|
||||||
syntax_error(ctx, lineno, "bad escape char");
|
e_syntax_error(ctx, lineno, "bad escape char");
|
||||||
|
return 0; /* not reached */
|
||||||
}
|
}
|
||||||
if (hexreq) {
|
if (hexreq) {
|
||||||
hexreq--;
|
hexreq--;
|
||||||
if (strchr("0123456789ABCDEF", *p)) continue;
|
if (strchr("0123456789ABCDEF", *p)) continue;
|
||||||
syntax_error(ctx, lineno, "expect hex char");
|
e_syntax_error(ctx, lineno, "expect hex char");
|
||||||
|
return 0; /* not reached */
|
||||||
}
|
}
|
||||||
if (*p == '\\') { escape = 1; continue; }
|
if (*p == '\\') { escape = 1; continue; }
|
||||||
qcnt = (*p == '"') ? qcnt + 1 : 0;
|
qcnt = (*p == '"') ? qcnt + 1 : 0;
|
||||||
}
|
}
|
||||||
if (qcnt != 3)
|
if (qcnt != 3) {
|
||||||
syntax_error(ctx, lineno, "unterminated triple-quote");
|
e_syntax_error(ctx, lineno, "unterminated triple-quote");
|
||||||
|
return 0; /* not reached */
|
||||||
|
}
|
||||||
|
|
||||||
return ret_token(ctx, STRING, lineno, orig, p - orig);
|
return ret_token(ctx, STRING, lineno, orig, p - orig);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('\'' == *p) {
|
if ('\'' == *p) {
|
||||||
for (p++; *p && *p != '\n' && *p != '\''; p++);
|
for (p++; *p && *p != '\n' && *p != '\''; p++);
|
||||||
if (*p != '\'')
|
if (*p != '\'') {
|
||||||
syntax_error(ctx, lineno, "unterminated s-quote");
|
e_syntax_error(ctx, lineno, "unterminated s-quote");
|
||||||
|
return 0; /* not reached */
|
||||||
|
}
|
||||||
|
|
||||||
return ret_token(ctx, STRING, lineno, orig, p + 1 - orig);
|
return ret_token(ctx, STRING, lineno, orig, p + 1 - orig);
|
||||||
}
|
}
|
||||||
|
@ -1351,19 +1465,23 @@ static tokentype_t scan_string(context_t* ctx, char* p, int lineno, int dotisspe
|
||||||
if (strchr("btnfr\"\\", *p)) continue;
|
if (strchr("btnfr\"\\", *p)) continue;
|
||||||
if (*p == 'u') { hexreq = 4; continue; }
|
if (*p == 'u') { hexreq = 4; continue; }
|
||||||
if (*p == 'U') { hexreq = 8; continue; }
|
if (*p == 'U') { hexreq = 8; continue; }
|
||||||
syntax_error(ctx, lineno, "bad escape char");
|
e_syntax_error(ctx, lineno, "bad escape char");
|
||||||
|
return 0; /* not reached */
|
||||||
}
|
}
|
||||||
if (hexreq) {
|
if (hexreq) {
|
||||||
hexreq--;
|
hexreq--;
|
||||||
if (strchr("0123456789ABCDEF", *p)) continue;
|
if (strchr("0123456789ABCDEF", *p)) continue;
|
||||||
syntax_error(ctx, lineno, "expect hex char");
|
e_syntax_error(ctx, lineno, "expect hex char");
|
||||||
|
return 0; /* not reached */
|
||||||
}
|
}
|
||||||
if (*p == '\\') { escape = 1; continue; }
|
if (*p == '\\') { escape = 1; continue; }
|
||||||
if (*p == '\n') break;
|
if (*p == '\n') break;
|
||||||
if (*p == '"') break;
|
if (*p == '"') break;
|
||||||
}
|
}
|
||||||
if (*p != '"')
|
if (*p != '"') {
|
||||||
syntax_error(ctx, lineno, "unterminated quote");
|
e_syntax_error(ctx, lineno, "unterminated quote");
|
||||||
|
return 0; /* not reached */
|
||||||
|
}
|
||||||
|
|
||||||
return ret_token(ctx, STRING, lineno, orig, p + 1 - orig);
|
return ret_token(ctx, STRING, lineno, orig, p + 1 - orig);
|
||||||
}
|
}
|
||||||
|
@ -1425,11 +1543,6 @@ static tokentype_t next_token(context_t* ctx, int dotisspecial)
|
||||||
return ret_eof(ctx, lineno);
|
return ret_eof(ctx, lineno);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int outofmemory(context_t* ctx, const char* fline)
|
|
||||||
{
|
|
||||||
snprintf(ctx->errbuf, ctx->errbufsz, "ERROR: out of memory (%s)", fline);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* toml_key_in(toml_table_t* tab, int keyidx)
|
const char* toml_key_in(toml_table_t* tab, int keyidx)
|
||||||
{
|
{
|
||||||
|
|
|
@ -78,7 +78,7 @@ static void print_table(toml_table_t* curtab)
|
||||||
if (0 != (raw = toml_raw_in(curtab, key))) {
|
if (0 != (raw = toml_raw_in(curtab, key))) {
|
||||||
printf("%s = %s\n", key, raw);
|
printf("%s = %s\n", key, raw);
|
||||||
} else if (0 != (arr = toml_array_in(curtab, key))) {
|
} else if (0 != (arr = toml_array_in(curtab, key))) {
|
||||||
if (toml_array_typ(arr) == 't') {
|
if (toml_array_kind(arr) == 't') {
|
||||||
print_array_of_tables(arr, key);
|
print_array_of_tables(arr, key);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -119,7 +119,7 @@ static void print_array(toml_array_t* curarr)
|
||||||
toml_table_t* tab;
|
toml_table_t* tab;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
switch (toml_array_typ(curarr)) {
|
switch (toml_array_kind(curarr)) {
|
||||||
|
|
||||||
case 'v':
|
case 'v':
|
||||||
for (i = 0; 0 != (raw = toml_raw_at(curarr, i)); i++) {
|
for (i = 0; 0 != (raw = toml_raw_at(curarr, i)); i++) {
|
||||||
|
|
Loading…
Reference in a new issue