├── .gitignore ├── CHANGELOG.md ├── CMakeLists.txt ├── LICENSE ├── README.md ├── cmake ├── Modules │ └── tomlTargetProperties.cmake └── tomlConfig.cmake.in ├── include └── toml.h ├── src └── toml.c └── tests ├── CMakeLists.txt ├── complex-structure.toml ├── example.toml ├── fruit.toml ├── hard_example.toml ├── hard_example_unicode.toml ├── key-values.toml ├── long_config.toml └── main.c /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Linker output 11 | *.ilk 12 | *.map 13 | *.exp 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Libraries 20 | *.lib 21 | *.a 22 | *.la 23 | *.lo 24 | 25 | # Shared objects (inc. Windows DLLs) 26 | *.dll 27 | *.so 28 | *.so.* 29 | *.dylib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | *.i*86 36 | *.x86_64 37 | *.hex 38 | 39 | # Debug files 40 | *.dSYM/ 41 | *.su 42 | *.idb 43 | *.pdb 44 | 45 | # Kernel Module Compile Results 46 | *.mod* 47 | *.cmd 48 | .tmp_versions/ 49 | modules.order 50 | Module.symvers 51 | Mkfile.old 52 | dkms.conf 53 | 54 | /build/ 55 | .ycm_extra_conf.py 56 | *.pyc 57 | *.swp 58 | *.swo 59 | .ccls 60 | .ccls* 61 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brglng/libtoml/6563ddeceb01d8828e12b7a5bdb53b18e4f05b09/CHANGELOG.md -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | if(POLICY CMP0025) 3 | cmake_policy(SET CMP0025 NEW) 4 | endif() 5 | if(POLICY CMP0048) 6 | cmake_policy(SET CMP0048 NEW) 7 | endif() 8 | if(POLICY CMP0054) 9 | cmake_policy(SET CMP0054 NEW) 10 | endif() 11 | if(POLICY CMP0077) 12 | cmake_policy(SET CMP0077 NEW) 13 | endif() 14 | list(INSERT CMAKE_MODULE_PATH 0 "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules") 15 | 16 | # Project Information 17 | project(toml C) 18 | set(PROJECT_VERSION_MAJOR 0) 19 | set(PROJECT_VERSION_MINOR 1) 20 | set(PROJECT_VERSION_PATCH 0) 21 | set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}") 22 | set(${PROJECT_NAME}_VERSION_MAJOR ${PROJECT_VERSION_MAJOR}) 23 | set(${PROJECT_NAME}_VERSION_MINOR ${PROJECT_VERSION_MINOR}) 24 | set(${PROJECT_NAME}_VERSION_PATCH ${PROJECT_VERSION_PATCH}) 25 | set(${PROJECT_NAME}_VERSION ${PROJECT_VERSION}) 26 | 27 | # CMake variables that affects building 28 | if(${CMAKE_PROJECT_NAME} STREQUAL ${PROJECT_NAME}) 29 | if(CMAKE_BUILD_TYPE STREQUAL "") 30 | set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE) 31 | endif() 32 | 33 | if(NOT DEFINED BUILD_SHARED_LIBS) 34 | set(BUILD_SHARED_LIBS ON CACHE BOOL "enable building of shared libraries instead of static ones" FORCE) 35 | endif() 36 | 37 | if(NOT DEFINED CMAKE_POSITION_INDEPENDENT_CODE) 38 | set(CMAKE_POSITION_INDEPENDENT_CODE ON CACHE BOOL "enable position independent code" FORCE) 39 | endif() 40 | 41 | include(CTest) 42 | message(STATUS "CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}") 43 | message(STATUS "CMAKE_INSTALL_PREFIX: ${CMAKE_INSTALL_PREFIX}") 44 | message(STATUS "CMAKE_POSITION_INDEPENDENT_CODE: ${CMAKE_POSITION_INDEPENDENT_CODE}") 45 | message(STATUS "BUILD_SHARED_LIBS: ${BUILD_SHARED_LIBS}") 46 | message(STATUS "BUILD_TESTING: ${BUILD_TESTING}") 47 | endif() 48 | 49 | include(GNUInstallDirs) 50 | include(tomlTargetProperties) 51 | 52 | add_library(${PROJECT_NAME} src/toml.c) 53 | add_library(toml::toml ALIAS toml) 54 | target_include_directories(${PROJECT_NAME} 55 | PUBLIC 56 | $ 57 | $ 58 | PRIVATE 59 | $ 60 | ) 61 | target_link_libraries(${PROJECT_NAME} pthread) 62 | target_compile_features(${PROJECT_NAME} PRIVATE ${wav_compile_features}) 63 | target_compile_definitions(${PROJECT_NAME} PRIVATE ${wav_compile_definitions}) 64 | target_compile_options(${PROJECT_NAME} PRIVATE 65 | ${wav_c_flags} 66 | $<$:${wav_compile_options_release}> 67 | $<$:${wav_compile_options_release}> 68 | ) 69 | 70 | export(TARGETS toml NAMESPACE toml FILE tomlTargets.cmake) 71 | 72 | install( 73 | TARGETS ${PROJECT_NAME} 74 | EXPORT tomlTargets 75 | RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} 76 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 77 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} 78 | ) 79 | 80 | if(BUILD_TESTING AND ${CMAKE_PROJECT_NAME} STREQUAL ${PROJECT_NAME}) 81 | add_subdirectory(tests) 82 | endif() 83 | 84 | export(PACKAGE toml) 85 | 86 | install(DIRECTORY include DESTINATION .) 87 | 88 | install( 89 | FILES README.md CHANGELOG.md LICENSE 90 | DESTINATION ${CMAKE_INSTALL_DATADIR}/doc/${PROJECT_NAME} 91 | ) 92 | 93 | install(EXPORT tomlTargets 94 | FILE tomlTargets.cmake 95 | NAMESPACE toml:: 96 | DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} 97 | ) 98 | 99 | include(CMakePackageConfigHelpers) 100 | write_basic_package_version_file( 101 | tomlConfigVersion.cmake 102 | VERSION ${PACKAGE_VERSION} 103 | COMPATIBILITY SameMajorVersion 104 | ) 105 | 106 | configure_file(cmake/tomlConfig.cmake.in tomlConfig.cmake @ONLY) 107 | install( 108 | FILES 109 | "${CMAKE_CURRENT_BINARY_DIR}/tomlConfig.cmake" 110 | "${CMAKE_CURRENT_BINARY_DIR}/tomlConfigVersion.cmake" 111 | DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} 112 | ) 113 | 114 | if(${CMAKE_PROJECT_NAME} STREQUAL ${PROJECT_NAME}) 115 | set(CPACK_PACKAGE_NAME "toml") 116 | set(CPACK_GENERATOR "TXZ") 117 | set(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR}) 118 | set(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR}) 119 | set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH}) 120 | set(CPACK_SOURCE_IGNORE_FILES "/\\\\.git/;\\\\.git.*;/build/;/backup/;/cmake-build-.*/;/\\\\.idea/;/\\\\.ycm_extra_conf\\\\..*;/GPATH$;/GRTAGS$;/GSYMS$;/GTAGS$;\\\\.swp$;\\\\.swo$;.DS_Store;.ccls;.ccls-cache") 121 | set(CPACK_SOURCE_GENERATOR "TXZ") 122 | include(CPack) 123 | endif() 124 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # libtoml 2 | Very tiny [TOML](https://github.com/toml-lang/toml) parser and encoder in C. 3 | 4 | # Build and Install 5 | 6 | To build this library, compiler with C99 support is required. 7 | 8 | On Linux and macOS: 9 | 10 | mkdir build 11 | cd build 12 | cmake .. 13 | make 14 | sudo make install 15 | 16 | On Windows: 17 | 18 | mkdir build 19 | cd build 20 | cmake .. 21 | cmake --build . 22 | 23 | ## CMake Support 24 | 25 | Use `FetchContent`: 26 | 27 | include(FetchContent) 28 | FetchContent_Declare(libtoml 29 | GIT_REPOSITORY "https://github.com/brglng/libtoml.git" 30 | GIT_SHALLOW ON 31 | ) 32 | FetchContent_MakeAvailable(libtoml) 33 | add_executable(yourprogram yourprogram.c) 34 | target_link_libraries(yourprogram toml::toml) 35 | 36 | Use `add_subdirectory`: 37 | 38 | add_subdirectory(libtoml) 39 | add_executable(yourprogram yourprogram.c) 40 | target_link_libraries(yourprogram toml::toml) 41 | 42 | Use `find_package`: 43 | 44 | find_package(toml) 45 | add_executable(yourprogram yourprogram.c) 46 | target_link_libraries(yourprogram toml::toml) 47 | 48 | # Usage 49 | 50 | Load from a file using a filename: 51 | ```c 52 | TomlTable *table = toml_load_filename("path/to/my/file.toml"); 53 | if (toml_err()->code == TOML_OK) { 54 | TomlTableIter it = toml_table_iter_new(table); 55 | while (toml_table_iter_has_next(&it)) { 56 | TomlKeyValue *keyval = toml_table_iter_get(&it); 57 | 58 | /* do something */ 59 | 60 | toml_table_iter_next(&it); 61 | } 62 | toml_table_free(table); 63 | } else { 64 | fprintf(stderr, "toml: %d: %s\n", toml_err()->code, toml_err()->message); 65 | 66 | /* 67 | * If error occurred, toml_clear_err() must be called before the next call 68 | * which can produce an error, or there can be an assertion failure. 69 | */ 70 | toml_err_clear(); 71 | } 72 | ``` 73 | 74 | # TODO 75 | 76 | - [ ] Update to TOML v1.0 spec 77 | - [ ] Array invariance checking 78 | - [ ] Date-time support 79 | - [ ] Support parsing while reading 80 | - [ ] Travis CI support 81 | - [ ] Encoding 82 | - [ ] Encoding to JSON 83 | - [ ] Documentation 84 | -------------------------------------------------------------------------------- /cmake/Modules/tomlTargetProperties.cmake: -------------------------------------------------------------------------------- 1 | set(toml_compile_features c_std_99) 2 | 3 | set(toml_compile_definitions 4 | __STDC_FORMAT_MACROS 5 | __STDC_LIMIT_MACROS 6 | __STDC_CONSTANT_MACROS 7 | ) 8 | 9 | if(NOT DEFINED toml_c_flags) 10 | set(toml_c_flags "") 11 | include(CheckCCompilerFlag) 12 | if(${CMAKE_C_COMPILER_ID} STREQUAL "MSVC") 13 | elseif(${CMAKE_C_COMPILER_ID} MATCHES "^(GNU|.*Clang)$") 14 | foreach(flag -fno-strict-aliasing 15 | -Wall 16 | -Wcast-align 17 | -Wduplicated-branches 18 | -Wduplicated-cond 19 | -Wextra 20 | -Wformat=2 21 | -Wmissing-include-dirs 22 | -Wnarrowing 23 | -Wpointer-arith 24 | -Wshadow 25 | -Wuninitialized 26 | -Wwrite-strings 27 | -Wno-format-truncation 28 | -Wno-format-nonliteral 29 | -Werror=discarded-qualifiers 30 | -Werror=ignored-qualifiers 31 | -Werror=implicit 32 | -Werror=implicit-function-declaration 33 | -Werror=implicit-int 34 | -Werror=init-self 35 | -Werror=incompatible-pointer-types 36 | -Werror=return-type 37 | -Werror=strict-prototypes 38 | ) 39 | check_c_compiler_flag(${flag} toml_has_c_flag_${flag}) 40 | if(toml_has_c_flag_${flag}) 41 | list(APPEND toml_c_flags ${flag}) 42 | endif() 43 | endforeach() 44 | endif() 45 | set(toml_c_flags ${toml_c_flags} CACHE INTERNAL "C Compiler Flags") 46 | endif() 47 | 48 | if(${CMAKE_C_COMPILER_ID} STREQUAL "GNU") 49 | set(audioedit_compile_options_release -fomit-frame-pointer -march=native -mtune=native) 50 | elseif(${CMAKE_C_COMPILER_ID} MATCHES "^.*Clang$") 51 | set(audioedit_compile_options_release -fomit-frame-pointer) 52 | endif() 53 | -------------------------------------------------------------------------------- /cmake/tomlConfig.cmake.in: -------------------------------------------------------------------------------- 1 | include(CMakeFindDependencyMacro) 2 | include("${CMAKE_CURRENT_LIST_DIR}/tomlTargets.cmake") 3 | -------------------------------------------------------------------------------- /include/toml.h: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | #ifndef __TOML_H__ 6 | #define __TOML_H__ 7 | 8 | #ifdef __cplusplus 9 | extern "C" { 10 | #endif 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #if defined(__cplusplus) && __cplusplus >= 201103L 18 | #define TOML_THREAD_LOCAL thread_local 19 | #elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L 20 | #define TOML_THREAD_LOCAL _Thread_local 21 | #elif defined(_MSC_VER) 22 | #define TOML_THREAD_LOCAL __declspec(thread) 23 | #else 24 | #define TOML_THREAD_LOCAL __thread 25 | #endif 26 | 27 | #if !defined(_MSC_VER) || _MSC_VER >= 1800 28 | #define TOML_INLINE static inline 29 | #define TOML_CONST const 30 | #define TOML_RESTRICT restrict 31 | #else 32 | #define TOML_INLINE static __inline 33 | #define TOML_CONST 34 | #define TOML_RESTRICT __restrict 35 | #endif 36 | 37 | #define TOML_FALSE 0 38 | #define TOML_TRUE 1 39 | 40 | typedef enum { 41 | TOML_OK, 42 | TOML_ERR, 43 | TOML_ERR_OS, 44 | TOML_ERR_NOMEM, 45 | TOML_ERR_SYNTAX, 46 | TOML_ERR_UNICODE 47 | } TomlErrCode; 48 | 49 | typedef struct { 50 | TomlErrCode code; 51 | char* message; 52 | int _is_literal; 53 | } TomlErr; 54 | 55 | typedef struct { 56 | char* str; 57 | size_t len; 58 | size_t _capacity; 59 | } TomlString; 60 | 61 | typedef struct _TomlValue TomlValue; 62 | 63 | typedef struct { 64 | TomlValue** elements; 65 | size_t len; 66 | size_t _capacity; 67 | } TomlArray; 68 | 69 | typedef struct _TomlKeyValue TomlKeyValue; 70 | 71 | typedef struct { 72 | size_t _capacity; 73 | size_t len; 74 | TomlKeyValue* _keyvals; 75 | } TomlTable; 76 | 77 | typedef struct { 78 | TomlTable* _table; 79 | TomlKeyValue* _keyval; 80 | } TomlTableIter; 81 | 82 | typedef enum { 83 | TOML_TABLE, 84 | TOML_ARRAY, 85 | TOML_STRING, 86 | TOML_INTEGER, 87 | TOML_FLOAT, 88 | TOML_DATETIME, 89 | TOML_BOOLEAN, 90 | } TomlType; 91 | 92 | struct _TomlValue { 93 | TomlType type; 94 | union { 95 | TomlTable* table; 96 | TomlArray* array; 97 | TomlString* string; 98 | #if defined(_MSC_VER) || defined(__APPLE__) 99 | long long integer; 100 | #else 101 | long integer; 102 | #endif 103 | double float_; 104 | struct tm datetime; 105 | int boolean; 106 | } value; 107 | }; 108 | 109 | struct _TomlKeyValue { 110 | TomlString* key; 111 | TomlValue* value; 112 | }; 113 | 114 | typedef struct { 115 | void* (*malloc)(void *context, size_t size); 116 | void* (*realloc)(void *context, void *p, size_t size); 117 | void (*free)(void *context, void *p); 118 | } TomlAllocFuncs; 119 | 120 | void toml_set_allocator(void *context, TOML_CONST TomlAllocFuncs *funcs); 121 | 122 | void* toml_malloc(size_t size); 123 | void* toml_realloc(void *p, size_t size); 124 | void toml_free(void *p); 125 | 126 | char* toml_strdup(TOML_CONST char *str); 127 | char* toml_strndup(TOML_CONST char *str, size_t n); 128 | int toml_vasprintf(char **str, TOML_CONST char *format, va_list args); 129 | int toml_asprintf(char **str, TOML_CONST char *format, ...); 130 | 131 | TOML_CONST TomlErr* toml_err(void); 132 | void toml_err_clear(void); 133 | 134 | TomlString* toml_string_new(void); 135 | TomlString* toml_string_from_str(TOML_CONST char *str); 136 | TomlString* toml_string_from_nstr(TOML_CONST char *str, size_t len); 137 | void toml_string_append_char(TomlString *self, char ch); 138 | void toml_string_append_str(TomlString *self, TOML_CONST char *str); 139 | void toml_string_append_nstr(TomlString *self, TOML_CONST char *str, size_t len); 140 | TomlString* toml_string_clone(TOML_CONST TomlString *self); 141 | void toml_string_free(TomlString *self); 142 | int toml_string_equals(TOML_CONST TomlString *self, TOML_CONST TomlString *other); 143 | 144 | TomlTable* toml_table_new(void); 145 | void toml_table_free(TomlTable *self); 146 | 147 | void toml_table_set_by_string(TomlTable *self, TomlString *key, TomlValue *value); 148 | TomlValue *toml_table_get_by_string(TOML_CONST TomlTable *self, TOML_CONST TomlString *key); 149 | void toml_table_set(TomlTable *self, TOML_CONST char *key, TomlValue *value); 150 | void toml_table_setn(TomlTable *self, TOML_CONST char *key, size_t key_len, TomlValue *value); 151 | TomlValue* toml_table_get(TOML_CONST TomlTable *self, TOML_CONST char *key); 152 | TomlTable* toml_table_get_as_table(TOML_CONST TomlTable *self, TOML_CONST char *key); 153 | TomlArray* toml_table_get_as_array(TOML_CONST TomlTable *self, TOML_CONST char *key); 154 | TomlString* toml_table_get_as_string(TOML_CONST TomlTable *self, TOML_CONST char *key); 155 | #if defined(_MSC_VER) || defined(__APPLE__) 156 | long long toml_table_get_as_integer(TOML_CONST TomlTable *self, TOML_CONST char *key); 157 | #else 158 | long toml_table_get_as_integer(TOML_CONST TomlTable *self, TOML_CONST char *key); 159 | #endif 160 | double toml_table_get_as_float(TOML_CONST TomlTable *self, TOML_CONST char *key); 161 | const struct tm* toml_table_get_as_datetime(TOML_CONST TomlTable *self, TOML_CONST char *key); 162 | int toml_table_get_as_boolean(TOML_CONST TomlTable *self, TOML_CONST char *key); 163 | TomlValue* toml_table_getn(TOML_CONST TomlTable *self, TOML_CONST char *key, size_t key_len); 164 | 165 | TomlTableIter toml_table_iter_new(TomlTable *table); 166 | TomlKeyValue* toml_table_iter_get(TomlTableIter *self); 167 | int toml_table_iter_has_next(TomlTableIter *self); 168 | void toml_table_iter_next(TomlTableIter *self); 169 | 170 | TomlArray* toml_array_new(void); 171 | void toml_array_free(TomlArray *self); 172 | void toml_array_append(TomlArray *self, TomlValue *value); 173 | 174 | TomlValue* toml_value_new(TomlType type); 175 | TomlValue* toml_value_new_string(TomlType type); 176 | TomlValue* toml_value_new_table(void); 177 | TomlValue* toml_value_new_array(void); 178 | #if defined(_MSC_VER) || defined(__APPLE__) 179 | TomlValue *toml_value_new_integer(long long integer); 180 | #else 181 | TomlValue *toml_value_new_integer(long integer); 182 | #endif 183 | TomlValue* toml_value_new_float(double flt); 184 | TomlValue* toml_value_new_datetime(void); 185 | TomlValue* toml_value_new_boolean(int boolean); 186 | TomlValue* toml_value_from_str(TOML_CONST char *str); 187 | TomlValue* toml_value_from_nstr(TOML_CONST char *str, size_t len); 188 | void toml_value_free(TomlValue *self); 189 | 190 | TomlTable* toml_load_str(TOML_CONST char *str); 191 | TomlTable* toml_load_nstr(TOML_CONST char *str, size_t len); 192 | TomlTable* toml_load_file(FILE *file); 193 | TomlTable* toml_load_filename(TOML_CONST char *filename); 194 | 195 | /* TODO: implement dump functions 196 | char *toml_dump_str(TOML_CONST TomlTable *self, TomlErr *err); 197 | TomlString *toml_dump_nstr(TOML_CONST TomlTable *self, TomlErr *err); 198 | void toml_dump_file(TOML_CONST TomlTable *self, FILE *file, TomlErr *err); 199 | */ 200 | 201 | #ifdef __cplusplus 202 | } 203 | #endif 204 | 205 | #endif /* end of include guard: __TOML_H__ */ 206 | -------------------------------------------------------------------------------- /src/toml.c: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "toml.h" 12 | 13 | static TOML_THREAD_LOCAL TomlErr g_err = {TOML_OK, (char*)"", TOML_TRUE}; 14 | 15 | static void* toml_default_malloc(void *context, size_t size) 16 | { 17 | (void)context; 18 | void *p = malloc(size); 19 | assert(p != NULL); 20 | return p; 21 | } 22 | 23 | static void* toml_default_realloc(void *context, void *p, size_t size) 24 | { 25 | (void)context; 26 | void *ptr = realloc(p, size); 27 | assert(ptr != NULL); 28 | return ptr; 29 | } 30 | 31 | static void toml_default_free(void *context, void *p) 32 | { 33 | (void)context; 34 | free(p); 35 | } 36 | 37 | static TomlAllocFuncs g_default_alloc_funcs = { 38 | &toml_default_malloc, 39 | &toml_default_realloc, 40 | &toml_default_free 41 | }; 42 | 43 | static void *g_alloc_context = NULL; 44 | static const TomlAllocFuncs* g_alloc_funcs = &g_default_alloc_funcs; 45 | 46 | void toml_set_allocator(void *context, TOML_CONST TomlAllocFuncs *funcs) 47 | { 48 | g_alloc_context = context; 49 | g_alloc_funcs = funcs; 50 | } 51 | 52 | void* toml_malloc(size_t size) 53 | { 54 | return g_alloc_funcs->malloc(g_alloc_context, size); 55 | } 56 | 57 | void* toml_realloc(void *p, size_t size) 58 | { 59 | return g_alloc_funcs->realloc(g_alloc_context, p, size); 60 | } 61 | 62 | void toml_free(void *p) 63 | { 64 | if (p != NULL) { 65 | g_alloc_funcs->free(g_alloc_context, p); 66 | } 67 | } 68 | 69 | char* toml_strdup(TOML_CONST char *str) 70 | { 71 | size_t len = strlen(str) + 1; 72 | void *new = toml_malloc(len); 73 | if (new == NULL) 74 | return NULL; 75 | 76 | return memcpy(new, str, len); 77 | } 78 | 79 | char *toml_strndup(TOML_CONST char *str, size_t n) 80 | { 81 | char *result = toml_malloc(n + 1); 82 | if (result == NULL) 83 | return NULL; 84 | 85 | result[n] = 0; 86 | return memcpy(result, str, n); 87 | } 88 | 89 | int toml_vasprintf(char **str, TOML_CONST char *format, va_list args) 90 | { 91 | int size = 0; 92 | 93 | va_list args_copy; 94 | va_copy(args_copy, args); 95 | size = vsnprintf(NULL, (size_t)size, format, args_copy); 96 | va_end(args_copy); 97 | 98 | if (size < 0) { 99 | return size; 100 | } 101 | 102 | *str = toml_malloc((size_t)size + 1); 103 | if (*str == NULL) 104 | return -1; 105 | 106 | return vsprintf(*str, format, args); 107 | } 108 | 109 | int toml_asprintf(char **str, TOML_CONST char *format, ...) 110 | { 111 | va_list args; 112 | va_start(args, format); 113 | int size = toml_vasprintf(str, format, args); 114 | va_end(args); 115 | return size; 116 | } 117 | 118 | TOML_CONST TomlErr* toml_err(void) 119 | { 120 | return &g_err; 121 | } 122 | 123 | void toml_err_clear(void) 124 | { 125 | if (g_err.code != TOML_OK) { 126 | if (!g_err._is_literal) { 127 | toml_free(g_err.message); 128 | } 129 | g_err.code = TOML_OK; 130 | g_err.message = (char*)""; 131 | g_err._is_literal = TOML_TRUE; 132 | } 133 | } 134 | 135 | TOML_INLINE void toml_err_set(TomlErrCode code, TOML_CONST char *format, ...) 136 | { 137 | assert(g_err.code == TOML_OK); 138 | va_list args; 139 | va_start(args, format); 140 | g_err.code = code; 141 | toml_vasprintf(&g_err.message, format, args); 142 | g_err._is_literal = TOML_FALSE; 143 | va_end(args); 144 | } 145 | 146 | TOML_INLINE void toml_err_set_literal(TomlErrCode code, TOML_CONST char *message) 147 | { 148 | assert(g_err.code == TOML_OK); 149 | g_err.code = code; 150 | g_err.message = (char *)message; 151 | g_err._is_literal = TOML_TRUE; 152 | } 153 | 154 | TOML_INLINE size_t toml_roundup_pow_of_two_size_t(size_t x) 155 | { 156 | size_t v = x; 157 | v--; 158 | v |= v >> 1; 159 | v |= v >> 2; 160 | v |= v >> 4; 161 | #if SIZE_MAX == 0xffff 162 | v |= v >> 8; 163 | #elif SIZE_MAX == 0xffffffff 164 | v |= v >> 8; 165 | v |= v >> 16; 166 | #elif SIZE_MAX == 0xffffffffffffffffll 167 | v |= v >> 8; 168 | v |= v >> 16; 169 | v |= v >> 32; 170 | #endif 171 | v++; 172 | return v; 173 | } 174 | 175 | TomlString *toml_string_new(void) 176 | { 177 | TomlString *self = toml_malloc(sizeof(TomlString)); 178 | self->str = NULL; 179 | self->len = 0; 180 | self->_capacity = 0; 181 | return self; 182 | } 183 | 184 | TomlString *toml_string_from_str(TOML_CONST char *str) 185 | { 186 | TomlString *self = toml_string_new(); 187 | toml_string_append_str(self, str); 188 | return self; 189 | } 190 | 191 | TomlString *toml_string_from_nstr(TOML_CONST char *str, size_t len) 192 | { 193 | TomlString *self = toml_string_new(); 194 | toml_string_append_nstr(self, str, len); 195 | return self; 196 | } 197 | 198 | TOML_INLINE void toml_string_expand_if_necessary(TomlString *self, size_t len_to_add) 199 | { 200 | if (self->len + len_to_add + 1 > self->_capacity) { 201 | size_t new_capacity = toml_roundup_pow_of_two_size_t(self->len + len_to_add + 1); 202 | new_capacity = new_capacity >= 8 ? new_capacity : 8; 203 | self->str = toml_realloc(self->str, new_capacity); 204 | self->_capacity = new_capacity; 205 | } 206 | } 207 | 208 | void toml_string_append_char(TomlString *self, char ch) 209 | { 210 | toml_string_expand_if_necessary(self, 1); 211 | self->str[self->len] = ch; 212 | self->str[self->len + 1] = 0; 213 | self->len++; 214 | } 215 | 216 | void toml_string_append_str(TomlString *self, TOML_CONST char *str) 217 | { 218 | size_t len = strlen(str); 219 | toml_string_expand_if_necessary(self, len); 220 | memcpy(&self->str[self->len], str, len + 1); 221 | self->len += len; 222 | } 223 | 224 | void toml_string_append_nstr(TomlString *self, TOML_CONST char *str, size_t len) 225 | { 226 | toml_string_expand_if_necessary(self, len); 227 | memcpy(&self->str[self->len], str, len); 228 | self->len += len; 229 | self->str[self->len] = 0; 230 | } 231 | 232 | void toml_string_free(TomlString *self) 233 | { 234 | if (self != NULL) { 235 | free(self->str); 236 | free(self); 237 | } 238 | } 239 | 240 | TomlString *toml_string_clone(TOML_CONST TomlString *self) 241 | { 242 | return toml_string_from_nstr(self->str, self->len); 243 | } 244 | 245 | int toml_string_equals(TOML_CONST TomlString *self, TOML_CONST TomlString *other) 246 | { 247 | if (self == other) { 248 | return TOML_TRUE; 249 | } 250 | 251 | if (self->len != other->len) { 252 | return TOML_FALSE; 253 | } 254 | 255 | if (self->str == other->str) { 256 | return TOML_TRUE; 257 | } 258 | 259 | for (size_t i = 0; i < self->len; i++) { 260 | if (self->str[i] != other->str[i]) { 261 | return TOML_FALSE; 262 | } 263 | } 264 | 265 | return TOML_TRUE; 266 | } 267 | 268 | TomlTable *toml_table_new(void) 269 | { 270 | TomlTable *self = toml_malloc(sizeof(TomlTable)); 271 | self->_capacity = 0; 272 | self->_keyvals = NULL; 273 | self->len = 0; 274 | return self; 275 | } 276 | 277 | void toml_table_free(TomlTable *self) 278 | { 279 | if (self != NULL) { 280 | for (size_t i = 0; i < self->len; i++) { 281 | toml_string_free(self->_keyvals[i].key); 282 | toml_value_free(self->_keyvals[i].value); 283 | } 284 | free(self->_keyvals); 285 | free(self); 286 | } 287 | } 288 | 289 | void toml_table_expand_if_necessary(TomlTable *self) 290 | { 291 | if (self->len + 1 > self->_capacity) { 292 | size_t new_capacity = self->_capacity > 0 ? self->_capacity * 2 : 8; 293 | void *p = toml_realloc(self->_keyvals, sizeof(TomlKeyValue) * new_capacity); 294 | self->_keyvals = p; 295 | self->_capacity = new_capacity; 296 | } 297 | } 298 | 299 | void toml_table_set_by_string(TomlTable *self, TomlString *key, TomlValue *value) 300 | { 301 | TomlValue **slot = NULL; 302 | for (size_t i = 0; i < self->len; i++) { 303 | if (toml_string_equals(self->_keyvals[i].key, key)) { 304 | slot = &self->_keyvals[i].value; 305 | } 306 | } 307 | 308 | if (slot == NULL) { 309 | toml_table_expand_if_necessary(self); 310 | self->_keyvals[self->len].key = key; 311 | self->_keyvals[self->len].value = value; 312 | self->len++; 313 | } else { 314 | *slot = value; 315 | } 316 | } 317 | 318 | TomlValue *toml_table_get_by_string(TOML_CONST TomlTable *self, TOML_CONST TomlString *key) 319 | { 320 | TomlValue *value = NULL; 321 | for (size_t i = 0; i < self->len; i++) { 322 | if (toml_string_equals(self->_keyvals[i].key, key)) { 323 | value = self->_keyvals[i].value; 324 | } 325 | } 326 | return value; 327 | } 328 | 329 | TomlValue *toml_table_getn(TOML_CONST TomlTable *self, TOML_CONST char *key, size_t key_len) 330 | { 331 | TomlString str = {(char *)key, key_len, 0}; 332 | return toml_table_get_by_string(self, &str); 333 | } 334 | 335 | TomlValue *toml_table_get(TOML_CONST TomlTable *self, TOML_CONST char *key) 336 | { 337 | return toml_table_getn(self, key, strlen(key)); 338 | } 339 | 340 | TomlTable* toml_table_get_as_table(TOML_CONST TomlTable *self, TOML_CONST char *key) 341 | { 342 | TomlValue *v = toml_table_get(self, key); 343 | assert(v != NULL); 344 | assert(v->type == TOML_TABLE); 345 | return v->value.table; 346 | } 347 | 348 | TomlArray* toml_table_get_as_array(TOML_CONST TomlTable *self, TOML_CONST char *key) 349 | { 350 | TomlValue *v = toml_table_get(self, key); 351 | assert(v != NULL); 352 | assert(v->type == TOML_ARRAY); 353 | return v->value.array; 354 | } 355 | 356 | TomlString* toml_table_get_as_string(TOML_CONST TomlTable *self, TOML_CONST char *key) 357 | { 358 | TomlValue *v = toml_table_get(self, key); 359 | assert(v != NULL); 360 | assert(v->type == TOML_STRING); 361 | return v->value.string; 362 | } 363 | 364 | #if defined(_MSC_VER) || defined(__APPLE__) 365 | long long toml_table_get_as_integer(TOML_CONST TomlTable *self, TOML_CONST char *key) 366 | #else 367 | long toml_table_get_as_integer(TOML_CONST TomlTable *self, TOML_CONST char *key) 368 | #endif 369 | { 370 | TomlValue *v = toml_table_get(self, key); 371 | assert(v != NULL); 372 | assert(v->type == TOML_INTEGER); 373 | return v->value.integer; 374 | } 375 | 376 | double toml_table_get_as_float(TOML_CONST TomlTable *self, TOML_CONST char *key) 377 | { 378 | TomlValue *v = toml_table_get(self, key); 379 | assert(v != NULL); 380 | assert(v->type == TOML_FLOAT); 381 | return v->value.float_; 382 | } 383 | 384 | const struct tm* toml_table_get_as_datetime(TOML_CONST TomlTable *self, TOML_CONST char *key) 385 | { 386 | TomlValue *v = toml_table_get(self, key); 387 | assert(v != NULL); 388 | assert(v->type == TOML_DATETIME); 389 | return &v->value.datetime; 390 | } 391 | 392 | int toml_table_get_as_boolean(TOML_CONST TomlTable *self, TOML_CONST char *key) 393 | { 394 | TomlValue *v = toml_table_get(self, key); 395 | assert(v != NULL); 396 | assert(v->type == TOML_BOOLEAN); 397 | return v->value.boolean; 398 | } 399 | 400 | void toml_table_setn(TomlTable *self, TOML_CONST char *key, size_t key_len, TomlValue *value) 401 | { 402 | TomlString *str = toml_string_from_nstr(key, key_len); 403 | toml_table_set_by_string(self, str, value); 404 | } 405 | 406 | void toml_table_set(TomlTable *self, TOML_CONST char *key, TomlValue *value) 407 | { 408 | toml_table_setn(self, key, strlen(key), value); 409 | } 410 | 411 | TomlTableIter toml_table_iter_new(TomlTable *table) 412 | { 413 | TomlTableIter self = { table, table->_keyvals }; 414 | return self; 415 | } 416 | 417 | TomlKeyValue* toml_table_iter_get(TomlTableIter *self) 418 | { 419 | return self->_keyval; 420 | } 421 | 422 | int toml_table_iter_has_next(TomlTableIter *self) 423 | { 424 | return self->_keyval != NULL; 425 | } 426 | 427 | void toml_table_iter_next(TomlTableIter *self) 428 | { 429 | if (self->_keyval < self->_table->_keyvals + self->_table->len) { 430 | self->_keyval++; 431 | } 432 | 433 | if (self->_keyval >= self->_table->_keyvals + self->_table->len) { 434 | self->_keyval = NULL; 435 | } 436 | } 437 | 438 | TomlArray *toml_array_new(void) 439 | { 440 | TomlArray *self = toml_malloc(sizeof(TomlArray)); 441 | self->elements = NULL; 442 | self->len = 0; 443 | self->_capacity = 0; 444 | return self; 445 | } 446 | 447 | void toml_array_free(TomlArray *self) 448 | { 449 | if (self != NULL) { 450 | for (size_t i = 0; i < self->len; i++) { 451 | toml_value_free(self->elements[i]); 452 | } 453 | free(self->elements); 454 | free(self); 455 | } 456 | } 457 | 458 | void toml_array_expand_if_necessary(TomlArray *self) 459 | { 460 | if (self->len + 1 > self->_capacity) { 461 | size_t new_capacity = self->_capacity > 0 ? self->_capacity * 2 : 8; 462 | void *p = toml_realloc(self->elements, sizeof(TomlValue *) * new_capacity); 463 | self->elements = p; 464 | self->_capacity = new_capacity; 465 | } 466 | } 467 | 468 | void toml_array_append(TomlArray *self, TomlValue *value) 469 | { 470 | toml_array_expand_if_necessary(self); 471 | self->elements[self->len++] = value; 472 | } 473 | 474 | TomlValue *toml_value_new(TomlType type) 475 | { 476 | TomlValue *self = toml_malloc(sizeof(TomlValue)); 477 | self->type = type; 478 | switch (type) { 479 | case TOML_TABLE: 480 | self->value.table = NULL; 481 | break; 482 | case TOML_ARRAY: 483 | self->value.array = NULL; 484 | break; 485 | case TOML_STRING: 486 | self->value.string = NULL; 487 | break; 488 | case TOML_INTEGER: 489 | self->value.integer = 0; 490 | break; 491 | case TOML_FLOAT: 492 | self->value.float_ = 0.0; 493 | break; 494 | case TOML_BOOLEAN: 495 | self->value.boolean = TOML_FALSE; 496 | break; 497 | case TOML_DATETIME: 498 | memset(&self->value.datetime, 0, sizeof(struct tm)); 499 | break; 500 | } 501 | return self; 502 | } 503 | 504 | TomlValue *toml_value_from_str(TOML_CONST char *str) 505 | { 506 | TomlValue *self = toml_malloc(sizeof(TomlValue)); 507 | self->value.string = toml_string_from_str(str); 508 | self->type = TOML_STRING; 509 | return self; 510 | } 511 | 512 | TomlValue *toml_value_from_nstr(TOML_CONST char *str, size_t len) 513 | { 514 | TomlValue *self = toml_malloc(sizeof(TomlValue)); 515 | self->value.string = toml_string_from_nstr(str, len); 516 | self->type = TOML_STRING; 517 | return self; 518 | } 519 | 520 | TomlValue *toml_value_new_table(void) 521 | { 522 | TomlValue *self = toml_malloc(sizeof(TomlValue)); 523 | self->value.table = toml_table_new(); 524 | self->type = TOML_TABLE; 525 | return self; 526 | } 527 | 528 | TomlValue *toml_value_new_array(void) 529 | { 530 | TomlValue *self = toml_malloc(sizeof(TomlValue)); 531 | self->value.array = toml_array_new(); 532 | self->type = TOML_ARRAY; 533 | return self; 534 | } 535 | 536 | #if defined(_MSC_VER) || defined(__APPLE__) 537 | TomlValue *toml_value_new_integer(long long integer) 538 | #else 539 | TomlValue *toml_value_new_integer(long integer) 540 | #endif 541 | { 542 | TomlValue *self = toml_malloc(sizeof(TomlValue)); 543 | self->value.integer = integer; 544 | self->type = TOML_INTEGER; 545 | return self; 546 | } 547 | 548 | TomlValue *toml_value_new_float(double float_) 549 | { 550 | TomlValue *self = toml_malloc(sizeof(TomlValue)); 551 | self->value.float_ = float_; 552 | self->type = TOML_FLOAT; 553 | return self; 554 | } 555 | 556 | TomlValue *toml_value_new_datetime(void) 557 | { 558 | return toml_value_new(TOML_DATETIME); 559 | } 560 | 561 | TomlValue *toml_value_new_boolean(int boolean) 562 | { 563 | TomlValue *self = toml_malloc(sizeof(TomlValue)); 564 | self->value.boolean = boolean; 565 | self->type = TOML_BOOLEAN; 566 | return self; 567 | } 568 | 569 | void toml_value_free(TomlValue *self) 570 | { 571 | if (self != NULL) { 572 | switch (self->type) { 573 | case TOML_STRING: 574 | toml_string_free(self->value.string); 575 | break; 576 | case TOML_TABLE: 577 | toml_table_free(self->value.table); 578 | break; 579 | case TOML_ARRAY: 580 | toml_array_free(self->value.array); 581 | break; 582 | case TOML_DATETIME: 583 | memset(&self->value.datetime, 0, sizeof(struct tm)); 584 | break; 585 | default: 586 | break; 587 | } 588 | free(self); 589 | } 590 | } 591 | 592 | typedef struct _TomlParser { 593 | TOML_CONST char* begin; 594 | TOML_CONST char* end; 595 | TOML_CONST char* ptr; 596 | int lineno; 597 | int colno; 598 | char* filename; 599 | } TomlParser; 600 | 601 | TomlParser *toml_parser_new(TOML_CONST char *str, size_t len) 602 | { 603 | TomlParser *self = toml_malloc(sizeof(TomlParser)); 604 | self->begin = str; 605 | self->end = str + len; 606 | self->ptr = str; 607 | self->lineno = 1; 608 | self->colno = 1; 609 | self->filename = NULL; 610 | return self; 611 | } 612 | 613 | void toml_parser_free(TomlParser *self) 614 | { 615 | if (self != NULL) { 616 | free(self->filename); 617 | free(self); 618 | } 619 | } 620 | 621 | void toml_move_next(TomlParser *self) 622 | { 623 | if (self->ptr < self->end) { 624 | if (*self->ptr == '\n') { 625 | self->lineno++; 626 | self->colno = 1; 627 | } else { 628 | self->colno++; 629 | } 630 | self->ptr++; 631 | } 632 | } 633 | 634 | void toml_next_n(TomlParser *self, int n) 635 | { 636 | for (int i = 0; i < n; i++) { 637 | toml_move_next(self); 638 | } 639 | } 640 | 641 | TomlString* toml_parse_bare_key(TomlParser *self) 642 | { 643 | TOML_CONST char *str = self->ptr; 644 | size_t len = 0; 645 | 646 | while (self->ptr < self->end) { 647 | char ch = *self->ptr; 648 | 649 | if (!(isalnum(ch) || ch == '_' || ch == '-')) 650 | break; 651 | 652 | len++; 653 | toml_move_next(self); 654 | } 655 | 656 | return toml_string_from_nstr(str, len); 657 | } 658 | 659 | char toml_hex_char_to_int(char ch) 660 | { 661 | assert(isxdigit(ch)); 662 | if (isdigit(ch)) { 663 | return ch - '0'; 664 | } else if (islower(ch)) { 665 | return ch - 'a' + 10; 666 | } else if (isupper(ch)) { 667 | return ch - 'A' + 10; 668 | } 669 | return 0; 670 | } 671 | 672 | int toml_encode_unicode_scalar(TomlString *result, TomlParser *parser, int n) 673 | { 674 | unsigned int scalar = 0; 675 | 676 | if (parser->ptr + n > parser->end) { 677 | toml_err_set(TOML_ERR_UNICODE, "%s:%d:%d: invalid unicode scalar", 678 | parser->filename, parser->lineno, parser->colno); 679 | return TOML_ERR_UNICODE; 680 | } 681 | 682 | for (int i = 0; i < n; i++) { 683 | char ch = *parser->ptr; 684 | if (isxdigit(ch)) { 685 | scalar = scalar * 16 + (unsigned int)toml_hex_char_to_int(ch); 686 | toml_move_next(parser); 687 | } else { 688 | toml_err_set(TOML_ERR_UNICODE, "%s:%d:%d: invalid unicode scalar", 689 | parser->filename, parser->lineno, parser->colno); 690 | return TOML_ERR_UNICODE; 691 | } 692 | } 693 | 694 | if ((scalar >= 0xd800 && scalar <= 0xdfff) || 695 | (scalar >= 0xfffe && scalar <= 0xffff)) { 696 | toml_err_set(TOML_ERR_UNICODE, "%s:%d:%d: invalid unicode scalar", 697 | parser->filename, parser->lineno, parser->colno); 698 | return TOML_ERR_UNICODE; 699 | } 700 | 701 | if (scalar <= 0x7f) { 702 | toml_string_append_char(result, (char)scalar); 703 | return 0; 704 | } 705 | 706 | if (scalar <= 0x7ff) { 707 | toml_string_append_char(result, (char)(0xc0 | (scalar >> 6))); 708 | toml_string_append_char(result, (char)(0x80 | (scalar & 0x3f))); 709 | return 0; 710 | } 711 | 712 | if (scalar <= 0xffff) { 713 | toml_string_append_char(result, (char)(0xe0 | (scalar >> 12))); 714 | toml_string_append_char(result, (char)(0x80 | ((scalar >> 6) & 0x3f))); 715 | toml_string_append_char(result, (char)(0x80 | (scalar & 0x3f))); 716 | return 0; 717 | } 718 | 719 | if (scalar <= 0x1fffff) { 720 | toml_string_append_char(result, (char)(0xf0 | (scalar >> 18))); 721 | toml_string_append_char(result, (char)(0x80 | ((scalar >> 12) & 0x3f))); 722 | toml_string_append_char(result, (char)(0x80 | ((scalar >> 6) & 0x3f))); 723 | toml_string_append_char(result, (char)(0x80 | (scalar & 0x3f))); 724 | return 0; 725 | } 726 | 727 | if (scalar <= 0x3ffffff) { 728 | toml_string_append_char(result, (char)(0xf8 | (scalar >> 24))); 729 | toml_string_append_char(result, (char)(0x80 | ((scalar >> 18) & 0x3f))); 730 | toml_string_append_char(result, (char)(0x80 | ((scalar >> 12) & 0x3f))); 731 | toml_string_append_char(result, (char)(0x80 | ((scalar >> 6) & 0x3f))); 732 | toml_string_append_char(result, (char)(0x80 | (scalar & 0x3f))); 733 | return 0; 734 | } 735 | 736 | if (scalar <= 0x7fffffff) { 737 | toml_string_append_char(result, (char)(0xfc | (scalar >> 30))); 738 | toml_string_append_char(result, (char)(0x80 | ((scalar >> 24) & 0x3f))); 739 | toml_string_append_char(result, (char)(0x80 | ((scalar >> 18) & 0x3f))); 740 | toml_string_append_char(result, (char)(0x80 | ((scalar >> 12) & 0x3f))); 741 | toml_string_append_char(result, (char)(0x80 | ((scalar >> 6) & 0x3f))); 742 | toml_string_append_char(result, (char)(0x80 | (scalar & 0x3f))); 743 | return 0; 744 | } 745 | 746 | toml_err_set(TOML_ERR_UNICODE, "%s:%d:%d: invalid unicode scalar", 747 | parser->filename, parser->lineno, parser->colno); 748 | 749 | return TOML_ERR_UNICODE; 750 | } 751 | 752 | TomlString* toml_parse_basic_string(TomlParser *self) 753 | { 754 | TomlString *result = toml_string_new(); 755 | 756 | while (self->ptr < self->end && *self->ptr != '\"' && *self->ptr != '\n') { 757 | char ch1 = *self->ptr; 758 | if (ch1 == '\\') { 759 | if (self->ptr >= self->end) break; 760 | 761 | toml_move_next(self); 762 | char ch2 = *self->ptr; 763 | 764 | if (ch2 == '\"') { 765 | toml_string_append_char(result, '\"'); 766 | toml_move_next(self); 767 | } else if (ch2 == 'b') { 768 | toml_string_append_char(result, '\b'); 769 | toml_move_next(self); 770 | } else if (ch2 == 't') { 771 | toml_string_append_char(result, '\t'); 772 | toml_move_next(self); 773 | } else if (ch2 == 'n') { 774 | toml_string_append_char(result, '\n'); 775 | toml_move_next(self); 776 | } else if (ch2 == 'f') { 777 | toml_string_append_char(result, '\f'); 778 | toml_move_next(self); 779 | } else if (ch2 == 'r') { 780 | toml_string_append_char(result, '\r'); 781 | toml_move_next(self); 782 | } else if (ch2 == '"') { 783 | toml_string_append_char(result, '\"'); 784 | toml_move_next(self); 785 | } else if (ch2 == '\\') { 786 | toml_string_append_char(result, '\\'); 787 | toml_move_next(self); 788 | } else if (ch2 == 'u') { 789 | toml_move_next(self); 790 | if (toml_encode_unicode_scalar(result, self, 4) != 0) { 791 | toml_string_free(result); 792 | return NULL; 793 | } 794 | } else if (ch2 == 'U') { 795 | toml_move_next(self); 796 | if (toml_encode_unicode_scalar(result, self, 8) != 0) { 797 | toml_string_free(result); 798 | return NULL; 799 | } 800 | } else { 801 | toml_err_set(TOML_ERR_SYNTAX, "%s:%d:%d: invalid escape charactor"); 802 | toml_string_free(result); 803 | } 804 | } else { 805 | toml_string_append_char(result, ch1); 806 | toml_move_next(self); 807 | } 808 | } 809 | 810 | if (self->ptr >= self->end || *self->ptr != '\"' || *self->ptr == '\n') { 811 | toml_err_set(TOML_ERR_SYNTAX, "%s:%d:%d: unterminated basic string", 812 | self->filename, self->lineno, self->colno); 813 | toml_string_free(result); 814 | } 815 | 816 | toml_move_next(self); 817 | 818 | return result; 819 | } 820 | 821 | TomlString* toml_parse_literal_string(TomlParser *self) 822 | { 823 | TomlString *result = toml_string_new(); 824 | 825 | while (self->ptr < self->end && *self->ptr != '\'' && *self->ptr != '\n') { 826 | toml_string_append_char(result, *self->ptr); 827 | toml_move_next(self); 828 | } 829 | 830 | if (self->ptr >= self->end || *self->ptr != '\'' || *self->ptr == '\n') { 831 | toml_err_set(TOML_ERR_SYNTAX, "%s:%d:%d: unterminated literal string", 832 | self->filename, self->lineno, self->colno); 833 | toml_string_free(result); 834 | return NULL; 835 | } 836 | 837 | toml_move_next(self); 838 | 839 | return result; 840 | } 841 | 842 | TomlValue* toml_parse_basic_string_value(TomlParser *self) 843 | { 844 | TomlValue *value = NULL; 845 | 846 | TomlString *string = toml_parse_basic_string(self); 847 | if (string == NULL) 848 | return NULL; 849 | 850 | value = toml_value_new(TOML_STRING); 851 | value->value.string = string; 852 | 853 | return value; 854 | } 855 | 856 | TomlValue *toml_parse_literal_string_value(TomlParser *self) 857 | { 858 | TomlValue *value = NULL; 859 | 860 | TomlString *string = toml_parse_literal_string(self); 861 | if (string == NULL) 862 | return NULL; 863 | 864 | value = toml_value_new(TOML_STRING); 865 | value->value.string = string; 866 | 867 | return value; 868 | } 869 | 870 | TomlValue* toml_parse_multi_line_basic_string(TomlParser *self) 871 | { 872 | TomlValue *value = NULL; 873 | 874 | TomlString *result = toml_string_new(); 875 | 876 | if (*self->ptr == '\n') { 877 | toml_move_next(self); 878 | } 879 | 880 | while (self->ptr + 3 <= self->end && strncmp(self->ptr, "\"\"\"", 3) != 0) { 881 | char ch1 = *self->ptr; 882 | 883 | if (ch1 == '\\') { 884 | if (self->ptr + 3 > self->end || strncmp(self->ptr, "\"\"\"", 3) == 0) 885 | break; 886 | 887 | toml_move_next(self); 888 | char ch2 = *self->ptr; 889 | 890 | if (ch2 == '\"') { 891 | toml_string_append_char(result, '\"'); 892 | toml_move_next(self); 893 | } else if (ch2 == 'b') { 894 | toml_string_append_char(result, '\b'); 895 | toml_move_next(self); 896 | } else if (ch2 == 't') { 897 | toml_string_append_char(result, '\t'); 898 | toml_move_next(self); 899 | } else if (ch2 == 'n') { 900 | toml_string_append_char(result, '\n'); 901 | toml_move_next(self); 902 | } else if (ch2 == 'f') { 903 | toml_string_append_char(result, '\f'); 904 | toml_move_next(self); 905 | } else if (ch2 == 'r') { 906 | toml_string_append_char(result, '\r'); 907 | toml_move_next(self); 908 | } else if (ch2 == '"') { 909 | toml_string_append_char(result, '\"'); 910 | toml_move_next(self); 911 | } else if (ch2 == '\\') { 912 | toml_string_append_char(result, '\\'); 913 | toml_move_next(self); 914 | } else if (ch2 == 'u') { 915 | toml_move_next(self); 916 | if (toml_encode_unicode_scalar(result, self, 4) != 0) { 917 | toml_string_free(result); 918 | return NULL; 919 | } 920 | } else if (ch2 == 'U') { 921 | toml_move_next(self); 922 | if (toml_encode_unicode_scalar(result, self, 8) != 0) { 923 | toml_string_free(result); 924 | return NULL; 925 | } 926 | } else if (ch2 == '\n') { 927 | do { 928 | toml_move_next(self); 929 | } while (self->ptr + 3 <= self->end && isspace(*self->ptr)); 930 | } else { 931 | toml_err_set(TOML_ERR_SYNTAX, "%s:%d:%d: invalid escape charactor", 932 | self->filename, self->lineno, self->colno); 933 | toml_string_free(result); 934 | return NULL; 935 | } 936 | } else { 937 | toml_string_append_char(result, ch1); 938 | toml_move_next(self); 939 | } 940 | } 941 | 942 | if (self->ptr + 3 > self->end || strncmp(self->ptr, "\"\"\"", 3) != 0) { 943 | toml_err_set(TOML_ERR_SYNTAX, "%s:%d:%d: unterminated multi-line basic string", 944 | self->filename, self->lineno, self->colno); 945 | toml_string_free(result); 946 | return NULL; 947 | } 948 | 949 | toml_next_n(self, 3); 950 | 951 | value = toml_value_new(TOML_STRING); 952 | value->value.string = result; 953 | 954 | return value; 955 | } 956 | 957 | TomlValue* toml_parse_multi_line_literal_string(TomlParser *self) 958 | { 959 | TomlValue *value = NULL; 960 | 961 | TomlString *result = toml_string_new(); 962 | 963 | if (*self->ptr == '\n') { 964 | toml_move_next(self); 965 | } 966 | 967 | while (self->ptr + 3 <= self->end && strncmp(self->ptr, "\'\'\'", 3) != 0) { 968 | toml_string_append_char(result, *self->ptr); 969 | toml_move_next(self); 970 | } 971 | 972 | if (self->ptr + 3 > self->end || strncmp(self->ptr, "\'\'\'", 3) != 0) { 973 | toml_err_set(TOML_ERR_SYNTAX, "%s:%d:%d: unterminated multi-line literal string", 974 | self->filename, self->lineno, self->colno); 975 | toml_string_free(result); 976 | return NULL; 977 | } 978 | 979 | toml_next_n(self, 3); 980 | 981 | value = toml_value_new(TOML_STRING); 982 | value->value.string = result; 983 | 984 | return value; 985 | } 986 | 987 | TomlValue* toml_parse_datetime(TOML_CONST char *str, size_t len) 988 | { 989 | (void)str; 990 | (void)len; 991 | return toml_value_new(TOML_DATETIME); 992 | } 993 | 994 | TomlValue* toml_parse_int_or_float_or_time(TomlParser *self) 995 | { 996 | TomlString *str = NULL; 997 | TomlValue *result = NULL; 998 | 999 | char type = 'i'; 1000 | int base = 10; 1001 | 1002 | str = toml_string_new(); 1003 | 1004 | // Determine nan and inf type as float, as we cannot determine by dot. 1005 | // But do not strip it because we will append it to the string later 1006 | if (self->ptr + 3 <= self->end && 1007 | (strncmp(self->ptr, "nan", 3) == 0 || strncmp(self->ptr, "inf", 3) == 0)) { 1008 | type = 'f'; 1009 | } 1010 | 1011 | if (self->ptr + 4 <= self->end && 1012 | (strncmp(self->ptr, "+nan", 4) == 0 || 1013 | strncmp(self->ptr, "-nan", 4) == 0 || 1014 | strncmp(self->ptr, "+inf", 4) == 0 || 1015 | strncmp(self->ptr, "-inf", 4) == 0)) { 1016 | type = 'f'; 1017 | } 1018 | 1019 | // If there is a base prefix, set the base and strip the prefix, 1020 | // because strtoll() do not recognize 0o and 0b 1021 | if (self->ptr + 2 <= self->end) { 1022 | if (strncmp(self->ptr, "0x", 2) == 0) { 1023 | base = 16; 1024 | toml_next_n(self, 2); 1025 | } else if (strncmp(self->ptr, "0o", 2) == 0) { 1026 | base = 8; 1027 | toml_next_n(self, 2); 1028 | } else if (strncmp(self->ptr, "0b", 2) == 0) { 1029 | base = 2; 1030 | toml_next_n(self, 2); 1031 | } 1032 | } 1033 | 1034 | char last_char = 0; 1035 | int has_exp = TOML_FALSE; 1036 | while (self->ptr < self->end) { 1037 | if (*self->ptr == '+' || *self->ptr == '-') { 1038 | if (last_char == 0 || ((last_char == 'e' || last_char == 'E') && !has_exp)) { 1039 | if (last_char != 0) { 1040 | type = 'f'; 1041 | has_exp = TOML_TRUE; 1042 | } 1043 | toml_string_append_char(str, *self->ptr); 1044 | } else { 1045 | break; 1046 | } 1047 | } else if (isalnum(*self->ptr)) { 1048 | if ((*self->ptr == 'e' || *self->ptr == 'E') && base == 10) { 1049 | type = 'f'; 1050 | } 1051 | 1052 | toml_string_append_char(str, *self->ptr); 1053 | } else if (*self->ptr == '.') { 1054 | if (type == 'i') { 1055 | type = 'f'; 1056 | toml_string_append_char(str, *self->ptr); 1057 | } else { 1058 | toml_err_set(TOML_ERR_SYNTAX, "%s:%d:%d: invalid float", 1059 | self->filename, self->lineno, self->colno); 1060 | toml_string_free(str); 1061 | return NULL; 1062 | } 1063 | } else if (*self->ptr == '_') { 1064 | if (type == 't') { 1065 | toml_err_set(TOML_ERR_SYNTAX, "%s:%d:%d: invalid datetime", 1066 | self->filename, self->lineno, self->colno); 1067 | toml_string_free(str); 1068 | return NULL; 1069 | } 1070 | 1071 | if (!isalnum(last_char)) { 1072 | toml_err_set(TOML_ERR_SYNTAX, "%s:%d:%d: invalid integer or float", 1073 | self->filename, self->lineno, self->colno); 1074 | toml_string_free(str); 1075 | return NULL; 1076 | } 1077 | } else if (*self->ptr == '-') { 1078 | type = 't'; 1079 | toml_string_append_char(str, *self->ptr); 1080 | } else { 1081 | break; 1082 | } 1083 | 1084 | last_char = *self->ptr; 1085 | toml_move_next(self); 1086 | } 1087 | 1088 | if (last_char == '_') { 1089 | toml_err_set(TOML_ERR_SYNTAX, "%s:%d:%d: invalid integer or float or datetime", 1090 | self->filename, self->lineno, self->colno); 1091 | toml_string_free(str); 1092 | return NULL; 1093 | } 1094 | 1095 | if (type == 'i') { 1096 | char *end = NULL; 1097 | long long n = strtoll(str->str, &end, base); 1098 | if (end < str->str + str->len) { 1099 | toml_err_set(TOML_ERR_SYNTAX, "%s:%d:%d: invalid integer", 1100 | self->filename, self->lineno, self->colno); 1101 | toml_string_free(str); 1102 | return NULL; 1103 | } 1104 | result = toml_value_new_integer(n); 1105 | } else if (type == 'f') { 1106 | char *end = NULL; 1107 | double n = strtod(str->str, &end); 1108 | if (end < str->str + str->len) { 1109 | toml_err_set(TOML_ERR_SYNTAX, "%s:%d:%d: invalid float"); 1110 | goto cleanup; 1111 | } 1112 | result = toml_value_new_float(n); 1113 | } else if (type == 't') { 1114 | result = toml_parse_datetime(str->str, str->len); 1115 | } 1116 | 1117 | cleanup: 1118 | toml_string_free(str); 1119 | return result; 1120 | } 1121 | 1122 | TomlValue* toml_parse_bool(TomlParser *self) 1123 | { 1124 | if (self->ptr + 4 <= self->end && strncmp(self->ptr, "true", 4) == 0 && 1125 | (self->ptr + 4 == self->end || isspace(*(self->ptr + 4)) || *(self->ptr + 4) == ',' || *(self->ptr + 4) == ']' || *(self->ptr + 4) == '}')) { 1126 | toml_next_n(self, 4); 1127 | return toml_value_new_boolean(TOML_TRUE); 1128 | } 1129 | 1130 | if (self->ptr + 5 <= self->end && strncmp(self->ptr, "false", 5) == 0 && 1131 | (self->ptr + 5 == self->end || isspace(*(self->ptr + 5)) || *(self->ptr + 5) == ',' || *(self->ptr + 5) == ']' || *(self->ptr + 5) == '}')) { 1132 | toml_next_n(self, 5); 1133 | return toml_value_new_boolean(TOML_FALSE); 1134 | } 1135 | 1136 | return NULL; 1137 | } 1138 | 1139 | TomlValue* toml_parse_array(TomlParser *self); 1140 | TomlValue* toml_parse_inline_table(TomlParser *self); 1141 | 1142 | TomlValue* toml_parse_value(TomlParser *self) 1143 | { 1144 | TomlValue *value = NULL; 1145 | 1146 | char ch = *self->ptr; 1147 | 1148 | if (strncmp(self->ptr, "\"\"\"", 3) == 0) { 1149 | toml_next_n(self, 3); 1150 | value = toml_parse_multi_line_basic_string(self); 1151 | } else if (strncmp(self->ptr, "\'\'\'", 3) == 0) { 1152 | toml_next_n(self, 3); 1153 | value = toml_parse_multi_line_literal_string(self); 1154 | } else if (ch == '\"') { 1155 | toml_move_next(self); 1156 | value = toml_parse_basic_string_value(self); 1157 | } else if (ch == '\'') { 1158 | toml_move_next(self); 1159 | value = toml_parse_literal_string_value(self); 1160 | } else if (isdigit(ch) || ch == '+' || ch == '-' || ch == '.' || ch == 'n' || ch == 'i') { 1161 | value = toml_parse_int_or_float_or_time(self); 1162 | } else if (ch == 't' || ch == 'f') { 1163 | value = toml_parse_bool(self); 1164 | } else if (ch == '[') { 1165 | toml_move_next(self); 1166 | value = toml_parse_array(self); 1167 | } else if (ch == '{') { 1168 | toml_move_next(self); 1169 | value = toml_parse_inline_table(self); 1170 | } else { 1171 | toml_err_set(TOML_ERR_SYNTAX, "%s:%d:%d: unexpected token", 1172 | self->filename, self->lineno, self->colno); 1173 | } 1174 | 1175 | return value; 1176 | } 1177 | 1178 | TomlErrCode toml_parse_key_value(TomlParser *self, TomlTable *table) 1179 | { 1180 | TomlString *key = NULL; 1181 | TomlValue *value = NULL; 1182 | 1183 | while (self->ptr < self->end) { 1184 | char ch; 1185 | 1186 | ch = *self->ptr; 1187 | while (self->ptr < self->end && isspace(ch)) { 1188 | toml_move_next(self); 1189 | ch = *self->ptr; 1190 | } 1191 | 1192 | if (self->ptr == self->end) break; 1193 | 1194 | if (isalnum(ch) || ch == '_') { 1195 | key = toml_parse_bare_key(self); 1196 | if (key == NULL) 1197 | return toml_err()->code; 1198 | } else if (ch == '\"') { 1199 | toml_move_next(self); 1200 | key = toml_parse_basic_string(self); 1201 | if (key == NULL) 1202 | return toml_err()->code; 1203 | } else if (ch == '\'') { 1204 | toml_move_next(self); 1205 | key = toml_parse_literal_string(self); 1206 | if (key == NULL) 1207 | return toml_err()->code; 1208 | } else if (ch == '[') { 1209 | break; 1210 | } else if (ch == '#') { 1211 | do { 1212 | toml_move_next(self); 1213 | ch = *self->ptr; 1214 | } while (self->ptr < self->end && ch != '\n'); 1215 | toml_move_next(self); 1216 | continue; 1217 | } else { 1218 | toml_err_set(TOML_ERR_SYNTAX, "%s:%d:%d: unexpected token", 1219 | self->filename, self->lineno, self->colno); 1220 | return TOML_ERR_SYNTAX; 1221 | } 1222 | 1223 | ch = *self->ptr; 1224 | while (self->ptr < self->end && (ch == ' ' || ch == '\t')) { 1225 | toml_move_next(self); 1226 | ch = *self->ptr; 1227 | } 1228 | 1229 | if (self->ptr == self->end) { 1230 | toml_err_set(TOML_ERR_SYNTAX, "%s:%d:%d: unterminated key value pair"); 1231 | return TOML_ERR_SYNTAX; 1232 | } 1233 | 1234 | if (ch != '=') { 1235 | toml_err_set(TOML_ERR_SYNTAX, "%s:%d:%d: unexpected token", 1236 | self->filename, self->lineno, self->colno); 1237 | return TOML_ERR_SYNTAX; 1238 | } 1239 | 1240 | toml_move_next(self); 1241 | 1242 | ch = *self->ptr; 1243 | while (self->ptr < self->end && (ch == ' ' || ch == '\t')) { 1244 | toml_move_next(self); 1245 | ch = *self->ptr; 1246 | } 1247 | 1248 | if (self->ptr == self->end) { 1249 | toml_err_set(TOML_ERR_SYNTAX, "%s:%d:%d: unterminated key value pair"); 1250 | return TOML_ERR_SYNTAX; 1251 | } 1252 | 1253 | value = toml_parse_value(self); 1254 | if (value == NULL) 1255 | return toml_err()->code; 1256 | 1257 | toml_table_set_by_string(table, key, value); 1258 | 1259 | key = NULL; 1260 | value = NULL; 1261 | 1262 | while (self->ptr < self->end && (*self->ptr == ' ' || *self->ptr == '\t')) { 1263 | toml_move_next(self); 1264 | } 1265 | 1266 | if (self->ptr == self->end) 1267 | break; 1268 | 1269 | if (*self->ptr == '#') { 1270 | do { 1271 | toml_move_next(self); 1272 | } while (self->ptr < self->end && *self->ptr != '\n'); 1273 | } 1274 | 1275 | if (*self->ptr == '\r') { 1276 | toml_move_next(self); 1277 | } 1278 | 1279 | if (*self->ptr == '\n') { 1280 | toml_move_next(self); 1281 | } else { 1282 | toml_err_set(TOML_ERR_SYNTAX, "%s:%d:%d: new line expected", 1283 | self->filename, self->lineno, self->colno); 1284 | toml_value_free(value); 1285 | toml_string_free(key); 1286 | return TOML_ERR_SYNTAX; 1287 | } 1288 | } 1289 | 1290 | return TOML_OK; 1291 | } 1292 | 1293 | TomlValue* toml_parse_array(TomlParser *self) 1294 | { 1295 | TomlValue *array = NULL; 1296 | TomlValue *value = NULL; 1297 | 1298 | array = toml_value_new_array(); 1299 | 1300 | while (self->ptr < self->end) { 1301 | if (isspace(*self->ptr)) { 1302 | while (self->ptr < self->end && isspace(*self->ptr)) { 1303 | toml_move_next(self); 1304 | } 1305 | } else if (*self->ptr == '#') { 1306 | do { 1307 | toml_move_next(self); 1308 | } while (self->ptr < self->end && *self->ptr != '\n'); 1309 | toml_move_next(self); 1310 | } else if (*self->ptr == '\n') { 1311 | toml_move_next(self); 1312 | } else if (*self->ptr == ']') { 1313 | toml_move_next(self); 1314 | break; 1315 | } else { 1316 | value = toml_parse_value(self); 1317 | if (value == NULL) { 1318 | goto error; 1319 | } 1320 | 1321 | toml_array_append(array->value.array, value); 1322 | 1323 | value = NULL; 1324 | 1325 | while (self->ptr < self->end) { 1326 | if (isspace(*self->ptr)) { 1327 | do { 1328 | toml_move_next(self); 1329 | } while (self->ptr < self->end && isspace(*self->ptr)); 1330 | } else if (*self->ptr == '#') { 1331 | do { 1332 | toml_move_next(self); 1333 | } while (self->ptr < self->end && *self->ptr != '\n'); 1334 | } else { 1335 | break; 1336 | } 1337 | } 1338 | 1339 | if (self->ptr < self->end && *self->ptr == ',') { 1340 | toml_move_next(self); 1341 | } 1342 | } 1343 | } 1344 | 1345 | goto end; 1346 | 1347 | error: 1348 | toml_value_free(value); 1349 | toml_value_free(array); 1350 | array = NULL; 1351 | 1352 | end: 1353 | return array; 1354 | } 1355 | 1356 | TomlValue* toml_parse_inline_table(TomlParser *self) 1357 | { 1358 | TomlValue *table = NULL; 1359 | 1360 | table = toml_value_new_table(); 1361 | 1362 | while (self->ptr < self->end) { 1363 | TomlString *key = NULL; 1364 | TomlValue *value = NULL; 1365 | char ch; 1366 | 1367 | ch = *self->ptr; 1368 | while (self->ptr < self->end && (ch == ' ' || ch == '\t')) { 1369 | toml_move_next(self); 1370 | ch = *self->ptr; 1371 | } 1372 | 1373 | if (isalnum(ch) || ch == '_') { 1374 | key = toml_parse_bare_key(self); 1375 | if (key == NULL) 1376 | goto error; 1377 | } else if (ch == '\"') { 1378 | toml_move_next(self); 1379 | key = toml_parse_basic_string(self); 1380 | if (key == NULL) 1381 | goto error; 1382 | } else if (ch == '\'') { 1383 | toml_move_next(self); 1384 | key = toml_parse_literal_string(self); 1385 | if (key == NULL) 1386 | goto error; 1387 | } else if (ch == '}') { 1388 | break; 1389 | } else { 1390 | toml_err_set(TOML_ERR_SYNTAX, "%s:%d:%d: unexpected token", 1391 | self->filename, self->lineno, self->colno); 1392 | goto error; 1393 | } 1394 | 1395 | ch = *self->ptr; 1396 | while (self->ptr < self->end && (ch == ' ' || ch == '\t')) { 1397 | toml_move_next(self); 1398 | ch = *self->ptr; 1399 | } 1400 | 1401 | if (self->ptr == self->end) { 1402 | toml_err_set(TOML_ERR_SYNTAX, "%s:%d:%d: unterminated key value pair"); 1403 | goto error; 1404 | } 1405 | 1406 | if (ch != '=') { 1407 | toml_err_set(TOML_ERR_SYNTAX, "%s:%d:%d: unexpected token", 1408 | self->filename, self->lineno, self->colno); 1409 | goto error; 1410 | } 1411 | 1412 | toml_move_next(self); 1413 | 1414 | ch = *self->ptr; 1415 | while (self->ptr < self->end && (ch == ' ' || ch == '\t')) { 1416 | toml_move_next(self); 1417 | ch = *self->ptr; 1418 | } 1419 | 1420 | if (self->ptr == self->end) { 1421 | toml_err_set(TOML_ERR_SYNTAX, "%s:%d:%d: unterminated key value pair"); 1422 | goto error; 1423 | } 1424 | 1425 | value = toml_parse_value(self); 1426 | if (value == NULL) 1427 | goto error; 1428 | 1429 | toml_table_set_by_string(table->value.table, key, value); 1430 | 1431 | while (self->ptr < self->end && (*self->ptr == ' ' || *self->ptr == '\t')) { 1432 | toml_move_next(self); 1433 | } 1434 | 1435 | if (*self->ptr == ',') { 1436 | toml_move_next(self); 1437 | } else if (*self->ptr == '}') { 1438 | toml_move_next(self); 1439 | break; 1440 | } 1441 | } 1442 | 1443 | goto end; 1444 | 1445 | error: 1446 | toml_value_free(table); 1447 | table = NULL; 1448 | 1449 | end: 1450 | return table; 1451 | } 1452 | 1453 | TomlTable* toml_walk_table_path(TomlParser *parser, TomlTable *table, 1454 | TomlArray *key_path, int is_array, 1455 | int create_if_not_exist) 1456 | { 1457 | TomlTable *real_table = table; 1458 | TomlValue *new_table = NULL; 1459 | TomlValue *array = NULL; 1460 | TomlString *part_copy = NULL; 1461 | 1462 | if (is_array) { 1463 | size_t i = 0; 1464 | for (; i < key_path->len - 1; i++) { 1465 | TomlString *part = key_path->elements[i]->value.string; 1466 | TomlValue *t = toml_table_get_by_string(real_table, part); 1467 | if (t == NULL) { 1468 | if (create_if_not_exist) { 1469 | new_table = toml_value_new_table(); 1470 | part_copy = toml_string_clone(part); 1471 | toml_table_set_by_string(real_table, part_copy, new_table); 1472 | real_table = new_table->value.table; 1473 | part_copy = NULL; 1474 | new_table = NULL; 1475 | } else { 1476 | real_table = NULL; 1477 | break; 1478 | } 1479 | } else { 1480 | real_table = t->value.table; 1481 | } 1482 | } 1483 | 1484 | TomlString *part = key_path->elements[i]->value.string; 1485 | TomlValue *t = toml_table_get_by_string(real_table, part); 1486 | if (t == NULL) { 1487 | if (create_if_not_exist) { 1488 | array = toml_value_new_array(); 1489 | new_table = toml_value_new_table(); 1490 | toml_array_append(array->value.array, new_table); 1491 | part_copy = toml_string_clone(part); 1492 | toml_table_set_by_string(real_table, part_copy, array); 1493 | real_table = new_table->value.table; 1494 | part_copy = NULL; 1495 | array = NULL; 1496 | new_table = NULL; 1497 | } else { 1498 | real_table = NULL; 1499 | } 1500 | } else { 1501 | if (t->type != TOML_ARRAY) { 1502 | toml_err_set(TOML_ERR_SYNTAX, "%s:%d:%d: this key was not an array", 1503 | parser->filename, parser->lineno, parser->colno); 1504 | goto error; 1505 | } 1506 | 1507 | TomlValue *new_table = toml_value_new_table(); 1508 | toml_array_append(t->value.array, new_table); 1509 | real_table = new_table->value.table; 1510 | } 1511 | } else { 1512 | for (size_t i = 0; i < key_path->len; i++) { 1513 | TomlString *part = key_path->elements[i]->value.string; 1514 | TomlValue *t = toml_table_get_by_string(real_table, part); 1515 | if (t == NULL) { 1516 | if (create_if_not_exist) { 1517 | new_table = toml_value_new_table(); 1518 | part_copy = toml_string_clone(part); 1519 | toml_table_set_by_string(real_table, part_copy, new_table); 1520 | real_table = new_table->value.table; 1521 | part_copy = NULL; 1522 | new_table = NULL; 1523 | } else { 1524 | real_table = NULL; 1525 | break; 1526 | } 1527 | } else { 1528 | if (t->type == TOML_ARRAY) { 1529 | real_table = t->value.array->elements[t->value.array->len - 1]->value.table; 1530 | } else if (t->type == TOML_TABLE) { 1531 | real_table = t->value.table; 1532 | } 1533 | } 1534 | } 1535 | } 1536 | 1537 | goto end; 1538 | 1539 | error: 1540 | toml_string_free(part_copy); 1541 | toml_value_free(new_table); 1542 | toml_value_free(array); 1543 | real_table = NULL; 1544 | 1545 | end: 1546 | return real_table; 1547 | } 1548 | 1549 | TomlErrCode toml_parse_table(TomlParser *self, TomlTable *table) 1550 | { 1551 | TomlArray *key_path = NULL; 1552 | int is_array = TOML_FALSE; 1553 | TomlTable *real_table = table; 1554 | TomlErrCode rc = 0; 1555 | 1556 | key_path = toml_array_new(); 1557 | 1558 | if (self->ptr < self->end && *self->ptr == '[') { 1559 | is_array = TOML_TRUE; 1560 | toml_move_next(self); 1561 | } 1562 | 1563 | while (1) { 1564 | if (*self->ptr == ' ' || *self->ptr == '\t') { 1565 | do { 1566 | toml_move_next(self); 1567 | } while (*self->ptr < *self->end && (*self->ptr == ' ' || *self->ptr == '\t')); 1568 | } else if (*self->ptr == ']') { 1569 | if (is_array) { 1570 | if (self->ptr + 2 <= self->end && strncmp(self->ptr, "]]", 2) == 0) { 1571 | toml_next_n(self, 2); 1572 | break; 1573 | } 1574 | } else { 1575 | toml_move_next(self); 1576 | break; 1577 | } 1578 | } else { 1579 | TomlString *key_part = NULL; 1580 | TomlValue *key_part_value = NULL; 1581 | 1582 | if (isalnum(*self->ptr) || *self->ptr == '_') { 1583 | key_part = toml_parse_bare_key(self); 1584 | if (key_part == NULL) 1585 | goto cleanup; 1586 | } else if (*self->ptr == '\"') { 1587 | toml_move_next(self); 1588 | key_part = toml_parse_basic_string(self); 1589 | if (key_part == NULL) 1590 | goto cleanup; 1591 | } else if (*self->ptr == '\'') { 1592 | toml_move_next(self); 1593 | key_part = toml_parse_literal_string(self); 1594 | if (key_part == NULL) 1595 | goto cleanup; 1596 | } else { 1597 | toml_err_set(TOML_ERR_SYNTAX, "%s:%d:%d: unexpected token", 1598 | self->filename, self->lineno, self->colno); 1599 | } 1600 | 1601 | key_part_value = toml_value_new(TOML_STRING); 1602 | key_part_value->value.string = key_part; 1603 | 1604 | toml_array_append(key_path, key_part_value); 1605 | 1606 | while (self->ptr < self->end && 1607 | (*self->ptr == ' ' || *self->ptr == '\t')) { 1608 | toml_move_next(self); 1609 | } 1610 | 1611 | if (self->ptr < self->end && *self->ptr == '.') { 1612 | toml_move_next(self); 1613 | } 1614 | } 1615 | } 1616 | 1617 | if (key_path->len == 0) { 1618 | toml_err_set(TOML_ERR_SYNTAX, "%s:%d:%d: empty table name", 1619 | self->filename, self->lineno, self->colno); 1620 | goto cleanup; 1621 | } 1622 | 1623 | while (self->ptr < self->end && 1624 | (*self->ptr == ' ' || *self->ptr == '\t' || *self->ptr == '\r')) { 1625 | toml_move_next(self); 1626 | } 1627 | 1628 | if (self->ptr < self->end && *self->ptr != '\n') { 1629 | toml_err_set(TOML_ERR_SYNTAX, "%s:%d:%d: new line expected", 1630 | self->filename, self->lineno, self->colno); 1631 | goto cleanup; 1632 | } 1633 | 1634 | real_table = toml_walk_table_path(self, table, key_path, is_array, TOML_TRUE); 1635 | if (real_table == NULL) 1636 | goto error; 1637 | 1638 | toml_parse_key_value(self, real_table); 1639 | 1640 | goto cleanup; 1641 | 1642 | error: 1643 | rc = toml_err()->code; 1644 | cleanup: 1645 | toml_array_free(key_path); 1646 | return rc; 1647 | } 1648 | 1649 | TomlTable* toml_parse(TomlParser *self) 1650 | { 1651 | TomlTable *table = NULL; 1652 | 1653 | table = toml_table_new(); 1654 | 1655 | while (self->ptr < self->end) { 1656 | char ch = *self->ptr; 1657 | 1658 | while (self->ptr < self->end && isspace(ch)) { 1659 | toml_move_next(self); 1660 | ch = *self->ptr; 1661 | } 1662 | 1663 | if (ch == '#') { 1664 | do { 1665 | toml_move_next(self); 1666 | ch = *self->ptr; 1667 | } while (self->ptr < self->end && ch != '\n'); 1668 | toml_move_next(self); 1669 | } else if (ch == '[') { 1670 | toml_move_next(self); 1671 | if (toml_parse_table(self, table) != 0) 1672 | return NULL; 1673 | } else if (isalnum(ch) || ch == '_' || ch == '-') { 1674 | if (toml_parse_key_value(self, table) != 0) 1675 | return NULL; 1676 | } else if (ch == ' ' || ch == '\t' || ch == '\r') { 1677 | do { 1678 | toml_move_next(self); 1679 | ch = *self->ptr; 1680 | } while (ch == ' ' || ch == '\t' || ch == '\r'); 1681 | } else if (ch == '\n') { 1682 | toml_move_next(self); 1683 | } 1684 | } 1685 | 1686 | return table; 1687 | } 1688 | 1689 | TomlTable* toml_load_nstr_filename(TOML_CONST char *str, size_t len, 1690 | TOML_CONST char *filename) 1691 | { 1692 | TomlParser *parser = NULL; 1693 | TomlTable *table = NULL; 1694 | 1695 | parser = toml_parser_new(str, len); 1696 | parser->filename = toml_strdup(filename); 1697 | 1698 | table = toml_parse(parser); 1699 | 1700 | toml_parser_free(parser); 1701 | return table; 1702 | } 1703 | 1704 | TomlTable* toml_load_nstr(TOML_CONST char *str, size_t len) 1705 | { 1706 | return toml_load_nstr_filename(str, len, ""); 1707 | } 1708 | 1709 | TomlTable* toml_load_str(TOML_CONST char *str) 1710 | { 1711 | return toml_load_nstr(str, sizeof(str)); 1712 | } 1713 | 1714 | TomlTable* toml_load_file_filename(FILE *file, TOML_CONST char *filename) 1715 | { 1716 | TomlTable *table = NULL; 1717 | TomlString *str = NULL; 1718 | 1719 | str = toml_string_new(); 1720 | 1721 | toml_string_expand_if_necessary(str, 4095); 1722 | 1723 | size_t count; 1724 | size_t bytes_to_read; 1725 | do { 1726 | bytes_to_read = str->_capacity - str->len - 1; 1727 | 1728 | count = fread(&str->str[str->len], 1, bytes_to_read, file); 1729 | if (ferror(file)) { 1730 | toml_err_set(TOML_ERR_OS, "Error when reading %s [errno %d: %s]", filename, errno, strerror(errno)); 1731 | goto error; 1732 | } 1733 | 1734 | str->len += count; 1735 | 1736 | if (str->len + 1 >= str->_capacity) { 1737 | toml_string_expand_if_necessary(str, str->_capacity * 2); 1738 | } 1739 | } while (count == bytes_to_read); 1740 | 1741 | str->str[str->len] = 0; 1742 | 1743 | table = toml_load_nstr_filename(str->str, str->len, filename); 1744 | 1745 | goto cleanup; 1746 | 1747 | error: 1748 | toml_table_free(table); 1749 | table = NULL; 1750 | 1751 | cleanup: 1752 | toml_string_free(str); 1753 | return table; 1754 | } 1755 | 1756 | TomlTable* toml_load_file(FILE *file) 1757 | { 1758 | return toml_load_file_filename(file, ""); 1759 | } 1760 | 1761 | TomlTable* toml_load_filename(TOML_CONST char *filename) 1762 | { 1763 | TomlTable *table = NULL; 1764 | FILE *f = NULL; 1765 | 1766 | f = fopen(filename, "r"); 1767 | if (f == NULL) { 1768 | toml_err_set(TOML_ERR_OS, "Cannot open file %s [errno %d: %s]", filename, errno, strerror(errno)); 1769 | goto error; 1770 | } 1771 | 1772 | table = toml_load_file_filename(f, filename); 1773 | 1774 | goto cleanup; 1775 | 1776 | error: 1777 | toml_table_free(table); 1778 | table = NULL; 1779 | 1780 | cleanup: 1781 | if (f != NULL) 1782 | fclose(f); 1783 | return table; 1784 | } 1785 | 1786 | // vim: sw=4 ts=8 sts=4 et 1787 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(toml-test main.c) 2 | target_compile_features(toml-test PRIVATE ${toml_compile_features}) 3 | target_compile_definitions(toml-test PRIVATE 4 | ${toml_compile_definitions} 5 | PROJECT_SOURCE_DIR="${PROJECT_SOURCE_DIR}") 6 | 7 | target_compile_options(toml-test PRIVATE 8 | ${toml_c_flags} 9 | $<$:${toml_compile_options_release}> 10 | $<$:${toml_compile_options_release}> 11 | ) 12 | 13 | target_link_libraries(toml-test toml ${toml_link_libraries}) 14 | -------------------------------------------------------------------------------- /tests/complex-structure.toml: -------------------------------------------------------------------------------- 1 | [a] 2 | aa = 1 3 | 4 | [[a.b]] 5 | bb = 2 6 | 7 | [[a.b]] 8 | cc = 3 9 | 10 | [a.c] 11 | dd = 4 12 | 13 | [a.c.d] 14 | ee = 5 15 | 16 | [[a.c.d.e]] 17 | ff = 6 18 | 19 | [[a.c.d.e]] 20 | gg = 7 21 | 22 | [a.c.d.e.f] 23 | hh = 8 24 | 25 | [[b]] 26 | ii = 9 27 | 28 | [[b]] 29 | jj = 10 30 | 31 | [b.a] 32 | kk = 11 33 | 34 | [this.is-a."complex" . 'table' . name] 35 | ok = true 36 | -------------------------------------------------------------------------------- /tests/example.toml: -------------------------------------------------------------------------------- 1 | # This is a TOML document. Boom. 2 | 3 | title = "TOML Example" 4 | 5 | [owner] 6 | name = "Tom Preston-Werner" 7 | organization = "GitHub" 8 | bio = "GitHub Cofounder & CEO\nLikes tater tots and beer." 9 | # dob = 1979-05-27T07:32:00Z # First class dates? Why not? 10 | 11 | [database] 12 | server = "192.168.1.1" 13 | ports = [ 8001, 8001, 8002 ] 14 | connection_max = 5000 15 | enabled = true 16 | 17 | [servers] 18 | 19 | # You can indent as you please. Tabs or spaces. TOML don't care. 20 | [servers.alpha] 21 | ip = "10.0.0.1" 22 | dc = "eqdc10" 23 | 24 | [servers.beta] 25 | ip = "10.0.0.2" 26 | dc = "eqdc10" 27 | country = "中国" # This should be parsed as UTF-8 28 | 29 | [clients] 30 | data = [ ["gamma", "delta"], [1, 2] ] # just an update to make sure parsers support it 31 | 32 | # Line breaks are OK when inside arrays 33 | hosts = [ 34 | "alpha", 35 | "omega" 36 | ] 37 | 38 | # Products 39 | 40 | [[products]] 41 | name = "Hammer" 42 | sku = 738594937 43 | 44 | [[products]] 45 | name = "Nail" 46 | sku = 284758393 47 | color = "gray" 48 | -------------------------------------------------------------------------------- /tests/fruit.toml: -------------------------------------------------------------------------------- 1 | [[fruit.blah]] 2 | name = "apple" 3 | 4 | [fruit.blah.physical] 5 | color = "red" 6 | shape = "round" 7 | 8 | [[fruit.blah]] 9 | name = "banana" 10 | 11 | [fruit.blah.physical] 12 | color = "yellow" 13 | shape = "bent" 14 | -------------------------------------------------------------------------------- /tests/hard_example.toml: -------------------------------------------------------------------------------- 1 | # Test file for TOML 2 | # Only this one tries to emulate a TOML file written by a user of the kind of parser writers probably hate 3 | # This part you'll really hate 4 | 5 | [the] 6 | test_string = "You'll hate me after this - #" # " Annoying, isn't it? 7 | 8 | [the.hard] 9 | test_array = [ "] ", " # "] # ] There you go, parse this! 10 | test_array2 = [ "Test #11 ]proved that", "Experiment #9 was a success" ] 11 | # You didn't think it'd as easy as chucking out the last #, did you? 12 | another_test_string = " Same thing, but with a string #" 13 | harder_test_string = " And when \"'s are in the string, along with # \"" # "and comments are there too" 14 | # Things will get harder 15 | 16 | [the.hard."bit#"] 17 | "what?" = "You don't think some user won't do that?" 18 | multi_line_array = [ 19 | "]", 20 | # ] Oh yes I did 21 | ] 22 | 23 | # Each of the following keygroups/key value pairs should produce an error. Uncomment to them to test 24 | 25 | #[error] if you didn't catch this, your parser is broken 26 | #string = "Anything other than tabs, spaces and newline after a keygroup or key value pair has ended should produce an error unless it is a comment" like this 27 | #array = [ 28 | # "This might most likely happen in multiline arrays", 29 | # Like here, 30 | # "or here, 31 | # and here" 32 | # ] End of array comment, forgot the # 33 | #number = 3.14 pi <--again forgot the # 34 | -------------------------------------------------------------------------------- /tests/hard_example_unicode.toml: -------------------------------------------------------------------------------- 1 | # Tèƨƭ ƒïℓè ƒôř TÓM£ 2 | 3 | # Óñℓ¥ ƭλïƨ ôñè ƭřïèƨ ƭô è₥úℓáƭè á TÓM£ ƒïℓè ωřïƭƭèñ β¥ á úƨèř ôƒ ƭλè ƙïñδ ôƒ ƥářƨèř ωřïƭèřƨ ƥřôβáβℓ¥ λáƭè 4 | # Tλïƨ ƥářƭ ¥ôú'ℓℓ řèáℓℓ¥ λáƭè 5 | 6 | [the] 7 | test_string = "Ýôú'ℓℓ λáƭè ₥è áƒƭèř ƭλïƨ - #" # " Âññô¥ïñϱ, ïƨñ'ƭ ïƭ? 8 | 9 | 10 | [the.hard] 11 | test_array = [ "] ", " # "] # ] Tλèřè ¥ôú ϱô, ƥářƨè ƭλïƨ! 12 | test_array2 = [ "Tèƨƭ #11 ]ƥřôƲèδ ƭλáƭ", "Éжƥèřï₥èñƭ #9 ωáƨ á ƨúççèƨƨ" ] 13 | # Ýôú δïδñ'ƭ ƭλïñƙ ïƭ'δ áƨ èáƨ¥ áƨ çλúçƙïñϱ ôúƭ ƭλè ℓáƨƭ #, δïδ ¥ôú? 14 | another_test_string = "§á₥è ƭλïñϱ, βúƭ ωïƭλ á ƨƭřïñϱ #" 15 | harder_test_string = " Âñδ ωλèñ \"'ƨ ářè ïñ ƭλè ƨƭřïñϱ, áℓôñϱ ωïƭλ # \"" # "áñδ çô₥₥èñƭƨ ářè ƭλèřè ƭôô" 16 | # Tλïñϱƨ ωïℓℓ ϱèƭ λářδèř 17 | 18 | [the.hard."βïƭ#"] 19 | "ωλáƭ?" = "Ýôú δôñ'ƭ ƭλïñƙ ƨô₥è úƨèř ωôñ'ƭ δô ƭλáƭ?" 20 | multi_line_array = [ 21 | "]", 22 | # ] Óλ ¥èƨ Ì δïδ 23 | ] 24 | 25 | # Each of the following keygroups/key value pairs should produce an error. Uncomment to them to test 26 | 27 | #[error] ïƒ ¥ôú δïδñ'ƭ çáƭçλ ƭλïƨ, ¥ôúř ƥářƨèř ïƨ βřôƙèñ 28 | #string = "Âñ¥ƭλïñϱ ôƭλèř ƭλáñ ƭáβƨ, ƨƥáçèƨ áñδ ñèωℓïñè áƒƭèř á ƙè¥ϱřôúƥ ôř ƙè¥ Ʋáℓúè ƥáïř λáƨ èñδèδ ƨλôúℓδ ƥřôδúçè áñ èřřôř úñℓèƨƨ ïƭ ïƨ á çô₥₥èñƭ" ℓïƙè ƭλïƨ 29 | 30 | #array = [ 31 | # "Tλïƨ ₥ïϱλƭ ₥ôƨƭ ℓïƙèℓ¥ λáƥƥèñ ïñ ₥úℓƭïℓïñè ářřá¥ƨ", 32 | # £ïƙè λèřè, 33 | # "ôř λèřè, 34 | # áñδ λèřè" 35 | # ] Éñδ ôƒ ářřᥠçô₥₥èñƭ, ƒôřϱôƭ ƭλè # 36 | #number = 3.14 ƥï <--áϱáïñ ƒôřϱôƭ ƭλè # -------------------------------------------------------------------------------- /tests/key-values.toml: -------------------------------------------------------------------------------- 1 | # All kinds of keys and string values 2 | 3 | key1-bare-with-dash = "basic string 1" 4 | 5 | key2-bare_with_underscore = 'literal string 2' 6 | 7 | 3-key-start-with-digit = "basic string \b 3 with \t space \f and\"escapes \\\u05d0\n" 8 | 9 | "key 4 double quoted\n" = "basic string \rbasic string with carriage return" 10 | 11 | 'key 5 single quoted' = """multi line basic string""" 12 | 13 | 'key 6 multi-line basic string strip beginning new line' = """ 14 | There should be no new line, but two leading spaces.""" 15 | 16 | 'key 7 line ending backslash' = """ 17 | lines with \ 18 | ending backslash \ 19 | should be concatenated into a single line and \ 20 | 21 | 22 | new lines and spaces after the line ending backslash should be stripped.""" 23 | 24 | 'key 8 multi-line basic string with escapes' = """ 25 | Escapes \b 26 | should\t 27 | also \f 28 | work \" 29 | in \\ 30 | multi-line \nbasic string.""" 31 | 32 | 'key 9 multi-line literal string' = ''' 33 | The first newline is 34 | trimmed in raw strings. 35 | All other whitespace or [(*&%$@!/\~`^#)] 36 | is preserved. 37 | ''' 38 | 39 | # Numbers, copied from README.md on https://github.com/toml-lang/toml 40 | 41 | int1 = +99 42 | int2 = 42 43 | int3 = 0 44 | int4 = -17 45 | int5 = 1_000 46 | int6 = 5_349_221 47 | int7 = 1_2_3_4_5 # VALID but discouraged 48 | 49 | # hexadecimal with prefix `0x` 50 | hex1 = 0xDEADBEEF 51 | hex2 = 0xdeadbeef 52 | hex3 = 0xdead_beef 53 | 54 | # octal with prefix `0o` 55 | oct1 = 0o01234567 56 | oct2 = 0o755 # useful for Unix file permissions 57 | oct3 = 0o0123_4567 58 | oct4 = 0o7_5_5 59 | 60 | # binary with prefix `0b` 61 | bin1 = 0b11010110 62 | bin2 = 0b1101_0110 63 | 64 | # fractional 65 | flt1 = +1.0 66 | flt2 = 3.1415 67 | flt3 = -0.01 68 | 69 | # exponent 70 | flt4 = 5e+22 71 | flt5 = 1e6 72 | flt6 = -2E-2 73 | 74 | # both 75 | flt7 = 6.626e-34 76 | flt8 = 9_224_617.445_991_228_313 77 | 78 | # infinity 79 | sf1 = inf # positive infinity 80 | sf2 = +inf # positive infinity 81 | sf3 = -inf # negative infinity 82 | 83 | # not a number 84 | sf4 = nan # actual sNaN/qNaN encoding is implementation specific 85 | sf5 = +nan # same as `nan` 86 | sf6 = -nan # valid, actual encoding is implementation specific 87 | 88 | # Boolean 89 | bool1 = true 90 | bool2 = false 91 | 92 | # Array 93 | arr1 = [ 1, 2, 3 ] 94 | arr2 = [ "red", "yellow", "green" ] 95 | arr3 = [ [ 1, 2 ], [3, 4, 5] ] 96 | arr4 = [ "all", 'strings', """are the same""", '''type'''] 97 | arr5 = [ [ 1, 2 ], ["a", "b", "c"] ] 98 | 99 | #arr6 = [ 1, 2.0 ] # INVALID 100 | 101 | arr7 = [ 102 | 1, 2, 3 103 | ] 104 | 105 | arr8 = [ 106 | 1, 107 | 2, # this is ok 108 | ] 109 | 110 | # Inline tables 111 | name = { first = "Tom", last = "Preston-Werner" } 112 | point = { x = 1, y = 2 } 113 | points = [ { x = 1, y = 2, z = 3 }, 114 | { x = 7, y = 8, z = 9 }, 115 | { x = 2, y = 4, z = 8 } ] 116 | -------------------------------------------------------------------------------- /tests/long_config.toml: -------------------------------------------------------------------------------- 1 | [voice-5band-compressor] 2 | input = "voice_44k_16bit_1ch.wav" 3 | output = "voice_44k_16bit_2ch_5band_compressor.wav" 4 | sidechain = "WhiteNoiseChange_44.1k_16bit_1ch.wav" 5 | block-size = 256 6 | out-channels = 2 7 | bandnum = 5 8 | enabled = [true, true, true, true, true] 9 | threshold = [-60.0, -10.0, -10.0, -10.0, -10.0] 10 | ratio = [1.5, 1.5, 1.5, 1.5, 1.5] 11 | attack-time = [30, 40, 50, 60, 70] 12 | release-time = [100, 110, 120, 130, 140] 13 | averaging-time = [30, 40, 50, 60, 70] 14 | crossover_fc = [500.0, 1000.0, 4000.0, 10000.0] 15 | saturation-threshold = [-1.0, -1.0, -1.0, -1.0, -1.0] 16 | input-gain = [1.0, 1.0, 1.0, 1.0, 1.0] 17 | output-gain = [1.0, 1.0, 1.0, 1.0, 1.0] 18 | side_chain_enabled = [true, true, true, true, true] 19 | 20 | [MultibandCompressor-SweepLevelChange-5Bands-LR-32k] 21 | input = "SweepLevelChange_32k_LR_2ch.wav" 22 | output = "MultibandCompressor_SweepLevelChange_5Bands_LR_32k.wav" 23 | sidechain = "WhiteNoiseChange_44.1k_16bit_1ch.wav" 24 | block-size = 256 25 | out-channels = 2 26 | bandnum = 5 27 | enabled = [true, true, true, true, true] 28 | threshold = [-60.0, -10.0, -10.0, -10.0, -10.0] 29 | ratio = [1.5, 1.5, 1.5, 1.5, 1.5] 30 | attack-time = [30, 40, 50, 60, 70] 31 | release-time = [100, 110, 120, 130, 140] 32 | averaging-time = [30, 40, 50, 60, 70] 33 | crossover_fc = [500.0, 1000.0, 4000.0, 10000.0] 34 | saturation-threshold = [-1.0, -1.0, -1.0, -1.0, -1.0] 35 | input-gain = [1.0, 1.0, 1.0, 1.0, 1.0] 36 | output-gain = [1.0, 1.0, 1.0, 1.0, 1.0] 37 | side_chain_enabled = [true, true, true, true, true] 38 | 39 | [MultibandCompressor-SweepLevelChange-5Bands-LR-44k] 40 | input = "SweepLevelChange_44k_LR_2ch.wav" 41 | output = "MultibandCompressor_SweepLevelChange_5Bands_LR_44k.wav" 42 | sidechain = "WhiteNoiseChange_44.1k_16bit_1ch.wav" 43 | block-size = 256 44 | out-channels = 2 45 | bandnum = 5 46 | enabled = [true, true, true, true, true] 47 | threshold = [-60.0, -10.0, -10.0, -10.0, -10.0] 48 | ratio = [1.5, 1.5, 1.5, 1.5, 1.5] 49 | attack-time = [30, 40, 50, 60, 70] 50 | release-time = [100, 110, 120, 130, 140] 51 | averaging-time = [30, 40, 50, 60, 70] 52 | crossover_fc = [500.0, 1000.0, 4000.0, 10000.0] 53 | saturation-threshold = [-1.0, -1.0, -1.0, -1.0, -1.0] 54 | input-gain = [1.0, 1.0, 1.0, 1.0, 1.0] 55 | output-gain = [1.0, 1.0, 1.0, 1.0, 1.0] 56 | side_chain_enabled = [true, true, true, true, true] 57 | 58 | [MultibandCompressor-SweepLevelChange-5Bands-LR-48k] 59 | input = "SweepLevelChange_48k_LR_2ch.wav" 60 | output = "MultibandCompressor_SweepLevelChange_5Bands_LR_48k.wav" 61 | sidechain = "WhiteNoiseChange_44.1k_16bit_1ch.wav" 62 | block-size = 256 63 | out-channels = 2 64 | bandnum = 5 65 | enabled = [true, true, true, true, true] 66 | threshold = [-60.0, -10.0, -10.0, -10.0, -10.0] 67 | ratio = [1.5, 1.5, 1.5, 1.5, 1.5] 68 | attack-time = [30, 40, 50, 60, 70] 69 | release-time = [100, 110, 120, 130, 140] 70 | averaging-time = [30, 40, 50, 60, 70] 71 | crossover_fc = [500.0, 1000.0, 4000.0, 10000.0] 72 | saturation-threshold = [-1.0, -1.0, -1.0, -1.0, -1.0] 73 | input-gain = [1.0, 1.0, 1.0, 1.0, 1.0] 74 | output-gain = [1.0, 1.0, 1.0, 1.0, 1.0] 75 | side_chain_enabled = [true, true, true, true, true] 76 | 77 | [MultibandCompressor-SweepLevelChange-5Bands-1BandBypass-LR-32k] 78 | input = "SweepLevelChange_32k_LR_2ch.wav" 79 | output = "MultibandCompressor_SweepLevelChange_5Bands_1BandBypass_LR_32k.wav" 80 | sidechain = "WhiteNoiseChange_44.1k_16bit_1ch.wav" 81 | block-size = 256 82 | out-channels = 2 83 | bandnum = 5 84 | enabled = [true, true, true, false, true] 85 | threshold = [-60.0, -10.0, -10.0, -10.0, -10.0] 86 | ratio = [1.5, 1.5, 1.5, 1.5, 1.5] 87 | attack-time = [30, 40, 50, 60, 70] 88 | release-time = [100, 110, 120, 130, 140] 89 | averaging-time = [30, 40, 50, 60, 70] 90 | crossover_fc = [500.0, 1000.0, 4000.0, 10000.0] 91 | saturation-threshold = [-1.0, -1.0, -1.0, -1.0, -1.0] 92 | input-gain = [1.0, 1.0, 1.0, 1.0, 1.0] 93 | output-gain = [1.0, 1.0, 1.0, 1.0, 1.0] 94 | side_chain_enabled = [true, true, true, true, true] 95 | 96 | [MultibandCompressor-SweepLevelChange-5Bands-1BandBypass-LR-44k] 97 | input = "SweepLevelChange_44k_LR_2ch.wav" 98 | output = "MultibandCompressor_SweepLevelChange_5Bands_1BandBypass_LR_44k.wav" 99 | sidechain = "WhiteNoiseChange_44.1k_16bit_1ch.wav" 100 | block-size = 256 101 | out-channels = 2 102 | bandnum = 5 103 | enabled = [true, true, true, false, true] 104 | threshold = [-60.0, -10.0, -10.0, -10.0, -10.0] 105 | ratio = [1.5, 1.5, 1.5, 1.5, 1.5] 106 | attack-time = [30, 40, 50, 60, 70] 107 | release-time = [100, 110, 120, 130, 140] 108 | averaging-time = [30, 40, 50, 60, 70] 109 | crossover_fc = [500.0, 1000.0, 4000.0, 10000.0] 110 | saturation-threshold = [-1.0, -1.0, -1.0, -1.0, -1.0] 111 | input-gain = [1.0, 1.0, 1.0, 1.0, 1.0] 112 | output-gain = [1.0, 1.0, 1.0, 1.0, 1.0] 113 | side_chain_enabled = [true, true, true, true, true] 114 | 115 | [MultibandCompressor-SweepLevelChange-5Bands-1BandBypass-LR-48k] 116 | input = "SweepLevelChange_48k_LR_2ch.wav" 117 | output = "MultibandCompressor_SweepLevelChange_5Bands_1BandBypass_LR_48k.wav" 118 | sidechain = "WhiteNoiseChange_44.1k_16bit_1ch.wav" 119 | block-size = 256 120 | out-channels = 2 121 | bandnum = 5 122 | enabled = [true, true, true, false, true] 123 | threshold = [-60.0, -10.0, -10.0, -10.0, -10.0] 124 | ratio = [1.5, 1.5, 1.5, 1.5, 1.5] 125 | attack-time = [30, 40, 50, 60, 70] 126 | release-time = [100, 110, 120, 130, 140] 127 | averaging-time = [30, 40, 50, 60, 70] 128 | crossover_fc = [500.0, 1000.0, 4000.0, 10000.0] 129 | saturation-threshold = [-1.0, -1.0, -1.0, -1.0, -1.0] 130 | input-gain = [1.0, 1.0, 1.0, 1.0, 1.0] 131 | output-gain = [1.0, 1.0, 1.0, 1.0, 1.0] 132 | side_chain_enabled = [true, true, true, true, true] 133 | -------------------------------------------------------------------------------- /tests/main.c: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | #include 6 | #include "toml.h" 7 | 8 | void print_table(const TomlTable *table); 9 | void print_value(const TomlValue *value); 10 | 11 | void print_array(const TomlArray *array) 12 | { 13 | printf("["); 14 | for (size_t i = 0; i < array->len; i++) { 15 | if (i > 0) { 16 | printf(", "); 17 | } 18 | print_value(array->elements[i]); 19 | } 20 | printf("]"); 21 | } 22 | 23 | void print_value(const TomlValue *value) 24 | { 25 | switch (value->type) { 26 | case TOML_TABLE: 27 | print_table(value->value.table); 28 | break; 29 | case TOML_ARRAY: 30 | print_array(value->value.array); 31 | break; 32 | case TOML_STRING: 33 | printf("\"%s\"", value->value.string->str); 34 | break; 35 | case TOML_INTEGER: 36 | printf("%" PRId64, value->value.integer); 37 | break; 38 | case TOML_FLOAT: 39 | printf("%f", value->value.float_); 40 | break; 41 | case TOML_DATETIME: 42 | printf("(datetime)"); 43 | break; 44 | case TOML_BOOLEAN: 45 | printf("%s", value->value.boolean ? "true" : "false"); 46 | break; 47 | } 48 | } 49 | 50 | void print_keyval(const TomlKeyValue *keyval) 51 | { 52 | printf("\"%s\": ", keyval->key->str); 53 | print_value(keyval->value); 54 | } 55 | 56 | void print_table(const TomlTable *table) 57 | { 58 | TomlTableIter it = toml_table_iter_new((TomlTable *)table); 59 | 60 | printf("{"); 61 | size_t i = 0; 62 | while (toml_table_iter_has_next(&it)) { 63 | TomlKeyValue *keyval = toml_table_iter_get(&it); 64 | 65 | if (i > 0) { 66 | printf(", "); 67 | } 68 | print_keyval(keyval); 69 | 70 | toml_table_iter_next(&it); 71 | i++; 72 | } 73 | printf("}"); 74 | } 75 | 76 | int test_run(const char *filename) 77 | { 78 | TomlTable *table = NULL; 79 | int rc = 0; 80 | 81 | table = toml_load_filename(filename); 82 | if (table == NULL) 83 | goto cleanup; 84 | 85 | print_table(table); 86 | printf("\n"); 87 | 88 | cleanup: 89 | toml_table_free(table); 90 | 91 | if (toml_err()->code != TOML_OK) { 92 | fprintf(stderr, "%s\n", toml_err()->message); 93 | rc = (int)toml_err()->code; 94 | } 95 | toml_err_clear(); 96 | return rc; 97 | } 98 | 99 | int main(void) 100 | { 101 | static const char *const filenames[] = { 102 | /* should parse */ 103 | PROJECT_SOURCE_DIR "/tests/key-values.toml", 104 | PROJECT_SOURCE_DIR "/tests/complex-structure.toml", 105 | PROJECT_SOURCE_DIR "/tests/long_config.toml", 106 | 107 | /* should not parse */ 108 | 109 | /* tests from https://github.com/toml-lang/toml */ 110 | PROJECT_SOURCE_DIR "/tests/example.toml", 111 | PROJECT_SOURCE_DIR "/tests/fruit.toml", 112 | PROJECT_SOURCE_DIR "/tests/hard_example.toml", 113 | PROJECT_SOURCE_DIR "/tests/hard_example_unicode.toml" 114 | }; 115 | 116 | int total_tests = sizeof(filenames) / sizeof(char *); 117 | int num_passed = 0; 118 | int num_failed = 0; 119 | 120 | for (int i = 0; i < total_tests; i++) { 121 | int rc = test_run(filenames[i]); 122 | if (rc == 0) { 123 | printf("test %d success\n", i); 124 | num_passed++; 125 | } else { 126 | printf("test %d returned %d\n", i, rc); 127 | num_failed++; 128 | } 129 | } 130 | 131 | printf("total %d tests, %d passed, %d failed\n", 132 | total_tests, num_passed, num_failed); 133 | 134 | return num_failed; 135 | } 136 | --------------------------------------------------------------------------------