├── .editorconfig ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── sample.toml ├── toml.c ├── toml.h ├── toml_json.cpp ├── toml_sample.cpp ├── tomlcpp.cpp └── tomlcpp.hpp /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = false 6 | trim_trailing_whitespace = true 7 | 8 | [*.{c,h,cpp,hpp}] 9 | indent_style = tab 10 | indent_size = 4 11 | 12 | [Makefile] 13 | indent_style = tab 14 | indent_size = 4 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | toml_json 3 | toml_sample 4 | 5 | # Prerequisites 6 | *.d 7 | 8 | # Compiled Object files 9 | *.slo 10 | *.lo 11 | *.o 12 | *.obj 13 | 14 | # Precompiled Headers 15 | *.gch 16 | *.pch 17 | 18 | # Compiled Dynamic libraries 19 | *.so 20 | *.dylib 21 | *.dll 22 | 23 | # Fortran module files 24 | *.mod 25 | *.smod 26 | 27 | # Compiled Static libraries 28 | *.lai 29 | *.la 30 | *.a 31 | *.lib 32 | 33 | # Executables 34 | *.exe 35 | *.out 36 | *.app 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 CK Tan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | HFILES = toml.h tomlcpp.hpp 2 | CFILES = toml.c 3 | CPPFILES = tomlcpp.cpp 4 | OBJ = $(CFILES:.c=.o) $(CPPFILES:.cpp=.o) 5 | EXEC = toml_json toml_sample 6 | 7 | CFLAGS = -Wall -Wextra -fpic 8 | LIB = libtomlcpp.a 9 | LIB_SHARED = libtomlcpp.so 10 | 11 | # to compile for debug: make DEBUG=1 12 | # to compile for no debug: make 13 | ifdef DEBUG 14 | CFLAGS += -O0 -g 15 | else 16 | CFLAGS += -O2 -DNDEBUG 17 | endif 18 | CXXFLAGS := $(CFLAGS) -std=c++17 19 | CFLAGS += -std=c99 20 | 21 | 22 | all: $(LIB) $(LIB_SHARED) $(EXEC) 23 | 24 | *.o: $(HFILES) 25 | 26 | libtomlcpp.a: $(OBJ) 27 | ar -rcs $@ $^ 28 | 29 | libtomlcpp.so: $(OBJ) 30 | $(CXX) $(CXXFLAGS) -shared -o $@ $^ 31 | 32 | $(EXEC): $(LIB) 33 | 34 | prefix ?= /usr/local 35 | 36 | install: all 37 | install -d ${prefix}/include ${prefix}/lib 38 | install toml.h ${prefix}/include 39 | install tomlcpp.hpp ${prefix}/include 40 | install $(LIB) ${prefix}/lib 41 | install $(LIB_SHARED) ${prefix}/lib 42 | 43 | clean: 44 | rm -f *.o $(EXEC) $(LIB) $(LIB_SHARED) 45 | 46 | format: 47 | clang-format -i $(shell find . -name '*.[ch]') $(shell find . -name '*.[ch]pp') 48 | 49 | .PHONY: all clean install format 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tomlcpp 2 | TOML in C++; v1.0 compliant. 3 | 4 | This is a C++ wrapper around the C library available here: https://github.com/cktan/tomlc99. 5 | 6 | * Compatible with [TOML v1.0.0](https://toml.io/en/v1.0.0). 7 | * Tested with multiple test suites, including 8 | [BurntSushi/toml-test](https://github.com/BurntSushi/toml-test) and 9 | [iarna/toml-spec-tests](https://github.com/iarna/toml-spec-tests). 10 | * Does not throw C++ exceptions. 11 | * Provides very simple and intuitive interface. 12 | 13 | 14 | ## Usage 15 | 16 | Here is a simple example that parses this config file: 17 | 18 | ``` 19 | [server] 20 | host = "example.com" 21 | port = [ 8080, 8181, 8282 ] 22 | ``` 23 | 24 | Steps for getting values: 25 | 26 | 1. Call toml::parseFile on a toml file 27 | 2. Get the top-level table 28 | 3. Get values from the top-level table 29 | 4. Examine the values 30 | 31 | ```c++ 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include "tomlcpp.hpp" 38 | 39 | using std::cerr; 40 | using std::cout; 41 | 42 | void error(std::string msg) 43 | { 44 | cerr << "ERROR: " << msg << "\n"; 45 | exit(1); 46 | } 47 | 48 | int main() 49 | { 50 | // 1. parse file 51 | auto res = toml::parseFile("sample.toml"); 52 | if (!res.table) { 53 | error("cannot parse file: " + res.errmsg); 54 | } 55 | 56 | // 2. get top level table 57 | auto server = res.table->getTable("server"); 58 | if (!server) { 59 | error("missing [server]"); 60 | } 61 | 62 | // 3. extract values from the top level table 63 | auto [ ok, host ] = server->getString("host"); 64 | if (!ok) { 65 | fatal("missing or bad host entry"); 66 | } 67 | 68 | auto portArray = server->getArray("port"); 69 | if (!portArray) { 70 | fatal("missing 'port' array"); 71 | } 72 | 73 | // 4. examine the values 74 | cout << "host: " << host << "\n"; 75 | cout << "port: "; 76 | for (int i = 0; ; i++) { 77 | auto p = portArray->getInt(i); 78 | if (!p.first) break; 79 | 80 | cout << p.second << " "; 81 | } 82 | cout << "\n"; 83 | 84 | return 0; 85 | } 86 | ``` 87 | 88 | ### Parsing 89 | 90 | To parse a toml text or file, invoke `toml::parse(text)` or `toml::parseFile(path)`. 91 | The return value is a `Result` struct. On success, the `Result.table` will have a non-NULL 92 | pointer to the toml table content. Otherwise, the `Result.table` will be NULL, and `Result.errmsg` 93 | stores a string describing the error. 94 | 95 | ### Traversing table 96 | 97 | Toml tables are key-value maps. 98 | 99 | #### Keys 100 | 101 | The method `Table::keys()` returns a vector of keys. 102 | 103 | #### Content 104 | 105 | To extract values in a Table, call the `Table::getXXXX(key)` methods and supply the key: 106 | 107 | ``` 108 | Table::getString(key) 109 | Table::getBool(key) 110 | Table::getInt(key) 111 | Table::getDouble(key) 112 | Table::getTimestamp(key) 113 | ``` 114 | 115 | These methods return a C++ `pair`, in which `pair.first` is a success indicator, and `pair.second` is the result value. 116 | 117 | To access table or array in a Table, use these methods which return a `unique_ptr` to a Table or Array: 118 | 119 | ``` 120 | Table::getTable(key) 121 | Table::getArray(key) 122 | ``` 123 | 124 | ### Traversing array 125 | 126 | Similarly, to extract the primitive content of a toml::Array, call one of these methods: 127 | 128 | ``` 129 | Array::getString(idx) 130 | Array::getBool(idx) 131 | Array::getInt(idx) 132 | Array::getDouble(idx) 133 | Array::getTimestamp(idx) 134 | Array::getArray(idx) 135 | Array::getTable(idx) 136 | ``` 137 | 138 | 139 | ## Building and installing 140 | 141 | A normal *make* suffices. You can also simply include the 142 | `toml.c`, `toml.h`, `tomlcpp.cpp`, `tomlcpp.hpp` files in your project. 143 | 144 | Invoking `make install` will install the header and library files into 145 | /usr/local/{include,lib}. 146 | 147 | Alternatively, specify `make install prefix=/a/file/path` to install into 148 | /a/file/path/{include,lib}. 149 | -------------------------------------------------------------------------------- /sample.toml: -------------------------------------------------------------------------------- 1 | [server] 2 | host = "example.com" 3 | port = [ 8080, 8181, 8282 ] 4 | -------------------------------------------------------------------------------- /toml.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) CK Tan 6 | https://github.com/cktan/tomlc99 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | 26 | */ 27 | #define _POSIX_C_SOURCE 200809L 28 | #include "toml.h" 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | static void *(*ppmalloc)(size_t) = malloc; 39 | static void (*ppfree)(void *) = free; 40 | 41 | void toml_set_memutil(void *(*xxmalloc)(size_t), void (*xxfree)(void *)) { 42 | if (xxmalloc) 43 | ppmalloc = xxmalloc; 44 | if (xxfree) 45 | ppfree = xxfree; 46 | } 47 | 48 | #define MALLOC(a) ppmalloc(a) 49 | #define FREE(a) ppfree(a) 50 | 51 | #define malloc(x) error - forbidden - use MALLOC instead 52 | #define free(x) error - forbidden - use FREE instead 53 | #define calloc(x, y) error - forbidden - use CALLOC instead 54 | 55 | static void *CALLOC(size_t nmemb, size_t sz) { 56 | int nb = sz * nmemb; 57 | void *p = MALLOC(nb); 58 | if (p) { 59 | memset(p, 0, nb); 60 | } 61 | return p; 62 | } 63 | 64 | // some old platforms define strdup macro -- drop it. 65 | #undef strdup 66 | #define strdup(x) error - forbidden - use STRDUP instead 67 | 68 | static char *STRDUP(const char *s) { 69 | int len = strlen(s); 70 | char *p = MALLOC(len + 1); 71 | if (p) { 72 | memcpy(p, s, len); 73 | p[len] = 0; 74 | } 75 | return p; 76 | } 77 | 78 | // some old platforms define strndup macro -- drop it. 79 | #undef strndup 80 | #define strndup(x) error - forbiden - use STRNDUP instead 81 | 82 | static char *STRNDUP(const char *s, size_t n) { 83 | size_t len = strnlen(s, n); 84 | char *p = MALLOC(len + 1); 85 | if (p) { 86 | memcpy(p, s, len); 87 | p[len] = 0; 88 | } 89 | return p; 90 | } 91 | 92 | /** 93 | * Convert a char in utf8 into UCS, and store it in *ret. 94 | * Return #bytes consumed or -1 on failure. 95 | */ 96 | int toml_utf8_to_ucs(const char *orig, int len, int64_t *ret) { 97 | const unsigned char *buf = (const unsigned char *)orig; 98 | unsigned i = *buf++; 99 | int64_t v; 100 | 101 | /* 0x00000000 - 0x0000007F: 102 | 0xxxxxxx 103 | */ 104 | if (0 == (i >> 7)) { 105 | if (len < 1) 106 | return -1; 107 | v = i; 108 | return *ret = v, 1; 109 | } 110 | /* 0x00000080 - 0x000007FF: 111 | 110xxxxx 10xxxxxx 112 | */ 113 | if (0x6 == (i >> 5)) { 114 | if (len < 2) 115 | return -1; 116 | v = i & 0x1f; 117 | for (int j = 0; j < 1; j++) { 118 | i = *buf++; 119 | if (0x2 != (i >> 6)) 120 | return -1; 121 | v = (v << 6) | (i & 0x3f); 122 | } 123 | return *ret = v, (const char *)buf - orig; 124 | } 125 | 126 | /* 0x00000800 - 0x0000FFFF: 127 | 1110xxxx 10xxxxxx 10xxxxxx 128 | */ 129 | if (0xE == (i >> 4)) { 130 | if (len < 3) 131 | return -1; 132 | v = i & 0x0F; 133 | for (int j = 0; j < 2; j++) { 134 | i = *buf++; 135 | if (0x2 != (i >> 6)) 136 | return -1; 137 | v = (v << 6) | (i & 0x3f); 138 | } 139 | return *ret = v, (const char *)buf - orig; 140 | } 141 | 142 | /* 0x00010000 - 0x001FFFFF: 143 | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 144 | */ 145 | if (0x1E == (i >> 3)) { 146 | if (len < 4) 147 | return -1; 148 | v = i & 0x07; 149 | for (int j = 0; j < 3; j++) { 150 | i = *buf++; 151 | if (0x2 != (i >> 6)) 152 | return -1; 153 | v = (v << 6) | (i & 0x3f); 154 | } 155 | return *ret = v, (const char *)buf - orig; 156 | } 157 | 158 | /* 0x00200000 - 0x03FFFFFF: 159 | 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 160 | */ 161 | if (0x3E == (i >> 2)) { 162 | if (len < 5) 163 | return -1; 164 | v = i & 0x03; 165 | for (int j = 0; j < 4; j++) { 166 | i = *buf++; 167 | if (0x2 != (i >> 6)) 168 | return -1; 169 | v = (v << 6) | (i & 0x3f); 170 | } 171 | return *ret = v, (const char *)buf - orig; 172 | } 173 | 174 | /* 0x04000000 - 0x7FFFFFFF: 175 | 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 176 | */ 177 | if (0x7e == (i >> 1)) { 178 | if (len < 6) 179 | return -1; 180 | v = i & 0x01; 181 | for (int j = 0; j < 5; j++) { 182 | i = *buf++; 183 | if (0x2 != (i >> 6)) 184 | return -1; 185 | v = (v << 6) | (i & 0x3f); 186 | } 187 | return *ret = v, (const char *)buf - orig; 188 | } 189 | return -1; 190 | } 191 | 192 | /** 193 | * Convert a UCS char to utf8 code, and return it in buf. 194 | * Return #bytes used in buf to encode the char, or 195 | * -1 on error. 196 | */ 197 | int toml_ucs_to_utf8(int64_t code, char buf[6]) { 198 | /* http://stackoverflow.com/questions/6240055/manually-converting-unicode-codepoints-into-utf-8-and-utf-16 199 | */ 200 | /* The UCS code values 0xd800–0xdfff (UTF-16 surrogates) as well 201 | * as 0xfffe and 0xffff (UCS noncharacters) should not appear in 202 | * conforming UTF-8 streams. 203 | */ 204 | if (0xd800 <= code && code <= 0xdfff) 205 | return -1; 206 | if (0xfffe <= code && code <= 0xffff) 207 | return -1; 208 | 209 | /* 0x00000000 - 0x0000007F: 210 | 0xxxxxxx 211 | */ 212 | if (code < 0) 213 | return -1; 214 | if (code <= 0x7F) { 215 | buf[0] = (unsigned char)code; 216 | return 1; 217 | } 218 | 219 | /* 0x00000080 - 0x000007FF: 220 | 110xxxxx 10xxxxxx 221 | */ 222 | if (code <= 0x000007FF) { 223 | buf[0] = 0xc0 | (code >> 6); 224 | buf[1] = 0x80 | (code & 0x3f); 225 | return 2; 226 | } 227 | 228 | /* 0x00000800 - 0x0000FFFF: 229 | 1110xxxx 10xxxxxx 10xxxxxx 230 | */ 231 | if (code <= 0x0000FFFF) { 232 | buf[0] = 0xe0 | (code >> 12); 233 | buf[1] = 0x80 | ((code >> 6) & 0x3f); 234 | buf[2] = 0x80 | (code & 0x3f); 235 | return 3; 236 | } 237 | 238 | /* 0x00010000 - 0x001FFFFF: 239 | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 240 | */ 241 | if (code <= 0x001FFFFF) { 242 | buf[0] = 0xf0 | (code >> 18); 243 | buf[1] = 0x80 | ((code >> 12) & 0x3f); 244 | buf[2] = 0x80 | ((code >> 6) & 0x3f); 245 | buf[3] = 0x80 | (code & 0x3f); 246 | return 4; 247 | } 248 | 249 | /* 0x00200000 - 0x03FFFFFF: 250 | 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 251 | */ 252 | if (code <= 0x03FFFFFF) { 253 | buf[0] = 0xf8 | (code >> 24); 254 | buf[1] = 0x80 | ((code >> 18) & 0x3f); 255 | buf[2] = 0x80 | ((code >> 12) & 0x3f); 256 | buf[3] = 0x80 | ((code >> 6) & 0x3f); 257 | buf[4] = 0x80 | (code & 0x3f); 258 | return 5; 259 | } 260 | 261 | /* 0x04000000 - 0x7FFFFFFF: 262 | 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 263 | */ 264 | if (code <= 0x7FFFFFFF) { 265 | buf[0] = 0xfc | (code >> 30); 266 | buf[1] = 0x80 | ((code >> 24) & 0x3f); 267 | buf[2] = 0x80 | ((code >> 18) & 0x3f); 268 | buf[3] = 0x80 | ((code >> 12) & 0x3f); 269 | buf[4] = 0x80 | ((code >> 6) & 0x3f); 270 | buf[5] = 0x80 | (code & 0x3f); 271 | return 6; 272 | } 273 | 274 | return -1; 275 | } 276 | 277 | /* 278 | * TOML has 3 data structures: value, array, table. 279 | * Each of them can have identification key. 280 | */ 281 | typedef struct toml_keyval_t toml_keyval_t; 282 | struct toml_keyval_t { 283 | const char *key; /* key to this value */ 284 | const char *val; /* the raw value */ 285 | }; 286 | 287 | typedef struct toml_arritem_t toml_arritem_t; 288 | struct toml_arritem_t { 289 | int valtype; /* for value kind: 'i'nt, 'd'ouble, 'b'ool, 's'tring, 't'ime, 290 | 'D'ate, 'T'imestamp */ 291 | char *val; 292 | toml_array_t *arr; 293 | toml_table_t *tab; 294 | }; 295 | 296 | struct toml_array_t { 297 | const char *key; /* key to this array */ 298 | int kind; /* element kind: 'v'alue, 'a'rray, or 't'able, 'm'ixed */ 299 | int type; /* for value kind: 'i'nt, 'd'ouble, 'b'ool, 's'tring, 't'ime, 300 | 'D'ate, 'T'imestamp, 'm'ixed */ 301 | 302 | int nitem; /* number of elements */ 303 | toml_arritem_t *item; 304 | }; 305 | 306 | struct toml_table_t { 307 | const char *key; /* key to this table */ 308 | bool implicit; /* table was created implicitly */ 309 | bool readonly; /* no more modification allowed */ 310 | 311 | /* key-values in the table */ 312 | int nkval; 313 | toml_keyval_t **kval; 314 | 315 | /* arrays in the table */ 316 | int narr; 317 | toml_array_t **arr; 318 | 319 | /* tables in the table */ 320 | int ntab; 321 | toml_table_t **tab; 322 | }; 323 | 324 | static inline void xfree(const void *x) { 325 | if (x) 326 | FREE((void *)(intptr_t)x); 327 | } 328 | 329 | enum tokentype_t { 330 | INVALID, 331 | DOT, 332 | COMMA, 333 | EQUAL, 334 | LBRACE, 335 | RBRACE, 336 | NEWLINE, 337 | LBRACKET, 338 | RBRACKET, 339 | STRING, 340 | }; 341 | typedef enum tokentype_t tokentype_t; 342 | 343 | typedef struct token_t token_t; 344 | struct token_t { 345 | tokentype_t tok; 346 | int lineno; 347 | char *ptr; /* points into context->start */ 348 | int len; 349 | int eof; 350 | }; 351 | 352 | typedef struct context_t context_t; 353 | struct context_t { 354 | char *start; 355 | char *stop; 356 | char *errbuf; 357 | int errbufsz; 358 | 359 | token_t tok; 360 | toml_table_t *root; 361 | toml_table_t *curtab; 362 | 363 | struct { 364 | int top; 365 | char *key[10]; 366 | token_t tok[10]; 367 | } tpath; 368 | }; 369 | 370 | #define STRINGIFY(x) #x 371 | #define TOSTRING(x) STRINGIFY(x) 372 | #define FLINE __FILE__ ":" TOSTRING(__LINE__) 373 | 374 | static int next_token(context_t *ctx, int dotisspecial); 375 | 376 | /* 377 | Error reporting. Call when an error is detected. Always return -1. 378 | */ 379 | static int e_outofmemory(context_t *ctx, const char *fline) { 380 | snprintf(ctx->errbuf, ctx->errbufsz, "ERROR: out of memory (%s)", fline); 381 | return -1; 382 | } 383 | 384 | static int e_internal(context_t *ctx, const char *fline) { 385 | snprintf(ctx->errbuf, ctx->errbufsz, "internal error (%s)", fline); 386 | return -1; 387 | } 388 | 389 | static int e_syntax(context_t *ctx, int lineno, const char *msg) { 390 | snprintf(ctx->errbuf, ctx->errbufsz, "line %d: %s", lineno, msg); 391 | return -1; 392 | } 393 | 394 | static int e_badkey(context_t *ctx, int lineno) { 395 | snprintf(ctx->errbuf, ctx->errbufsz, "line %d: bad key", lineno); 396 | return -1; 397 | } 398 | 399 | static int e_keyexists(context_t *ctx, int lineno) { 400 | snprintf(ctx->errbuf, ctx->errbufsz, "line %d: key exists", lineno); 401 | return -1; 402 | } 403 | 404 | static int e_forbid(context_t *ctx, int lineno, const char *msg) { 405 | snprintf(ctx->errbuf, ctx->errbufsz, "line %d: %s", lineno, msg); 406 | return -1; 407 | } 408 | 409 | static void *expand(void *p, int sz, int newsz) { 410 | void *s = MALLOC(newsz); 411 | if (!s) 412 | return 0; 413 | 414 | memcpy(s, p, sz); 415 | FREE(p); 416 | return s; 417 | } 418 | 419 | static void **expand_ptrarr(void **p, int n) { 420 | void **s = MALLOC((n + 1) * sizeof(void *)); 421 | if (!s) 422 | return 0; 423 | 424 | s[n] = 0; 425 | memcpy(s, p, n * sizeof(void *)); 426 | FREE(p); 427 | return s; 428 | } 429 | 430 | static toml_arritem_t *expand_arritem(toml_arritem_t *p, int n) { 431 | toml_arritem_t *pp = expand(p, n * sizeof(*p), (n + 1) * sizeof(*p)); 432 | if (!pp) 433 | return 0; 434 | 435 | memset(&pp[n], 0, sizeof(pp[n])); 436 | return pp; 437 | } 438 | 439 | static char *norm_lit_str(const char *src, int srclen, int multiline, 440 | char *errbuf, int errbufsz) { 441 | char *dst = 0; /* will write to dst[] and return it */ 442 | int max = 0; /* max size of dst[] */ 443 | int off = 0; /* cur offset in dst[] */ 444 | const char *sp = src; 445 | const char *sq = src + srclen; 446 | int ch; 447 | 448 | /* scan forward on src */ 449 | for (;;) { 450 | if (off >= max - 10) { /* have some slack for misc stuff */ 451 | int newmax = max + 50; 452 | char *x = expand(dst, max, newmax); 453 | if (!x) { 454 | xfree(dst); 455 | snprintf(errbuf, errbufsz, "out of memory"); 456 | return 0; 457 | } 458 | dst = x; 459 | max = newmax; 460 | } 461 | 462 | /* finished? */ 463 | if (sp >= sq) 464 | break; 465 | 466 | ch = *sp++; 467 | /* control characters other than tab is not allowed */ 468 | if ((0 <= ch && ch <= 0x08) || (0x0a <= ch && ch <= 0x1f) || (ch == 0x7f)) { 469 | if (!(multiline && (ch == '\r' || ch == '\n'))) { 470 | xfree(dst); 471 | snprintf(errbuf, errbufsz, "invalid char U+%04x", ch); 472 | return 0; 473 | } 474 | } 475 | 476 | // a plain copy suffice 477 | dst[off++] = ch; 478 | } 479 | 480 | dst[off++] = 0; 481 | return dst; 482 | } 483 | 484 | /* 485 | * Convert src to raw unescaped utf-8 string. 486 | * Returns NULL if error with errmsg in errbuf. 487 | */ 488 | static char *norm_basic_str(const char *src, int srclen, int multiline, 489 | char *errbuf, int errbufsz) { 490 | char *dst = 0; /* will write to dst[] and return it */ 491 | int max = 0; /* max size of dst[] */ 492 | int off = 0; /* cur offset in dst[] */ 493 | const char *sp = src; 494 | const char *sq = src + srclen; 495 | int ch; 496 | 497 | /* scan forward on src */ 498 | for (;;) { 499 | if (off >= max - 10) { /* have some slack for misc stuff */ 500 | int newmax = max + 50; 501 | char *x = expand(dst, max, newmax); 502 | if (!x) { 503 | xfree(dst); 504 | snprintf(errbuf, errbufsz, "out of memory"); 505 | return 0; 506 | } 507 | dst = x; 508 | max = newmax; 509 | } 510 | 511 | /* finished? */ 512 | if (sp >= sq) 513 | break; 514 | 515 | ch = *sp++; 516 | if (ch != '\\') { 517 | /* these chars must be escaped: U+0000 to U+0008, U+000A to U+001F, U+007F 518 | */ 519 | if ((0 <= ch && ch <= 0x08) || (0x0a <= ch && ch <= 0x1f) || 520 | (ch == 0x7f)) { 521 | if (!(multiline && (ch == '\r' || ch == '\n'))) { 522 | xfree(dst); 523 | snprintf(errbuf, errbufsz, "invalid char U+%04x", ch); 524 | return 0; 525 | } 526 | } 527 | 528 | // a plain copy suffice 529 | dst[off++] = ch; 530 | continue; 531 | } 532 | 533 | /* ch was backslash. we expect the escape char. */ 534 | if (sp >= sq) { 535 | snprintf(errbuf, errbufsz, "last backslash is invalid"); 536 | xfree(dst); 537 | return 0; 538 | } 539 | 540 | /* for multi-line, we want to kill line-ending-backslash ... */ 541 | if (multiline) { 542 | 543 | // if there is only whitespace after the backslash ... 544 | if (sp[strspn(sp, " \t\r")] == '\n') { 545 | /* skip all the following whitespaces */ 546 | sp += strspn(sp, " \t\r\n"); 547 | continue; 548 | } 549 | } 550 | 551 | /* get the escaped char */ 552 | ch = *sp++; 553 | switch (ch) { 554 | case 'u': 555 | case 'U': { 556 | int64_t ucs = 0; 557 | int nhex = (ch == 'u' ? 4 : 8); 558 | for (int i = 0; i < nhex; i++) { 559 | if (sp >= sq) { 560 | snprintf(errbuf, errbufsz, "\\%c expects %d hex chars", ch, nhex); 561 | xfree(dst); 562 | return 0; 563 | } 564 | ch = *sp++; 565 | int v = ('0' <= ch && ch <= '9') 566 | ? ch - '0' 567 | : (('A' <= ch && ch <= 'F') ? ch - 'A' + 10 : -1); 568 | if (-1 == v) { 569 | snprintf(errbuf, errbufsz, "invalid hex chars for \\u or \\U"); 570 | xfree(dst); 571 | return 0; 572 | } 573 | ucs = ucs * 16 + v; 574 | } 575 | int n = toml_ucs_to_utf8(ucs, &dst[off]); 576 | if (-1 == n) { 577 | snprintf(errbuf, errbufsz, "illegal ucs code in \\u or \\U"); 578 | xfree(dst); 579 | return 0; 580 | } 581 | off += n; 582 | } 583 | continue; 584 | 585 | case 'b': 586 | ch = '\b'; 587 | break; 588 | case 't': 589 | ch = '\t'; 590 | break; 591 | case 'n': 592 | ch = '\n'; 593 | break; 594 | case 'f': 595 | ch = '\f'; 596 | break; 597 | case 'r': 598 | ch = '\r'; 599 | break; 600 | case '"': 601 | ch = '"'; 602 | break; 603 | case '\\': 604 | ch = '\\'; 605 | break; 606 | default: 607 | snprintf(errbuf, errbufsz, "illegal escape char \\%c", ch); 608 | xfree(dst); 609 | return 0; 610 | } 611 | 612 | dst[off++] = ch; 613 | } 614 | 615 | // Cap with NUL and return it. 616 | dst[off++] = 0; 617 | return dst; 618 | } 619 | 620 | /* Normalize a key. Convert all special chars to raw unescaped utf-8 chars. */ 621 | static char *normalize_key(context_t *ctx, token_t strtok) { 622 | const char *sp = strtok.ptr; 623 | const char *sq = strtok.ptr + strtok.len; 624 | int lineno = strtok.lineno; 625 | char *ret; 626 | int ch = *sp; 627 | char ebuf[80]; 628 | 629 | /* handle quoted string */ 630 | if (ch == '\'' || ch == '\"') { 631 | /* if ''' or """, take 3 chars off front and back. Else, take 1 char off. */ 632 | int multiline = 0; 633 | if (sp[1] == ch && sp[2] == ch) { 634 | sp += 3, sq -= 3; 635 | multiline = 1; 636 | } else 637 | sp++, sq--; 638 | 639 | if (ch == '\'') { 640 | /* for single quote, take it verbatim. */ 641 | if (!(ret = STRNDUP(sp, sq - sp))) { 642 | e_outofmemory(ctx, FLINE); 643 | return 0; 644 | } 645 | } else { 646 | /* for double quote, we need to normalize */ 647 | ret = norm_basic_str(sp, sq - sp, multiline, ebuf, sizeof(ebuf)); 648 | if (!ret) { 649 | e_syntax(ctx, lineno, ebuf); 650 | return 0; 651 | } 652 | } 653 | 654 | /* newlines are not allowed in keys */ 655 | if (strchr(ret, '\n')) { 656 | xfree(ret); 657 | e_badkey(ctx, lineno); 658 | return 0; 659 | } 660 | return ret; 661 | } 662 | 663 | /* for bare-key allow only this regex: [A-Za-z0-9_-]+ */ 664 | const char *xp; 665 | for (xp = sp; xp != sq; xp++) { 666 | int k = *xp; 667 | if (isalnum(k)) 668 | continue; 669 | if (k == '_' || k == '-') 670 | continue; 671 | e_badkey(ctx, lineno); 672 | return 0; 673 | } 674 | 675 | /* dup and return it */ 676 | if (!(ret = STRNDUP(sp, sq - sp))) { 677 | e_outofmemory(ctx, FLINE); 678 | return 0; 679 | } 680 | return ret; 681 | } 682 | 683 | /* 684 | * Look up key in tab. Return 0 if not found, or 685 | * 'v'alue, 'a'rray or 't'able depending on the element. 686 | */ 687 | static int check_key(toml_table_t *tab, const char *key, 688 | toml_keyval_t **ret_val, toml_array_t **ret_arr, 689 | toml_table_t **ret_tab) { 690 | int i; 691 | void *dummy; 692 | 693 | if (!ret_tab) 694 | ret_tab = (toml_table_t **)&dummy; 695 | if (!ret_arr) 696 | ret_arr = (toml_array_t **)&dummy; 697 | if (!ret_val) 698 | ret_val = (toml_keyval_t **)&dummy; 699 | 700 | *ret_tab = 0; 701 | *ret_arr = 0; 702 | *ret_val = 0; 703 | 704 | for (i = 0; i < tab->nkval; i++) { 705 | if (0 == strcmp(key, tab->kval[i]->key)) { 706 | *ret_val = tab->kval[i]; 707 | return 'v'; 708 | } 709 | } 710 | for (i = 0; i < tab->narr; i++) { 711 | if (0 == strcmp(key, tab->arr[i]->key)) { 712 | *ret_arr = tab->arr[i]; 713 | return 'a'; 714 | } 715 | } 716 | for (i = 0; i < tab->ntab; i++) { 717 | if (0 == strcmp(key, tab->tab[i]->key)) { 718 | *ret_tab = tab->tab[i]; 719 | return 't'; 720 | } 721 | } 722 | return 0; 723 | } 724 | 725 | static int key_kind(toml_table_t *tab, const char *key) { 726 | return check_key(tab, key, 0, 0, 0); 727 | } 728 | 729 | /* Create a keyval in the table. 730 | */ 731 | static toml_keyval_t *create_keyval_in_table(context_t *ctx, toml_table_t *tab, 732 | token_t keytok) { 733 | /* first, normalize the key to be used for lookup. 734 | * remember to free it if we error out. 735 | */ 736 | char *newkey = normalize_key(ctx, keytok); 737 | if (!newkey) 738 | return 0; 739 | 740 | /* if key exists: error out. */ 741 | toml_keyval_t *dest = 0; 742 | if (key_kind(tab, newkey)) { 743 | xfree(newkey); 744 | e_keyexists(ctx, keytok.lineno); 745 | return 0; 746 | } 747 | 748 | /* make a new entry */ 749 | int n = tab->nkval; 750 | toml_keyval_t **base; 751 | if (0 == (base = (toml_keyval_t **)expand_ptrarr((void **)tab->kval, n))) { 752 | xfree(newkey); 753 | e_outofmemory(ctx, FLINE); 754 | return 0; 755 | } 756 | tab->kval = base; 757 | 758 | if (0 == (base[n] = (toml_keyval_t *)CALLOC(1, sizeof(*base[n])))) { 759 | xfree(newkey); 760 | e_outofmemory(ctx, FLINE); 761 | return 0; 762 | } 763 | dest = tab->kval[tab->nkval++]; 764 | 765 | /* save the key in the new value struct */ 766 | dest->key = newkey; 767 | return dest; 768 | } 769 | 770 | /* Create a table in the table. 771 | */ 772 | static toml_table_t *create_keytable_in_table(context_t *ctx, toml_table_t *tab, 773 | token_t keytok) { 774 | /* first, normalize the key to be used for lookup. 775 | * remember to free it if we error out. 776 | */ 777 | char *newkey = normalize_key(ctx, keytok); 778 | if (!newkey) 779 | return 0; 780 | 781 | /* if key exists: error out */ 782 | toml_table_t *dest = 0; 783 | if (check_key(tab, newkey, 0, 0, &dest)) { 784 | xfree(newkey); /* don't need this anymore */ 785 | 786 | /* special case: if table exists, but was created implicitly ... */ 787 | if (dest && dest->implicit) { 788 | /* we make it explicit now, and simply return it. */ 789 | dest->implicit = false; 790 | return dest; 791 | } 792 | e_keyexists(ctx, keytok.lineno); 793 | return 0; 794 | } 795 | 796 | /* create a new table entry */ 797 | int n = tab->ntab; 798 | toml_table_t **base; 799 | if (0 == (base = (toml_table_t **)expand_ptrarr((void **)tab->tab, n))) { 800 | xfree(newkey); 801 | e_outofmemory(ctx, FLINE); 802 | return 0; 803 | } 804 | tab->tab = base; 805 | 806 | if (0 == (base[n] = (toml_table_t *)CALLOC(1, sizeof(*base[n])))) { 807 | xfree(newkey); 808 | e_outofmemory(ctx, FLINE); 809 | return 0; 810 | } 811 | dest = tab->tab[tab->ntab++]; 812 | 813 | /* save the key in the new table struct */ 814 | dest->key = newkey; 815 | return dest; 816 | } 817 | 818 | /* Create an array in the table. 819 | */ 820 | static toml_array_t *create_keyarray_in_table(context_t *ctx, toml_table_t *tab, 821 | token_t keytok, char kind) { 822 | /* first, normalize the key to be used for lookup. 823 | * remember to free it if we error out. 824 | */ 825 | char *newkey = normalize_key(ctx, keytok); 826 | if (!newkey) 827 | return 0; 828 | 829 | /* if key exists: error out */ 830 | if (key_kind(tab, newkey)) { 831 | xfree(newkey); /* don't need this anymore */ 832 | e_keyexists(ctx, keytok.lineno); 833 | return 0; 834 | } 835 | 836 | /* make a new array entry */ 837 | int n = tab->narr; 838 | toml_array_t **base; 839 | if (0 == (base = (toml_array_t **)expand_ptrarr((void **)tab->arr, n))) { 840 | xfree(newkey); 841 | e_outofmemory(ctx, FLINE); 842 | return 0; 843 | } 844 | tab->arr = base; 845 | 846 | if (0 == (base[n] = (toml_array_t *)CALLOC(1, sizeof(*base[n])))) { 847 | xfree(newkey); 848 | e_outofmemory(ctx, FLINE); 849 | return 0; 850 | } 851 | toml_array_t *dest = tab->arr[tab->narr++]; 852 | 853 | /* save the key in the new array struct */ 854 | dest->key = newkey; 855 | dest->kind = kind; 856 | return dest; 857 | } 858 | 859 | static toml_arritem_t *create_value_in_array(context_t *ctx, 860 | toml_array_t *parent) { 861 | const int n = parent->nitem; 862 | toml_arritem_t *base = expand_arritem(parent->item, n); 863 | if (!base) { 864 | e_outofmemory(ctx, FLINE); 865 | return 0; 866 | } 867 | parent->item = base; 868 | parent->nitem++; 869 | return &parent->item[n]; 870 | } 871 | 872 | /* Create an array in an array 873 | */ 874 | static toml_array_t *create_array_in_array(context_t *ctx, 875 | toml_array_t *parent) { 876 | const int n = parent->nitem; 877 | toml_arritem_t *base = expand_arritem(parent->item, n); 878 | if (!base) { 879 | e_outofmemory(ctx, FLINE); 880 | return 0; 881 | } 882 | toml_array_t *ret = (toml_array_t *)CALLOC(1, sizeof(toml_array_t)); 883 | if (!ret) { 884 | e_outofmemory(ctx, FLINE); 885 | return 0; 886 | } 887 | base[n].arr = ret; 888 | parent->item = base; 889 | parent->nitem++; 890 | return ret; 891 | } 892 | 893 | /* Create a table in an array 894 | */ 895 | static toml_table_t *create_table_in_array(context_t *ctx, 896 | toml_array_t *parent) { 897 | int n = parent->nitem; 898 | toml_arritem_t *base = expand_arritem(parent->item, n); 899 | if (!base) { 900 | e_outofmemory(ctx, FLINE); 901 | return 0; 902 | } 903 | toml_table_t *ret = (toml_table_t *)CALLOC(1, sizeof(toml_table_t)); 904 | if (!ret) { 905 | e_outofmemory(ctx, FLINE); 906 | return 0; 907 | } 908 | base[n].tab = ret; 909 | parent->item = base; 910 | parent->nitem++; 911 | return ret; 912 | } 913 | 914 | static int skip_newlines(context_t *ctx, int isdotspecial) { 915 | while (ctx->tok.tok == NEWLINE) { 916 | if (next_token(ctx, isdotspecial)) 917 | return -1; 918 | if (ctx->tok.eof) 919 | break; 920 | } 921 | return 0; 922 | } 923 | 924 | static int parse_keyval(context_t *ctx, toml_table_t *tab); 925 | 926 | static inline int eat_token(context_t *ctx, tokentype_t typ, int isdotspecial, 927 | const char *fline) { 928 | if (ctx->tok.tok != typ) 929 | return e_internal(ctx, fline); 930 | 931 | if (next_token(ctx, isdotspecial)) 932 | return -1; 933 | 934 | return 0; 935 | } 936 | 937 | /* We are at '{ ... }'. 938 | * Parse the table. 939 | */ 940 | static int parse_inline_table(context_t *ctx, toml_table_t *tab) { 941 | if (eat_token(ctx, LBRACE, 1, FLINE)) 942 | return -1; 943 | 944 | for (;;) { 945 | if (ctx->tok.tok == NEWLINE) 946 | return e_syntax(ctx, ctx->tok.lineno, 947 | "newline not allowed in inline table"); 948 | 949 | /* until } */ 950 | if (ctx->tok.tok == RBRACE) 951 | break; 952 | 953 | if (ctx->tok.tok != STRING) 954 | return e_syntax(ctx, ctx->tok.lineno, "expect a string"); 955 | 956 | if (parse_keyval(ctx, tab)) 957 | return -1; 958 | 959 | if (ctx->tok.tok == NEWLINE) 960 | return e_syntax(ctx, ctx->tok.lineno, 961 | "newline not allowed in inline table"); 962 | 963 | /* on comma, continue to scan for next keyval */ 964 | if (ctx->tok.tok == COMMA) { 965 | if (eat_token(ctx, COMMA, 1, FLINE)) 966 | return -1; 967 | continue; 968 | } 969 | break; 970 | } 971 | 972 | if (eat_token(ctx, RBRACE, 1, FLINE)) 973 | return -1; 974 | 975 | tab->readonly = 1; 976 | 977 | return 0; 978 | } 979 | 980 | static int valtype(const char *val) { 981 | toml_timestamp_t ts; 982 | if (*val == '\'' || *val == '"') 983 | return 's'; 984 | if (0 == toml_rtob(val, 0)) 985 | return 'b'; 986 | if (0 == toml_rtoi(val, 0)) 987 | return 'i'; 988 | if (0 == toml_rtod(val, 0)) 989 | return 'd'; 990 | if (0 == toml_rtots(val, &ts)) { 991 | if (ts.year && ts.hour) 992 | return 'T'; /* timestamp */ 993 | if (ts.year) 994 | return 'D'; /* date */ 995 | return 't'; /* time */ 996 | } 997 | return 'u'; /* unknown */ 998 | } 999 | 1000 | /* We are at '[...]' */ 1001 | static int parse_array(context_t *ctx, toml_array_t *arr) { 1002 | if (eat_token(ctx, LBRACKET, 0, FLINE)) 1003 | return -1; 1004 | 1005 | for (;;) { 1006 | if (skip_newlines(ctx, 0)) 1007 | return -1; 1008 | 1009 | /* until ] */ 1010 | if (ctx->tok.tok == RBRACKET) 1011 | break; 1012 | 1013 | switch (ctx->tok.tok) { 1014 | case STRING: { 1015 | /* set array kind if this will be the first entry */ 1016 | if (arr->kind == 0) 1017 | arr->kind = 'v'; 1018 | else if (arr->kind != 'v') 1019 | arr->kind = 'm'; 1020 | 1021 | char *val = ctx->tok.ptr; 1022 | int vlen = ctx->tok.len; 1023 | 1024 | /* make a new value in array */ 1025 | toml_arritem_t *newval = create_value_in_array(ctx, arr); 1026 | if (!newval) 1027 | return e_outofmemory(ctx, FLINE); 1028 | 1029 | if (!(newval->val = STRNDUP(val, vlen))) 1030 | return e_outofmemory(ctx, FLINE); 1031 | 1032 | newval->valtype = valtype(newval->val); 1033 | 1034 | /* set array type if this is the first entry */ 1035 | if (arr->nitem == 1) 1036 | arr->type = newval->valtype; 1037 | else if (arr->type != newval->valtype) 1038 | arr->type = 'm'; /* mixed */ 1039 | 1040 | if (eat_token(ctx, STRING, 0, FLINE)) 1041 | return -1; 1042 | break; 1043 | } 1044 | 1045 | case LBRACKET: { /* [ [array], [array] ... ] */ 1046 | /* set the array kind if this will be the first entry */ 1047 | if (arr->kind == 0) 1048 | arr->kind = 'a'; 1049 | else if (arr->kind != 'a') 1050 | arr->kind = 'm'; 1051 | 1052 | toml_array_t *subarr = create_array_in_array(ctx, arr); 1053 | if (!subarr) 1054 | return -1; 1055 | if (parse_array(ctx, subarr)) 1056 | return -1; 1057 | break; 1058 | } 1059 | 1060 | case LBRACE: { /* [ {table}, {table} ... ] */ 1061 | /* set the array kind if this will be the first entry */ 1062 | if (arr->kind == 0) 1063 | arr->kind = 't'; 1064 | else if (arr->kind != 't') 1065 | arr->kind = 'm'; 1066 | 1067 | toml_table_t *subtab = create_table_in_array(ctx, arr); 1068 | if (!subtab) 1069 | return -1; 1070 | if (parse_inline_table(ctx, subtab)) 1071 | return -1; 1072 | break; 1073 | } 1074 | 1075 | default: 1076 | return e_syntax(ctx, ctx->tok.lineno, "syntax error"); 1077 | } 1078 | 1079 | if (skip_newlines(ctx, 0)) 1080 | return -1; 1081 | 1082 | /* on comma, continue to scan for next element */ 1083 | if (ctx->tok.tok == COMMA) { 1084 | if (eat_token(ctx, COMMA, 0, FLINE)) 1085 | return -1; 1086 | continue; 1087 | } 1088 | break; 1089 | } 1090 | 1091 | if (eat_token(ctx, RBRACKET, 1, FLINE)) 1092 | return -1; 1093 | return 0; 1094 | } 1095 | 1096 | /* handle lines like these: 1097 | key = "value" 1098 | key = [ array ] 1099 | key = { table } 1100 | */ 1101 | static int parse_keyval(context_t *ctx, toml_table_t *tab) { 1102 | if (tab->readonly) { 1103 | return e_forbid(ctx, ctx->tok.lineno, 1104 | "cannot insert new entry into existing table"); 1105 | } 1106 | 1107 | token_t key = ctx->tok; 1108 | if (eat_token(ctx, STRING, 1, FLINE)) 1109 | return -1; 1110 | 1111 | if (ctx->tok.tok == DOT) { 1112 | /* handle inline dotted key. 1113 | e.g. 1114 | physical.color = "orange" 1115 | physical.shape = "round" 1116 | */ 1117 | toml_table_t *subtab = 0; 1118 | { 1119 | char *subtabstr = normalize_key(ctx, key); 1120 | if (!subtabstr) 1121 | return -1; 1122 | 1123 | subtab = toml_table_in(tab, subtabstr); 1124 | xfree(subtabstr); 1125 | } 1126 | if (!subtab) { 1127 | subtab = create_keytable_in_table(ctx, tab, key); 1128 | if (!subtab) 1129 | return -1; 1130 | } 1131 | if (next_token(ctx, 1)) 1132 | return -1; 1133 | if (parse_keyval(ctx, subtab)) 1134 | return -1; 1135 | return 0; 1136 | } 1137 | 1138 | if (ctx->tok.tok != EQUAL) { 1139 | return e_syntax(ctx, ctx->tok.lineno, "missing ="); 1140 | } 1141 | 1142 | if (next_token(ctx, 0)) 1143 | return -1; 1144 | 1145 | switch (ctx->tok.tok) { 1146 | case STRING: { /* key = "value" */ 1147 | toml_keyval_t *keyval = create_keyval_in_table(ctx, tab, key); 1148 | if (!keyval) 1149 | return -1; 1150 | token_t val = ctx->tok; 1151 | 1152 | assert(keyval->val == 0); 1153 | if (!(keyval->val = STRNDUP(val.ptr, val.len))) 1154 | return e_outofmemory(ctx, FLINE); 1155 | 1156 | if (next_token(ctx, 1)) 1157 | return -1; 1158 | 1159 | return 0; 1160 | } 1161 | 1162 | case LBRACKET: { /* key = [ array ] */ 1163 | toml_array_t *arr = create_keyarray_in_table(ctx, tab, key, 0); 1164 | if (!arr) 1165 | return -1; 1166 | if (parse_array(ctx, arr)) 1167 | return -1; 1168 | return 0; 1169 | } 1170 | 1171 | case LBRACE: { /* key = { table } */ 1172 | toml_table_t *nxttab = create_keytable_in_table(ctx, tab, key); 1173 | if (!nxttab) 1174 | return -1; 1175 | if (parse_inline_table(ctx, nxttab)) 1176 | return -1; 1177 | return 0; 1178 | } 1179 | 1180 | default: 1181 | return e_syntax(ctx, ctx->tok.lineno, "syntax error"); 1182 | } 1183 | return 0; 1184 | } 1185 | 1186 | typedef struct tabpath_t tabpath_t; 1187 | struct tabpath_t { 1188 | int cnt; 1189 | token_t key[10]; 1190 | }; 1191 | 1192 | /* at [x.y.z] or [[x.y.z]] 1193 | * Scan forward and fill tabpath until it enters ] or ]] 1194 | * There will be at least one entry on return. 1195 | */ 1196 | static int fill_tabpath(context_t *ctx) { 1197 | int lineno = ctx->tok.lineno; 1198 | int i; 1199 | 1200 | /* clear tpath */ 1201 | for (i = 0; i < ctx->tpath.top; i++) { 1202 | char **p = &ctx->tpath.key[i]; 1203 | xfree(*p); 1204 | *p = 0; 1205 | } 1206 | ctx->tpath.top = 0; 1207 | 1208 | for (;;) { 1209 | if (ctx->tpath.top >= 10) 1210 | return e_syntax(ctx, lineno, 1211 | "table path is too deep; max allowed is 10."); 1212 | 1213 | if (ctx->tok.tok != STRING) 1214 | return e_syntax(ctx, lineno, "invalid or missing key"); 1215 | 1216 | char *key = normalize_key(ctx, ctx->tok); 1217 | if (!key) 1218 | return -1; 1219 | ctx->tpath.tok[ctx->tpath.top] = ctx->tok; 1220 | ctx->tpath.key[ctx->tpath.top] = key; 1221 | ctx->tpath.top++; 1222 | 1223 | if (next_token(ctx, 1)) 1224 | return -1; 1225 | 1226 | if (ctx->tok.tok == RBRACKET) 1227 | break; 1228 | 1229 | if (ctx->tok.tok != DOT) 1230 | return e_syntax(ctx, lineno, "invalid key"); 1231 | 1232 | if (next_token(ctx, 1)) 1233 | return -1; 1234 | } 1235 | 1236 | if (ctx->tpath.top <= 0) 1237 | return e_syntax(ctx, lineno, "empty table selector"); 1238 | 1239 | return 0; 1240 | } 1241 | 1242 | /* Walk tabpath from the root, and create new tables on the way. 1243 | * Sets ctx->curtab to the final table. 1244 | */ 1245 | static int walk_tabpath(context_t *ctx) { 1246 | /* start from root */ 1247 | toml_table_t *curtab = ctx->root; 1248 | 1249 | for (int i = 0; i < ctx->tpath.top; i++) { 1250 | const char *key = ctx->tpath.key[i]; 1251 | 1252 | toml_keyval_t *nextval = 0; 1253 | toml_array_t *nextarr = 0; 1254 | toml_table_t *nexttab = 0; 1255 | switch (check_key(curtab, key, &nextval, &nextarr, &nexttab)) { 1256 | case 't': 1257 | /* found a table. nexttab is where we will go next. */ 1258 | break; 1259 | 1260 | case 'a': 1261 | /* found an array. nexttab is the last table in the array. */ 1262 | if (nextarr->kind != 't') 1263 | return e_internal(ctx, FLINE); 1264 | 1265 | if (nextarr->nitem == 0) 1266 | return e_internal(ctx, FLINE); 1267 | 1268 | nexttab = nextarr->item[nextarr->nitem - 1].tab; 1269 | break; 1270 | 1271 | case 'v': 1272 | return e_keyexists(ctx, ctx->tpath.tok[i].lineno); 1273 | 1274 | default: { /* Not found. Let's create an implicit table. */ 1275 | int n = curtab->ntab; 1276 | toml_table_t **base = 1277 | (toml_table_t **)expand_ptrarr((void **)curtab->tab, n); 1278 | if (0 == base) 1279 | return e_outofmemory(ctx, FLINE); 1280 | 1281 | curtab->tab = base; 1282 | 1283 | if (0 == (base[n] = (toml_table_t *)CALLOC(1, sizeof(*base[n])))) 1284 | return e_outofmemory(ctx, FLINE); 1285 | 1286 | if (0 == (base[n]->key = STRDUP(key))) 1287 | return e_outofmemory(ctx, FLINE); 1288 | 1289 | nexttab = curtab->tab[curtab->ntab++]; 1290 | 1291 | /* tabs created by walk_tabpath are considered implicit */ 1292 | nexttab->implicit = true; 1293 | } break; 1294 | } 1295 | 1296 | /* switch to next tab */ 1297 | curtab = nexttab; 1298 | } 1299 | 1300 | /* save it */ 1301 | ctx->curtab = curtab; 1302 | 1303 | return 0; 1304 | } 1305 | 1306 | /* handle lines like [x.y.z] or [[x.y.z]] */ 1307 | static int parse_select(context_t *ctx) { 1308 | assert(ctx->tok.tok == LBRACKET); 1309 | 1310 | /* true if [[ */ 1311 | int llb = (ctx->tok.ptr + 1 < ctx->stop && ctx->tok.ptr[1] == '['); 1312 | /* need to detect '[[' on our own because next_token() will skip whitespace, 1313 | and '[ [' would be taken as '[[', which is wrong. */ 1314 | 1315 | /* eat [ or [[ */ 1316 | if (eat_token(ctx, LBRACKET, 1, FLINE)) 1317 | return -1; 1318 | if (llb) { 1319 | assert(ctx->tok.tok == LBRACKET); 1320 | if (eat_token(ctx, LBRACKET, 1, FLINE)) 1321 | return -1; 1322 | } 1323 | 1324 | if (fill_tabpath(ctx)) 1325 | return -1; 1326 | 1327 | /* For [x.y.z] or [[x.y.z]], remove z from tpath. 1328 | */ 1329 | token_t z = ctx->tpath.tok[ctx->tpath.top - 1]; 1330 | xfree(ctx->tpath.key[ctx->tpath.top - 1]); 1331 | ctx->tpath.top--; 1332 | 1333 | /* set up ctx->curtab */ 1334 | if (walk_tabpath(ctx)) 1335 | return -1; 1336 | 1337 | if (!llb) { 1338 | /* [x.y.z] -> create z = {} in x.y */ 1339 | toml_table_t *curtab = create_keytable_in_table(ctx, ctx->curtab, z); 1340 | if (!curtab) 1341 | return -1; 1342 | ctx->curtab = curtab; 1343 | } else { 1344 | /* [[x.y.z]] -> create z = [] in x.y */ 1345 | toml_array_t *arr = 0; 1346 | { 1347 | char *zstr = normalize_key(ctx, z); 1348 | if (!zstr) 1349 | return -1; 1350 | arr = toml_array_in(ctx->curtab, zstr); 1351 | xfree(zstr); 1352 | } 1353 | if (!arr) { 1354 | arr = create_keyarray_in_table(ctx, ctx->curtab, z, 't'); 1355 | if (!arr) 1356 | return -1; 1357 | } 1358 | if (arr->kind != 't') 1359 | return e_syntax(ctx, z.lineno, "array mismatch"); 1360 | 1361 | /* add to z[] */ 1362 | toml_table_t *dest; 1363 | { 1364 | toml_table_t *t = create_table_in_array(ctx, arr); 1365 | if (!t) 1366 | return -1; 1367 | 1368 | if (0 == (t->key = STRDUP("__anon__"))) 1369 | return e_outofmemory(ctx, FLINE); 1370 | 1371 | dest = t; 1372 | } 1373 | 1374 | ctx->curtab = dest; 1375 | } 1376 | 1377 | if (ctx->tok.tok != RBRACKET) { 1378 | return e_syntax(ctx, ctx->tok.lineno, "expects ]"); 1379 | } 1380 | if (llb) { 1381 | if (!(ctx->tok.ptr + 1 < ctx->stop && ctx->tok.ptr[1] == ']')) { 1382 | return e_syntax(ctx, ctx->tok.lineno, "expects ]]"); 1383 | } 1384 | if (eat_token(ctx, RBRACKET, 1, FLINE)) 1385 | return -1; 1386 | } 1387 | 1388 | if (eat_token(ctx, RBRACKET, 1, FLINE)) 1389 | return -1; 1390 | 1391 | if (ctx->tok.tok != NEWLINE) 1392 | return e_syntax(ctx, ctx->tok.lineno, "extra chars after ] or ]]"); 1393 | 1394 | return 0; 1395 | } 1396 | 1397 | toml_table_t *toml_parse(char *conf, char *errbuf, int errbufsz) { 1398 | context_t ctx; 1399 | 1400 | // clear errbuf 1401 | if (errbufsz <= 0) 1402 | errbufsz = 0; 1403 | if (errbufsz > 0) 1404 | errbuf[0] = 0; 1405 | 1406 | // init context 1407 | memset(&ctx, 0, sizeof(ctx)); 1408 | ctx.start = conf; 1409 | ctx.stop = ctx.start + strlen(conf); 1410 | ctx.errbuf = errbuf; 1411 | ctx.errbufsz = errbufsz; 1412 | 1413 | // start with an artificial newline of length 0 1414 | ctx.tok.tok = NEWLINE; 1415 | ctx.tok.lineno = 1; 1416 | ctx.tok.ptr = conf; 1417 | ctx.tok.len = 0; 1418 | 1419 | // make a root table 1420 | if (0 == (ctx.root = CALLOC(1, sizeof(*ctx.root)))) { 1421 | e_outofmemory(&ctx, FLINE); 1422 | // Do not goto fail, root table not set up yet 1423 | return 0; 1424 | } 1425 | 1426 | // set root as default table 1427 | ctx.curtab = ctx.root; 1428 | 1429 | /* Scan forward until EOF */ 1430 | for (token_t tok = ctx.tok; !tok.eof; tok = ctx.tok) { 1431 | switch (tok.tok) { 1432 | 1433 | case NEWLINE: 1434 | if (next_token(&ctx, 1)) 1435 | goto fail; 1436 | break; 1437 | 1438 | case STRING: 1439 | if (parse_keyval(&ctx, ctx.curtab)) 1440 | goto fail; 1441 | 1442 | if (ctx.tok.tok != NEWLINE) { 1443 | e_syntax(&ctx, ctx.tok.lineno, "extra chars after value"); 1444 | goto fail; 1445 | } 1446 | 1447 | if (eat_token(&ctx, NEWLINE, 1, FLINE)) 1448 | goto fail; 1449 | break; 1450 | 1451 | case LBRACKET: /* [ x.y.z ] or [[ x.y.z ]] */ 1452 | if (parse_select(&ctx)) 1453 | goto fail; 1454 | break; 1455 | 1456 | default: 1457 | e_syntax(&ctx, tok.lineno, "syntax error"); 1458 | goto fail; 1459 | } 1460 | } 1461 | 1462 | /* success */ 1463 | for (int i = 0; i < ctx.tpath.top; i++) 1464 | xfree(ctx.tpath.key[i]); 1465 | return ctx.root; 1466 | 1467 | fail: 1468 | // Something bad has happened. Free resources and return error. 1469 | for (int i = 0; i < ctx.tpath.top; i++) 1470 | xfree(ctx.tpath.key[i]); 1471 | toml_free(ctx.root); 1472 | return 0; 1473 | } 1474 | 1475 | toml_table_t *toml_parse_file(FILE *fp, char *errbuf, int errbufsz) { 1476 | int bufsz = 0; 1477 | char *buf = 0; 1478 | int off = 0; 1479 | 1480 | /* read from fp into buf */ 1481 | while (!feof(fp)) { 1482 | 1483 | if (off == bufsz) { 1484 | int xsz = bufsz + 1000; 1485 | char *x = expand(buf, bufsz, xsz); 1486 | if (!x) { 1487 | snprintf(errbuf, errbufsz, "out of memory"); 1488 | xfree(buf); 1489 | return 0; 1490 | } 1491 | buf = x; 1492 | bufsz = xsz; 1493 | } 1494 | 1495 | errno = 0; 1496 | int n = fread(buf + off, 1, bufsz - off, fp); 1497 | if (ferror(fp)) { 1498 | snprintf(errbuf, errbufsz, "%s", 1499 | errno ? strerror(errno) : "Error reading file"); 1500 | xfree(buf); 1501 | return 0; 1502 | } 1503 | off += n; 1504 | } 1505 | 1506 | /* tag on a NUL to cap the string */ 1507 | if (off == bufsz) { 1508 | int xsz = bufsz + 1; 1509 | char *x = expand(buf, bufsz, xsz); 1510 | if (!x) { 1511 | snprintf(errbuf, errbufsz, "out of memory"); 1512 | xfree(buf); 1513 | return 0; 1514 | } 1515 | buf = x; 1516 | bufsz = xsz; 1517 | } 1518 | buf[off] = 0; 1519 | 1520 | /* parse it, cleanup and finish */ 1521 | toml_table_t *ret = toml_parse(buf, errbuf, errbufsz); 1522 | xfree(buf); 1523 | return ret; 1524 | } 1525 | 1526 | static void xfree_kval(toml_keyval_t *p) { 1527 | if (!p) 1528 | return; 1529 | xfree(p->key); 1530 | xfree(p->val); 1531 | xfree(p); 1532 | } 1533 | 1534 | static void xfree_tab(toml_table_t *p); 1535 | 1536 | static void xfree_arr(toml_array_t *p) { 1537 | if (!p) 1538 | return; 1539 | 1540 | xfree(p->key); 1541 | const int n = p->nitem; 1542 | for (int i = 0; i < n; i++) { 1543 | toml_arritem_t *a = &p->item[i]; 1544 | if (a->val) 1545 | xfree(a->val); 1546 | else if (a->arr) 1547 | xfree_arr(a->arr); 1548 | else if (a->tab) 1549 | xfree_tab(a->tab); 1550 | } 1551 | xfree(p->item); 1552 | xfree(p); 1553 | } 1554 | 1555 | static void xfree_tab(toml_table_t *p) { 1556 | int i; 1557 | 1558 | if (!p) 1559 | return; 1560 | 1561 | xfree(p->key); 1562 | 1563 | for (i = 0; i < p->nkval; i++) 1564 | xfree_kval(p->kval[i]); 1565 | xfree(p->kval); 1566 | 1567 | for (i = 0; i < p->narr; i++) 1568 | xfree_arr(p->arr[i]); 1569 | xfree(p->arr); 1570 | 1571 | for (i = 0; i < p->ntab; i++) 1572 | xfree_tab(p->tab[i]); 1573 | xfree(p->tab); 1574 | 1575 | xfree(p); 1576 | } 1577 | 1578 | void toml_free(toml_table_t *tab) { xfree_tab(tab); } 1579 | 1580 | static void set_token(context_t *ctx, tokentype_t tok, int lineno, char *ptr, 1581 | int len) { 1582 | token_t t; 1583 | t.tok = tok; 1584 | t.lineno = lineno; 1585 | t.ptr = ptr; 1586 | t.len = len; 1587 | t.eof = 0; 1588 | ctx->tok = t; 1589 | } 1590 | 1591 | static void set_eof(context_t *ctx, int lineno) { 1592 | set_token(ctx, NEWLINE, lineno, ctx->stop, 0); 1593 | ctx->tok.eof = 1; 1594 | } 1595 | 1596 | /* Scan p for n digits compositing entirely of [0-9] */ 1597 | static int scan_digits(const char *p, int n) { 1598 | int ret = 0; 1599 | for (; n > 0 && isdigit(*p); n--, p++) { 1600 | ret = 10 * ret + (*p - '0'); 1601 | } 1602 | return n ? -1 : ret; 1603 | } 1604 | 1605 | static int scan_date(const char *p, int *YY, int *MM, int *DD) { 1606 | int year, month, day; 1607 | year = scan_digits(p, 4); 1608 | month = (year >= 0 && p[4] == '-') ? scan_digits(p + 5, 2) : -1; 1609 | day = (month >= 0 && p[7] == '-') ? scan_digits(p + 8, 2) : -1; 1610 | if (YY) 1611 | *YY = year; 1612 | if (MM) 1613 | *MM = month; 1614 | if (DD) 1615 | *DD = day; 1616 | return (year >= 0 && month >= 0 && day >= 0) ? 0 : -1; 1617 | } 1618 | 1619 | static int scan_time(const char *p, int *hh, int *mm, int *ss) { 1620 | int hour, minute, second; 1621 | hour = scan_digits(p, 2); 1622 | minute = (hour >= 0 && p[2] == ':') ? scan_digits(p + 3, 2) : -1; 1623 | second = (minute >= 0 && p[5] == ':') ? scan_digits(p + 6, 2) : -1; 1624 | if (hh) 1625 | *hh = hour; 1626 | if (mm) 1627 | *mm = minute; 1628 | if (ss) 1629 | *ss = second; 1630 | return (hour >= 0 && minute >= 0 && second >= 0) ? 0 : -1; 1631 | } 1632 | 1633 | static int scan_string(context_t *ctx, char *p, int lineno, int dotisspecial) { 1634 | char *orig = p; 1635 | if (0 == strncmp(p, "'''", 3)) { 1636 | char *q = p + 3; 1637 | 1638 | while (1) { 1639 | q = strstr(q, "'''"); 1640 | if (0 == q) { 1641 | return e_syntax(ctx, lineno, "unterminated triple-s-quote"); 1642 | } 1643 | while (q[3] == '\'') 1644 | q++; 1645 | break; 1646 | } 1647 | 1648 | set_token(ctx, STRING, lineno, orig, q + 3 - orig); 1649 | return 0; 1650 | } 1651 | 1652 | if (0 == strncmp(p, "\"\"\"", 3)) { 1653 | char *q = p + 3; 1654 | 1655 | while (1) { 1656 | q = strstr(q, "\"\"\""); 1657 | if (0 == q) { 1658 | return e_syntax(ctx, lineno, "unterminated triple-d-quote"); 1659 | } 1660 | if (q[-1] == '\\') { 1661 | q++; 1662 | continue; 1663 | } 1664 | while (q[3] == '\"') 1665 | q++; 1666 | break; 1667 | } 1668 | 1669 | // the string is [p+3, q-1] 1670 | 1671 | int hexreq = 0; /* #hex required */ 1672 | int escape = 0; 1673 | for (p += 3; p < q; p++) { 1674 | if (escape) { 1675 | escape = 0; 1676 | if (strchr("btnfr\"\\", *p)) 1677 | continue; 1678 | if (*p == 'u') { 1679 | hexreq = 4; 1680 | continue; 1681 | } 1682 | if (*p == 'U') { 1683 | hexreq = 8; 1684 | continue; 1685 | } 1686 | if (p[strspn(p, " \t\r")] == '\n') 1687 | continue; /* allow for line ending backslash */ 1688 | return e_syntax(ctx, lineno, "bad escape char"); 1689 | } 1690 | if (hexreq) { 1691 | hexreq--; 1692 | if (strchr("0123456789ABCDEF", *p)) 1693 | continue; 1694 | return e_syntax(ctx, lineno, "expect hex char"); 1695 | } 1696 | if (*p == '\\') { 1697 | escape = 1; 1698 | continue; 1699 | } 1700 | } 1701 | if (escape) 1702 | return e_syntax(ctx, lineno, "expect an escape char"); 1703 | if (hexreq) 1704 | return e_syntax(ctx, lineno, "expected more hex char"); 1705 | 1706 | set_token(ctx, STRING, lineno, orig, q + 3 - orig); 1707 | return 0; 1708 | } 1709 | 1710 | if ('\'' == *p) { 1711 | for (p++; *p && *p != '\n' && *p != '\''; p++) 1712 | ; 1713 | if (*p != '\'') { 1714 | return e_syntax(ctx, lineno, "unterminated s-quote"); 1715 | } 1716 | 1717 | set_token(ctx, STRING, lineno, orig, p + 1 - orig); 1718 | return 0; 1719 | } 1720 | 1721 | if ('\"' == *p) { 1722 | int hexreq = 0; /* #hex required */ 1723 | int escape = 0; 1724 | for (p++; *p; p++) { 1725 | if (escape) { 1726 | escape = 0; 1727 | if (strchr("btnfr\"\\", *p)) 1728 | continue; 1729 | if (*p == 'u') { 1730 | hexreq = 4; 1731 | continue; 1732 | } 1733 | if (*p == 'U') { 1734 | hexreq = 8; 1735 | continue; 1736 | } 1737 | return e_syntax(ctx, lineno, "bad escape char"); 1738 | } 1739 | if (hexreq) { 1740 | hexreq--; 1741 | if (strchr("0123456789ABCDEF", *p)) 1742 | continue; 1743 | return e_syntax(ctx, lineno, "expect hex char"); 1744 | } 1745 | if (*p == '\\') { 1746 | escape = 1; 1747 | continue; 1748 | } 1749 | if (*p == '\'') { 1750 | if (p[1] == '\'' && p[2] == '\'') { 1751 | return e_syntax(ctx, lineno, "triple-s-quote inside string lit"); 1752 | } 1753 | continue; 1754 | } 1755 | if (*p == '\n') 1756 | break; 1757 | if (*p == '"') 1758 | break; 1759 | } 1760 | if (*p != '"') { 1761 | return e_syntax(ctx, lineno, "unterminated quote"); 1762 | } 1763 | 1764 | set_token(ctx, STRING, lineno, orig, p + 1 - orig); 1765 | return 0; 1766 | } 1767 | 1768 | /* check for timestamp without quotes */ 1769 | if (0 == scan_date(p, 0, 0, 0) || 0 == scan_time(p, 0, 0, 0)) { 1770 | // forward thru the timestamp 1771 | p += strspn(p, "0123456789.:+-T Z"); 1772 | // squeeze out any spaces at end of string 1773 | for (; p[-1] == ' '; p--) 1774 | ; 1775 | // tokenize 1776 | set_token(ctx, STRING, lineno, orig, p - orig); 1777 | return 0; 1778 | } 1779 | 1780 | /* literals */ 1781 | for (; *p && *p != '\n'; p++) { 1782 | int ch = *p; 1783 | if (ch == '.' && dotisspecial) 1784 | break; 1785 | if ('A' <= ch && ch <= 'Z') 1786 | continue; 1787 | if ('a' <= ch && ch <= 'z') 1788 | continue; 1789 | if (strchr("0123456789+-_.", ch)) 1790 | continue; 1791 | break; 1792 | } 1793 | 1794 | set_token(ctx, STRING, lineno, orig, p - orig); 1795 | return 0; 1796 | } 1797 | 1798 | static int next_token(context_t *ctx, int dotisspecial) { 1799 | int lineno = ctx->tok.lineno; 1800 | char *p = ctx->tok.ptr; 1801 | int i; 1802 | 1803 | /* eat this tok */ 1804 | for (i = 0; i < ctx->tok.len; i++) { 1805 | if (*p++ == '\n') 1806 | lineno++; 1807 | } 1808 | 1809 | /* make next tok */ 1810 | while (p < ctx->stop) { 1811 | /* skip comment. stop just before the \n. */ 1812 | if (*p == '#') { 1813 | for (p++; p < ctx->stop && *p != '\n'; p++) 1814 | ; 1815 | continue; 1816 | } 1817 | 1818 | if (dotisspecial && *p == '.') { 1819 | set_token(ctx, DOT, lineno, p, 1); 1820 | return 0; 1821 | } 1822 | 1823 | switch (*p) { 1824 | case ',': 1825 | set_token(ctx, COMMA, lineno, p, 1); 1826 | return 0; 1827 | case '=': 1828 | set_token(ctx, EQUAL, lineno, p, 1); 1829 | return 0; 1830 | case '{': 1831 | set_token(ctx, LBRACE, lineno, p, 1); 1832 | return 0; 1833 | case '}': 1834 | set_token(ctx, RBRACE, lineno, p, 1); 1835 | return 0; 1836 | case '[': 1837 | set_token(ctx, LBRACKET, lineno, p, 1); 1838 | return 0; 1839 | case ']': 1840 | set_token(ctx, RBRACKET, lineno, p, 1); 1841 | return 0; 1842 | case '\n': 1843 | set_token(ctx, NEWLINE, lineno, p, 1); 1844 | return 0; 1845 | case '\r': 1846 | case ' ': 1847 | case '\t': 1848 | /* ignore white spaces */ 1849 | p++; 1850 | continue; 1851 | } 1852 | 1853 | return scan_string(ctx, p, lineno, dotisspecial); 1854 | } 1855 | 1856 | set_eof(ctx, lineno); 1857 | return 0; 1858 | } 1859 | 1860 | const char *toml_key_in(const toml_table_t *tab, int keyidx) { 1861 | if (keyidx < tab->nkval) 1862 | return tab->kval[keyidx]->key; 1863 | 1864 | keyidx -= tab->nkval; 1865 | if (keyidx < tab->narr) 1866 | return tab->arr[keyidx]->key; 1867 | 1868 | keyidx -= tab->narr; 1869 | if (keyidx < tab->ntab) 1870 | return tab->tab[keyidx]->key; 1871 | 1872 | return 0; 1873 | } 1874 | 1875 | int toml_key_exists(const toml_table_t *tab, const char *key) { 1876 | int i; 1877 | for (i = 0; i < tab->nkval; i++) { 1878 | if (0 == strcmp(key, tab->kval[i]->key)) 1879 | return 1; 1880 | } 1881 | for (i = 0; i < tab->narr; i++) { 1882 | if (0 == strcmp(key, tab->arr[i]->key)) 1883 | return 1; 1884 | } 1885 | for (i = 0; i < tab->ntab; i++) { 1886 | if (0 == strcmp(key, tab->tab[i]->key)) 1887 | return 1; 1888 | } 1889 | return 0; 1890 | } 1891 | 1892 | toml_raw_t toml_raw_in(const toml_table_t *tab, const char *key) { 1893 | int i; 1894 | for (i = 0; i < tab->nkval; i++) { 1895 | if (0 == strcmp(key, tab->kval[i]->key)) 1896 | return tab->kval[i]->val; 1897 | } 1898 | return 0; 1899 | } 1900 | 1901 | toml_array_t *toml_array_in(const toml_table_t *tab, const char *key) { 1902 | int i; 1903 | for (i = 0; i < tab->narr; i++) { 1904 | if (0 == strcmp(key, tab->arr[i]->key)) 1905 | return tab->arr[i]; 1906 | } 1907 | return 0; 1908 | } 1909 | 1910 | toml_table_t *toml_table_in(const toml_table_t *tab, const char *key) { 1911 | int i; 1912 | for (i = 0; i < tab->ntab; i++) { 1913 | if (0 == strcmp(key, tab->tab[i]->key)) 1914 | return tab->tab[i]; 1915 | } 1916 | return 0; 1917 | } 1918 | 1919 | toml_raw_t toml_raw_at(const toml_array_t *arr, int idx) { 1920 | return (0 <= idx && idx < arr->nitem) ? arr->item[idx].val : 0; 1921 | } 1922 | 1923 | char toml_array_kind(const toml_array_t *arr) { return arr->kind; } 1924 | 1925 | char toml_array_type(const toml_array_t *arr) { 1926 | if (arr->kind != 'v') 1927 | return 0; 1928 | 1929 | if (arr->nitem == 0) 1930 | return 0; 1931 | 1932 | return arr->type; 1933 | } 1934 | 1935 | int toml_array_nelem(const toml_array_t *arr) { return arr->nitem; } 1936 | 1937 | const char *toml_array_key(const toml_array_t *arr) { 1938 | return arr ? arr->key : (const char *)NULL; 1939 | } 1940 | 1941 | int toml_table_nkval(const toml_table_t *tab) { return tab->nkval; } 1942 | 1943 | int toml_table_narr(const toml_table_t *tab) { return tab->narr; } 1944 | 1945 | int toml_table_ntab(const toml_table_t *tab) { return tab->ntab; } 1946 | 1947 | const char *toml_table_key(const toml_table_t *tab) { 1948 | return tab ? tab->key : (const char *)NULL; 1949 | } 1950 | 1951 | toml_array_t *toml_array_at(const toml_array_t *arr, int idx) { 1952 | return (0 <= idx && idx < arr->nitem) ? arr->item[idx].arr : 0; 1953 | } 1954 | 1955 | toml_table_t *toml_table_at(const toml_array_t *arr, int idx) { 1956 | return (0 <= idx && idx < arr->nitem) ? arr->item[idx].tab : 0; 1957 | } 1958 | 1959 | static int parse_millisec(const char *p, const char **endp); 1960 | 1961 | int toml_rtots(toml_raw_t src_, toml_timestamp_t *ret) { 1962 | if (!src_) 1963 | return -1; 1964 | 1965 | const char *p = src_; 1966 | int must_parse_time = 0; 1967 | 1968 | memset(ret, 0, sizeof(*ret)); 1969 | 1970 | int *year = &ret->__buffer.year; 1971 | int *month = &ret->__buffer.month; 1972 | int *day = &ret->__buffer.day; 1973 | int *hour = &ret->__buffer.hour; 1974 | int *minute = &ret->__buffer.minute; 1975 | int *second = &ret->__buffer.second; 1976 | int *millisec = &ret->__buffer.millisec; 1977 | 1978 | /* parse date YYYY-MM-DD */ 1979 | if (0 == scan_date(p, year, month, day)) { 1980 | ret->year = year; 1981 | ret->month = month; 1982 | ret->day = day; 1983 | 1984 | p += 10; 1985 | if (*p) { 1986 | // parse the T or space separator 1987 | if (*p != 'T' && *p != ' ') 1988 | return -1; 1989 | must_parse_time = 1; 1990 | p++; 1991 | } 1992 | } 1993 | 1994 | /* parse time HH:MM:SS */ 1995 | if (0 == scan_time(p, hour, minute, second)) { 1996 | ret->hour = hour; 1997 | ret->minute = minute; 1998 | ret->second = second; 1999 | 2000 | /* optionally, parse millisec */ 2001 | p += 8; 2002 | if (*p == '.') { 2003 | p++; /* skip '.' */ 2004 | const char *qq; 2005 | *millisec = parse_millisec(p, &qq); 2006 | ret->millisec = millisec; 2007 | p = qq; 2008 | } 2009 | 2010 | if (*p) { 2011 | /* parse and copy Z */ 2012 | char *z = ret->__buffer.z; 2013 | ret->z = z; 2014 | if (*p == 'Z' || *p == 'z') { 2015 | *z++ = 'Z'; 2016 | p++; 2017 | *z = 0; 2018 | 2019 | } else if (*p == '+' || *p == '-') { 2020 | *z++ = *p++; 2021 | 2022 | if (!(isdigit(p[0]) && isdigit(p[1]))) 2023 | return -1; 2024 | *z++ = *p++; 2025 | *z++ = *p++; 2026 | 2027 | if (*p == ':') { 2028 | *z++ = *p++; 2029 | 2030 | if (!(isdigit(p[0]) && isdigit(p[1]))) 2031 | return -1; 2032 | *z++ = *p++; 2033 | *z++ = *p++; 2034 | } 2035 | 2036 | *z = 0; 2037 | } 2038 | } 2039 | } 2040 | if (*p != 0) 2041 | return -1; 2042 | 2043 | if (must_parse_time && !ret->hour) 2044 | return -1; 2045 | 2046 | return 0; 2047 | } 2048 | 2049 | /* Raw to boolean */ 2050 | int toml_rtob(toml_raw_t src, int *ret_) { 2051 | if (!src) 2052 | return -1; 2053 | int dummy; 2054 | int *ret = ret_ ? ret_ : &dummy; 2055 | 2056 | if (0 == strcmp(src, "true")) { 2057 | *ret = 1; 2058 | return 0; 2059 | } 2060 | if (0 == strcmp(src, "false")) { 2061 | *ret = 0; 2062 | return 0; 2063 | } 2064 | return -1; 2065 | } 2066 | 2067 | /* Raw to integer */ 2068 | int toml_rtoi(toml_raw_t src, int64_t *ret_) { 2069 | if (!src) 2070 | return -1; 2071 | 2072 | char buf[100]; 2073 | char *p = buf; 2074 | char *q = p + sizeof(buf); 2075 | const char *s = src; 2076 | int base = 0; 2077 | int64_t dummy; 2078 | int64_t *ret = ret_ ? ret_ : &dummy; 2079 | 2080 | /* allow +/- */ 2081 | if (s[0] == '+' || s[0] == '-') 2082 | *p++ = *s++; 2083 | 2084 | /* disallow +_100 */ 2085 | if (s[0] == '_') 2086 | return -1; 2087 | 2088 | /* if 0* ... */ 2089 | if ('0' == s[0]) { 2090 | switch (s[1]) { 2091 | case 'x': 2092 | base = 16; 2093 | s += 2; 2094 | break; 2095 | case 'o': 2096 | base = 8; 2097 | s += 2; 2098 | break; 2099 | case 'b': 2100 | base = 2; 2101 | s += 2; 2102 | break; 2103 | case '\0': 2104 | return *ret = 0, 0; 2105 | default: 2106 | /* ensure no other digits after it */ 2107 | if (s[1]) 2108 | return -1; 2109 | } 2110 | } 2111 | 2112 | /* just strip underscores and pass to strtoll */ 2113 | while (*s && p < q) { 2114 | int ch = *s++; 2115 | if (ch == '_') { 2116 | // disallow '__' 2117 | if (s[0] == '_') 2118 | return -1; 2119 | // numbers cannot end with '_' 2120 | if (s[0] == '\0') 2121 | return -1; 2122 | continue; /* skip _ */ 2123 | } 2124 | *p++ = ch; 2125 | } 2126 | 2127 | // if not at end-of-string or we ran out of buffer ... 2128 | if (*s || p == q) 2129 | return -1; 2130 | 2131 | /* cap with NUL */ 2132 | *p = 0; 2133 | 2134 | /* Run strtoll on buf to get the integer */ 2135 | char *endp; 2136 | errno = 0; 2137 | *ret = strtoll(buf, &endp, base); 2138 | return (errno || *endp) ? -1 : 0; 2139 | } 2140 | 2141 | int toml_rtod_ex(toml_raw_t src, double *ret_, char *buf, int buflen) { 2142 | if (!src) 2143 | return -1; 2144 | 2145 | char *p = buf; 2146 | char *q = p + buflen; 2147 | const char *s = src; 2148 | double dummy; 2149 | double *ret = ret_ ? ret_ : &dummy; 2150 | 2151 | /* allow +/- */ 2152 | if (s[0] == '+' || s[0] == '-') 2153 | *p++ = *s++; 2154 | 2155 | /* disallow +_1.00 */ 2156 | if (s[0] == '_') 2157 | return -1; 2158 | 2159 | /* decimal point, if used, must be surrounded by at least one digit on each 2160 | * side */ 2161 | { 2162 | char *dot = strchr(s, '.'); 2163 | if (dot) { 2164 | if (dot == s || !isdigit(dot[-1]) || !isdigit(dot[1])) 2165 | return -1; 2166 | } 2167 | } 2168 | 2169 | /* zero must be followed by . or 'e', or NUL */ 2170 | if (s[0] == '0' && s[1] && !strchr("eE.", s[1])) 2171 | return -1; 2172 | 2173 | /* just strip underscores and pass to strtod */ 2174 | while (*s && p < q) { 2175 | int ch = *s++; 2176 | if (ch == '_') { 2177 | // disallow '__' 2178 | if (s[0] == '_') 2179 | return -1; 2180 | // disallow last char '_' 2181 | if (s[0] == 0) 2182 | return -1; 2183 | continue; /* skip _ */ 2184 | } 2185 | *p++ = ch; 2186 | } 2187 | if (*s || p == q) 2188 | return -1; /* reached end of string or buffer is full? */ 2189 | 2190 | /* cap with NUL */ 2191 | *p = 0; 2192 | 2193 | /* Run strtod on buf to get the value */ 2194 | char *endp; 2195 | errno = 0; 2196 | *ret = strtod(buf, &endp); 2197 | return (errno || *endp) ? -1 : 0; 2198 | } 2199 | 2200 | int toml_rtod(toml_raw_t src, double *ret_) { 2201 | char buf[100]; 2202 | return toml_rtod_ex(src, ret_, buf, sizeof(buf)); 2203 | } 2204 | 2205 | int toml_rtos(toml_raw_t src, char **ret) { 2206 | int multiline = 0; 2207 | const char *sp; 2208 | const char *sq; 2209 | 2210 | *ret = 0; 2211 | if (!src) 2212 | return -1; 2213 | 2214 | int qchar = src[0]; 2215 | int srclen = strlen(src); 2216 | if (!(qchar == '\'' || qchar == '"')) { 2217 | return -1; 2218 | } 2219 | 2220 | // triple quotes? 2221 | if (qchar == src[1] && qchar == src[2]) { 2222 | multiline = 1; 2223 | sp = src + 3; 2224 | sq = src + srclen - 3; 2225 | /* last 3 chars in src must be qchar */ 2226 | if (!(sp <= sq && sq[0] == qchar && sq[1] == qchar && sq[2] == qchar)) 2227 | return -1; 2228 | 2229 | /* skip new line immediate after qchar */ 2230 | if (sp[0] == '\n') 2231 | sp++; 2232 | else if (sp[0] == '\r' && sp[1] == '\n') 2233 | sp += 2; 2234 | 2235 | } else { 2236 | sp = src + 1; 2237 | sq = src + srclen - 1; 2238 | /* last char in src must be qchar */ 2239 | if (!(sp <= sq && *sq == qchar)) 2240 | return -1; 2241 | } 2242 | 2243 | if (qchar == '\'') { 2244 | *ret = norm_lit_str(sp, sq - sp, multiline, 0, 0); 2245 | } else { 2246 | *ret = norm_basic_str(sp, sq - sp, multiline, 0, 0); 2247 | } 2248 | 2249 | return *ret ? 0 : -1; 2250 | } 2251 | 2252 | toml_datum_t toml_string_at(const toml_array_t *arr, int idx) { 2253 | toml_datum_t ret; 2254 | memset(&ret, 0, sizeof(ret)); 2255 | ret.ok = (0 == toml_rtos(toml_raw_at(arr, idx), &ret.u.s)); 2256 | return ret; 2257 | } 2258 | 2259 | toml_datum_t toml_bool_at(const toml_array_t *arr, int idx) { 2260 | toml_datum_t ret; 2261 | memset(&ret, 0, sizeof(ret)); 2262 | ret.ok = (0 == toml_rtob(toml_raw_at(arr, idx), &ret.u.b)); 2263 | return ret; 2264 | } 2265 | 2266 | toml_datum_t toml_int_at(const toml_array_t *arr, int idx) { 2267 | toml_datum_t ret; 2268 | memset(&ret, 0, sizeof(ret)); 2269 | ret.ok = (0 == toml_rtoi(toml_raw_at(arr, idx), &ret.u.i)); 2270 | return ret; 2271 | } 2272 | 2273 | toml_datum_t toml_double_at(const toml_array_t *arr, int idx) { 2274 | toml_datum_t ret; 2275 | memset(&ret, 0, sizeof(ret)); 2276 | ret.ok = (0 == toml_rtod(toml_raw_at(arr, idx), &ret.u.d)); 2277 | return ret; 2278 | } 2279 | 2280 | toml_datum_t toml_timestamp_at(const toml_array_t *arr, int idx) { 2281 | toml_timestamp_t ts; 2282 | toml_datum_t ret; 2283 | memset(&ret, 0, sizeof(ret)); 2284 | ret.ok = (0 == toml_rtots(toml_raw_at(arr, idx), &ts)); 2285 | if (ret.ok) { 2286 | ret.ok = !!(ret.u.ts = MALLOC(sizeof(*ret.u.ts))); 2287 | if (ret.ok) { 2288 | *ret.u.ts = ts; 2289 | if (ret.u.ts->year) 2290 | ret.u.ts->year = &ret.u.ts->__buffer.year; 2291 | if (ret.u.ts->month) 2292 | ret.u.ts->month = &ret.u.ts->__buffer.month; 2293 | if (ret.u.ts->day) 2294 | ret.u.ts->day = &ret.u.ts->__buffer.day; 2295 | if (ret.u.ts->hour) 2296 | ret.u.ts->hour = &ret.u.ts->__buffer.hour; 2297 | if (ret.u.ts->minute) 2298 | ret.u.ts->minute = &ret.u.ts->__buffer.minute; 2299 | if (ret.u.ts->second) 2300 | ret.u.ts->second = &ret.u.ts->__buffer.second; 2301 | if (ret.u.ts->millisec) 2302 | ret.u.ts->millisec = &ret.u.ts->__buffer.millisec; 2303 | if (ret.u.ts->z) 2304 | ret.u.ts->z = ret.u.ts->__buffer.z; 2305 | } 2306 | } 2307 | return ret; 2308 | } 2309 | 2310 | toml_datum_t toml_string_in(const toml_table_t *arr, const char *key) { 2311 | toml_datum_t ret; 2312 | memset(&ret, 0, sizeof(ret)); 2313 | toml_raw_t raw = toml_raw_in(arr, key); 2314 | if (raw) { 2315 | ret.ok = (0 == toml_rtos(raw, &ret.u.s)); 2316 | } 2317 | return ret; 2318 | } 2319 | 2320 | toml_datum_t toml_bool_in(const toml_table_t *arr, const char *key) { 2321 | toml_datum_t ret; 2322 | memset(&ret, 0, sizeof(ret)); 2323 | ret.ok = (0 == toml_rtob(toml_raw_in(arr, key), &ret.u.b)); 2324 | return ret; 2325 | } 2326 | 2327 | toml_datum_t toml_int_in(const toml_table_t *arr, const char *key) { 2328 | toml_datum_t ret; 2329 | memset(&ret, 0, sizeof(ret)); 2330 | ret.ok = (0 == toml_rtoi(toml_raw_in(arr, key), &ret.u.i)); 2331 | return ret; 2332 | } 2333 | 2334 | toml_datum_t toml_double_in(const toml_table_t *arr, const char *key) { 2335 | toml_datum_t ret; 2336 | memset(&ret, 0, sizeof(ret)); 2337 | ret.ok = (0 == toml_rtod(toml_raw_in(arr, key), &ret.u.d)); 2338 | return ret; 2339 | } 2340 | 2341 | toml_datum_t toml_timestamp_in(const toml_table_t *arr, const char *key) { 2342 | toml_timestamp_t ts; 2343 | toml_datum_t ret; 2344 | memset(&ret, 0, sizeof(ret)); 2345 | ret.ok = (0 == toml_rtots(toml_raw_in(arr, key), &ts)); 2346 | if (ret.ok) { 2347 | ret.ok = !!(ret.u.ts = MALLOC(sizeof(*ret.u.ts))); 2348 | if (ret.ok) { 2349 | *ret.u.ts = ts; 2350 | if (ret.u.ts->year) 2351 | ret.u.ts->year = &ret.u.ts->__buffer.year; 2352 | if (ret.u.ts->month) 2353 | ret.u.ts->month = &ret.u.ts->__buffer.month; 2354 | if (ret.u.ts->day) 2355 | ret.u.ts->day = &ret.u.ts->__buffer.day; 2356 | if (ret.u.ts->hour) 2357 | ret.u.ts->hour = &ret.u.ts->__buffer.hour; 2358 | if (ret.u.ts->minute) 2359 | ret.u.ts->minute = &ret.u.ts->__buffer.minute; 2360 | if (ret.u.ts->second) 2361 | ret.u.ts->second = &ret.u.ts->__buffer.second; 2362 | if (ret.u.ts->millisec) 2363 | ret.u.ts->millisec = &ret.u.ts->__buffer.millisec; 2364 | if (ret.u.ts->z) 2365 | ret.u.ts->z = ret.u.ts->__buffer.z; 2366 | } 2367 | } 2368 | return ret; 2369 | } 2370 | 2371 | static int parse_millisec(const char *p, const char **endp) { 2372 | int ret = 0; 2373 | int unit = 100; /* unit in millisec */ 2374 | for (; '0' <= *p && *p <= '9'; p++, unit /= 10) { 2375 | ret += (*p - '0') * unit; 2376 | } 2377 | *endp = p; 2378 | return ret; 2379 | } 2380 | -------------------------------------------------------------------------------- /toml.h: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) CK Tan 5 | https://github.com/cktan/tomlc99 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | */ 25 | #ifndef TOML_H 26 | #define TOML_H 27 | 28 | #include 29 | #include 30 | 31 | #ifdef __cplusplus 32 | #define TOML_EXTERN extern "C" 33 | #else 34 | #define TOML_EXTERN extern 35 | #endif 36 | 37 | typedef struct toml_timestamp_t toml_timestamp_t; 38 | typedef struct toml_table_t toml_table_t; 39 | typedef struct toml_array_t toml_array_t; 40 | typedef struct toml_datum_t toml_datum_t; 41 | 42 | /* Parse a file. Return a table on success, or 0 otherwise. 43 | * Caller must toml_free(the-return-value) after use. 44 | */ 45 | TOML_EXTERN toml_table_t *toml_parse_file(FILE *fp, char *errbuf, int errbufsz); 46 | 47 | /* Parse a string containing the full config. 48 | * Return a table on success, or 0 otherwise. 49 | * Caller must toml_free(the-return-value) after use. 50 | */ 51 | TOML_EXTERN toml_table_t *toml_parse(char *conf, /* NUL terminated, please. */ 52 | char *errbuf, int errbufsz); 53 | 54 | /* Free the table returned by toml_parse() or toml_parse_file(). Once 55 | * this function is called, any handles accessed through this tab 56 | * directly or indirectly are no longer valid. 57 | */ 58 | TOML_EXTERN void toml_free(toml_table_t *tab); 59 | 60 | /* Timestamp types. The year, month, day, hour, minute, second, z 61 | * fields may be NULL if they are not relevant. e.g. In a DATE 62 | * type, the hour, minute, second and z fields will be NULLs. 63 | */ 64 | struct toml_timestamp_t { 65 | struct { /* internal. do not use. */ 66 | int year, month, day; 67 | int hour, minute, second, millisec; 68 | char z[10]; 69 | } __buffer; 70 | int *year, *month, *day; 71 | int *hour, *minute, *second, *millisec; 72 | char *z; 73 | }; 74 | 75 | /*----------------------------------------------------------------- 76 | * Enhanced access methods 77 | */ 78 | struct toml_datum_t { 79 | int ok; 80 | union { 81 | toml_timestamp_t *ts; /* ts must be freed after use */ 82 | char *s; /* string value. s must be freed after use */ 83 | int b; /* bool value */ 84 | int64_t i; /* int value */ 85 | double d; /* double value */ 86 | } u; 87 | }; 88 | 89 | /* on arrays: */ 90 | /* ... retrieve size of array. */ 91 | TOML_EXTERN int toml_array_nelem(const toml_array_t *arr); 92 | /* ... retrieve values using index. */ 93 | TOML_EXTERN toml_datum_t toml_string_at(const toml_array_t *arr, int idx); 94 | TOML_EXTERN toml_datum_t toml_bool_at(const toml_array_t *arr, int idx); 95 | TOML_EXTERN toml_datum_t toml_int_at(const toml_array_t *arr, int idx); 96 | TOML_EXTERN toml_datum_t toml_double_at(const toml_array_t *arr, int idx); 97 | TOML_EXTERN toml_datum_t toml_timestamp_at(const toml_array_t *arr, int idx); 98 | /* ... retrieve array or table using index. */ 99 | TOML_EXTERN toml_array_t *toml_array_at(const toml_array_t *arr, int idx); 100 | TOML_EXTERN toml_table_t *toml_table_at(const toml_array_t *arr, int idx); 101 | 102 | /* on tables: */ 103 | /* ... retrieve the key in table at keyidx. Return 0 if out of range. */ 104 | TOML_EXTERN const char *toml_key_in(const toml_table_t *tab, int keyidx); 105 | /* ... returns 1 if key exists in tab, 0 otherwise */ 106 | TOML_EXTERN int toml_key_exists(const toml_table_t *tab, const char *key); 107 | /* ... retrieve values using key. */ 108 | TOML_EXTERN toml_datum_t toml_string_in(const toml_table_t *arr, 109 | const char *key); 110 | TOML_EXTERN toml_datum_t toml_bool_in(const toml_table_t *arr, const char *key); 111 | TOML_EXTERN toml_datum_t toml_int_in(const toml_table_t *arr, const char *key); 112 | TOML_EXTERN toml_datum_t toml_double_in(const toml_table_t *arr, 113 | const char *key); 114 | TOML_EXTERN toml_datum_t toml_timestamp_in(const toml_table_t *arr, 115 | const char *key); 116 | /* .. retrieve array or table using key. */ 117 | TOML_EXTERN toml_array_t *toml_array_in(const toml_table_t *tab, 118 | const char *key); 119 | TOML_EXTERN toml_table_t *toml_table_in(const toml_table_t *tab, 120 | const char *key); 121 | 122 | /*----------------------------------------------------------------- 123 | * lesser used 124 | */ 125 | /* Return the array kind: 't'able, 'a'rray, 'v'alue, 'm'ixed */ 126 | TOML_EXTERN char toml_array_kind(const toml_array_t *arr); 127 | 128 | /* For array kind 'v'alue, return the type of values 129 | i:int, d:double, b:bool, s:string, t:time, D:date, T:timestamp, 'm'ixed 130 | 0 if unknown 131 | */ 132 | TOML_EXTERN char toml_array_type(const toml_array_t *arr); 133 | 134 | /* Return the key of an array */ 135 | TOML_EXTERN const char *toml_array_key(const toml_array_t *arr); 136 | 137 | /* Return the number of key-values in a table */ 138 | TOML_EXTERN int toml_table_nkval(const toml_table_t *tab); 139 | 140 | /* Return the number of arrays in a table */ 141 | TOML_EXTERN int toml_table_narr(const toml_table_t *tab); 142 | 143 | /* Return the number of sub-tables in a table */ 144 | TOML_EXTERN int toml_table_ntab(const toml_table_t *tab); 145 | 146 | /* Return the key of a table*/ 147 | TOML_EXTERN const char *toml_table_key(const toml_table_t *tab); 148 | 149 | /*-------------------------------------------------------------- 150 | * misc 151 | */ 152 | TOML_EXTERN int toml_utf8_to_ucs(const char *orig, int len, int64_t *ret); 153 | TOML_EXTERN int toml_ucs_to_utf8(int64_t code, char buf[6]); 154 | TOML_EXTERN void toml_set_memutil(void *(*xxmalloc)(size_t), 155 | void (*xxfree)(void *)); 156 | 157 | /*-------------------------------------------------------------- 158 | * deprecated 159 | */ 160 | /* A raw value, must be processed by toml_rto* before using. */ 161 | typedef const char *toml_raw_t; 162 | TOML_EXTERN toml_raw_t toml_raw_in(const toml_table_t *tab, const char *key); 163 | TOML_EXTERN toml_raw_t toml_raw_at(const toml_array_t *arr, int idx); 164 | TOML_EXTERN int toml_rtos(toml_raw_t s, char **ret); 165 | TOML_EXTERN int toml_rtob(toml_raw_t s, int *ret); 166 | TOML_EXTERN int toml_rtoi(toml_raw_t s, int64_t *ret); 167 | TOML_EXTERN int toml_rtod(toml_raw_t s, double *ret); 168 | TOML_EXTERN int toml_rtod_ex(toml_raw_t s, double *ret, char *buf, int buflen); 169 | TOML_EXTERN int toml_rtots(toml_raw_t s, toml_timestamp_t *ret); 170 | 171 | #endif /* TOML_H */ 172 | -------------------------------------------------------------------------------- /toml_json.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2020 CK Tan 5 | https://github.com/cktan/tomlcpp 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | */ 25 | #ifdef NDEBUG 26 | #undef NDEBUG 27 | #endif 28 | 29 | #include "tomlcpp.hpp" 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | using std::cerr; 43 | using std::cout; 44 | using std::pair; 45 | using std::string; 46 | 47 | static void print_escape_string(const string &str) { 48 | for (char ch : str) { 49 | switch (ch) { 50 | case '\b': 51 | cout << "\\b"; 52 | break; 53 | case '\t': 54 | cout << "\\t"; 55 | break; 56 | case '\n': 57 | cout << "\\n"; 58 | break; 59 | case '\f': 60 | cout << "\\f"; 61 | break; 62 | case '\r': 63 | cout << "\\r"; 64 | break; 65 | case '"': 66 | cout << "\\\""; 67 | break; 68 | case '\\': 69 | cout << "\\\\"; 70 | break; 71 | default: 72 | cout << ch; 73 | break; 74 | } 75 | } 76 | } 77 | 78 | static string z4(int i) { 79 | char buf[10]; 80 | sprintf(buf, "%04d", i); 81 | return buf; 82 | } 83 | 84 | static string z2(int i) { 85 | char buf[10]; 86 | sprintf(buf, "%02d", i); 87 | return buf; 88 | } 89 | 90 | static void print(const string &s) { 91 | cout << "{\"type\":\"string\",\"value\":\""; 92 | print_escape_string(s); 93 | cout << "\"}"; 94 | return; 95 | } 96 | 97 | static void print(bool b) { 98 | cout << "{\"type\":\"bool\",\"value\":\"" << b << "\"}"; 99 | } 100 | 101 | static void print(int64_t i) { 102 | cout << "{\"type\":\"integer\",\"value\":\"" << i << "\"}"; 103 | } 104 | 105 | static void print(double d) { 106 | cout << "{\"type\":\"float\",\"value\":\"" << d << "\"}"; 107 | } 108 | 109 | static void print(const toml::Timestamp &t) { 110 | if (t.year != -1 && t.hour != -1) { 111 | cout << "{\"type\":\"datetime\",\"value\":\""; 112 | cout << z4(t.year); 113 | cout << "-" << z2(t.month); 114 | cout << "-" << z2(t.day); 115 | cout << "T" << z2(t.hour); 116 | cout << ":" << z2(t.minute); 117 | cout << ":" << z2(t.second); 118 | if (t.millisec != -1) 119 | cout << "." << t.millisec; 120 | cout << t.z << "\"}"; 121 | return; 122 | } 123 | 124 | if (t.year != -1) { 125 | cout << "{\"type\":\"date\",\"value\":\""; 126 | cout << z4(t.year); 127 | cout << "-" << z2(t.month); 128 | cout << "-" << z2(t.day); 129 | cout << "\"}"; 130 | return; 131 | } 132 | 133 | if (t.hour != -1) { 134 | cout << "{\"type\":\"time\",\"value\":\""; 135 | cout << z2(t.hour); 136 | cout << ":" << z2(t.minute); 137 | cout << ":" << z2(t.second); 138 | if (t.millisec != -1) 139 | cout << "." << t.millisec; 140 | cout << "\"}"; 141 | return; 142 | } 143 | } 144 | 145 | static bool print(pair v) { 146 | if (!v.first) 147 | return 0; 148 | print(v.second); 149 | return true; 150 | } 151 | 152 | static bool print(pair v) { 153 | if (!v.first) 154 | return 0; 155 | print(v.second); 156 | return true; 157 | } 158 | 159 | static bool print(pair v) { 160 | if (!v.first) 161 | return 0; 162 | print(v.second); 163 | return true; 164 | } 165 | 166 | static bool print(pair v) { 167 | if (!v.first) 168 | return 0; 169 | print(v.second); 170 | return true; 171 | } 172 | 173 | static bool print(pair v) { 174 | if (!v.first) 175 | return 0; 176 | print(v.second); 177 | return true; 178 | } 179 | 180 | static void print(const toml::Array &arr); 181 | static void print(const toml::Table &curtab) { 182 | bool first = true; 183 | cout << "{"; 184 | for (auto &key : curtab.keys()) { 185 | cout << (first ? "" : ","); 186 | first = false; 187 | 188 | // print the key 189 | cout << "\""; 190 | print_escape_string(key); 191 | cout << "\":"; 192 | 193 | // print the associated value 194 | 195 | auto a = curtab.getArray(key); 196 | if (a) { 197 | print(*a); 198 | continue; 199 | } 200 | 201 | auto t = curtab.getTable(key); 202 | if (t) { 203 | print(*t); 204 | continue; 205 | } 206 | 207 | if (print(curtab.getString(key))) 208 | continue; 209 | 210 | if (print(curtab.getInt(key))) 211 | continue; 212 | 213 | if (print(curtab.getBool(key))) 214 | continue; 215 | 216 | if (print(curtab.getDouble(key))) 217 | continue; 218 | 219 | if (print(curtab.getTimestamp(key))) 220 | continue; 221 | 222 | abort(); 223 | } 224 | cout << "}"; 225 | } 226 | 227 | static void print(const toml::Array &arr) { 228 | if (arr.kind() == 't') { 229 | auto tvec = arr.getTableVector(); 230 | cout << "["; 231 | int i = 0; 232 | for (auto &t : *tvec) { 233 | if (i) 234 | cout << ","; 235 | print(t); 236 | i++; 237 | } 238 | cout << "]"; 239 | return; 240 | } 241 | 242 | if (arr.kind() == 'a') { 243 | auto avec = arr.getArrayVector(); 244 | cout << "{\"type\":\"array\",\"value\":["; 245 | int i = 0; 246 | for (auto &a : *avec) { 247 | if (i) 248 | cout << ","; 249 | print(a); 250 | i++; 251 | } 252 | cout << "]}"; 253 | return; 254 | } 255 | 256 | cout << "{\"type\":\"array\",\"value\":["; 257 | { 258 | auto v = arr.getStringVector(); 259 | if (v) { 260 | bool first = 1; 261 | for (const auto &s : *v) { 262 | cout << (first ? "" : ","); 263 | print(s); 264 | first = 0; 265 | } 266 | cout << "]}"; 267 | return; 268 | } 269 | } 270 | 271 | { 272 | auto v = arr.getIntVector(); 273 | if (v) { 274 | bool first = 1; 275 | for (auto s : *v) { 276 | cout << (first ? "" : ","); 277 | print(s); 278 | first = 0; 279 | } 280 | cout << "]}"; 281 | return; 282 | } 283 | } 284 | 285 | { 286 | auto v = arr.getBoolVector(); 287 | if (v) { 288 | bool first = 1; 289 | for (const auto &&s : *v) { 290 | cout << (first ? "" : ","); 291 | print(s); 292 | first = 0; 293 | } 294 | cout << "]}"; 295 | return; 296 | } 297 | } 298 | 299 | { 300 | auto v = arr.getDoubleVector(); 301 | if (v) { 302 | bool first = 1; 303 | for (auto s : *v) { 304 | cout << (first ? "" : ","); 305 | print(s); 306 | first = 0; 307 | } 308 | cout << "]}"; 309 | return; 310 | } 311 | } 312 | 313 | { 314 | auto v = arr.getTimestampVector(); 315 | if (v) { 316 | bool first = 1; 317 | for (const auto &s : *v) { 318 | cout << (first ? "" : ","); 319 | print(s); 320 | first = 0; 321 | } 322 | cout << "]}"; 323 | return; 324 | } 325 | } 326 | 327 | abort(); 328 | } 329 | 330 | static void cat(std::istream &stream) { 331 | std::string str(std::istreambuf_iterator{stream}, {}); 332 | /* 333 | cout << "---------------\n"; 334 | cout << str << "\n"; 335 | cout << "---------------\n"; 336 | */ 337 | auto result = toml::parse(str); 338 | if (!result.table) { 339 | cerr << "ERROR: " << result.errmsg << '\n'; 340 | exit(1); 341 | } 342 | 343 | print(*result.table); 344 | cout << '\n'; 345 | } 346 | 347 | int main(int argc, const char *argv[]) { 348 | int i; 349 | if (argc == 1) { 350 | cat(std::cin); 351 | } else { 352 | for (i = 1; i < argc; i++) { 353 | std::ifstream stream(argv[i]); 354 | if (!stream) { 355 | cerr << "ERROR: cannot open " << argv[i] << ":" << strerror(errno) 356 | << "\n"; 357 | exit(1); 358 | } 359 | cat(stream); 360 | } 361 | } 362 | return 0; 363 | } 364 | -------------------------------------------------------------------------------- /toml_sample.cpp: -------------------------------------------------------------------------------- 1 | #include "tomlcpp.hpp" 2 | #include 3 | 4 | using std::cerr; 5 | using std::cout; 6 | 7 | void fatal(std::string msg) { 8 | cerr << "FATAL: " << msg << "\n"; 9 | exit(1); 10 | } 11 | 12 | int main() { 13 | // 1. parse file 14 | auto res = toml::parseFile("sample.toml"); 15 | if (!res.table) { 16 | fatal("cannot parse file: " + res.errmsg); 17 | } 18 | 19 | // 2. get top level table 20 | auto server = res.table->getTable("server"); 21 | if (!server) { 22 | fatal("missing [server]"); 23 | } 24 | 25 | // 3. extract values from the top level table 26 | auto [ok, host] = server->getString("host"); 27 | if (!ok) { 28 | fatal("missing or bad host entry"); 29 | } 30 | 31 | auto portArray = server->getArray("port"); 32 | if (!portArray) { 33 | fatal("missing 'port' array"); 34 | } 35 | 36 | // 4. examine the values 37 | cout << "host: " << host << "\n"; 38 | cout << "port: "; 39 | for (int i = 0;; i++) { 40 | auto p = portArray->getInt(i); 41 | if (!p.first) 42 | break; 43 | 44 | cout << p.second << " "; 45 | } 46 | cout << "\n"; 47 | 48 | return 0; 49 | } 50 | -------------------------------------------------------------------------------- /tomlcpp.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2020 CK Tan 6 | https://github.com/cktan/tomlcpp 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | 26 | */ 27 | #include "tomlcpp.hpp" 28 | #include "toml.h" 29 | #include 30 | #include 31 | 32 | using namespace toml; 33 | using std::pair; 34 | using std::string; 35 | using std::vector; 36 | 37 | static void *toml_mymalloc(size_t sz) { return new char[sz]; } 38 | 39 | static void toml_myfree(void *p) { 40 | if (p) { 41 | char *pp = (char *)p; 42 | delete[] pp; 43 | } 44 | } 45 | 46 | /** 47 | * Keep track of memory to be freed when all references 48 | * to the tree returned by toml::parse is no longer reachable. 49 | */ 50 | struct toml::Backing { 51 | char *ptr = 0; 52 | toml_table_t *root = 0; 53 | Backing(const string &conf) { 54 | ptr = new char[conf.length() + 1]; 55 | strcpy(ptr, conf.c_str()); 56 | } 57 | ~Backing() { 58 | if (ptr) 59 | delete[] ptr; 60 | if (root) 61 | toml_free(root); 62 | } 63 | }; 64 | 65 | pair Table::getString(const string &key) const { 66 | string str; 67 | toml_datum_t p = toml_string_in(m_table, key.c_str()); 68 | if (p.ok) { 69 | str = p.u.s; 70 | toml_myfree(p.u.s); 71 | } 72 | return {p.ok, str}; 73 | } 74 | 75 | pair Table::getBool(const string &key) const { 76 | toml_datum_t p = toml_bool_in(m_table, key.c_str()); 77 | return {p.ok, !!p.u.b}; 78 | } 79 | 80 | pair Table::getInt(const string &key) const { 81 | toml_datum_t p = toml_int_in(m_table, key.c_str()); 82 | return {p.ok, p.u.i}; 83 | } 84 | 85 | pair Table::getDouble(const string &key) const { 86 | toml_datum_t p = toml_double_in(m_table, key.c_str()); 87 | return {p.ok, p.u.d}; 88 | } 89 | 90 | pair Table::getTimestamp(const string &key) const { 91 | Timestamp ret; 92 | toml_datum_t p = toml_timestamp_in(m_table, key.c_str()); 93 | if (p.ok) { 94 | toml_timestamp_t &ts = *p.u.ts; 95 | ret.year = (ts.year ? *ts.year : -1); 96 | ret.month = (ts.month ? *ts.month : -1); 97 | ret.day = (ts.day ? *ts.day : -1); 98 | ret.hour = (ts.hour ? *ts.hour : -1); 99 | ret.minute = (ts.minute ? *ts.minute : -1); 100 | ret.second = (ts.second ? *ts.second : -1); 101 | ret.millisec = (ts.millisec ? *ts.millisec : -1); 102 | ret.z = ts.z ? string(ts.z) : ""; 103 | toml_myfree(p.u.ts); 104 | } 105 | return {p.ok, ret}; 106 | } 107 | 108 | std::unique_ptr Table::getArray(const string &key) const { 109 | toml_array_t *a = toml_array_in(m_table, key.c_str()); 110 | if (!a) 111 | return 0; 112 | 113 | auto ret = std::make_unique(a, m_backing); 114 | return ret; 115 | } 116 | 117 | std::unique_ptr Table::getTable(const string &key) const { 118 | toml_table_t *t = toml_table_in(m_table, key.c_str()); 119 | if (!t) 120 | return 0; 121 | 122 | auto ret = std::make_unique
(t, m_backing); 123 | return ret; 124 | } 125 | 126 | vector Table::keys() const { 127 | vector vec; 128 | for (int i = 0;; i++) { 129 | const char *k = toml_key_in(m_table, i); 130 | if (!k) 131 | break; 132 | vec.push_back(k); 133 | } 134 | return vec; 135 | } 136 | 137 | char Array::kind() const { return toml_array_kind(m_array); } 138 | 139 | char Array::type() const { return toml_array_type(m_array); } 140 | 141 | pair Array::getString(int idx) const { 142 | string str; 143 | toml_datum_t p = toml_string_at(m_array, idx); 144 | if (p.ok) { 145 | str = p.u.s; 146 | toml_myfree(p.u.s); 147 | } 148 | return {p.ok, str}; 149 | } 150 | 151 | pair Array::getBool(int idx) const { 152 | toml_datum_t p = toml_bool_at(m_array, idx); 153 | return {p.ok, !!p.u.b}; 154 | } 155 | 156 | pair Array::getInt(int idx) const { 157 | toml_datum_t p = toml_int_at(m_array, idx); 158 | return {p.ok, p.u.i}; 159 | } 160 | 161 | pair Array::getDouble(int idx) const { 162 | toml_datum_t p = toml_double_at(m_array, idx); 163 | return {p.ok, p.u.d}; 164 | } 165 | 166 | pair Array::getTimestamp(int idx) const { 167 | Timestamp ret; 168 | toml_datum_t p = toml_timestamp_at(m_array, idx); 169 | if (p.ok) { 170 | toml_timestamp_t &ts = *p.u.ts; 171 | ret.year = (ts.year ? *ts.year : -1); 172 | ret.month = (ts.month ? *ts.month : -1); 173 | ret.day = (ts.day ? *ts.day : -1); 174 | ret.hour = (ts.hour ? *ts.hour : -1); 175 | ret.second = (ts.second ? *ts.second : -1); 176 | ret.millisec = (ts.millisec ? *ts.millisec : -1); 177 | ret.z = ts.z ? string(ts.z) : ""; 178 | toml_myfree(p.u.ts); 179 | } 180 | return {p.ok, ret}; 181 | } 182 | 183 | std::unique_ptr Array::getArray(int idx) const { 184 | toml_array_t *a = toml_array_at(m_array, idx); 185 | if (!a) 186 | return 0; 187 | 188 | auto ret = std::make_unique(a, m_backing); 189 | return ret; 190 | } 191 | 192 | std::unique_ptr
Array::getTable(int idx) const { 193 | toml_table_t *t = toml_table_at(m_array, idx); 194 | if (!t) 195 | return 0; 196 | 197 | auto ret = std::make_unique
(t, m_backing); 198 | return ret; 199 | } 200 | 201 | std::unique_ptr> Array::getArrayVector() const { 202 | int top = toml_array_nelem(m_array); 203 | if (top < 0) 204 | return 0; 205 | 206 | auto ret = std::make_unique>(); 207 | ret->reserve(top); 208 | for (int i = 0; i < top; i++) { 209 | toml_array_t *a = toml_array_at(m_array, i); 210 | if (!a) 211 | return 0; 212 | 213 | ret->push_back(Array(a, m_backing)); 214 | } 215 | 216 | return ret; 217 | } 218 | 219 | std::unique_ptr> Array::getTableVector() const { 220 | int top = toml_array_nelem(m_array); 221 | if (top < 0) 222 | return 0; 223 | 224 | auto ret = std::make_unique>(); 225 | ret->reserve(top); 226 | for (int i = 0; i < top; i++) { 227 | toml_table_t *t = toml_table_at(m_array, i); 228 | if (!t) 229 | return 0; 230 | 231 | ret->push_back(Table(t, m_backing)); 232 | } 233 | 234 | return ret; 235 | } 236 | 237 | std::unique_ptr> Array::getStringVector() const { 238 | int top = toml_array_nelem(m_array); 239 | if (top < 0) 240 | return 0; 241 | 242 | auto ret = std::make_unique>(); 243 | ret->reserve(top); 244 | for (int i = 0; i < top; i++) { 245 | toml_datum_t p = toml_string_at(m_array, i); 246 | if (!p.ok) 247 | return 0; 248 | ret->push_back(p.u.s); 249 | toml_myfree(p.u.s); 250 | } 251 | 252 | return ret; 253 | } 254 | 255 | std::unique_ptr> Array::getBoolVector() const { 256 | int top = toml_array_nelem(m_array); 257 | if (top < 0) 258 | return 0; 259 | 260 | auto ret = std::make_unique>(); 261 | ret->reserve(top); 262 | for (int i = 0; i < top; i++) { 263 | toml_datum_t p = toml_bool_at(m_array, i); 264 | if (!p.ok) 265 | return 0; 266 | ret->push_back(!!p.u.b); 267 | } 268 | 269 | return ret; 270 | } 271 | 272 | std::unique_ptr> Array::getIntVector() const { 273 | int top = toml_array_nelem(m_array); 274 | if (top < 0) 275 | return 0; 276 | 277 | auto ret = std::make_unique>(); 278 | ret->reserve(top); 279 | for (int i = 0; i < top; i++) { 280 | toml_datum_t p = toml_int_at(m_array, i); 281 | if (!p.ok) 282 | return 0; 283 | ret->push_back(p.u.i); 284 | } 285 | 286 | return ret; 287 | } 288 | 289 | std::unique_ptr> Array::getTimestampVector() const { 290 | int top = toml_array_nelem(m_array); 291 | if (top < 0) 292 | return 0; 293 | 294 | auto ret = std::make_unique>(); 295 | ret->reserve(top); 296 | for (int i = 0; i < top; i++) { 297 | toml_datum_t p = toml_timestamp_at(m_array, i); 298 | if (!p.ok) 299 | return 0; 300 | 301 | toml_timestamp_t &ts = *p.u.ts; 302 | Timestamp v; 303 | v.year = (ts.year ? *ts.year : -1); 304 | v.month = (ts.month ? *ts.month : -1); 305 | v.day = (ts.day ? *ts.day : -1); 306 | v.hour = (ts.hour ? *ts.hour : -1); 307 | v.second = (ts.second ? *ts.second : -1); 308 | v.millisec = (ts.millisec ? *ts.millisec : -1); 309 | v.z = ts.z ? string(ts.z) : ""; 310 | toml_myfree(p.u.ts); 311 | 312 | ret->push_back(v); 313 | } 314 | 315 | return ret; 316 | } 317 | 318 | std::unique_ptr> Array::getDoubleVector() const { 319 | int top = toml_array_nelem(m_array); 320 | if (top < 0) 321 | return 0; 322 | 323 | auto ret = std::make_unique>(); 324 | ret->reserve(top); 325 | for (int i = 0; i < top; i++) { 326 | toml_datum_t p = toml_double_at(m_array, i); 327 | if (!p.ok) 328 | return 0; 329 | ret->push_back(p.u.d); 330 | } 331 | 332 | return ret; 333 | } 334 | 335 | int toml::Array::size() const { return toml_array_nelem(m_array); } 336 | 337 | toml::Result toml::parse(const string &conf) { 338 | toml::Result ret; 339 | char errbuf[200]; 340 | auto backing = std::make_shared(conf); 341 | 342 | toml_set_memutil(toml_mymalloc, toml_myfree); 343 | toml_table_t *t = toml_parse(backing->ptr, errbuf, sizeof(errbuf)); 344 | if (t) { 345 | ret.table = std::make_shared
(t, backing); 346 | backing->root = t; 347 | } else { 348 | ret.errmsg = (*errbuf) ? string(errbuf) : "unknown error"; 349 | } 350 | return ret; 351 | } 352 | 353 | toml::Result toml::parseFile(const string &path) { 354 | toml::Result ret; 355 | std::ifstream stream(path); 356 | if (!stream) { 357 | ret.errmsg = strerror(errno); 358 | return ret; 359 | } 360 | string conf(std::istreambuf_iterator{stream}, {}); 361 | return toml::parse(conf); 362 | } 363 | -------------------------------------------------------------------------------- /tomlcpp.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2020 CK Tan 5 | https://github.com/cktan/tomlcpp 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | */ 25 | #ifndef TOML_HPP 26 | #define TOML_HPP 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | struct toml_table_t; 34 | struct toml_array_t; 35 | 36 | namespace toml { 37 | 38 | struct Backing; 39 | class Array; 40 | class Table; 41 | using std::pair; 42 | using std::string; 43 | using std::vector; 44 | 45 | /* A Timestamp value */ 46 | struct Timestamp { 47 | // -1 means it is not valid 48 | int year = -1; 49 | int month = -1; 50 | int day = -1; 51 | int hour = -1; 52 | int minute = -1; 53 | int second = -1; 54 | int millisec = -1; 55 | string z; // "" if no timezone 56 | }; 57 | 58 | /* A table in toml. You can extract value/table/array using a key. */ 59 | class Table { 60 | public: 61 | vector keys() const; 62 | 63 | // get content 64 | pair getString(const string &key) const; 65 | pair getBool(const string &key) const; 66 | pair getInt(const string &key) const; 67 | pair getDouble(const string &key) const; 68 | pair getTimestamp(const string &key) const; 69 | std::unique_ptr
getTable(const string &key) const; 70 | std::unique_ptr getArray(const string &key) const; 71 | 72 | // internal 73 | Table(toml_table_t *t, std::shared_ptr backing) 74 | : m_table(t), m_backing(backing) {} 75 | 76 | private: 77 | toml_table_t *const m_table = 0; 78 | std::shared_ptr m_backing; 79 | 80 | Table() = delete; 81 | }; 82 | 83 | /* An array in toml. You can extract value/table/array using an index. */ 84 | class Array { 85 | public: 86 | // Content kind 87 | // t:table, a:array, v:value, m:mixed 88 | char kind() const; 89 | 90 | // For Value kind only, check the type of the value 91 | // i:int, d:double, b:bool, s:string, t:time, D: date, T:timestamp, m:mixed, 92 | // 0:unknown 93 | char type() const; 94 | 95 | // Return the #elements in the array 96 | int size() const; 97 | 98 | // You may have to use these methods for arrays with mixed values 99 | pair getString(int idx) const; 100 | pair getBool(int idx) const; 101 | pair getInt(int idx) const; 102 | pair getDouble(int idx) const; 103 | pair getTimestamp(int idx) const; 104 | 105 | std::unique_ptr
getTable(int idx) const; 106 | std::unique_ptr getArray(int idx) const; 107 | 108 | // Use these methods only if you know the array has no mixed values! 109 | // For values, some conveniet methods to obtain vector 110 | std::unique_ptr> getStringVector() const; 111 | std::unique_ptr> getBoolVector() const; 112 | std::unique_ptr> getIntVector() const; 113 | std::unique_ptr> getDoubleVector() const; 114 | std::unique_ptr> getTimestampVector() const; 115 | 116 | // Obtain vectors of table or array 117 | std::unique_ptr> getTableVector() const; 118 | std::unique_ptr> getArrayVector() const; 119 | 120 | // internal 121 | Array(toml_array_t *a, std::shared_ptr backing) 122 | : m_array(a), m_backing(backing) {} 123 | 124 | private: 125 | toml_array_t *const m_array = 0; 126 | std::shared_ptr m_backing; 127 | 128 | Array() = delete; 129 | }; 130 | 131 | /* The main function: Parse */ 132 | struct Result { 133 | std::shared_ptr
table; 134 | string errmsg; 135 | }; 136 | 137 | Result parse(const string &conf); 138 | Result parseFile(const string &path); 139 | }; // namespace toml 140 | 141 | #endif /* TOML_HPP */ 142 | --------------------------------------------------------------------------------