├── .github └── workflows │ └── ccpp.yml ├── .gitignore ├── .travis.yml ├── .vscode └── settings.json ├── CMakeLists.txt ├── LICENSE ├── README.md ├── appveyor.yml ├── autogen.sh ├── configure.ac ├── include └── xml │ ├── attrib.h │ ├── call │ └── xml.h │ ├── common.h │ ├── impl │ ├── impl_common.h │ ├── impl_mem.h │ ├── impl_objmap.h │ └── impl_parse.h │ ├── objmap.h │ ├── print.h │ ├── util.h │ ├── version.h │ └── xml.h ├── makefile.am ├── src └── xml.c └── win ├── .gitignore ├── build.bat ├── xml.sln ├── xml.vcxproj └── xml.vcxproj.filters /.github/workflows/ccpp.yml: -------------------------------------------------------------------------------- 1 | name: C/C++ CI 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: sh autogen.sh 17 | run: sh autogen.sh 18 | - name: configure 19 | run: ./configure 20 | - name: make 21 | run: make 22 | - name: make check 23 | run: make check 24 | - name: make distcheck 25 | run: make distcheck 26 | -------------------------------------------------------------------------------- /.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 | # 55 | *.xcodeproj 56 | *.xcworkspace 57 | *.sln 58 | *.vcxproj 59 | *.vcxproj.* 60 | *.suo 61 | *.sdf 62 | *.opensdf 63 | ipch/ 64 | Debug/ 65 | Release/ 66 | .DS_Store 67 | .vs 68 | *.nupkg 69 | *.opendb 70 | packages.config 71 | /aclocal.m4 72 | /ar-lib 73 | /autom4te.cache/ 74 | /compile 75 | /config.guess 76 | /config.log 77 | /config.status 78 | /config.sub 79 | /configure 80 | /depcomp 81 | /install-sh 82 | /ltmain.sh 83 | /missing 84 | /libtool 85 | /.libs/ 86 | .deps/ 87 | *.[oa] 88 | *.l[oa] 89 | Makefile 90 | Makefile.in 91 | m4/*.m4 92 | .buildstamp 93 | .dirstamp 94 | packages/ 95 | .anjuta/* 96 | *.anjuta* 97 | config.h.* 98 | /config.h 99 | stamp* 100 | COPYING 101 | .idea/* 102 | *.VC.db 103 | cscope.* 104 | *-git-ignored-file.* 105 | test/*.trs 106 | test/test_* 107 | *.log 108 | test-* 109 | test/.libs/* 110 | test/tests 111 | docs/build/* 112 | * copy.* 113 | *.o 114 | *.obj 115 | *codeanalysis.*.xml 116 | *codeanalysis.xml 117 | *.lib 118 | *.tlog 119 | win/x64 120 | win/x85 121 | win/Debug 122 | xml_test/* 123 | main.c 124 | build 125 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | 3 | os: 4 | - linux 5 | - osx 6 | 7 | sudo: required 8 | dist: trusty 9 | 10 | compiler: 11 | - clang 12 | - gcc 13 | 14 | matrix: 15 | fast_finish: true 16 | exclude: 17 | # Skip GCC builds on macOS. 18 | - os: osx 19 | compiler: gcc 20 | include: 21 | # Additional GCC builds for code coverage. 22 | - os: linux 23 | compiler: gcc 24 | env: CODE_COVERAGE=ON 25 | 26 | cache: 27 | apt: true 28 | 29 | addons: 30 | apt: 31 | packages: 32 | - clang-3.6 33 | - lcov 34 | 35 | branches: 36 | only: 37 | - master 38 | 39 | script: 40 | - sh ./autogen.sh 41 | - if [[ "$CC" == "gcc" && "$CODE_COVERAGE" == "ON" ]]; then 42 | ./configure CFLAGS="-ftest-coverage -fprofile-arcs"; 43 | else 44 | ./configure; 45 | fi 46 | - make 47 | - make check 48 | 49 | after_success: 50 | - if [[ "$CC" == "gcc" && "$CODE_COVERAGE" == "ON" ]]; then 51 | pip install --user cpp-coveralls && 52 | coveralls 53 | --build-root . 54 | --exclude lib 55 | --exclude test 56 | --gcov-options '\-lp' 57 | --verbose; 58 | fi 59 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "C_Cpp.default.configurationProvider": "vector-of-bool.cmake-tools" 3 | } -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.11...3.16) 2 | project(xml VERSION 0.1.0 LANGUAGES C) 3 | 4 | set(CMAKE_C_STANDARD 11) 5 | set(DEFAULT_BUILD_TYPE "Release") 6 | 7 | set(XML_BUILD) 8 | option(XML_SHARED "Shared build" ON) 9 | option(XML_STATIC "Static build" OFF) 10 | option(XML_USE_C99 "" OFF) 11 | 12 | if(NOT XML_STATIC AND XML_SHARED) 13 | set(XML_BUILD SHARED) 14 | else(XML_STATIC) 15 | set(XML_BUILD STATIC) 16 | endif() 17 | 18 | if(XML_USE_C99) 19 | set(CMAKE_C_STANDARD 99) 20 | endif() 21 | 22 | if(MSVC) 23 | add_definitions(-D_WINDOWS -D_USRDLL -DXML_EXPORTS -DXML_DLL) 24 | add_compile_options(/W3 /Ox /Gy /Oi /TC) 25 | else() 26 | add_compile_options(-Wall -Werror -O3 -fstrict-aliasing) 27 | endif() 28 | 29 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 30 | message(STATUS "Setting build type to '${DEFAULT_BUILD_TYPE}' as none was specified.") 31 | set(CMAKE_BUILD_TYPE "${DEFAULT_BUILD_TYPE}" CACHE STRING "Choose the type of build." FORCE) 32 | # Set the possible values of build type for cmake-gui 33 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo") 34 | endif() 35 | 36 | include(GNUInstallDirs) 37 | 38 | set(CPACK_PROJECT_NAME ${PROJECT_NAME}) 39 | set(CPACK_PROJECT_VERSION ${PROJECT_VERSION}) 40 | include(CPack) 41 | 42 | # Target Start 43 | add_library(${PROJECT_NAME} ${XML_BUILD} src/xml.c) 44 | 45 | set_target_properties(${PROJECT_NAME} PROPERTIES 46 | VERSION ${PROJECT_VERSION} 47 | SOVERSION ${PROJECT_VERSION_MAJOR}) 48 | 49 | target_include_directories(${PROJECT_NAME} 50 | PUBLIC 51 | $ 52 | $ 53 | PRIVATE 54 | ${CMAKE_CURRENT_SOURCE_DIR}/src 55 | ) 56 | 57 | # Test Configuration 58 | if(XML_USE_TEST) 59 | include(CTest) 60 | enable_testing() 61 | add_subdirectory(test) 62 | endif() 63 | 64 | # Install 65 | install(TARGETS ${PROJECT_NAME} 66 | EXPORT ${PROJECT_NAME} 67 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 68 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} 69 | RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR}) 70 | 71 | install(DIRECTORY include/${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} 72 | PATTERN ".*" EXCLUDE) 73 | 74 | # Config 75 | export(TARGETS ${PROJECT_NAME} 76 | NAMESPACE ${PROJECT_NAME}:: 77 | FILE "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" 78 | ) 79 | 80 | install(EXPORT ${PROJECT_NAME} 81 | NAMESPACE ${PROJECT_NAME}:: 82 | DESTINATION ${CMAKE_INSTALL_LIBDIR}/${PROJECT_NAME}/cmake) 83 | 84 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Recep Aslantas 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🔋 XML parser for `C` 2 | 3 | [![Build Status](https://travis-ci.org/recp/xml.svg?branch=master)](https://travis-ci.org/recp/xml) 4 | [![Build status](https://ci.appveyor.com/api/projects/status/ugk4im145es00v3g/branch/master?svg=true)](https://ci.appveyor.com/project/recp/xml/branch/master) 5 | [![Codacy Badge](https://api.codacy.com/project/badge/Grade/c3439f84b19f40d2a5da3b748b3fe6b4)](https://www.codacy.com/manual/recp/xml?utm_source=github.com&utm_medium=referral&utm_content=recp/xml&utm_campaign=Badge_Grade) 6 | 7 | This is very simple and very powerful XML parser. It creates DOM-like data structure and allows to iterate and process XML objects very simple way. 8 | It does not alloc any memory for XML itself, it only allocs memory for tokens. It also does not use recursive way to build data structure which makes it very fast to build DOM-like tree structure. 9 | 10 | #### Documentation 11 | 12 | Almost all functions (inline versions) and parameters are documented inside related headers.
13 | Complete documentation: TODO. 14 | 15 | ## Features 16 | 17 | - header-only or optional compiled library 18 | - option to store members and arrays as reverse order or normal 19 | - option to separate xml tag prefix 20 | - doesn't alloc memory for keys and values only for tokens 21 | - creates DOM-like data structure to make it easy to iterate though 22 | - simple api 23 | - provides some util functions to print xml, get int32, int64, float, double... 24 | - very small library 25 | - unique way to parse XML (check the object map section) 26 | - helper to get string nodes, primitive values (int, float, bool) for both attribs and values 27 | 28 | ## TODOs 29 | 30 | - [x] provide header only library and optionally compile version 31 | - [x] provide option to preserve array order (currently array order is reversed, because it is easy to parse it in this way; this may be changed. Please follow new commits or releases) 32 | - [x] provide option to separate tag prefixes 33 | - [x] windows build 34 | - [ ] documentation 35 | - [x] handle or ignore comments? (ignored) 36 | - [x] handle or ignore CDATA? (ignored) 37 | - [x] cmake 38 | - [ ] tests 39 | - [ ] extra optimizations 40 | - [ ] usage in detail 41 | 42 | ## Build 43 | 44 | ### Unix (Autotools) 45 | 46 | ```bash 47 | sh autogen.sh 48 | ./configure 49 | make 50 | [sudo] make install 51 | ``` 52 | 53 | you can grap library in .libs folder after build finished 54 | 55 | ### Windows (MSBuild) 56 | 57 | Windows related build files, project files are located in `win` folder, 58 | make sure you are inside `tm/win` folder. 59 | Code Analysis are enabled, it may take awhile to build 60 | 61 | ```Powershell 62 | cd win 63 | .\build.bat 64 | ``` 65 | 66 | ##### Cmake options with Defaults: 67 | 68 | ```CMake 69 | option(XML_SHARED "Shared build" ON) 70 | option(XML_STATIC "Static build" OFF) 71 | option(XML_USE_C99 "" OFF) # C11 72 | option(XML_USE_TEST "Enable Tests" OFF) # for make check - make test 73 | ``` 74 | 75 | ```bash 76 | mkdir build && cd build 77 | cmake .. 78 | make 79 | [sudo] make install 80 | ``` 81 | 82 | ### Header-only or Compiled Library 83 | 84 | The functions has the `xmlc_` prefix are compiled version which is called from library. To use this feature you must include `xml/call/xml.h` header. 85 | 86 | To use header-only library you must include `xml/xml.h` header. The functions has the `xml_` prefix are forced to be inlined. When you use this, you don't have to compile the library. 87 | 88 | todo. 89 | 90 | #### Example usage 91 | 92 | You can inspect `xml_print()` to view usage in more detail. The example will be updated later to give more detail. 93 | 94 | ```C 95 | #include 96 | #include 97 | 98 | int main(int argc, const char * argv[]) { 99 | xml_doc_t *doc; 100 | xml_t *root; 101 | 102 | doc = xml_parse(/* XML string */, XML_DEFAULTS); 103 | root = doc->root; 104 | 105 | xml_print_human(stderr, root); 106 | 107 | xml_free(doc); 108 | 109 | return 0; 110 | } 111 | 112 | ``` 113 | 114 | ```C 115 | 116 | const xml_doc_t *xmlDoc; 117 | const xml_t *xml; 118 | 119 | xmlDoc = xml_parse(/* XML string */, XML_DEFAULTS); 120 | xml = xmlDoc->root->value; 121 | 122 | /* already defined in util.h */ 123 | XML_INLINE 124 | bool 125 | xml_tag_eq(const xml_t * __restrict obj, const char * __restrict str); 126 | 127 | while (xml) { 128 | if (xml_key_eq(xml, "tag 1")) { 129 | int aNumber; 130 | 131 | aNumber = xml_int32(xml, 0); 132 | 133 | /* ... */ 134 | } else if (xml_tag_eq(xml, "tag 2")) { 135 | const char *nonNullTerminatedString; 136 | const char *nullTerminatedString; 137 | 138 | /* just pointer */ 139 | nonNullTerminatedString = xml_string(xml); 140 | 141 | /* null-terminated string (strdup), needs to be freed */ 142 | nullTerminatedString = xml_string_dup(xml); 143 | 144 | /* ... */ 145 | } else if (xml_key_eq(xml, "tag 3")) { 146 | xml_t *aChild; 147 | 148 | aChild = xml->value; 149 | while (aChild) { 150 | /* handle child node */ 151 | aChild = aChild->next; 152 | } 153 | } 154 | 155 | xml = xml->next; 156 | } 157 | ``` 158 | 159 | #### Using Object Map 160 | 161 | ```C 162 | void 163 | callback_1(xml_t * __restrict xml, void * __restrict obj) { 164 | printf("entered callback_1\n"); 165 | } 166 | 167 | xml = xml_parse(/* XML string */, XML_DEFAULTS); 168 | 169 | xml_objmap_t objmap[] = { 170 | { 171 | .key = "key1", 172 | .foundFunc = { 173 | .func = callback_1 174 | } 175 | }, 176 | { 177 | .key = "key2", 178 | .foundFunc = { 179 | .func = callback_1 180 | } 181 | } 182 | }; 183 | 184 | /* or you can use macro helpers which is more readable if you don't need more details: */ 185 | xml_objmap_t objmap[] = { 186 | XML_OBJMAP_FN("key 1", func1, param1), 187 | XML_OBJMAP_FN("key 2", func2, param2), 188 | XML_OBJMAP_FN("key 3", func3, param3), 189 | /* ... */ 190 | }; 191 | 192 | xml_objmap_call(xml, objmap, ARRAY_LEN(objmap), NULL); 193 | ``` 194 | 195 | In this way you don't have to compare keys in a loop, just map the keys with a function or with userdata. You don't have to use function in this way, you may use to map xml object to userdata which may be a GOTO LABEL (to use compound gotos) or something else. 196 | 197 | ## License 198 | 199 | MIT. check the LICENSE file 200 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | image: Visual Studio 2019 2 | 3 | build_script: 4 | - ps: >- 5 | cd win 6 | 7 | .\build.bat 8 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # 3 | # Copyright (c), Recep Aslantas. 4 | # 5 | # MIT License (MIT), http://opensource.org/licenses/MIT 6 | # Full license can be found in the LICENSE file 7 | # 8 | 9 | cd $(dirname "$0") 10 | 11 | autoheader 12 | 13 | if [ "$(uname)" = "Darwin" ]; then 14 | glibtoolize 15 | else 16 | libtoolize 17 | fi 18 | 19 | aclocal -I m4 20 | autoconf 21 | automake --add-missing --copy 22 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | #***************************************************************************** 2 | # Copyright (c), Recep Aslantas. * 3 | # * 4 | # MIT License (MIT), http://opensource.org/licenses/MIT * 5 | # Full license can be found in the LICENSE file * 6 | # * 7 | #***************************************************************************** 8 | 9 | AC_PREREQ([2.69]) 10 | AC_INIT([xml], [0.1.1], [info@recp.me]) 11 | AM_INIT_AUTOMAKE([-Wall foreign subdir-objects]) 12 | 13 | AC_CONFIG_MACRO_DIR([m4]) 14 | AC_CONFIG_SRCDIR([src/]) 15 | AC_CONFIG_HEADERS([config.h]) 16 | 17 | # Checks for programs. 18 | AC_PROG_CC 19 | AM_PROG_CC_C_O 20 | 21 | AC_PROG_INSTALL 22 | AM_PROG_AR 23 | 24 | AC_ENABLE_SHARED 25 | AC_ENABLE_STATIC 26 | 27 | LT_INIT 28 | 29 | # Checks for libraries. 30 | AC_CHECK_LIB([c], [strlen, strncmp, calloc, free]) 31 | 32 | m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) 33 | AC_SYS_LARGEFILE 34 | 35 | # Checks for header files. 36 | AC_CHECK_HEADERS([limits.h \ 37 | stddef.h \ 38 | stdint.h \ 39 | stdlib.h \ 40 | string.h ]) 41 | 42 | # Checks for typedefs, structures, and compiler characteristics. 43 | AC_CHECK_HEADER_STDBOOL 44 | AC_C_INLINE 45 | AC_TYPE_INT32_T 46 | AC_TYPE_INT64_T 47 | AC_TYPE_SIZE_T 48 | AC_TYPE_UINT16_T 49 | AC_TYPE_UINT32_T 50 | AC_TYPE_UINT64_T 51 | AC_TYPE_UINT8_T 52 | 53 | # Checks for library functions. 54 | AC_FUNC_ERROR_AT_LINE 55 | 56 | AC_CONFIG_FILES([makefile]) 57 | 58 | AC_OUTPUT 59 | -------------------------------------------------------------------------------- /include/xml/attrib.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c), Recep Aslantas. 3 | * 4 | * MIT License (MIT), http://opensource.org/licenses/MIT 5 | * Full license can be found in the LICENSE file 6 | */ 7 | 8 | #ifndef xml_attr_h 9 | #define xml_attr_h 10 | 11 | #include "common.h" 12 | 13 | /*! 14 | * @brief creates number (int32) from string value 15 | * 16 | * @param[in] attr xml attribute 17 | * @param[in] defaultValue default value if operation fails 18 | * @return number 19 | */ 20 | XML_INLINE 21 | int32_t 22 | xmla_i32(const xml_attr_t * __restrict attr, int32_t defaultValue) { 23 | if (!attr || !attr->val) 24 | return defaultValue; 25 | 26 | return (int32_t)strtol(attr->val, NULL, 10); 27 | } 28 | 29 | /*! 30 | * @brief creates number (uint32) from string value 31 | * 32 | * @param[in] attr xml attribute 33 | * @param[in] defaultValue default value if operation fails 34 | * @return number 35 | */ 36 | XML_INLINE 37 | uint32_t 38 | xmla_u32(const xml_attr_t * __restrict attr, uint32_t defaultValue) { 39 | if (!attr || !attr->val) 40 | return defaultValue; 41 | 42 | return (uint32_t)strtoul(attr->val, NULL, 10); 43 | } 44 | 45 | /*! 46 | * @brief creates number (int64) from string value 47 | * 48 | * @param[in] attr xml attribute 49 | * @param[in] defaultValue default value if operation fails 50 | * @return number 51 | */ 52 | XML_INLINE 53 | int64_t 54 | xmla_i64(const xml_attr_t * __restrict attr, int64_t defaultValue) { 55 | if (!attr || !attr->val) 56 | return defaultValue; 57 | 58 | return strtoll(attr->val, NULL, 10); 59 | } 60 | 61 | /*! 62 | * @brief creates number (uint64) from string value 63 | * 64 | * @param[in] attr xml attribute 65 | * @param[in] defaultValue default value if operation fails 66 | * @return number 67 | */ 68 | XML_INLINE 69 | uint64_t 70 | xmla_u64(const xml_attr_t * __restrict attr, uint64_t defaultValue) { 71 | if (!attr || !attr->val) 72 | return defaultValue; 73 | 74 | return strtoull(attr->val, NULL, 10); 75 | } 76 | 77 | /*! 78 | * @brief creates number (float) from string value 79 | * 80 | * @param[in] attr xml attribute 81 | * @param[in] defaultValue default value if operation fails 82 | * @return number 83 | */ 84 | XML_INLINE 85 | float 86 | xmla_float(const xml_attr_t * __restrict attr, float defaultValue) { 87 | if (!attr || !attr->val) 88 | return defaultValue; 89 | 90 | return strtof(attr->val, NULL); 91 | } 92 | 93 | /*! 94 | * @brief creates number (double) from string value 95 | * 96 | * @param[in] attr xml attribute 97 | * @param[in] defaultValue default value if operation fails 98 | * @return number 99 | */ 100 | XML_INLINE 101 | double 102 | xmla_double(const xml_attr_t * __restrict attr, double defaultValue) { 103 | if (!attr || !attr->val) 104 | return defaultValue; 105 | 106 | return strtod(attr->val, NULL); 107 | } 108 | 109 | /*! 110 | * @brief creates boolean from string value 111 | * 112 | * @param[in] attr xml attribute 113 | * @param[in] defaultValue default value if operation fails 114 | * @return boolean values as integer: 1 true, 0 false, error: defaultValue 115 | */ 116 | XML_INLINE 117 | int 118 | xmla_bool(const xml_attr_t * __restrict attr, int defaultValue) { 119 | const char *boolString; 120 | char first; 121 | 122 | if (!attr || !(boolString = attr->val)) 123 | return defaultValue; 124 | 125 | first = boolString[0]; 126 | 127 | if (first == 't' || first == '1') { 128 | return 1; 129 | } else if (first == 'n' || first == '0') { 130 | return 0; 131 | } 132 | 133 | return defaultValue; 134 | } 135 | 136 | #endif /* xml_attr_h */ 137 | -------------------------------------------------------------------------------- /include/xml/call/xml.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c), Recep Aslantas. 3 | * 4 | * MIT License (MIT), http://opensource.org/licenses/MIT 5 | * Full license can be found in the LICENSE file 6 | */ 7 | 8 | #ifndef xml_call_xml_h 9 | #define xml_call_xml_h 10 | #ifdef __cplusplus 11 | extern "C" { 12 | #endif 13 | 14 | #include "../common.h" 15 | #include "../xml.h" 16 | 17 | /*! 18 | * @brief parse xml string 19 | * 20 | * this function parses XML string and retuns a document which contains: 21 | * - XML object 22 | * - allocated memory 23 | * after XML is processed, the object must be freed with xml_free() 24 | * 25 | * this library doesn't alloc any memory for XML itsef and doesn't copy XML 26 | * contents into a data structure. It only allocs memory for tokens. 27 | * So don't free 'contents' parameter until you finished to process XML. 28 | * 29 | * Desired order: 30 | * 1. Read XML file 31 | * 2. Pass the contents to xml_parse() 32 | * 3. Process/Handle XML 33 | * 4. free XML document with xml_free() 34 | * 5. free `contents` 35 | * 36 | * @param[in] contents XML string 37 | * @param[in] options options use XML_DEFAULTS or XML_NONE for default 38 | * @return xml document which contains xml object as root object 39 | */ 40 | XML_EXPORT 41 | xml_doc_t* 42 | xmlc_parse(const char * __restrict contents, xml_options_t options); 43 | 44 | /*! 45 | * @brief frees xml document and its allocated memory 46 | */ 47 | XML_EXPORT 48 | void 49 | xmlc_free(xml_doc_t * __restrict xmldoc); 50 | 51 | #ifdef __cplusplus 52 | } 53 | #endif 54 | #endif /* xml_call_xml_h */ 55 | -------------------------------------------------------------------------------- /include/xml/common.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c), Recep Aslantas. 3 | * 4 | * MIT License (MIT), http://opensource.org/licenses/MIT 5 | * Full license can be found in the LICENSE file 6 | */ 7 | 8 | #ifndef xml_common_h 9 | #define xml_common_h 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #if defined(_MSC_VER) 18 | # ifdef XML_DLL 19 | # define XML_EXPORT __declspec(dllexport) 20 | # else 21 | # define XML_EXPORT __declspec(dllimport) 22 | # endif 23 | # define XML_INLINE __forceinline 24 | #else 25 | # define XML_EXPORT __attribute__((visibility("default"))) 26 | # define XML_INLINE static inline __attribute((always_inline)) 27 | #endif 28 | 29 | #define XML_ARR_LEN(ARR) (sizeof(ARR)/sizeof(ARR[0])) 30 | 31 | struct xml_t; 32 | struct xml_attr_t; 33 | 34 | typedef enum xml_type_t { 35 | XML_UNKOWN = 0, 36 | XML_ELEMENT = 1, 37 | XML_STRING = 2 38 | 39 | /* 40 | XML_BOOL = 5, 41 | XML_NUMBER = 4, 42 | 43 | XML_INTEGER = 6, 44 | XML_FLOAT = 7, 45 | XML_NULL = 8 46 | */ 47 | } xml_type_t; 48 | 49 | typedef struct xml_attr_t { 50 | struct xml_attr_t *next; 51 | const char *name; 52 | const char *val; 53 | uint16_t valsize; 54 | uint16_t namesize; 55 | uint8_t namequote; 56 | uint8_t valquote; 57 | } xml_attr_t; 58 | 59 | typedef struct xml_t { 60 | struct xml_t *parent; 61 | struct xml_t *next; 62 | struct xml_attr_t *attr; 63 | const char *prefix; 64 | const char *tag; 65 | void *val; 66 | uint32_t valsize; 67 | uint16_t tagsize; 68 | uint16_t prefixsize; 69 | xml_type_t type:16; 70 | bool readonly:1; 71 | bool reverse:1; 72 | } xml_t; 73 | 74 | typedef struct xml_doc_t { 75 | void *memroot; 76 | xml_t *root; 77 | const char *ptr; 78 | bool readonly:1; 79 | bool reverse:1; 80 | bool sepPrefixes:1; 81 | } xml_doc_t; 82 | 83 | #endif /* xml_common_h */ 84 | -------------------------------------------------------------------------------- /include/xml/impl/impl_common.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c), Recep Aslantas. 3 | * 4 | * MIT License (MIT), http://opensource.org/licenses/MIT 5 | * Full license can be found in the LICENSE file 6 | */ 7 | 8 | #ifndef xml_impl_common_h 9 | #define xml_impl_common_h 10 | 11 | #include "../xml.h" 12 | #include "impl_mem.h" 13 | 14 | XML_INLINE 15 | void 16 | xml_free(xml_doc_t * __restrict doc) { 17 | xml_mem_t *mem, *tofree; 18 | 19 | mem = doc->memroot; 20 | while (mem) { 21 | tofree = mem; 22 | mem = mem->next; 23 | free(tofree); 24 | } 25 | 26 | free(doc); 27 | } 28 | 29 | XML_INLINE 30 | xml_attr_t* 31 | xmla(const xml_t * __restrict object, const char * __restrict name) { 32 | xml_attr_t *iter; 33 | size_t namesize; 34 | 35 | if (!object || !name || !(iter = object->attr)) 36 | return NULL; 37 | 38 | namesize = strlen(name); 39 | while (iter && strncmp(iter->name, name, namesize) != 0) 40 | iter = iter->next; 41 | 42 | return iter; 43 | } 44 | 45 | XML_INLINE 46 | xml_t* 47 | xml_elem(const xml_t * __restrict object, const char * __restrict name) { 48 | xml_t *iter; 49 | size_t namesize; 50 | 51 | if (!object || !name || object->type != XML_ELEMENT || !(iter = object->val)) 52 | return NULL; 53 | 54 | namesize = strlen(name); 55 | while (iter && strncmp(iter->tag, name, namesize) != 0) 56 | iter = iter->next; 57 | 58 | return iter; 59 | } 60 | 61 | XML_INLINE 62 | xml_t* 63 | xml_elem_next(const xml_t * __restrict current, const char * __restrict name) { 64 | xml_t *iter; 65 | size_t namesize; 66 | 67 | if (!current || !name || !(iter = current->next)) 68 | return NULL; 69 | 70 | namesize = strlen(name); 71 | while (iter && strncmp(iter->tag, name, namesize) != 0) 72 | iter = iter->next; 73 | 74 | return iter; 75 | } 76 | 77 | #endif /* xml_impl_common_h */ 78 | -------------------------------------------------------------------------------- /include/xml/impl/impl_mem.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c), Recep Aslantas. 3 | * 4 | * MIT License (MIT), http://opensource.org/licenses/MIT 5 | * Full license can be found in the LICENSE file 6 | */ 7 | 8 | #ifndef xml_impl_mem_h 9 | #define xml_impl_mem_h 10 | 11 | #include "../common.h" 12 | #include 13 | #include 14 | 15 | typedef struct xml_mem_t { 16 | struct xml_mem_t *next; 17 | size_t size; 18 | size_t capacity; 19 | char data[]; 20 | } xml_mem_t; 21 | 22 | #define XML_MEM_PAGE (32768 - sizeof(xml_mem_t)) /* 32K */ 23 | 24 | XML_INLINE 25 | void* 26 | xml__impl_calloc(xml_doc_t * __restrict doc, size_t size) { 27 | void *data; 28 | xml_mem_t *mem; 29 | size_t sizeToAlloc; 30 | 31 | mem = doc->memroot; 32 | sizeToAlloc = (XML_MEM_PAGE < size) ? size : XML_MEM_PAGE; 33 | 34 | if (mem->capacity < (mem->size + size)) { 35 | mem = calloc(1, sizeof(*mem) + sizeToAlloc); 36 | mem->capacity = sizeToAlloc; 37 | mem->next = doc->memroot; 38 | doc->memroot = mem; 39 | } 40 | 41 | data = (char *)mem->data + mem->size; 42 | mem->size += size; 43 | 44 | return data; 45 | } 46 | 47 | #endif /* xml_impl_mem_h */ 48 | -------------------------------------------------------------------------------- /include/xml/impl/impl_objmap.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c), Recep Aslantas. 3 | * 4 | * MIT License (MIT), http://opensource.org/licenses/MIT 5 | * Full license can be found in the LICENSE file 6 | */ 7 | 8 | #ifndef xml_impl_objmap_h 9 | #define xml_impl_objmap_h 10 | 11 | #include "../xml.h" 12 | #include "impl_mem.h" 13 | #include "../util.h" 14 | 15 | XML_INLINE 16 | void 17 | xml_objmap(xml_t * __restrict obj, 18 | xml_objmap_t * __restrict objmap, 19 | size_t count) { 20 | xml_objmap_t *item; 21 | size_t start, end, i; 22 | 23 | if (!obj || obj->type != XML_ELEMENT || !(obj = obj->val)) 24 | return; 25 | 26 | start = 0; 27 | end = count; 28 | 29 | while (obj) { 30 | for (i = start; i < end; i++) { 31 | item = &objmap[i]; 32 | 33 | if (xml_tag_eq(obj, item->key)) { 34 | item->object = obj; 35 | if (i == start) 36 | start++; 37 | else if (i == end - 1) 38 | end--; 39 | 40 | /* duplicated keys are not allowed for now */ 41 | break; 42 | } 43 | } 44 | obj = obj->next; 45 | } 46 | } 47 | 48 | XML_INLINE 49 | void 50 | xml_objmap_call(xml_t * __restrict obj, 51 | xml_objmap_t * __restrict objmap, 52 | size_t count, 53 | bool * __restrict stop) { 54 | xml_objmap_t *item; 55 | size_t i; 56 | 57 | if (!obj || obj->type != XML_ELEMENT) 58 | return; 59 | 60 | xml_objmap(obj, objmap, count); 61 | 62 | for (i = 0; i < count; i++) { 63 | if (stop && *stop) 64 | break; 65 | 66 | item = &objmap[i]; 67 | if (item->object) { 68 | if (item->foundFunc.func) 69 | item->foundFunc.func(item->object, item->foundFunc.param); 70 | } else if (item->notFoundFunc.func) { 71 | item->notFoundFunc.func(item->object, item->notFoundFunc.param); 72 | } 73 | } 74 | } 75 | 76 | #endif /* xml_impl_objmap_h */ 77 | -------------------------------------------------------------------------------- /include/xml/impl/impl_parse.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c), Recep Aslantas. 3 | * 4 | * MIT License (MIT), http://opensource.org/licenses/MIT 5 | * Full license can be found in the LICENSE file 6 | */ 7 | 8 | #ifndef xml_impl_parse_h 9 | #define xml_impl_parse_h 10 | 11 | #include "../xml.h" 12 | #include "impl_mem.h" 13 | 14 | typedef enum xml_position { 15 | unknown = 0, 16 | begintag = 1, 17 | endtag = 2, 18 | beginel = 3, 19 | endel = 4, 20 | beginattr = 5 21 | } xml_position; 22 | 23 | XML_INLINE 24 | xml_doc_t* 25 | xml_parse(const char * __restrict contents, xml_options_t options) { 26 | xml_doc_t *doc; 27 | xml_t *obj, *parent, *val; 28 | xml_attr_t *attr; 29 | const char *tag, *s; 30 | xml_t tmproot; 31 | xml_position pos; 32 | char *p, *end, c, quote; 33 | bool foundQuote, reverse, sepPrefixes, readonly; 34 | 35 | if (!contents || (c = *contents) == '\0') 36 | return NULL; 37 | 38 | doc = calloc(1, sizeof(*doc)); 39 | doc->memroot = calloc(1, sizeof(xml_mem_t) + XML_MEM_PAGE); 40 | p = (char *)contents; 41 | 42 | memset(&tmproot, 0, sizeof(tmproot)); 43 | tmproot.type = XML_ELEMENT; 44 | 45 | s = NULL; 46 | attr = NULL; 47 | tag = NULL; 48 | parent = &tmproot; 49 | obj = parent; 50 | pos = unknown; 51 | quote = '"'; 52 | 53 | reverse = options & XML_REVERSE; 54 | sepPrefixes = options & XML_PREFIXES; 55 | readonly = options & XML_READONLY; 56 | 57 | doc->reverse = reverse; 58 | doc->readonly = readonly; 59 | doc->sepPrefixes = sepPrefixes; 60 | 61 | tmproot.readonly = readonly; 62 | tmproot.reverse = reverse; 63 | 64 | ((xml_mem_t *)doc->memroot)->capacity = XML_MEM_PAGE; 65 | 66 | do { 67 | again: 68 | /* child */ 69 | switch (c) { 70 | /* trim */ 71 | case ' ': 72 | case '\r': 73 | case '\n': 74 | case '\t': 75 | break; 76 | case '<': { /* TODO: what if we get << or <