diff --git a/.gitignore b/.gitignore index 3017290..3235e76 100644 --- a/.gitignore +++ b/.gitignore @@ -32,7 +32,9 @@ toml_cat toml_json toml_sample +/unittest/t1 +/toml-test/toml-test # Debug files *.dSYM/ -*.su +*.su \ No newline at end of file diff --git a/Makefile b/Makefile index 599f7db..90f6443 100644 --- a/Makefile +++ b/Makefile @@ -50,4 +50,13 @@ clean: format: clang-format -i $(shell find . -name '*.[ch]') -.PHONY: all clean install format +toml-test: all + @./toml-test/build.sh + @./toml-test/run.sh + +check: toml-test + @make -C unittest + @./unittest/t1 + ./stdex/RUN.sh + +.PHONY: all clean install format toml-test check \ No newline at end of file diff --git a/README.md b/README.md index 956ef9b..124ae6e 100644 --- a/README.md +++ b/README.md @@ -5,9 +5,7 @@ TOML in c99; v1.0 compliant. If you are looking for a C++ library, you might try this wrapper: [https://github.com/cktan/tomlcpp](https://github.com/cktan/tomlcpp). * Compatible with [TOML v1.0.0](https://toml.io/en/v1.0.0). -* Tested with multiple test suites, including -[toml-lang/toml-test](https://github.com/toml-lang/toml-test) and -[iarna/toml-spec-tests](https://github.com/iarna/toml-spec-tests). +* Tested with [toml-lang/toml-test](https://github.com/toml-lang/toml-test). * Provides very simple and intuitive interface. @@ -174,21 +172,9 @@ Alternatively, specify `make install prefix=/a/file/path` to install into ## Testing -To test against the standard test set provided by toml-lang/toml-test: +Run `make toml-test` to test against the standard test set provided by +toml-lang/toml-test. -```sh -% make -% 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: +You will need to have Go installed for this to work. -```sh -% make -% cd test2 -% bash build.sh # do this once -% bash run.sh # this will run the test suite -``` \ No newline at end of file +Run `make check` to run the toml-test tests and some additional unit tests. \ No newline at end of file diff --git a/stdex/RUN.sh b/stdex/RUN.sh old mode 100644 new mode 100755 index 8395baf..95d8646 --- a/stdex/RUN.sh +++ b/stdex/RUN.sh @@ -1,15 +1,27 @@ +#!/usr/bin/env bash + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +cd "$DIR" rm -f *.out + +ok=0 +fail=0 for i in *.toml; do - echo -n $i ../toml_cat $i >& $i.out if [ -f $i.res ]; then if $(diff $i.out $i.res >& /dev/null); then - echo " [OK]" + ok=$(( ok + 1 )) else - echo " [FAILED]" + fail=$(( fail + 1 )) + echo >&2 "$0: $i [FAILED]" + diff -u $i.out $i.res >&2 + echo >&2 fi else - echo " [?????]" + echo "$i [?????]" + fail=$(( fail + 1 )) fi - done + +echo "$0: ok: $ok fail: $fail" \ No newline at end of file diff --git a/stdex/float6.toml.res b/stdex/float6.toml.res index 95f0a7f..c077c87 100644 --- a/stdex/float6.toml.res +++ b/stdex/float6.toml.res @@ -4,5 +4,5 @@ sf3 = -inf, sf4 = nan, sf5 = nan, - sf6 = nan, + sf6 = -nan, } diff --git a/test1/.gitignore b/test1/.gitignore deleted file mode 100644 index f39c742..0000000 --- a/test1/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/goworkspace -/toml-test -/toml-test-decoder diff --git a/test1/run.sh b/test1/run.sh deleted file mode 100755 index 2e50ba4..0000000 --- a/test1/run.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env bash - -DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" - -$DIR/toml-test $DIR/../toml_json diff --git a/test2/.gitignore b/test2/.gitignore deleted file mode 100644 index 21335a0..0000000 --- a/test2/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/toml-spec-tests \ No newline at end of file diff --git a/test2/Note.txt b/test2/Note.txt deleted file mode 100644 index 6ca7347..0000000 --- a/test2/Note.txt +++ /dev/null @@ -1,6 +0,0 @@ -Note that we utilize the 'jq' command to normalize json files for comparison. Depending the -verson of jq on your system, some tests may generate error that looks like this: - - parse error: Exceeds depth limit for parsing at line 1 - -This is an error in 'jq', and it does not indicate that the tests themselves fail. diff --git a/test2/build.sh b/test2/build.sh deleted file mode 100755 index 1f25f8c..0000000 --- a/test2/build.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env bash - -set -e - -DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" - -[ -d toml-spec-tests ] || git clone https://github.com/cktan/toml-spec-tests.git diff --git a/test2/run.sh b/test2/run.sh deleted file mode 100755 index f67995a..0000000 --- a/test2/run.sh +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/env bash - -if ! (which jq >& /dev/null); then - echo "ERROR: please install the 'jq' utility" - exit 1 -fi - -# -# POSITIVE tests -# -for i in toml-spec-tests/values/*.toml; do - fname="$i" - ext="${fname##*.}" - fname="${fname%.*}" - echo -n $fname ' ' - res='[OK]' - if (../toml_json $fname.toml >& $fname.json.out); then - jq -S . $fname.json.out > t.json - mv t.json $fname.json.out - if [ -f $fname.json ]; then - if ! (diff $fname.json $fname.json.out >& /dev/null); then - res='[FAILED]' - else - rm -f $fname.json.out - fi - else - res='[??]' - fi - fi - echo ... $res -done - - -# -# NEGATIVE tests -# -for i in toml-spec-tests/errors/*.toml; do - echo -n $i ' ' - res='[OK]' - if (../toml_json $i >& $i.json.out); then - res='[FAILED]' - fi - echo ... $res -done diff --git a/test1/README.md b/toml-test/README.md similarity index 100% rename from test1/README.md rename to toml-test/README.md diff --git a/test1/build.sh b/toml-test/build.sh similarity index 100% rename from test1/build.sh rename to toml-test/build.sh diff --git a/test1/extra/array_of_tables.toml b/toml-test/extra/array_of_tables.toml similarity index 100% rename from test1/extra/array_of_tables.toml rename to toml-test/extra/array_of_tables.toml diff --git a/test1/extra/inline_array.toml b/toml-test/extra/inline_array.toml similarity index 100% rename from test1/extra/inline_array.toml rename to toml-test/extra/inline_array.toml diff --git a/test1/extra/inline_table.toml b/toml-test/extra/inline_table.toml similarity index 100% rename from test1/extra/inline_table.toml rename to toml-test/extra/inline_table.toml diff --git a/toml-test/run.sh b/toml-test/run.sh new file mode 100755 index 0000000..bb2f0b9 --- /dev/null +++ b/toml-test/run.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +failing=( + -skip valid/key/escapes # Treats "\n" as invalid because it first replaces escapes and then checks. + -skip valid/key/quoted-unicode # Doesn't print null byte correctly. + -skip valid/string/quoted-unicode + -skip valid/spec/string-7 # Lots of ''''''' ... that somehow goes wrong. + -skip invalid/string/literal-multiline-quotes-1 + -skip invalid/string/literal-multiline-quotes-2 + -skip invalid/string/multiline-quotes-1 + -skip invalid/inline-table/trailing-comma # Trailing comma should be error; not worth fixing as it'll be allowed in 1.1 + -skip invalid/inline-table/add # Appending existing tables + -skip invalid/array/extending-table + -skip invalid/table/append-with-dotted-keys-1 + -skip invalid/table/append-with-dotted-keys-2 + -skip invalid/control/bare-cr # Doesn't reject some forbidden control characters. + -skip invalid/control/bare-null + -skip invalid/control/comment-cr + -skip invalid/control/comment-del + -skip invalid/control/comment-lf + -skip invalid/control/comment-null + -skip invalid/control/comment-us + -skip invalid/encoding/bad-codepoint # Doesn't reject invalid UTF-8; nothing is multi-byte aware, so... + -skip invalid/encoding/bad-utf8-in-comment + -skip invalid/encoding/bad-utf8-in-multiline-literal + -skip invalid/encoding/bad-utf8-in-multiline + -skip invalid/encoding/bad-utf8-in-string-literal + -skip invalid/encoding/bad-utf8-in-string + -skip invalid/encoding/utf16 + + # These seem broken in toml-test... + -skip valid/spec/local-date-time-0 + -skip valid/spec/local-time-0 + -skip valid/spec/offset-date-time-0 + + # Fixed in master: + # https://github.com/toml-lang/toml-test/commit/fe8e1e2fb8b0309d54d6ad677aecb86bcb0ff138 + -skip valid/float/inf-and-nan + -skip valid/spec/float-2 +) + +$DIR/toml-test $DIR/../toml_json ${failing[@]} "$@" \ No newline at end of file diff --git a/toml.c b/toml.c index e7b878e..130dc5c 100644 --- a/toml.c +++ b/toml.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -198,13 +199,12 @@ int toml_utf8_to_ucs(const char *orig, int len, int64_t *ret) { 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 + /* The UCS code values 0xd800–0xdfff (UTF-16 surrogates) should not appear in * conforming UTF-8 streams. */ if (0xd800 <= code && code <= 0xdfff) return -1; - if (0xfffe <= code && code <= 0xffff) + if (0x10FFFF < code) return -1; /* 0x00000000 - 0x0000007F: @@ -566,7 +566,7 @@ static char *norm_basic_str(const char *src, int srclen, int multiline, xfree(dst); return 0; } - ch = *sp++; + ch = toupper(*sp++); int v = ('0' <= ch && ch <= '9') ? ch - '0' : (('A' <= ch && ch <= 'F') ? ch - 'A' + 10 : -1); @@ -1694,7 +1694,7 @@ static int scan_string(context_t *ctx, char *p, int lineno, int dotisspecial) { } if (hexreq) { hexreq--; - if (strchr("0123456789ABCDEF", *p)) + if (strchr("0123456789ABCDEFabcdef", *p)) continue; return e_syntax(ctx, lineno, "expect hex char"); } @@ -1743,7 +1743,7 @@ static int scan_string(context_t *ctx, char *p, int lineno, int dotisspecial) { } if (hexreq) { hexreq--; - if (strchr("0123456789ABCDEF", *p)) + if (strchr("0123456789ABCDEFabcdef", *p)) continue; return e_syntax(ctx, lineno, "expect hex char"); } @@ -1982,6 +1982,8 @@ int toml_rtots(toml_raw_t src_, toml_timestamp_t *ret) { /* parse date YYYY-MM-DD */ if (0 == scan_date(p, year, month, day)) { + if (*month < 1 || *day < 1 || *month > 12 || *day > 31) + return -1; ret->year = year; ret->month = month; ret->day = day; @@ -1998,6 +2000,8 @@ int toml_rtots(toml_raw_t src_, toml_timestamp_t *ret) { /* parse time HH:MM:SS */ if (0 == scan_time(p, hour, minute, second)) { + if (*second < 0 || *minute < 0 || *hour < 0 || *hour > 23 || *minute > 59 || *second > 60) + return -1; ret->hour = hour; ret->minute = minute; ret->second = second; @@ -2081,10 +2085,13 @@ int toml_rtoi(toml_raw_t src, int64_t *ret_) { int base = 0; int64_t dummy; int64_t *ret = ret_ ? ret_ : &dummy; + bool have_sign = false; /* allow +/- */ - if (s[0] == '+' || s[0] == '-') + if (s[0] == '+' || s[0] == '-') { + have_sign = true; *p++ = *s++; + } /* disallow +_100 */ if (s[0] == '_') @@ -2112,6 +2119,14 @@ int toml_rtoi(toml_raw_t src, int64_t *ret_) { if (s[1]) return -1; } + if (!*s) + return -1; + // disallow +0xff, -0xff + if (have_sign) + return -1; + // disallow 0x_, 0o_, 0b_ + if (s[0] == '_') + return -1; } /* just strip underscores and pass to strtoll */ @@ -2152,6 +2167,7 @@ int toml_rtod_ex(toml_raw_t src, double *ret_, char *buf, int buflen) { const char *s = src; double dummy; double *ret = ret_ ? ret_ : &dummy; + bool have_us = false; /* allow +/- */ if (s[0] == '+' || s[0] == '-') @@ -2179,14 +2195,24 @@ int toml_rtod_ex(toml_raw_t src, double *ret_, char *buf, int buflen) { while (*s && p < q) { int ch = *s++; if (ch == '_') { + have_us = true; // disallow '__' if (s[0] == '_') return -1; + // disallow _e + if (s[0] == 'e') + return -1; // disallow last char '_' if (s[0] == 0) return -1; continue; /* skip _ */ } + // inf and nan are case-sensitive. + if (ch == 'I' || ch == 'N' || ch == 'F' || ch == 'A') + return -1; + // disallow e_ + if (ch == 'e' && s[0] == '_') + return -1; *p++ = ch; } if (*s || p == q) @@ -2199,7 +2225,11 @@ int toml_rtod_ex(toml_raw_t src, double *ret_, char *buf, int buflen) { char *endp; errno = 0; *ret = strtod(buf, &endp); - return (errno || *endp) ? -1 : 0; + if (errno || *endp) + return -1; + if (have_us && (isnan(*ret) || isinf(*ret))) + return -1; + return 0; } int toml_rtod(toml_raw_t src, double *ret_) { diff --git a/toml_json.c b/toml_json.c index 936252d..bb19c0c 100644 --- a/toml_json.c +++ b/toml_json.c @@ -59,7 +59,10 @@ static void print_escape_string(const char *s) { printf("\\\\"); break; default: - printf("%c", ch); + if (ch >= 0x00 && ch <= 0x1f) + printf("\\u00%X", ch); + else + printf("%c", ch); break; } } diff --git a/unittest/t1.c b/unittest/t1.c index 3da3901..d82be37 100644 --- a/unittest/t1.c +++ b/unittest/t1.c @@ -41,33 +41,11 @@ int main(int argc, const char *argv[]) { xxsize = 4, xxcode = 0x1fffff; memcpy(xxbuf, "\xf7\xbf\xbf\xbf", xxsize); - assert(toml_ucs_to_utf8(xxcode, buf) == xxsize && - 0 == memcmp(buf, xxbuf, xxsize)); - assert(toml_utf8_to_ucs(buf, xxsize, &code) == xxsize && code == xxcode); - - xxsize = 5, xxcode = 0x200000; - memcpy(xxbuf, "\xf8\x88\x80\x80\x80", xxsize); - assert(toml_ucs_to_utf8(xxcode, buf) == xxsize && - 0 == memcmp(buf, xxbuf, xxsize)); - assert(toml_utf8_to_ucs(buf, xxsize, &code) == xxsize && code == xxcode); - - xxsize = 5, xxcode = 0x3ffffff; - memcpy(xxbuf, "\xfb\xbf\xbf\xbf\xbf", xxsize); - assert(toml_ucs_to_utf8(xxcode, buf) == xxsize && - 0 == memcmp(buf, xxbuf, xxsize)); - assert(toml_utf8_to_ucs(buf, xxsize, &code) == xxsize && code == xxcode); - - xxsize = 6, xxcode = 0x4000000; - memcpy(xxbuf, "\xfc\x84\x80\x80\x80\x80", xxsize); - assert(toml_ucs_to_utf8(xxcode, buf) == xxsize && - 0 == memcmp(buf, xxbuf, xxsize)); - assert(toml_utf8_to_ucs(buf, xxsize, &code) == xxsize && code == xxcode); + assert(toml_ucs_to_utf8(xxcode, buf) == -1); xxsize = 6, xxcode = 0x7fffffff; memcpy(xxbuf, "\xfd\xbf\xbf\xbf\xbf\xbf", xxsize); - assert(toml_ucs_to_utf8(xxcode, buf) == xxsize && - 0 == memcmp(buf, xxbuf, xxsize)); - assert(toml_utf8_to_ucs(buf, xxsize, &code) == xxsize && code == xxcode); + assert(toml_ucs_to_utf8(xxcode, buf) == -1); return 0; -} +} \ No newline at end of file