v0.5 compliant

This commit is contained in:
CK Tan 2019-10-08 16:58:18 -07:00
parent 63793f92ef
commit c32a6e92f1
13 changed files with 1592 additions and 1444 deletions

2
.gitignore vendored
View file

@ -1,3 +1,5 @@
*~
# Object files # Object files
*.o *.o
*.ko *.ko

View file

@ -93,9 +93,17 @@ To test against the standard test set provided by BurntSushi/toml-test:
``` ```
% make % make
% cd test % cd test1
% bash build.sh # do this once % bash build.sh # do this once
% bash run.sh # this will run the test suite % bash run.sh # this will run the test suite
``` ```
To test against the standard test set provided by iarna/toml:
```
% make
% cd test2
% bash build.sh # do this once
% bash run.sh # this will run the test suite
```

View file

View file

@ -2,7 +2,7 @@
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
mkdir -p $DIR/goworkspace mkdir -p $DIR/goworkspace
export GOPATH=$DIR/goworkspace # if it isn't already set export GOPATH=$DIR/goworkspace
go get github.com/BurntSushi/toml-test # install test suite go get github.com/BurntSushi/toml-test # install test suite
go get github.com/BurntSushi/toml/cmd/toml-test-decoder # e.g., install my parser go get github.com/BurntSushi/toml/cmd/toml-test-decoder # e.g., install my parser
cp $GOPATH/bin/* . cp $GOPATH/bin/* .

1
test2/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/toml-spec-tests

6
test2/build.sh Normal file
View file

@ -0,0 +1,6 @@
set -e
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
[ -d toml-spec-tests ] || git clone https://github.com/iarna/toml-spec-tests.git

31
test2/run.sh Normal file
View file

@ -0,0 +1,31 @@
#
# POSITIVE tests
#
for i in toml-spec-tests/values/*.toml; do
echo -n $i ' '
../toml_json $i >& $i.json.out
rc=$?
[ -f $i.json ] && diff=$(diff $i.json $i.json.out) || diff=''
if [ "$rc" != "0" ] || [ "$diff" != "" ]; then
echo '[FAILED]'
else
echo '[OK]'
fi
done
#
# NEGATIVE tests
#
for i in toml-spec-tests/errors/*.toml; do
echo -n $i ' '
../toml_json $i >& $i.json.out
rc=$?
if [ "$rc" != "0" ]; then
echo '[OK]'
else
echo '[FAILED]'
fi
done

436
toml.c
View file

@ -58,7 +58,7 @@ void toml_set_memutil(void* (*xxmalloc)(size_t),
#define CALLOC(a,b) ppcalloc(a,b) #define CALLOC(a,b) ppcalloc(a,b)
#define REALLOC(a,b) pprealloc(a,b) #define REALLOC(a,b) pprealloc(a,b)
static char* STRDUP(const char* s) char* STRDUP(const char* s)
{ {
int len = strlen(s); int len = strlen(s);
char* p = MALLOC(len+1); char* p = MALLOC(len+1);
@ -69,7 +69,7 @@ static char* STRDUP(const char* s)
return p; return p;
} }
static char* STRNDUP(const char* s, size_t n) char* STRNDUP(const char* s, size_t n)
{ {
size_t len = strnlen(s, n); size_t len = strnlen(s, n);
char* p = MALLOC(len+1); char* p = MALLOC(len+1);
@ -390,29 +390,16 @@ static int e_noimpl(context_t* ctx, const char* feature)
} }
*/ */
static int e_key_exists_error(context_t* ctx, token_t keytok) static int e_key_exists_error(context_t* ctx, int lineno, const char* key)
{ {
char buf[100];
int i;
for (i = 0; i < keytok.len && i < (int)sizeof(buf) - 1; i++) {
buf[i] = keytok.ptr[i];
}
buf[i] = 0;
snprintf(ctx->errbuf, ctx->errbufsz, snprintf(ctx->errbuf, ctx->errbufsz,
"line %d: key %s exists", keytok.lineno, buf); "line %d: key %s exists", lineno, key);
longjmp(ctx->jmp, 1); longjmp(ctx->jmp, 1);
return -1; return -1;
} }
static char* norm_lit_str(const char* src, int srclen,
int multiline,
/*
* Convert src to raw unescaped utf-8 string.
* Returns NULL if error with errmsg in errbuf.
*/
static char* normalize_string(const char* src, int srclen,
int kill_line_ending_backslash,
char* errbuf, int errbufsz) char* errbuf, int errbufsz)
{ {
char* dst = 0; /* will write to dst[] and return it */ char* dst = 0; /* will write to dst[] and return it */
@ -425,7 +412,60 @@ static char* normalize_string(const char* src, int srclen,
/* scan forward on src */ /* scan forward on src */
for (;;) { for (;;) {
if (off >= max - 10) { /* have some slack for misc stuff */ if (off >= max - 10) { /* have some slack for misc stuff */
char* x = REALLOC(dst, max += 100); 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) { if (!x) {
xfree(dst); xfree(dst);
snprintf(errbuf, errbufsz, "out of memory"); snprintf(errbuf, errbufsz, "out of memory");
@ -439,6 +479,17 @@ static char* normalize_string(const char* src, int srclen,
ch = *sp++; ch = *sp++;
if (ch != '\\') { 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 // a plain copy suffice
dst[off++] = ch; dst[off++] = ch;
continue; continue;
@ -451,10 +502,11 @@ static char* normalize_string(const char* src, int srclen,
return 0; return 0;
} }
/* if we want to kill line-ending-backslash ... */ /* for multi-line, we want to kill line-ending-backslash ... */
if (kill_line_ending_backslash) { if (multiline) {
/* if this is a newline immediately following the backslash ... */
if (*sp == '\n' || (*sp == '\r' && sp[1] == '\n')) { // if there is only whitespace after the backslash ...
if (sp[strspn(sp, " \t\r")] == '\n') {
/* skip all the following whitespaces */ /* skip all the following whitespaces */
sp += strspn(sp, " \t\r\n"); sp += strspn(sp, " \t\r\n");
continue; continue;
@ -530,8 +582,11 @@ static char* normalize_key(context_t* ctx, token_t strtok)
/* handle quoted string */ /* handle quoted string */
if (ch == '\'' || ch == '\"') { if (ch == '\'' || ch == '\"') {
/* if ''' or """, take 3 chars off front and back. Else, take 1 char off. */ /* if ''' or """, take 3 chars off front and back. Else, take 1 char off. */
if (sp[1] == ch && sp[2] == ch) int multiline = 0;
if (sp[1] == ch && sp[2] == ch) {
sp += 3, sq -= 3; sp += 3, sq -= 3;
multiline = 1;
}
else else
sp++, sq--; sp++, sq--;
@ -543,7 +598,7 @@ static char* normalize_key(context_t* ctx, token_t strtok)
} }
} 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 = norm_basic_str(sp, sq - sp, multiline, ebuf, sizeof(ebuf));
if (!ret) { if (!ret) {
snprintf(ctx->errbuf, ctx->errbufsz, "line %d: %s", lineno, ebuf); snprintf(ctx->errbuf, ctx->errbufsz, "line %d: %s", lineno, ebuf);
longjmp(ctx->jmp, 1); longjmp(ctx->jmp, 1);
@ -617,6 +672,12 @@ static int check_key(toml_table_t* tab, const char* key,
return 0; 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. /* Create a keyval in the table.
*/ */
static toml_keyval_t* create_keyval_in_table(context_t* ctx, toml_table_t* tab, token_t keytok) static toml_keyval_t* create_keyval_in_table(context_t* ctx, toml_table_t* tab, token_t keytok)
@ -628,9 +689,9 @@ static toml_keyval_t* create_keyval_in_table(context_t* ctx, toml_table_t* tab,
/* if key exists: error out. */ /* if key exists: error out. */
toml_keyval_t* dest = 0; toml_keyval_t* dest = 0;
if (check_key(tab, newkey, 0, 0, 0)) { if (key_kind(tab, newkey)) {
xfree(newkey); xfree(newkey);
e_key_exists_error(ctx, keytok); e_key_exists_error(ctx, keytok.lineno, newkey);
return 0; /* not reached */ return 0; /* not reached */
} }
@ -677,7 +738,7 @@ 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;
} }
e_key_exists_error(ctx, keytok); e_key_exists_error(ctx, keytok.lineno, newkey);
return 0; /* not reached */ return 0; /* not reached */
} }
@ -709,7 +770,7 @@ static toml_table_t* create_keytable_in_table(context_t* ctx, toml_table_t* tab,
static toml_array_t* create_keyarray_in_table(context_t* ctx, static toml_array_t* create_keyarray_in_table(context_t* ctx,
toml_table_t* tab, toml_table_t* tab,
token_t keytok, token_t keytok,
int skip_if_exist) char kind)
{ {
/* first, normalize the key to be used for lookup. /* first, normalize the key to be used for lookup.
* remember to free it if we error out. * remember to free it if we error out.
@ -717,14 +778,9 @@ static toml_array_t* create_keyarray_in_table(context_t* ctx,
char* newkey = normalize_key(ctx, keytok); char* newkey = normalize_key(ctx, keytok);
/* if key exists: error out */ /* if key exists: error out */
toml_array_t* dest = 0; if (key_kind(tab, newkey)) {
if (check_key(tab, newkey, 0, &dest, 0)) {
xfree(newkey); /* don't need this anymore */ xfree(newkey); /* don't need this anymore */
e_key_exists_error(ctx, keytok.lineno, newkey);
/* special case skip if exists? */
if (skip_if_exist) return dest;
e_key_exists_error(ctx, keytok);
return 0; /* not reached */ return 0; /* not reached */
} }
@ -743,10 +799,11 @@ static toml_array_t* create_keyarray_in_table(context_t* ctx,
e_outofmemory(ctx, FLINE); e_outofmemory(ctx, FLINE);
return 0; /* not reached */ return 0; /* not reached */
} }
dest = tab->arr[tab->narr++]; toml_array_t* dest = tab->arr[tab->narr++];
/* save the key in the new array struct */ /* save the key in the new array struct */
dest->key = newkey; dest->key = newkey;
dest->kind = kind;
return dest; return dest;
} }
@ -793,9 +850,9 @@ 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, isdotspecial) while (ctx->tok.tok == NEWLINE) next_token(ctx, isdotspecial)
#define EAT_TOKEN(ctx, typ) \ #define EAT_TOKEN(ctx, typ, isdotspecial) \
if ((ctx)->tok.tok != typ) e_internal_error(ctx, FLINE); else next_token(ctx, 0) 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); static void parse_keyval(context_t* ctx, toml_table_t* tab);
@ -806,7 +863,7 @@ static void parse_keyval(context_t* ctx, toml_table_t* tab);
*/ */
static void parse_table(context_t* ctx, toml_table_t* tab) static void parse_table(context_t* ctx, toml_table_t* tab)
{ {
EAT_TOKEN(ctx, LBRACE); EAT_TOKEN(ctx, LBRACE, 1);
for (;;) { for (;;) {
if (ctx->tok.tok == NEWLINE) { if (ctx->tok.tok == NEWLINE) {
@ -830,7 +887,7 @@ static void parse_table(context_t* ctx, toml_table_t* tab)
/* on comma, continue to scan for next keyval */ /* on comma, continue to scan for next keyval */
if (ctx->tok.tok == COMMA) { if (ctx->tok.tok == COMMA) {
EAT_TOKEN(ctx, COMMA); EAT_TOKEN(ctx, COMMA, 1);
continue; continue;
} }
break; break;
@ -841,7 +898,7 @@ static void parse_table(context_t* ctx, toml_table_t* tab)
return; /* not reached */ return; /* not reached */
} }
EAT_TOKEN(ctx, RBRACE); EAT_TOKEN(ctx, RBRACE, 1);
} }
static int valtype(const char* val) static int valtype(const char* val)
@ -863,10 +920,10 @@ static int valtype(const char* val)
/* We are at '[...]' */ /* We are at '[...]' */
static void parse_array(context_t* ctx, toml_array_t* arr) static void parse_array(context_t* ctx, toml_array_t* arr)
{ {
EAT_TOKEN(ctx, LBRACKET); EAT_TOKEN(ctx, LBRACKET, 0);
for (;;) { for (;;) {
SKIP_NEWLINES(ctx); SKIP_NEWLINES(ctx, 0);
/* until ] */ /* until ] */
if (ctx->tok.tok == RBRACKET) break; if (ctx->tok.tok == RBRACKET) break;
@ -903,11 +960,12 @@ static void parse_array(context_t* ctx, toml_array_t* arr)
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)) {
e_syntax_error(ctx, ctx->tok.lineno, "array type mismatch"); e_syntax_error(ctx, ctx->tok.lineno,
"array type mismatch while processing array of values");
return; /* not reached */ return; /* not reached */
} }
EAT_TOKEN(ctx, STRING); EAT_TOKEN(ctx, STRING, 0);
break; break;
} }
@ -917,7 +975,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') {
e_syntax_error(ctx, ctx->tok.lineno, "array type mismatch"); e_syntax_error(ctx, ctx->tok.lineno,
"array type mismatch while processing array of arrays");
return; /* not reached */ return; /* not reached */
} }
parse_array(ctx, create_array_in_array(ctx, arr)); parse_array(ctx, create_array_in_array(ctx, arr));
@ -930,7 +989,8 @@ 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') {
e_syntax_error(ctx, ctx->tok.lineno, "array type mismatch"); e_syntax_error(ctx, ctx->tok.lineno,
"array type mismatch while processing array of tables");
return; /* not reached */ return; /* not reached */
} }
parse_table(ctx, create_table_in_array(ctx, arr)); parse_table(ctx, create_table_in_array(ctx, arr));
@ -942,11 +1002,11 @@ static void parse_array(context_t* ctx, toml_array_t* arr)
return; /* not reached */ return; /* not reached */
} }
SKIP_NEWLINES(ctx); SKIP_NEWLINES(ctx, 0);
/* on comma, continue to scan for next element */ /* on comma, continue to scan for next element */
if (ctx->tok.tok == COMMA) { if (ctx->tok.tok == COMMA) {
EAT_TOKEN(ctx, COMMA); EAT_TOKEN(ctx, COMMA, 0);
continue; continue;
} }
break; break;
@ -957,11 +1017,10 @@ static void parse_array(context_t* ctx, toml_array_t* arr)
return; /* not reached */ return; /* not reached */
} }
EAT_TOKEN(ctx, RBRACKET); EAT_TOKEN(ctx, RBRACKET, 1);
} }
/* handle lines like these: /* handle lines like these:
key = "value" key = "value"
key = [ array ] key = [ array ]
@ -969,20 +1028,35 @@ 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) { token_t key = ctx->tok;
e_internal_error(ctx, FLINE); EAT_TOKEN(ctx, STRING, 1);
return; /* not reached */
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;
} }
token_t key = ctx->tok;
EAT_TOKEN(ctx, STRING);
if (ctx->tok.tok != EQUAL) { if (ctx->tok.tok != EQUAL) {
e_syntax_error(ctx, ctx->tok.lineno, "missing ="); e_syntax_error(ctx, ctx->tok.lineno, "missing =");
return; /* not reached */ return; /* not reached */
} }
EAT_TOKEN(ctx, EQUAL); next_token(ctx, 0);
switch (ctx->tok.tok) { switch (ctx->tok.tok) {
case STRING: case STRING:
@ -996,7 +1070,7 @@ static void parse_keyval(context_t* ctx, toml_table_t* tab)
return; /* not reached */ return; /* not reached */
} }
EAT_TOKEN(ctx, STRING); next_token(ctx, 1);
return; return;
} }
@ -1112,7 +1186,7 @@ static void walk_tabpath(context_t* ctx)
break; break;
case 'v': case 'v':
e_key_exists_error(ctx, ctx->tpath.tok[i]); e_key_exists_error(ctx, ctx->tpath.tok[i].lineno, key);
return; /* not reached */ return; /* not reached */
default: default:
@ -1163,10 +1237,10 @@ static void parse_select(context_t* ctx)
and '[ [' would be taken as '[[', which is wrong. */ and '[ [' would be taken as '[[', which is wrong. */
/* eat [ or [[ */ /* eat [ or [[ */
next_token(ctx, 1 /* DOT IS SPECIAL */); EAT_TOKEN(ctx, LBRACKET, 1);
if (llb) { if (llb) {
assert(ctx->tok.tok == LBRACKET); assert(ctx->tok.tok == LBRACKET);
next_token(ctx, 1 /* DOT IS SPECIAL */); EAT_TOKEN(ctx, LBRACKET, 1);
} }
fill_tabpath(ctx); fill_tabpath(ctx);
@ -1185,13 +1259,19 @@ static void parse_select(context_t* ctx)
ctx->curtab = create_keytable_in_table(ctx, ctx->curtab, z); ctx->curtab = create_keytable_in_table(ctx, ctx->curtab, z);
} else { } else {
/* [[x.y.z]] -> create z = [] in x.y */ /* [[x.y.z]] -> create z = [] in x.y */
toml_array_t* arr = create_keyarray_in_table(ctx, ctx->curtab, z, toml_array_t* arr = 0;
1 /*skip_if_exist*/); {
char* zstr = normalize_key(ctx, z);
arr = toml_array_in(ctx->curtab, zstr);
xfree(zstr);
}
if (!arr) { if (!arr) {
e_syntax_error(ctx, z.lineno, "key exists"); arr = create_keyarray_in_table(ctx, ctx->curtab, z, 't');
if (!arr) {
e_internal_error(ctx, FLINE);
return; return;
} }
if (arr->kind == 0) arr->kind = 't'; }
if (arr->kind != 't') { if (arr->kind != 't') {
e_syntax_error(ctx, z.lineno, "array mismatch"); e_syntax_error(ctx, z.lineno, "array mismatch");
return; /* not reached */ return; /* not reached */
@ -1233,9 +1313,9 @@ static void parse_select(context_t* ctx)
e_syntax_error(ctx, ctx->tok.lineno, "expects ]]"); e_syntax_error(ctx, ctx->tok.lineno, "expects ]]");
return; /* not reached */ return; /* not reached */
} }
EAT_TOKEN(ctx, RBRACKET); EAT_TOKEN(ctx, RBRACKET, 1);
} }
EAT_TOKEN(ctx, RBRACKET); EAT_TOKEN(ctx, RBRACKET, 1);
if (ctx->tok.tok != NEWLINE) { if (ctx->tok.tok != NEWLINE) {
e_syntax_error(ctx, ctx->tok.lineno, "extra chars after ] or ]]"); e_syntax_error(ctx, ctx->tok.lineno, "extra chars after ] or ]]");
@ -1302,7 +1382,7 @@ toml_table_t* toml_parse(char* conf,
return 0; /* not reached */ return 0; /* not reached */
} }
EAT_TOKEN(&ctx, NEWLINE); EAT_TOKEN(&ctx, NEWLINE, 1);
break; break;
case LBRACKET: /* [ x.y.z ] or [[ x.y.z ]] */ case LBRACKET: /* [ x.y.z ] or [[ x.y.z ]] */
@ -1453,6 +1533,41 @@ static tokentype_t ret_eof(context_t* ctx, int lineno)
} }
/* Scan p for n digits compositing entirely of [0-9] */
static int scan_digits(const char* p, int n)
{
int ret = 0;
for ( ; n > 0 && isdigit(*p); n--, p++) {
ret = 10 * ret + (*p - '0');
}
return n ? -1 : ret;
}
static int scan_date(const char* p, int* YY, int* MM, int* DD)
{
int year, month, day;
year = scan_digits(p, 4);
month = (year >= 0 && p[4] == '-') ? scan_digits(p+5, 2) : -1;
day = (month >= 0 && p[7] == '-') ? scan_digits(p+8, 2) : -1;
if (YY) *YY = year;
if (MM) *MM = month;
if (DD) *DD = day;
return (year >= 0 && month >= 0 && day >= 0) ? 0 : -1;
}
static int scan_time(const char* p, int* hh, int* mm, int* ss)
{
int hour, minute, second;
hour = scan_digits(p, 2);
minute = (hour >= 0 && p[2] == ':') ? scan_digits(p+3, 2) : -1;
second = (minute >= 0 && p[5] == ':') ? scan_digits(p+6, 2) : -1;
if (hh) *hh = hour;
if (mm) *mm = minute;
if (ss) *ss = second;
return (hour >= 0 && minute >= 0 && second >= 0) ? 0 : -1;
}
static tokentype_t scan_string(context_t* ctx, char* p, int lineno, int dotisspecial) static tokentype_t scan_string(context_t* ctx, char* p, int lineno, int dotisspecial)
{ {
char* orig = p; char* orig = p;
@ -1476,7 +1591,7 @@ 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; }
if (*p == '\n') continue; /* allow for line ending backslash */ if (p[strspn(p, " \t\r")] == '\n') continue; /* allow for line ending backslash */
e_syntax_error(ctx, lineno, "bad escape char"); e_syntax_error(ctx, lineno, "bad escape char");
return 0; /* not reached */ return 0; /* not reached */
} }
@ -1537,13 +1652,23 @@ static tokentype_t scan_string(context_t* ctx, char* p, int lineno, int dotisspe
return ret_token(ctx, STRING, lineno, orig, p + 1 - orig); return ret_token(ctx, STRING, lineno, orig, p + 1 - orig);
} }
/* check for timestamp without quotes */
if (0 == scan_date(p, 0, 0, 0) || 0 == scan_time(p, 0, 0, 0)) {
// forward thru the timestamp
for ( ; strchr("0123456789.:+-T Z", toupper(*p)); p++);
// squeeze out any spaces at end of string
for ( ; p[-1] == ' '; p--);
// tokenize
return ret_token(ctx, STRING, lineno, orig, p - orig);
}
/* literals */
for ( ; *p && *p != '\n'; p++) { for ( ; *p && *p != '\n'; p++) {
int ch = *p; int ch = *p;
if (ch == '.' && dotisspecial) break; if (ch == '.' && dotisspecial) break;
if ('A' <= ch && ch <= 'Z') continue; if ('A' <= ch && ch <= 'Z') continue;
if ('a' <= ch && ch <= 'z') continue; if ('a' <= ch && ch <= 'z') continue;
if ('0' <= ch && ch <= '9') continue; if (strchr("0123456789+-_.", ch)) continue;
if (strchr("+-_.:", ch)) continue;
break; break;
} }
@ -1720,85 +1845,66 @@ int toml_rtots(const char* src_, toml_timestamp_t* ret)
if (! src_) return -1; if (! src_) return -1;
const char* p = src_; const char* p = src_;
const char* q = src_ + strlen(src_); int must_parse_time = 0;
int64_t val;
int i;
memset(ret, 0, sizeof(*ret)); memset(ret, 0, sizeof(*ret));
int* year = &ret->__buffer.year;
int* month = &ret->__buffer.month;
int* day = &ret->__buffer.day;
int* hour = &ret->__buffer.hour;
int* minute = &ret->__buffer.minute;
int* second = &ret->__buffer.second;
int* millisec = &ret->__buffer.millisec;
/* parse date YYYY-MM-DD */ /* parse date YYYY-MM-DD */
val = 0; if (0 == scan_date(p, year, month, day)) {
if (q - p > 4 && p[4] == '-') { ret->year = year;
for (i = 0; i < 10; i++, p++) { ret->month = month;
int xx = *p; ret->day = day;
if (xx == '-') {
if (i == 4 || i == 7) continue; else return -1;
}
if (! ('0' <= xx && xx <= '9')) return -1;
val = val * 10 + (xx - '0');
}
ret->day = &ret->__buffer.day;
ret->month = &ret->__buffer.month;
ret->year = &ret->__buffer.year;
*ret->day = val % 100; val /= 100;
*ret->month = val % 100; val /= 100;
*ret->year = val;
p += 10;
if (*p) { if (*p) {
// parse the T or space separator // parse the T or space separator
if (*p != 'T' && *p != ' ') return -1; if (*p != 'T' && *p != ' ') return -1;
must_parse_time = 1;
p++; p++;
} }
} }
if (q == p) return 0;
/* parse time HH:MM:SS */ /* parse time HH:MM:SS */
val = 0; if (0 == scan_time(p, hour, minute, second)) {
if (q - p < 8) return -1; ret->hour = hour;
for (i = 0; i < 8; i++, p++) { ret->minute = minute;
int xx = *p; ret->second = second;
if (xx == ':') {
if (i == 2 || i == 5) continue; else return -1;
}
if (! ('0' <= xx && xx <= '9')) return -1;
val = val * 10 + (xx - '0');
}
ret->second = &ret->__buffer.second;
ret->minute = &ret->__buffer.minute;
ret->hour = &ret->__buffer.hour;
*ret->second = val % 100; val /= 100; /* optionally, parse millisec */
*ret->minute = val % 100; val /= 100; p += 8;
*ret->hour = val;
/* parse millisec */
if (*p == '.') { if (*p == '.') {
val = 0; char* qq;
p++; p++;
if ('0' <= *p && *p <= '9') { errno = 0;
val = (*p++ - '0') * 100; *millisec = strtol(p, &qq, 0);
if ('0' <= *p && *p <= '9') { if (errno) {
val += (*p++ - '0') * 10; return -1;
if ('0' <= *p && *p <= '9') {
val += (*p++ - '0');
} }
while (*millisec > 999) {
*millisec /= 10;
} }
}
ret->millisec = &ret->__buffer.millisec;
*ret->millisec = val;
}
if (q == p) return 0;
/* parse and copy Z */ ret->millisec = millisec;
ret->z = ret->__buffer.z; p = qq;
char* z = ret->z;
if (*p == 'Z') {
*z++ = *p++;
*z = 0;
return (p == q) ? 0 : -1;
} }
if (*p == '+' || *p == '-') {
if (*p) {
/* parse and copy Z */
char* z = ret->__buffer.z;
ret->z = z;
if (*p == 'Z' || *p == 'z') {
*z++ = 'Z'; p++;
*z = 0;
} else if (*p == '+' || *p == '-') {
*z++ = *p++; *z++ = *p++;
if (! (isdigit(p[0]) && isdigit(p[1]))) return -1; if (! (isdigit(p[0]) && isdigit(p[1]))) return -1;
@ -1815,7 +1921,15 @@ int toml_rtots(const char* src_, toml_timestamp_t* ret)
*z = 0; *z = 0;
} }
return (p == q) ? 0 : -1; }
}
if (*p != 0)
return -1;
if (must_parse_time && !ret->hour)
return -1;
return 0;
} }
@ -1925,8 +2039,8 @@ int toml_rtod_ex(const char* src, double* ret_, char* buf, int buflen)
if (s[0] == '.') if (s[0] == '.')
return -1; return -1;
/* zero must be followed by . or NUL */ /* zero must be followed by . or 'e', or NUL */
if (s[0] == '0' && s[1] && s[1] != '.') if (s[0] == '0' && s[1] && !strchr("eE.", s[1]))
return -1; return -1;
/* just strip underscores and pass to strtod */ /* just strip underscores and pass to strtod */
@ -1971,31 +2085,15 @@ int toml_rtod(const char* src, double* ret_)
} }
static char* kill_line_ending_backslash(char* str)
{
if (!str) return 0;
/* first round: find (backslash, \n) */
char* p = str;
while (0 != (p = strstr(p, "\\\n"))) {
char* q = (p + 1);
q += strspn(q, " \t\r\n");
memmove(p, q, strlen(q) + 1);
}
/* second round: find (backslash, \r, \n) */
p = str;
while (0 != (p = strstr(p, "\\\r\n"))) {
char* q = (p + 1);
q += strspn(q, " \t\r\n");
memmove(p, q, strlen(q) + 1);
}
return str;
}
int toml_rtos(const char* src, char** ret) int toml_rtos(const char* src, char** ret)
{ {
char dummy_errbuf[1];
int multiline = 0;
const char* sp;
const char* sq;
if (!src) return -1; if (!src) return -1;
if (*src != '\'' && *src != '"') return -1; if (*src != '\'' && *src != '"') return -1;
@ -2003,8 +2101,9 @@ int toml_rtos(const char* src, char** ret)
int srclen = strlen(src); int srclen = strlen(src);
if (*src == '\'') { if (*src == '\'') {
if (0 == strncmp(src, "'''", 3)) { if (0 == strncmp(src, "'''", 3)) {
const char* sp = src + 3; multiline = 1;
const char* sq = src + srclen - 3; sp = src + 3;
sq = src + srclen - 3;
/* last 3 chars in src must be ''' */ /* last 3 chars in src must be ''' */
if (! (sp <= sq && 0 == strcmp(sq, "'''"))) if (! (sp <= sq && 0 == strcmp(sq, "'''")))
return -1; return -1;
@ -2015,22 +2114,24 @@ int toml_rtos(const char* src, char** ret)
else if (sp[0] == '\r' && sp[1] == '\n') else if (sp[0] == '\r' && sp[1] == '\n')
sp += 2; sp += 2;
*ret = kill_line_ending_backslash(STRNDUP(sp, sq - sp));
} else { } else {
const char* sp = src + 1; sp = src + 1;
const char* sq = src + srclen - 1; sq = src + srclen - 1;
/* last char in src must be ' */ /* last char in src must be ' */
if (! (sp <= sq && *sq == '\'')) if (! (sp <= sq && *sq == '\''))
return -1; return -1;
/* copy from sp to p */ /* copy from sp to p */
*ret = STRNDUP(sp, sq - sp); *ret = STRNDUP(sp, sq - sp);
} }
*ret = norm_lit_str(sp, sq - sp,
multiline,
dummy_errbuf, sizeof(dummy_errbuf));
return *ret ? 0 : -1; return *ret ? 0 : -1;
} }
const char* sp;
const char* sq;
if (0 == strncmp(src, "\"\"\"", 3)) { if (0 == strncmp(src, "\"\"\"", 3)) {
multiline = 1;
sp = src + 3; sp = src + 3;
sq = src + srclen - 3; sq = src + srclen - 3;
if (! (sp <= sq && 0 == strcmp(sq, "\"\"\""))) if (! (sp <= sq && 0 == strcmp(sq, "\"\"\"")))
@ -2048,9 +2149,8 @@ int toml_rtos(const char* src, char** ret)
return -1; return -1;
} }
char dummy_errbuf[1]; *ret = norm_basic_str(sp, sq - sp,
*ret = normalize_string(sp, sq - sp, multiline,
1, // flag kill_line_ending_backslash
dummy_errbuf, sizeof(dummy_errbuf)); dummy_errbuf, sizeof(dummy_errbuf));
return *ret ? 0 : -1; return *ret ? 0 : -1;
} }