v0.5 compliant
This commit is contained in:
parent
63793f92ef
commit
c32a6e92f1
13 changed files with 1592 additions and 1444 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,3 +1,5 @@
|
|||
*~
|
||||
|
||||
# Object files
|
||||
*.o
|
||||
*.ko
|
||||
|
|
10
README.md
10
README.md
|
@ -93,9 +93,17 @@ To test against the standard test set provided by BurntSushi/toml-test:
|
|||
|
||||
```
|
||||
% make
|
||||
% cd test
|
||||
% cd test1
|
||||
% bash build.sh # do this once
|
||||
% 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
|
||||
```
|
||||
|
|
0
test/.gitignore → test1/.gitignore
vendored
0
test/.gitignore → test1/.gitignore
vendored
|
@ -2,7 +2,7 @@
|
|||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
|
||||
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/cmd/toml-test-decoder # e.g., install my parser
|
||||
cp $GOPATH/bin/* .
|
1
test2/.gitignore
vendored
Normal file
1
test2/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/toml-spec-tests
|
6
test2/build.sh
Normal file
6
test2/build.sh
Normal 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
31
test2/run.sh
Normal 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
436
toml.c
|
@ -58,7 +58,7 @@ void toml_set_memutil(void* (*xxmalloc)(size_t),
|
|||
#define CALLOC(a,b) ppcalloc(a,b)
|
||||
#define REALLOC(a,b) pprealloc(a,b)
|
||||
|
||||
static char* STRDUP(const char* s)
|
||||
char* STRDUP(const char* s)
|
||||
{
|
||||
int len = strlen(s);
|
||||
char* p = MALLOC(len+1);
|
||||
|
@ -69,7 +69,7 @@ static char* STRDUP(const char* s)
|
|||
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);
|
||||
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,
|
||||
"line %d: key %s exists", keytok.lineno, buf);
|
||||
"line %d: key %s exists", lineno, key);
|
||||
longjmp(ctx->jmp, 1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* 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,
|
||||
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 */
|
||||
|
@ -425,7 +412,60 @@ static char* normalize_string(const char* src, int srclen,
|
|||
/* scan forward on src */
|
||||
for (;;) {
|
||||
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) {
|
||||
xfree(dst);
|
||||
snprintf(errbuf, errbufsz, "out of memory");
|
||||
|
@ -439,6 +479,17 @@ static char* normalize_string(const char* src, int srclen,
|
|||
|
||||
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;
|
||||
|
@ -451,10 +502,11 @@ static char* normalize_string(const char* src, int srclen,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* if we want to kill line-ending-backslash ... */
|
||||
if (kill_line_ending_backslash) {
|
||||
/* if this is a newline immediately following the backslash ... */
|
||||
if (*sp == '\n' || (*sp == '\r' && sp[1] == '\n')) {
|
||||
/* 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;
|
||||
|
@ -530,8 +582,11 @@ static char* normalize_key(context_t* ctx, token_t strtok)
|
|||
/* handle quoted string */
|
||||
if (ch == '\'' || ch == '\"') {
|
||||
/* 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;
|
||||
multiline = 1;
|
||||
}
|
||||
else
|
||||
sp++, sq--;
|
||||
|
||||
|
@ -543,7 +598,7 @@ static char* normalize_key(context_t* ctx, token_t strtok)
|
|||
}
|
||||
} else {
|
||||
/* 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) {
|
||||
snprintf(ctx->errbuf, ctx->errbufsz, "line %d: %s", lineno, ebuf);
|
||||
longjmp(ctx->jmp, 1);
|
||||
|
@ -617,6 +672,12 @@ static int check_key(toml_table_t* tab, const char* key,
|
|||
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)
|
||||
|
@ -628,9 +689,9 @@ static toml_keyval_t* create_keyval_in_table(context_t* ctx, toml_table_t* tab,
|
|||
|
||||
/* if key exists: error out. */
|
||||
toml_keyval_t* dest = 0;
|
||||
if (check_key(tab, newkey, 0, 0, 0)) {
|
||||
if (key_kind(tab, newkey)) {
|
||||
xfree(newkey);
|
||||
e_key_exists_error(ctx, keytok);
|
||||
e_key_exists_error(ctx, keytok.lineno, newkey);
|
||||
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;
|
||||
return dest;
|
||||
}
|
||||
e_key_exists_error(ctx, keytok);
|
||||
e_key_exists_error(ctx, keytok.lineno, newkey);
|
||||
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,
|
||||
toml_table_t* tab,
|
||||
token_t keytok,
|
||||
int skip_if_exist)
|
||||
char kind)
|
||||
{
|
||||
/* first, normalize the key to be used for lookup.
|
||||
* 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);
|
||||
|
||||
/* if key exists: error out */
|
||||
toml_array_t* dest = 0;
|
||||
if (check_key(tab, newkey, 0, &dest, 0)) {
|
||||
if (key_kind(tab, newkey)) {
|
||||
xfree(newkey); /* don't need this anymore */
|
||||
|
||||
/* special case skip if exists? */
|
||||
if (skip_if_exist) return dest;
|
||||
|
||||
e_key_exists_error(ctx, keytok);
|
||||
e_key_exists_error(ctx, keytok.lineno, newkey);
|
||||
return 0; /* not reached */
|
||||
}
|
||||
|
||||
|
@ -743,10 +799,11 @@ static toml_array_t* create_keyarray_in_table(context_t* ctx,
|
|||
e_outofmemory(ctx, FLINE);
|
||||
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 */
|
||||
dest->key = newkey;
|
||||
dest->kind = kind;
|
||||
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 EAT_TOKEN(ctx, typ) \
|
||||
if ((ctx)->tok.tok != typ) e_internal_error(ctx, FLINE); else next_token(ctx, 0)
|
||||
#define SKIP_NEWLINES(ctx, isdotspecial) while (ctx->tok.tok == NEWLINE) next_token(ctx, isdotspecial)
|
||||
#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);
|
||||
|
@ -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)
|
||||
{
|
||||
EAT_TOKEN(ctx, LBRACE);
|
||||
EAT_TOKEN(ctx, LBRACE, 1);
|
||||
|
||||
for (;;) {
|
||||
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 */
|
||||
if (ctx->tok.tok == COMMA) {
|
||||
EAT_TOKEN(ctx, COMMA);
|
||||
EAT_TOKEN(ctx, COMMA, 1);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
|
@ -841,7 +898,7 @@ static void parse_table(context_t* ctx, toml_table_t* tab)
|
|||
return; /* not reached */
|
||||
}
|
||||
|
||||
EAT_TOKEN(ctx, RBRACE);
|
||||
EAT_TOKEN(ctx, RBRACE, 1);
|
||||
}
|
||||
|
||||
static int valtype(const char* val)
|
||||
|
@ -863,10 +920,10 @@ static int valtype(const char* val)
|
|||
/* We are at '[...]' */
|
||||
static void parse_array(context_t* ctx, toml_array_t* arr)
|
||||
{
|
||||
EAT_TOKEN(ctx, LBRACKET);
|
||||
EAT_TOKEN(ctx, LBRACKET, 0);
|
||||
|
||||
for (;;) {
|
||||
SKIP_NEWLINES(ctx);
|
||||
SKIP_NEWLINES(ctx, 0);
|
||||
|
||||
/* until ] */
|
||||
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)
|
||||
arr->type = valtype(arr->u.val[0]);
|
||||
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 */
|
||||
}
|
||||
|
||||
EAT_TOKEN(ctx, STRING);
|
||||
EAT_TOKEN(ctx, STRING, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -917,7 +975,8 @@ static void parse_array(context_t* ctx, toml_array_t* arr)
|
|||
if (arr->kind == 0) arr->kind = 'a';
|
||||
/* check array kind */
|
||||
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 */
|
||||
}
|
||||
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';
|
||||
/* check array kind */
|
||||
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 */
|
||||
}
|
||||
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 */
|
||||
}
|
||||
|
||||
SKIP_NEWLINES(ctx);
|
||||
SKIP_NEWLINES(ctx, 0);
|
||||
|
||||
/* on comma, continue to scan for next element */
|
||||
if (ctx->tok.tok == COMMA) {
|
||||
EAT_TOKEN(ctx, COMMA);
|
||||
EAT_TOKEN(ctx, COMMA, 0);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
|
@ -957,11 +1017,10 @@ static void parse_array(context_t* ctx, toml_array_t* arr)
|
|||
return; /* not reached */
|
||||
}
|
||||
|
||||
EAT_TOKEN(ctx, RBRACKET);
|
||||
EAT_TOKEN(ctx, RBRACKET, 1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* handle lines like these:
|
||||
key = "value"
|
||||
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)
|
||||
{
|
||||
if (ctx->tok.tok != STRING) {
|
||||
e_internal_error(ctx, FLINE);
|
||||
return; /* not reached */
|
||||
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;
|
||||
}
|
||||
|
||||
token_t key = ctx->tok;
|
||||
|
||||
EAT_TOKEN(ctx, STRING);
|
||||
if (ctx->tok.tok != EQUAL) {
|
||||
e_syntax_error(ctx, ctx->tok.lineno, "missing =");
|
||||
return; /* not reached */
|
||||
}
|
||||
|
||||
EAT_TOKEN(ctx, EQUAL);
|
||||
next_token(ctx, 0);
|
||||
|
||||
switch (ctx->tok.tok) {
|
||||
case STRING:
|
||||
|
@ -996,7 +1070,7 @@ static void parse_keyval(context_t* ctx, toml_table_t* tab)
|
|||
return; /* not reached */
|
||||
}
|
||||
|
||||
EAT_TOKEN(ctx, STRING);
|
||||
next_token(ctx, 1);
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -1112,7 +1186,7 @@ static void walk_tabpath(context_t* ctx)
|
|||
break;
|
||||
|
||||
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 */
|
||||
|
||||
default:
|
||||
|
@ -1163,10 +1237,10 @@ static void parse_select(context_t* ctx)
|
|||
and '[ [' would be taken as '[[', which is wrong. */
|
||||
|
||||
/* eat [ or [[ */
|
||||
next_token(ctx, 1 /* DOT IS SPECIAL */);
|
||||
EAT_TOKEN(ctx, LBRACKET, 1);
|
||||
if (llb) {
|
||||
assert(ctx->tok.tok == LBRACKET);
|
||||
next_token(ctx, 1 /* DOT IS SPECIAL */);
|
||||
EAT_TOKEN(ctx, LBRACKET, 1);
|
||||
}
|
||||
|
||||
fill_tabpath(ctx);
|
||||
|
@ -1185,13 +1259,19 @@ static void parse_select(context_t* ctx)
|
|||
ctx->curtab = create_keytable_in_table(ctx, ctx->curtab, z);
|
||||
} else {
|
||||
/* [[x.y.z]] -> create z = [] in x.y */
|
||||
toml_array_t* arr = create_keyarray_in_table(ctx, ctx->curtab, z,
|
||||
1 /*skip_if_exist*/);
|
||||
toml_array_t* arr = 0;
|
||||
{
|
||||
char* zstr = normalize_key(ctx, z);
|
||||
arr = toml_array_in(ctx->curtab, zstr);
|
||||
xfree(zstr);
|
||||
}
|
||||
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;
|
||||
}
|
||||
if (arr->kind == 0) arr->kind = 't';
|
||||
}
|
||||
if (arr->kind != 't') {
|
||||
e_syntax_error(ctx, z.lineno, "array mismatch");
|
||||
return; /* not reached */
|
||||
|
@ -1233,9 +1313,9 @@ static void parse_select(context_t* ctx)
|
|||
e_syntax_error(ctx, ctx->tok.lineno, "expects ]]");
|
||||
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) {
|
||||
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 */
|
||||
}
|
||||
|
||||
EAT_TOKEN(&ctx, NEWLINE);
|
||||
EAT_TOKEN(&ctx, NEWLINE, 1);
|
||||
break;
|
||||
|
||||
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)
|
||||
{
|
||||
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 (*p == 'u') { hexreq = 4; 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");
|
||||
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);
|
||||
}
|
||||
|
||||
/* 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++) {
|
||||
int ch = *p;
|
||||
if (ch == '.' && dotisspecial) break;
|
||||
if ('A' <= ch && ch <= 'Z') continue;
|
||||
if ('a' <= ch && ch <= 'z') continue;
|
||||
if ('0' <= ch && ch <= '9') continue;
|
||||
if (strchr("+-_.:", ch)) continue;
|
||||
if (strchr("0123456789+-_.", ch)) continue;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1720,85 +1845,66 @@ int toml_rtots(const char* src_, toml_timestamp_t* ret)
|
|||
if (! src_) return -1;
|
||||
|
||||
const char* p = src_;
|
||||
const char* q = src_ + strlen(src_);
|
||||
int64_t val;
|
||||
int i;
|
||||
int must_parse_time = 0;
|
||||
|
||||
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 */
|
||||
val = 0;
|
||||
if (q - p > 4 && p[4] == '-') {
|
||||
for (i = 0; i < 10; i++, p++) {
|
||||
int xx = *p;
|
||||
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;
|
||||
if (0 == scan_date(p, year, month, day)) {
|
||||
ret->year = year;
|
||||
ret->month = month;
|
||||
ret->day = day;
|
||||
|
||||
p += 10;
|
||||
if (*p) {
|
||||
// parse the T or space separator
|
||||
if (*p != 'T' && *p != ' ') return -1;
|
||||
must_parse_time = 1;
|
||||
p++;
|
||||
}
|
||||
}
|
||||
if (q == p) return 0;
|
||||
|
||||
/* parse time HH:MM:SS */
|
||||
val = 0;
|
||||
if (q - p < 8) return -1;
|
||||
for (i = 0; i < 8; i++, p++) {
|
||||
int xx = *p;
|
||||
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;
|
||||
if (0 == scan_time(p, hour, minute, second)) {
|
||||
ret->hour = hour;
|
||||
ret->minute = minute;
|
||||
ret->second = second;
|
||||
|
||||
*ret->second = val % 100; val /= 100;
|
||||
*ret->minute = val % 100; val /= 100;
|
||||
*ret->hour = val;
|
||||
|
||||
/* parse millisec */
|
||||
/* optionally, parse millisec */
|
||||
p += 8;
|
||||
if (*p == '.') {
|
||||
val = 0;
|
||||
char* qq;
|
||||
p++;
|
||||
if ('0' <= *p && *p <= '9') {
|
||||
val = (*p++ - '0') * 100;
|
||||
if ('0' <= *p && *p <= '9') {
|
||||
val += (*p++ - '0') * 10;
|
||||
if ('0' <= *p && *p <= '9') {
|
||||
val += (*p++ - '0');
|
||||
errno = 0;
|
||||
*millisec = strtol(p, &qq, 0);
|
||||
if (errno) {
|
||||
return -1;
|
||||
}
|
||||
while (*millisec > 999) {
|
||||
*millisec /= 10;
|
||||
}
|
||||
}
|
||||
ret->millisec = &ret->__buffer.millisec;
|
||||
*ret->millisec = val;
|
||||
}
|
||||
if (q == p) return 0;
|
||||
|
||||
/* parse and copy Z */
|
||||
ret->z = ret->__buffer.z;
|
||||
char* z = ret->z;
|
||||
if (*p == 'Z') {
|
||||
*z++ = *p++;
|
||||
*z = 0;
|
||||
return (p == q) ? 0 : -1;
|
||||
ret->millisec = millisec;
|
||||
p = qq;
|
||||
}
|
||||
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++;
|
||||
|
||||
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;
|
||||
}
|
||||
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] == '.')
|
||||
return -1;
|
||||
|
||||
/* zero must be followed by . or NUL */
|
||||
if (s[0] == '0' && s[1] && s[1] != '.')
|
||||
/* zero must be followed by . or 'e', or NUL */
|
||||
if (s[0] == '0' && s[1] && !strchr("eE.", s[1]))
|
||||
return -1;
|
||||
|
||||
/* 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)
|
||||
{
|
||||
char dummy_errbuf[1];
|
||||
int multiline = 0;
|
||||
const char* sp;
|
||||
const char* sq;
|
||||
|
||||
if (!src) return -1;
|
||||
if (*src != '\'' && *src != '"') return -1;
|
||||
|
||||
|
@ -2003,8 +2101,9 @@ int toml_rtos(const char* src, char** ret)
|
|||
int srclen = strlen(src);
|
||||
if (*src == '\'') {
|
||||
if (0 == strncmp(src, "'''", 3)) {
|
||||
const char* sp = src + 3;
|
||||
const char* sq = src + srclen - 3;
|
||||
multiline = 1;
|
||||
sp = src + 3;
|
||||
sq = src + srclen - 3;
|
||||
/* last 3 chars in src must be ''' */
|
||||
if (! (sp <= sq && 0 == strcmp(sq, "'''")))
|
||||
return -1;
|
||||
|
@ -2015,22 +2114,24 @@ int toml_rtos(const char* src, char** ret)
|
|||
else if (sp[0] == '\r' && sp[1] == '\n')
|
||||
sp += 2;
|
||||
|
||||
*ret = kill_line_ending_backslash(STRNDUP(sp, sq - sp));
|
||||
} else {
|
||||
const char* sp = src + 1;
|
||||
const char* sq = src + srclen - 1;
|
||||
sp = src + 1;
|
||||
sq = src + srclen - 1;
|
||||
/* last char in src must be ' */
|
||||
if (! (sp <= sq && *sq == '\''))
|
||||
return -1;
|
||||
/* copy from sp to p */
|
||||
*ret = STRNDUP(sp, sq - sp);
|
||||
}
|
||||
|
||||
*ret = norm_lit_str(sp, sq - sp,
|
||||
multiline,
|
||||
dummy_errbuf, sizeof(dummy_errbuf));
|
||||
return *ret ? 0 : -1;
|
||||
}
|
||||
|
||||
const char* sp;
|
||||
const char* sq;
|
||||
if (0 == strncmp(src, "\"\"\"", 3)) {
|
||||
multiline = 1;
|
||||
sp = src + 3;
|
||||
sq = src + srclen - 3;
|
||||
if (! (sp <= sq && 0 == strcmp(sq, "\"\"\"")))
|
||||
|
@ -2048,9 +2149,8 @@ int toml_rtos(const char* src, char** ret)
|
|||
return -1;
|
||||
}
|
||||
|
||||
char dummy_errbuf[1];
|
||||
*ret = normalize_string(sp, sq - sp,
|
||||
1, // flag kill_line_ending_backslash
|
||||
*ret = norm_basic_str(sp, sq - sp,
|
||||
multiline,
|
||||
dummy_errbuf, sizeof(dummy_errbuf));
|
||||
return *ret ? 0 : -1;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue