diff options
author | Muhammad Moinur Rahman <bofh@FreeBSD.org> | 2025-05-22 13:42:38 +0000 |
---|---|---|
committer | Muhammad Moinur Rahman <bofh@FreeBSD.org> | 2025-05-22 13:42:38 +0000 |
commit | 1e2f270469c61337ef7f5f92ab93f691e5d86492 (patch) | |
tree | 92f1d8dc1abac73131ddeaa9867d4ef3d36ab1da | |
parent | 76f58d4432898b68da2a65237ed6089d0ef90653 (diff) |
libucl: Update to 0.9.2vendor/libucl/0.9.2vendor/libucl
Approved by: bapt
Differential Revision: https://reviews.freebsd.org/D50363
-rw-r--r-- | .github/workflows/cmake-multi-platform.yml | 75 | ||||
-rw-r--r-- | .github/workflows/makefile.yml | 30 | ||||
-rw-r--r-- | .gitignore | 35 | ||||
-rw-r--r-- | CMakeLists.txt | 27 | ||||
-rw-r--r-- | ChangeLog.md | 140 | ||||
-rw-r--r-- | Makefile.am | 2 | ||||
-rw-r--r-- | Makefile.unix | 4 | ||||
-rw-r--r-- | README.md | 23 | ||||
-rw-r--r-- | configure.ac | 6 | ||||
-rw-r--r-- | include/ucl.h | 4 | ||||
-rw-r--r-- | libucl.pc | 11 | ||||
-rw-r--r-- | lua/lua_ucl.c | 83 | ||||
-rw-r--r-- | m4/.gitignore | 4 | ||||
-rw-r--r-- | python/setup.py | 4 | ||||
-rw-r--r-- | src/ucl_emitter.c | 80 | ||||
-rw-r--r-- | src/ucl_emitter_streamline.c | 9 | ||||
-rw-r--r-- | src/ucl_hash.c | 146 | ||||
-rw-r--r-- | src/ucl_parser.c | 214 | ||||
-rw-r--r-- | src/ucl_schema.c | 1 | ||||
-rw-r--r-- | src/ucl_util.c | 87 | ||||
-rw-r--r-- | tests/schema/definitions.json | 32 | ||||
-rw-r--r-- | tests/schema/ref.json | 16 | ||||
-rw-r--r-- | tests/schema/refRemote.json | 76 | ||||
-rw-r--r-- | tests/test_speed.c | 2 | ||||
-rw-r--r-- | tests/test_streamline.c | 43 | ||||
-rw-r--r-- | uthash/utlist.h | 749 |
26 files changed, 1301 insertions, 602 deletions
diff --git a/.github/workflows/cmake-multi-platform.yml b/.github/workflows/cmake-multi-platform.yml new file mode 100644 index 000000000000..9ec0432eeb03 --- /dev/null +++ b/.github/workflows/cmake-multi-platform.yml @@ -0,0 +1,75 @@ +# This starter workflow is for a CMake project running on multiple platforms. There is a different starter workflow if you just want a single platform. +# See: https://github.com/actions/starter-workflows/blob/main/ci/cmake-single-platform.yml +name: CMake on multiple platforms + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +jobs: + build: + runs-on: ${{ matrix.os }} + + strategy: + # Set fail-fast to false to ensure that feedback is delivered for all matrix combinations. Consider changing this to true when your workflow is stable. + fail-fast: false + + # Set up a matrix to run the following 3 configurations: + # 1. <Windows, Release, latest MSVC compiler toolchain on the default runner image, default generator> + # 2. <Linux, Release, latest GCC compiler toolchain on the default runner image, default generator> + # 3. <Linux, Release, latest Clang compiler toolchain on the default runner image, default generator> + # + # To add more build types (Release, Debug, RelWithDebInfo, etc.) customize the build_type list. + matrix: + os: [ubuntu-latest, windows-latest] + build_type: [Release] + c_compiler: [gcc, clang, cl] + include: + - os: windows-latest + c_compiler: cl + cpp_compiler: cl + - os: ubuntu-latest + c_compiler: gcc + cpp_compiler: g++ + - os: ubuntu-latest + c_compiler: clang + cpp_compiler: clang++ + exclude: + - os: windows-latest + c_compiler: gcc + - os: windows-latest + c_compiler: clang + - os: ubuntu-latest + c_compiler: cl + + steps: + - uses: actions/checkout@v3 + + - name: Set reusable strings + # Turn repeated input strings (such as the build output directory) into step outputs. These step outputs can be used throughout the workflow file. + id: strings + shell: bash + run: | + echo "build-output-dir=${{ github.workspace }}/build" >> "$GITHUB_OUTPUT" + + - name: Configure CMake + # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. + # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type + run: > + cmake -B ${{ steps.strings.outputs.build-output-dir }} + -DCMAKE_CXX_COMPILER=${{ matrix.cpp_compiler }} + -DCMAKE_C_COMPILER=${{ matrix.c_compiler }} + -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} + -S ${{ github.workspace }} + + - name: Build + # Build your program with the given configuration. Note that --config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). + run: cmake --build ${{ steps.strings.outputs.build-output-dir }} --config ${{ matrix.build_type }} + + - name: Test + working-directory: ${{ steps.strings.outputs.build-output-dir }} + # Execute tests defined by the CMake configuration. Note that --build-config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). + # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail + run: ctest --build-config ${{ matrix.build_type }} diff --git a/.github/workflows/makefile.yml b/.github/workflows/makefile.yml new file mode 100644 index 000000000000..ddb16b7880b2 --- /dev/null +++ b/.github/workflows/makefile.yml @@ -0,0 +1,30 @@ +name: Makefile CI + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - name: Install Lua + run: sudo apt-get install -y liblua5.4-dev lua5.4 + + - uses: actions/checkout@v3 + + - name: configure + run: ./autogen.sh && ./configure --enable-lua + + - name: Install dependencies + run: make + + - name: Run check + run: make check + + - name: Run distcheck + run: make distcheck diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000000..3887386865cc --- /dev/null +++ b/.gitignore @@ -0,0 +1,35 @@ +.cproject +.project +.settings + +# Auto*/libtool +Makefile +Makefile.in +/aclocal.m4 +/autom4te.cache/ +/config.* +/configure +/depcomp +/install-sh +/libtool +/ltmain.sh +/missing +/stamp-h* +/ar-lib +/test-driver +/compile +.deps/ +.dirstamp +.libs/ +*.l[ao] +*~ +*.o + +# pkgconf +libucl.pc + +# python extension +python/build + +# Default CMake build directory +/build/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 5fe772a30f88..3d73910c887a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,9 +1,9 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 3.1.0 FATAL_ERROR) PROJECT(libucl C) -CMAKE_MINIMUM_REQUIRED(VERSION 2.6.0 FATAL_ERROR) SET(LIBUCL_VERSION_MAJOR 0) -SET(LIBUCL_VERSION_MINOR 5) -SET(LIBUCL_VERSION_PATCH 0) +SET(LIBUCL_VERSION_MINOR 9) +SET(LIBUCL_VERSION_PATCH 2) SET(LIBUCL_VERSION "${LIBUCL_VERSION_MAJOR}.${LIBUCL_VERSION_MINOR}.${LIBUCL_VERSION_PATCH}") @@ -237,12 +237,13 @@ ADD_LIBRARY(ucl ${LIB_TYPE} ${UCLSRC}) ADD_LIBRARY(ucl::ucl ALIAS ucl) SET_TARGET_PROPERTIES(ucl PROPERTIES VERSION ${LIBUCL_VERSION} SOVERSION ${LIBUCL_VERSION_MAJOR}) TARGET_INCLUDE_DIRECTORIES(ucl - PUBLIC - include - PRIVATE - src - uthash - klib) + PUBLIC + $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> + $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}> + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/src + ${CMAKE_CURRENT_SOURCE_DIR}/uthash + ${CMAKE_CURRENT_SOURCE_DIR}/klib) TARGET_COMPILE_DEFINITIONS(ucl PRIVATE ${UCL_COMPILE_DEFS} @@ -305,10 +306,16 @@ ENDIF(UNIX) SET_TARGET_PROPERTIES(ucl PROPERTIES PUBLIC_HEADER "${UCLHDR}") -INSTALL(TARGETS ucl DESTINATION ${CMAKE_INSTALL_LIBDIR} +INSTALL(TARGETS ucl EXPORT uclConfig DESTINATION ${CMAKE_INSTALL_LIBDIR} PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) IF(ENABLE_UTILS MATCHES "ON") ADD_SUBDIRECTORY(utils) ENDIF() +install(EXPORT uclConfig + FILE ucl-config.cmake + NAMESPACE ucl:: + DESTINATION "share/ucl" +) + diff --git a/ChangeLog.md b/ChangeLog.md index cba29aa9a7b5..b32b9e08d019 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,5 +1,21 @@ # Version history +## Libucl 0.9.0 + +* 803b588 Breaking: Try to fix streamline embedding +* 9eddef0 Fix: set p to endptr before checking +* 25d3f51 Fix broken tests +* ac644e2 Update makefile.yml +* 0a5739e Create makefile.yml +* 987389a Merge branch 'master' into vstakhov-gh-actions +* 7433904 Import lua code from Rspamd +* 3912614 Create cmake-multi-platform.yml +* 3a04c92 lua: Push string with len +* 2fefed6 Use `_WIN32` instead of `_MSC_VER` +* aecf17e Avoid build failure trying to create setup.py link if it already exists. +* 4ef9e6d Add inttypes.h for PRId64 +* dcb43f0 Fix excessive escaping when using ucl_object_fromstring() + ## Libucl 0.5 - Streamline emitter has been added, so it is now possible to output partial `ucl` objects @@ -100,4 +116,126 @@ - Performance improvements in Lua API - Allow to pass opaque objects in Lua API for transparent C passthrough - Various bugs fixed -- Couple of memory leaks plugged
\ No newline at end of file +- Couple of memory leaks plugged + +### Libucl 0.8.2 + +* .include: also validate priority to be within range +* Add -W into list of warnings +* Add ability to add file preprocessors +* Add ability to pass both the parser and userdata into a macro handler +* Add missing tests for .gitignore +* Add more safe guards when trying to insert objects +* Add some documentation/example about the .priority macro +* Add tests for single quotes +* Added CMake compile definitions +* Added CMake support to build utils +* Added a fuzzer for OSS-fuzz integration +* Added a return statement if the string is 0 +* Added default CMake "build" directory to gitignore +* Added fuzzer for msgpack +* Adding another fix +* Adjust example. +* Allow to test msgpack inputs +* Another sync +* Assume gcov absense as a non-fatal error +* Avoid read when a chunk is ended +* CMake: Install headers and library. +* Check for NULL inputs in ucl_object_compare() +* Cleanup CURL handle after use +* Cleanup CURL handle after use +* Convert ucl_hash_insert() from returning int to returning bool. +* Convert ucl_hash_reserve() from returning int to bool. +* Do not try to emit single quoted strings in json mode +* Document single quotes +* Document ucl_object_iter_chk_excpn(). +* Document usage of ucl_object_iter_chk_excpn(). +* Don't double-escape Lua strings +* Excercise ucl_object_iter_chk_excpn(). +* Fix '\v' encoding +* Fix 68d87c362b0d7fbb45f395bfae616a28439e0bbc by setting error to 0 always. Which makes it even uglier. +* Fix cmake public include install +* Fix emitting of the bad unicode escapes +* Fix format strings, add printf attribute to schema functions +* Fix levels and objects closing +* Fix load macro with try=true +* Fix mismerge. +* Fix mismerge. +* Fix old issue with parsing numbers +* Fix processing of the incomplete msgpack objects +* Fix remain calculations +* Fix remain lenght calculation that led to assertion failure +* Fix single quotes emitting +* Fix spelling and markup errors. +* Fix typos: replace missmatch with mismatch +* Fix ucl++ bug where iterators stop on a null field. +* Fix ucl_util.c not having the prototype for ucl_hash_sort() +* Fix variables expansion +* Fix vertical tab handling +* Fixed Visual Studio compilation error +* Fixed expanding variables at runtime +* Fixed linker error +* Fixed ucl_tool's command line argument parsing +* Fixing error with installing using pip from git with following command: 'pip install -e git+https://github.com/vstakhov/libucl.git/#egg=ucl +* Forgot hash sort function +* Improve ENOMEM handling: handle most of errors while consuructing parser, also extend iterator routines to allow capturing such exception and checking it in the higher level code using new ucl_object_iter_chk_excpn() API. +* Mark + as unsafe which fixes export a key with + in config mode +* Modernise the CMake build system slightly. +* Modernize CMake file with target-based includes. +* Pass correct pointer to var_handler +* Port util objdump to Windows (Visual Studio) +* Port util ucl-tool to Windows +* Provide inline free(3) wrapper, so it's easier to plug the code into out memory usage tracking framework. +* Provide inline free(3) wrapper, so it's easier to plug the code into out memory usage tracking framework. +* Provide priority validation for the .priority macro +* Put space between "exit" and (). +* Put space between name of teh function and (). +* Python build fixes +* Read data in chunks +* Remove leak in the test +* Remove one more bit of unused logic +* Remove one more stupid assertion +* Remove unnecessary (and ignored) `const` from return types. +* Remove unnecessary std::move from return statement. +* Remove unused CMake logic and ad -Wno-pointer-sign. +* Removed dependency from rspamd CMake file +* Removed null-terminator for input data +* Rename ENOMEM-safe version of kv_xxx macros from kv_xxx into kv_xxx_safe and put back old version as well (with a big warning in the header file) for a compat purposes. +* Renamed util binaries to match autotools +* Replace *neat* and *tidy* implementation of kv_xxx() macros using error handling labels with a much *uglier* implementation using "error code pointer". One man's "ugly" is other man's "pretty", I suppose. +* Replaced spaces by tabs to match coding style +* Rework hash table structure to provide pointers and order safety +* Save chunk in the parser stack +* Save filename in chunk +* Split level and flags, add obrace flag, fix msgpack flags +* Squelch incompatible pointer type warning +* Support single quoted strings +* Suppress the [-Wunused-parameter] warning. +* Sync changes from Rspamd +* Sync changes from rspamd +* Sync with Rspamd +* Understand nan and inf +* Use safe iterator - avoid leaking memory. +* docs: fix simple typo, tectual -> textual +* fix: Changed OpenSSL check inside configure.am +* fix: Incorrect pointer arithmetics in ucl_expand_single_variable +* fix: ucl_expand_single_variable doesn't call free +* lua: Return early when init fails +* make use of the undocumented flag UCL_PARSER_NO_IMPLICIT_ARRAYS, so that multiple keys are treated as arrays, and special code doesn't have to be added to the Python module to handle it. +* mypy/stubgen: add typeinterfaces for ucl python module +* o `ucl_object_iterate2()` -> `ucl_object_iterate_with_error()`; +* python: update package to 0.8.1 +* `ucl_check_variable`: fix out_len on unterminated variable +* `ucl_chunk_skipc`: avoid out-of-bounds read +* `ucl_expand_single_variable`: better bounds check +* `ucl_expand_variable`: fix out-of-bounds read +* `ucl_inherit_handler`: fix format string for non-null-terminated strings +* `ucl_lc_cmp` is not used outside ucl_hash.c +* `ucl_lex_json_string`: fix out-of-bounds read +* `ucl_maybe_parse_number`: if there is trailing content, it is not a number +* `ucl_object_copy_internal`: null terminate keys +* `ucl_object_copy_internal`: use memcpy instead of strdup +* `ucl_object_free` is deprecated +* `ucl_parse_value`: fix out-of-bounds read +* `ucl_strnstr`: fix out-of-bounds read +* update JSON example to match w/ UCL example diff --git a/Makefile.am b/Makefile.am index 5b51bcc3b468..090542d14e8b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -69,7 +69,7 @@ clean-coverage-report: -rm -rf $(COVERAGE_REPORT_DIR) clean-coverage: clean-coverage-report - -$(LCOV) --gcov-tool $(GCOV) --zerocounters --directory $(top_builddir) + -$(LCOV) --gcov-tool $(GCOV) --zerocounters --directory $(top_builddir) || true @if xargs --version 2>/dev/null; then \ find $(top_builddir) -name "*.gcno" | xargs --no-run-if-empty rm; \ else \ diff --git a/Makefile.unix b/Makefile.unix index 0653d4843f7e..dbb8f4cb5a90 100644 --- a/Makefile.unix +++ b/Makefile.unix @@ -3,8 +3,8 @@ DESTDIR ?= /usr/local LD ?= gcc C_COMMON_FLAGS ?= -fPIC -Wall -W -Wno-unused-parameter -Wno-pointer-sign -I./include -I./uthash -I./src -I./klib MAJOR_VERSION = 0 -MINOR_VERSION = 2 -PATCH_VERSION = 9 +MINOR_VERSION = 9 +PATCH_VERSION = 2 VERSION = "$(MAJOR_VERSION).$(MINOR_VERSION).$(PATCH_VERSION)" SONAME = libucl.so SONAME_FULL = $(SONAME).$(MAJOR_VERSION) diff --git a/README.md b/README.md index 53d8a651d73b..321db170db88 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,4 @@ -# LIBUCL - -[](https://circleci.com/gh/vstakhov/libucl) -[](https://scan.coverity.com/projects/4138) -[](https://coveralls.io/github/vstakhov/libucl?branch=master) +# UCL - Universal Configuration Language **Table of Contents** *generated with [DocToc](http://doctoc.herokuapp.com/)* @@ -26,15 +22,19 @@ ## Introduction -This document describes the main features and principles of the configuration -language called `UCL` - universal configuration language. +This repository provides the `C` library for parsing configurations written in `UCL` - universal configuration language. It also provides functions to operate with other formats: + +* `JSON`: read, write and pretty format +* `Messagepack`: read and write +* `S-Expressions`: read only (canonical form) +* `Yaml`: limited write support (mainly for compatibility) If you are looking for the libucl API documentation you can find it at [this page](doc/api.md). ## Basic structure -UCL is heavily infused by `nginx` configuration as the example of a convenient configuration -system. However, UCL is fully compatible with `JSON` format and is able to parse json files. +`UCL` is heavily infused by `nginx` configuration as the example of a convenient configuration +system. However, `UCL` is fully compatible with `JSON` format and is able to parse json files. For example, you can write the same configuration in the following ways: * in nginx like: @@ -234,7 +234,7 @@ UCL supports external macros both multiline and single line ones: ``` Moreover, each macro can accept an optional list of arguments in braces. These -arguments themselves are the UCL object that is parsed and passed to a macro as +arguments themselves are the `UCL` object that is parsed and passed to a macro as options: ```nginx @@ -374,7 +374,8 @@ Each UCL object can be serialized to one of the four supported formats: * `JSON` - canonic json notation (with spaces indented structure); * `Compacted JSON` - compact json notation (without spaces or newlines); * `Configuration` - nginx like notation; -* `YAML` - yaml inlined notation. +* `YAML` - yaml inlined notation; +* `messagepack` - MessagePack binary format. ## Validation diff --git a/configure.ac b/configure.ac index 731b7113e689..2e8cbabded3b 100644 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,7 @@ m4_define([maj_ver], [0]) -m4_define([med_ver], [8]) -m4_define([min_ver], [1]) -m4_define([so_version], [6:0:1]) +m4_define([med_ver], [9]) +m4_define([min_ver], [2]) +m4_define([so_version], [9:0:2]) m4_define([ucl_version], [maj_ver.med_ver.min_ver]) AC_INIT([libucl],[ucl_version],[https://github.com/vstakhov/libucl],[libucl]) diff --git a/include/ucl.h b/include/ucl.h index 39da2593648d..b8625b9fce2f 100644 --- a/include/ucl.h +++ b/include/ucl.h @@ -1411,13 +1411,13 @@ struct ucl_emitter_operations { const ucl_object_t *obj, bool first, bool print_key); /** Start ucl object */ void (*ucl_emitter_start_object) (struct ucl_emitter_context *ctx, - const ucl_object_t *obj, bool print_key); + const ucl_object_t *obj, bool first, bool print_key); /** End ucl object */ void (*ucl_emitter_end_object) (struct ucl_emitter_context *ctx, const ucl_object_t *obj); /** Start ucl array */ void (*ucl_emitter_start_array) (struct ucl_emitter_context *ctx, - const ucl_object_t *obj, bool print_key); + const ucl_object_t *obj, bool first, bool print_key); void (*ucl_emitter_end_array) (struct ucl_emitter_context *ctx, const ucl_object_t *obj); }; diff --git a/libucl.pc b/libucl.pc deleted file mode 100644 index 4878bebafcdd..000000000000 --- a/libucl.pc +++ /dev/null @@ -1,11 +0,0 @@ -prefix=/usr/local -exec_prefix=${prefix} -libdir=${exec_prefix}/lib -includedir=${prefix}/include - -Name: LibUCL -Description: Universal configuration library -Version: 0.9.0 -Libs: -L${libdir} -lucl -Libs.private: -Cflags: -I${includedir}/ diff --git a/lua/lua_ucl.c b/lua/lua_ucl.c index b34fd56878b8..d6be69e42a71 100644 --- a/lua/lua_ucl.c +++ b/lua/lua_ucl.c @@ -82,6 +82,11 @@ static ucl_object_t* ucl_object_lua_fromelt (lua_State *L, int idx, ucl_string_f static void *ucl_null; +struct _rspamd_lua_text { + const char *start; + unsigned int len; + unsigned int flags; +}; enum lua_ucl_push_flags { LUA_UCL_DEFAULT_FLAGS = 0, @@ -240,7 +245,7 @@ ucl_object_lua_push_scalar (lua_State *L, const ucl_object_t *obj, lua_pushboolean (L, ucl_obj_toboolean (obj)); break; case UCL_STRING: - lua_pushstring (L, ucl_obj_tostring (obj)); + lua_pushlstring (L, ucl_obj_tostring (obj), obj->len); break; case UCL_INT: #if LUA_VERSION_NUM >= 501 @@ -401,7 +406,6 @@ ucl_object_lua_fromtable (lua_State *L, int idx, ucl_string_flags_t flags) /* Table iterate */ if (is_array) { - int i; if (!is_implicit) { top = ucl_object_typed_new (UCL_ARRAY); @@ -411,7 +415,7 @@ ucl_object_lua_fromtable (lua_State *L, int idx, ucl_string_flags_t flags) top = NULL; } - for (i = 1; i <= max; i ++) { + for (size_t i = 1; i <= max; i ++) { lua_pushinteger (L, i); lua_gettable (L, idx); @@ -479,7 +483,16 @@ ucl_object_lua_fromelt (lua_State *L, int idx, ucl_string_flags_t flags) str = lua_tolstring (L, idx, &sz); if (str) { - obj = ucl_object_fromstring_common (str, sz, flags); + /* + * ucl_object_fromstring_common has a `logic` to use strlen if sz is zero + * which is totally broken... + */ + if (sz > 0) { + obj = ucl_object_fromstring_common(str, sz, flags); + } + else { + obj = ucl_object_fromstring_common("", sz, flags); + } } else { obj = ucl_object_typed_new (UCL_NULL); @@ -501,6 +514,24 @@ ucl_object_lua_fromelt (lua_State *L, int idx, ucl_string_flags_t flags) if (lua_topointer (L, idx) == ucl_null) { obj = ucl_object_typed_new (UCL_NULL); } + else { + /* Assume it is a text like object */ + struct _rspamd_lua_text *t = lua_touserdata (L, idx); + + if (t) { + if (t->len >0) { + obj = ucl_object_fromstring_common(t->start, t->len, 0); + } + else { + obj = ucl_object_fromstring_common("", 0, 0); + } + + /* Binary text */ + if (t->flags & (1u << 5u)) { + obj->flags |= UCL_OBJECT_BINARY; + } + } + } break; case LUA_TTABLE: case LUA_TFUNCTION: @@ -556,10 +587,10 @@ ucl_object_lua_import (lua_State *L, int idx) t = lua_type (L, idx); switch (t) { case LUA_TTABLE: - obj = ucl_object_lua_fromtable (L, idx, 0); + obj = ucl_object_lua_fromtable (L, idx, UCL_STRING_RAW); break; default: - obj = ucl_object_lua_fromelt (L, idx, 0); + obj = ucl_object_lua_fromelt (L, idx, UCL_STRING_RAW); break; } @@ -584,10 +615,10 @@ ucl_object_lua_import_escape (lua_State *L, int idx) t = lua_type (L, idx); switch (t) { case LUA_TTABLE: - obj = ucl_object_lua_fromtable (L, idx, UCL_STRING_RAW); + obj = ucl_object_lua_fromtable (L, idx, UCL_STRING_ESCAPE); break; default: - obj = ucl_object_lua_fromelt (L, idx, UCL_STRING_RAW); + obj = ucl_object_lua_fromelt (L, idx, UCL_STRING_ESCAPE); break; } @@ -598,11 +629,12 @@ static int lua_ucl_to_string (lua_State *L, const ucl_object_t *obj, enum ucl_emitter type) { unsigned char *result; + size_t outlen; - result = ucl_object_emit (obj, type); + result = ucl_object_emit_len (obj, type, &outlen); if (result != NULL) { - lua_pushstring (L, (const char *)result); + lua_pushlstring (L, (const char *)result, outlen); free (result); } else { @@ -625,7 +657,6 @@ lua_ucl_parser_init (lua_State *L) parser = ucl_parser_new (flags); if (parser == NULL) { lua_pushnil (L); - return 1; } pparser = lua_newuserdata (L, sizeof (parser)); @@ -834,12 +865,6 @@ lua_ucl_parser_parse_string (lua_State *L) return ret; } -struct _rspamd_lua_text { - const char *start; - unsigned int len; - unsigned int flags; -}; - /*** * @method parser:parse_text(input) * Parse UCL object from text object (Rspamd specific). @@ -855,7 +880,24 @@ lua_ucl_parser_parse_text (lua_State *L) int ret = 2; parser = lua_ucl_parser_get (L, 1); - t = lua_touserdata (L, 2); + + if (lua_type (L, 2) == LUA_TUSERDATA) { + t = lua_touserdata (L, 2); + } + else if (lua_type (L, 2) == LUA_TSTRING) { + const char *s; + size_t len; + static struct _rspamd_lua_text st_t; + + s = lua_tolstring (L, 2, &len); + st_t.start = s; + st_t.len = len; + + t = &st_t; + } + else { + return luaL_error(L, "invalid argument as input, expected userdata or a string"); + } if (lua_type (L, 3) == LUA_TSTRING) { type = lua_ucl_str_to_parse_type (lua_tostring (L, 3)); @@ -1426,10 +1468,11 @@ lua_ucl_to_format (lua_State *L) format = UCL_EMIT_YAML; } else if (strcasecmp (strtype, "config") == 0 || - strcasecmp (strtype, "ucl") == 0) { + strcasecmp (strtype, "ucl") == 0) { format = UCL_EMIT_CONFIG; } - else if (strcasecmp (strtype, "msgpack") == 0) { + else if (strcasecmp (strtype, "msgpack") == 0 || + strcasecmp (strtype, "messagepack") == 0) { format = UCL_EMIT_MSGPACK; } } diff --git a/m4/.gitignore b/m4/.gitignore new file mode 100644 index 000000000000..5e7d2734cfc6 --- /dev/null +++ b/m4/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore diff --git a/python/setup.py b/python/setup.py index 8da832bac381..c30395d97b5a 100644 --- a/python/setup.py +++ b/python/setup.py @@ -41,12 +41,12 @@ template = 'python/MANIFEST.in' # distutils assume setup.py is in the root of the project # we need to include C source from the parent so trick it in_ucl_root = 'setup.py' in os.listdir('python') -if in_ucl_root: +if not os.path.isfile('setup.py') and in_ucl_root: os.link('python/setup.py', 'setup.py') setup( name = 'ucl', - version = '0.8.1', + version = '0.8.2', description = 'ucl parser and emitter', ext_modules = [uclmodule], template=template, # no longer supported with setuptools but doesn't hurt diff --git a/src/ucl_emitter.c b/src/ucl_emitter.c index 4f4465dfbf4a..97d8f618021f 100644 --- a/src/ucl_emitter.c +++ b/src/ucl_emitter.c @@ -47,9 +47,9 @@ static void ucl_emitter_common_elt (struct ucl_emitter_context *ctx, static void ucl_emit_ ## type ## _elt (struct ucl_emitter_context *ctx, \ const ucl_object_t *obj, bool first, bool print_key); \ static void ucl_emit_ ## type ## _start_obj (struct ucl_emitter_context *ctx, \ - const ucl_object_t *obj, bool print_key); \ + const ucl_object_t *obj, bool first, bool print_key); \ static void ucl_emit_ ## type## _start_array (struct ucl_emitter_context *ctx, \ - const ucl_object_t *obj, bool print_key); \ + const ucl_object_t *obj, bool first, bool print_key); \ static void ucl_emit_ ##type## _end_object (struct ucl_emitter_context *ctx, \ const ucl_object_t *obj); \ static void ucl_emit_ ##type## _end_array (struct ucl_emitter_context *ctx, \ @@ -248,12 +248,26 @@ ucl_emitter_common_end_array (struct ucl_emitter_context *ctx, */ static void ucl_emitter_common_start_array (struct ucl_emitter_context *ctx, - const ucl_object_t *obj, bool print_key, bool compact) + const ucl_object_t *obj, bool first, bool print_key, bool compact) { const ucl_object_t *cur; ucl_object_iter_t iter = NULL; const struct ucl_emitter_functions *func = ctx->func; - bool first = true; + bool first_key = true; + + if (ctx->id != UCL_EMIT_CONFIG && !first) { + if (compact) { + func->ucl_emitter_append_character (',', 1, func->ud); + } + else { + if (ctx->id == UCL_EMIT_YAML && ctx->indent == 0) { + func->ucl_emitter_append_len ("\n", 1, func->ud); + } else { + func->ucl_emitter_append_len (",\n", 2, func->ud); + } + } + ucl_add_tabs (func, ctx->indent, compact); + } ucl_emitter_print_key (print_key, ctx, obj, compact); @@ -269,16 +283,16 @@ ucl_emitter_common_start_array (struct ucl_emitter_context *ctx, if (obj->type == UCL_ARRAY) { /* explicit array */ while ((cur = ucl_object_iterate (obj, &iter, true)) != NULL) { - ucl_emitter_common_elt (ctx, cur, first, false, compact); - first = false; + ucl_emitter_common_elt (ctx, cur, first_key, false, compact); + first_key = false; } } else { /* implicit array */ cur = obj; while (cur) { - ucl_emitter_common_elt (ctx, cur, first, false, compact); - first = false; + ucl_emitter_common_elt (ctx, cur, first_key, false, compact); + first_key = false; cur = cur->next; } } @@ -294,12 +308,26 @@ ucl_emitter_common_start_array (struct ucl_emitter_context *ctx, */ static void ucl_emitter_common_start_object (struct ucl_emitter_context *ctx, - const ucl_object_t *obj, bool print_key, bool compact) + const ucl_object_t *obj, bool first, bool print_key, bool compact) { ucl_hash_iter_t it = NULL; const ucl_object_t *cur, *elt; const struct ucl_emitter_functions *func = ctx->func; - bool first = true; + bool first_key = true; + + if (ctx->id != UCL_EMIT_CONFIG && !first) { + if (compact) { + func->ucl_emitter_append_character (',', 1, func->ud); + } + else { + if (ctx->id == UCL_EMIT_YAML && ctx->indent == 0) { + func->ucl_emitter_append_len ("\n", 1, func->ud); + } else { + func->ucl_emitter_append_len (",\n", 2, func->ud); + } + } + ucl_add_tabs (func, ctx->indent, compact); + } ucl_emitter_print_key (print_key, ctx, obj, compact); /* @@ -320,13 +348,13 @@ ucl_emitter_common_start_object (struct ucl_emitter_context *ctx, if (ctx->id == UCL_EMIT_CONFIG) { LL_FOREACH (cur, elt) { - ucl_emitter_common_elt (ctx, elt, first, true, compact); + ucl_emitter_common_elt (ctx, elt, first_key, true, compact); } } else { /* Expand implicit arrays */ if (cur->next != NULL) { - if (!first) { + if (!first_key) { if (compact) { func->ucl_emitter_append_character (',', 1, func->ud); } @@ -335,15 +363,15 @@ ucl_emitter_common_start_object (struct ucl_emitter_context *ctx, } } ucl_add_tabs (func, ctx->indent, compact); - ucl_emitter_common_start_array (ctx, cur, true, compact); + ucl_emitter_common_start_array (ctx, cur, first_key, true, compact); ucl_emitter_common_end_array (ctx, cur, compact); } else { - ucl_emitter_common_elt (ctx, cur, first, true, compact); + ucl_emitter_common_elt (ctx, cur, first_key, true, compact); } } - first = false; + first_key = false; } } @@ -446,11 +474,11 @@ ucl_emitter_common_elt (struct ucl_emitter_context *ctx, ucl_emitter_finish_object (ctx, obj, compact, !print_key); break; case UCL_OBJECT: - ucl_emitter_common_start_object (ctx, obj, print_key, compact); + ucl_emitter_common_start_object (ctx, obj, true, print_key, compact); ucl_emitter_common_end_object (ctx, obj, compact); break; case UCL_ARRAY: - ucl_emitter_common_start_array (ctx, obj, print_key, compact); + ucl_emitter_common_start_array (ctx, obj, true, print_key, compact); ucl_emitter_common_end_array (ctx, obj, compact); break; case UCL_USERDATA: @@ -490,12 +518,12 @@ ucl_emitter_common_elt (struct ucl_emitter_context *ctx, ucl_emitter_common_elt (ctx, obj, first, print_key, (compact)); \ } \ static void ucl_emit_ ## type ## _start_obj (struct ucl_emitter_context *ctx, \ - const ucl_object_t *obj, bool print_key) { \ - ucl_emitter_common_start_object (ctx, obj, print_key, (compact)); \ + const ucl_object_t *obj, bool first, bool print_key) { \ + ucl_emitter_common_start_object (ctx, obj, first, print_key, (compact)); \ } \ static void ucl_emit_ ## type## _start_array (struct ucl_emitter_context *ctx, \ - const ucl_object_t *obj, bool print_key) { \ - ucl_emitter_common_start_array (ctx, obj, print_key, (compact)); \ + const ucl_object_t *obj, bool first, bool print_key) { \ + ucl_emitter_common_start_array (ctx, obj, first, print_key, (compact)); \ } \ static void ucl_emit_ ##type## _end_object (struct ucl_emitter_context *ctx, \ const ucl_object_t *obj) { \ @@ -513,7 +541,7 @@ UCL_EMIT_TYPE_IMPL(yaml, false) static void ucl_emit_msgpack_elt (struct ucl_emitter_context *ctx, - const ucl_object_t *obj, bool first, bool print_key) + const ucl_object_t *obj, bool _first, bool print_key) { ucl_object_iter_t it; struct ucl_object_userdata *ud; @@ -556,7 +584,7 @@ ucl_emit_msgpack_elt (struct ucl_emitter_context *ctx, case UCL_OBJECT: ucl_emitter_print_key_msgpack (print_key, ctx, obj); - ucl_emit_msgpack_start_obj (ctx, obj, print_key); + ucl_emit_msgpack_start_obj (ctx, obj, false, print_key); it = NULL; while ((cur = ucl_object_iterate (obj, &it, true)) != NULL) { @@ -575,7 +603,7 @@ ucl_emit_msgpack_elt (struct ucl_emitter_context *ctx, case UCL_ARRAY: ucl_emitter_print_key_msgpack (print_key, ctx, obj); - ucl_emit_msgpack_start_array (ctx, obj, print_key); + ucl_emit_msgpack_start_array (ctx, obj, false, print_key); it = NULL; while ((cur = ucl_object_iterate (obj, &it, true)) != NULL) { @@ -601,14 +629,14 @@ ucl_emit_msgpack_elt (struct ucl_emitter_context *ctx, static void ucl_emit_msgpack_start_obj (struct ucl_emitter_context *ctx, - const ucl_object_t *obj, bool print_key) + const ucl_object_t *obj, bool _first, bool _print_key) { ucl_emitter_print_object_msgpack (ctx, obj->len); } static void ucl_emit_msgpack_start_array (struct ucl_emitter_context *ctx, - const ucl_object_t *obj, bool print_key) + const ucl_object_t *obj, bool _first, bool _print_key) { ucl_emitter_print_array_msgpack (ctx, obj->len); } diff --git a/src/ucl_emitter_streamline.c b/src/ucl_emitter_streamline.c index a7178c5d74b0..8ca86fa081c9 100644 --- a/src/ucl_emitter_streamline.c +++ b/src/ucl_emitter_streamline.c @@ -103,18 +103,19 @@ ucl_object_emit_streamline_start_container (struct ucl_emitter_context *ctx, top = sctx->containers; st = malloc (sizeof (*st)); if (st != NULL) { - if (top != NULL && !top->is_array) { + st->empty = true; + if (top && !top->is_array) { print_key = true; } - st->empty = true; + st->obj = obj; if (obj != NULL && obj->type == UCL_ARRAY) { st->is_array = true; - sctx->ops->ucl_emitter_start_array (ctx, obj, print_key); + sctx->ops->ucl_emitter_start_array (ctx, obj, top == NULL, print_key); } else { st->is_array = false; - sctx->ops->ucl_emitter_start_object (ctx, obj, print_key); + sctx->ops->ucl_emitter_start_object (ctx, obj, top == NULL, print_key); } LL_PREPEND (sctx->containers, st); } diff --git a/src/ucl_hash.c b/src/ucl_hash.c index a74dfcdee68e..0208cfd29c9a 100644 --- a/src/ucl_hash.c +++ b/src/ucl_hash.c @@ -32,12 +32,12 @@ struct ucl_hash_elt { const ucl_object_t *obj; - size_t ar_idx; + struct ucl_hash_elt *prev, *next; }; struct ucl_hash_struct { void *hash; - kvec_t(const ucl_object_t *) ar; + struct ucl_hash_elt *head; bool caseless; }; @@ -45,7 +45,6 @@ static uint64_t ucl_hash_seed (void) { static uint64_t seed; - if (seed == 0) { #ifdef UCL_RANDOM_FUNCTION seed = UCL_RANDOM_FUNCTION; @@ -115,7 +114,7 @@ ucl_hash_equal (const ucl_object_t *k1, const ucl_object_t *k2) return 0; } -KHASH_INIT (ucl_hash_node, const ucl_object_t *, struct ucl_hash_elt, 1, +KHASH_INIT (ucl_hash_node, const ucl_object_t *, struct ucl_hash_elt *, 1, ucl_hash_func, ucl_hash_equal) static inline uint32_t @@ -227,7 +226,7 @@ ucl_hash_caseless_equal (const ucl_object_t *k1, const ucl_object_t *k2) return 0; } -KHASH_INIT (ucl_hash_caseless_node, const ucl_object_t *, struct ucl_hash_elt, 1, +KHASH_INIT (ucl_hash_caseless_node, const ucl_object_t *, struct ucl_hash_elt *, 1, ucl_hash_caseless_func, ucl_hash_caseless_equal) ucl_hash_t* @@ -238,8 +237,7 @@ ucl_hash_create (bool ignore_case) new = UCL_ALLOC (sizeof (ucl_hash_t)); if (new != NULL) { void *h; - kv_init (new->ar); - + new->head = NULL; new->caseless = ignore_case; if (ignore_case) { h = (void *)kh_init (ucl_hash_caseless_node); @@ -258,7 +256,6 @@ ucl_hash_create (bool ignore_case) void ucl_hash_destroy (ucl_hash_t* hashlin, ucl_hash_free_func func) { - const ucl_object_t *cur, *tmp; if (hashlin == NULL) { return; @@ -269,10 +266,11 @@ void ucl_hash_destroy (ucl_hash_t* hashlin, ucl_hash_free_func func) khash_t(ucl_hash_node) *h = (khash_t(ucl_hash_node) *) hashlin->hash; khiter_t k; + const ucl_object_t *cur, *tmp; for (k = kh_begin (h); k != kh_end (h); ++k) { if (kh_exist (h, k)) { - cur = (kh_value (h, k)).obj; + cur = (kh_value (h, k))->obj; while (cur != NULL) { tmp = cur->next; func (__DECONST (ucl_object_t *, cur)); @@ -293,7 +291,12 @@ void ucl_hash_destroy (ucl_hash_t* hashlin, ucl_hash_free_func func) kh_destroy (ucl_hash_node, h); } - kv_destroy (hashlin->ar); + struct ucl_hash_elt *cur, *tmp; + + DL_FOREACH_SAFE(hashlin->head, cur, tmp) { + UCL_FREE(sizeof(*cur), cur); + } + UCL_FREE (sizeof (*hashlin), hashlin); } @@ -303,7 +306,7 @@ ucl_hash_insert (ucl_hash_t* hashlin, const ucl_object_t *obj, { khiter_t k; int ret; - struct ucl_hash_elt *elt; + struct ucl_hash_elt **pelt, *elt; if (hashlin == NULL) { return false; @@ -314,10 +317,14 @@ ucl_hash_insert (ucl_hash_t* hashlin, const ucl_object_t *obj, hashlin->hash; k = kh_put (ucl_hash_caseless_node, h, obj, &ret); if (ret > 0) { - elt = &kh_value (h, k); - kv_push_safe (const ucl_object_t *, hashlin->ar, obj, e0); + elt = UCL_ALLOC(sizeof(*elt)); + pelt = &kh_value (h, k); + *pelt = elt; + DL_APPEND(hashlin->head, elt); elt->obj = obj; - elt->ar_idx = kv_size (hashlin->ar) - 1; + } + else if (ret < 0) { + goto e0; } } else { @@ -325,10 +332,11 @@ ucl_hash_insert (ucl_hash_t* hashlin, const ucl_object_t *obj, hashlin->hash; k = kh_put (ucl_hash_node, h, obj, &ret); if (ret > 0) { - elt = &kh_value (h, k); - kv_push_safe (const ucl_object_t *, hashlin->ar, obj, e0); + elt = UCL_ALLOC(sizeof(*elt)); + pelt = &kh_value (h, k); + *pelt = elt; + DL_APPEND(hashlin->head, elt); elt->obj = obj; - elt->ar_idx = kv_size (hashlin->ar) - 1; } else if (ret < 0) { goto e0; } @@ -343,7 +351,7 @@ void ucl_hash_replace (ucl_hash_t* hashlin, const ucl_object_t *old, { khiter_t k; int ret; - struct ucl_hash_elt elt, *pelt; + struct ucl_hash_elt *elt, *nelt; if (hashlin == NULL) { return; @@ -354,13 +362,14 @@ void ucl_hash_replace (ucl_hash_t* hashlin, const ucl_object_t *old, hashlin->hash; k = kh_put (ucl_hash_caseless_node, h, old, &ret); if (ret == 0) { - elt = kh_value (h, k); + elt = kh_value(h, k); kh_del (ucl_hash_caseless_node, h, k); k = kh_put (ucl_hash_caseless_node, h, new, &ret); - pelt = &kh_value (h, k); - pelt->obj = new; - pelt->ar_idx = elt.ar_idx; - kv_A (hashlin->ar, elt.ar_idx) = new; + nelt = UCL_ALLOC(sizeof(*nelt)); + nelt->obj = new; + kh_value(h, k) = nelt; + DL_REPLACE_ELEM(hashlin->head, elt, nelt); + UCL_FREE(sizeof(*elt), elt); } } else { @@ -371,17 +380,17 @@ void ucl_hash_replace (ucl_hash_t* hashlin, const ucl_object_t *old, elt = kh_value (h, k); kh_del (ucl_hash_node, h, k); k = kh_put (ucl_hash_node, h, new, &ret); - pelt = &kh_value (h, k); - pelt->obj = new; - pelt->ar_idx = elt.ar_idx; - kv_A (hashlin->ar, elt.ar_idx) = new; + nelt = UCL_ALLOC(sizeof(*nelt)); + nelt->obj = new; + kh_value(h, k) = nelt; + DL_REPLACE_ELEM(hashlin->head, elt, nelt); + UCL_FREE(sizeof(*elt), elt); } } } struct ucl_hash_real_iter { - const ucl_object_t **cur; - const ucl_object_t **end; + const struct ucl_hash_elt *cur; }; #define UHI_SETERR(ep, ern) {if (ep != NULL) *ep = (ern);} @@ -405,13 +414,13 @@ ucl_hash_iterate2 (ucl_hash_t *hashlin, ucl_hash_iter_t *iter, int *ep) return NULL; } - it->cur = &hashlin->ar.a[0]; - it->end = it->cur + hashlin->ar.n; + it->cur = hashlin->head; } UHI_SETERR(ep, 0); - if (it->cur < it->end) { - ret = *it->cur++; + if (it->cur) { + ret = it->cur->obj; + it->cur = it->cur->next; } else { UCL_FREE (sizeof (*it), it); @@ -429,7 +438,7 @@ ucl_hash_iter_has_next (ucl_hash_t *hashlin, ucl_hash_iter_t iter) { struct ucl_hash_real_iter *it = (struct ucl_hash_real_iter *)(iter); - return it->cur < it->end - 1; + return it->cur != NULL; } @@ -454,7 +463,7 @@ ucl_hash_search (ucl_hash_t* hashlin, const char *key, unsigned keylen) k = kh_get (ucl_hash_caseless_node, h, &search); if (k != kh_end (h)) { - elt = &kh_value (h, k); + elt = kh_value (h, k); ret = elt->obj; } } @@ -463,7 +472,7 @@ ucl_hash_search (ucl_hash_t* hashlin, const char *key, unsigned keylen) hashlin->hash; k = kh_get (ucl_hash_node, h, &search); if (k != kh_end (h)) { - elt = &kh_value (h, k); + elt = kh_value (h, k); ret = elt->obj; } } @@ -476,7 +485,6 @@ ucl_hash_delete (ucl_hash_t* hashlin, const ucl_object_t *obj) { khiter_t k; struct ucl_hash_elt *elt; - size_t i; if (hashlin == NULL) { return; @@ -488,16 +496,10 @@ ucl_hash_delete (ucl_hash_t* hashlin, const ucl_object_t *obj) k = kh_get (ucl_hash_caseless_node, h, obj); if (k != kh_end (h)) { - elt = &kh_value (h, k); - i = elt->ar_idx; - kv_del (const ucl_object_t *, hashlin->ar, elt->ar_idx); + elt = kh_value (h, k); + DL_DELETE(hashlin->head, elt); kh_del (ucl_hash_caseless_node, h, k); - - /* Update subsequent elts */ - for (; i < hashlin->ar.n; i ++) { - elt = &kh_value (h, i); - elt->ar_idx --; - } + UCL_FREE(sizeof(*elt), elt); } } else { @@ -505,16 +507,10 @@ ucl_hash_delete (ucl_hash_t* hashlin, const ucl_object_t *obj) hashlin->hash; k = kh_get (ucl_hash_node, h, obj); if (k != kh_end (h)) { - elt = &kh_value (h, k); - i = elt->ar_idx; - kv_del (const ucl_object_t *, hashlin->ar, elt->ar_idx); + elt = kh_value (h, k); + DL_DELETE(hashlin->head, elt); kh_del (ucl_hash_node, h, k); - - /* Update subsequent elts */ - for (; i < hashlin->ar.n; i ++) { - elt = &kh_value (h, i); - elt->ar_idx --; - } + UCL_FREE(sizeof(*elt), elt); } } } @@ -525,9 +521,7 @@ bool ucl_hash_reserve (ucl_hash_t *hashlin, size_t sz) return false; } - if (sz > hashlin->ar.m) { - kv_resize_safe (const ucl_object_t *, hashlin->ar, sz, e0); - + if (sz > kh_size((khash_t(ucl_hash_node) *)hashlin->hash)) { if (hashlin->caseless) { khash_t(ucl_hash_caseless_node) *h = (khash_t( ucl_hash_caseless_node) *) @@ -540,8 +534,6 @@ bool ucl_hash_reserve (ucl_hash_t *hashlin, size_t sz) } } return true; -e0: - return false; } static int @@ -591,27 +583,27 @@ ucl_lc_cmp (const char *s, const char *d, size_t l) static int ucl_hash_cmp_icase (const void *a, const void *b) { - const ucl_object_t *oa = *(const ucl_object_t **)a, - *ob = *(const ucl_object_t **)b; + const struct ucl_hash_elt *oa = (const struct ucl_hash_elt *)a, + *ob = (const struct ucl_hash_elt *)b; - if (oa->keylen == ob->keylen) { - return ucl_lc_cmp (oa->key, ob->key, oa->keylen); + if (oa->obj->keylen == ob->obj->keylen) { + return ucl_lc_cmp (oa->obj->key, ob->obj->key, oa->obj->keylen); } - return ((int)(oa->keylen)) - ob->keylen; + return ((int)(oa->obj->keylen)) - ob->obj->keylen; } static int ucl_hash_cmp_case_sens (const void *a, const void *b) { - const ucl_object_t *oa = *(const ucl_object_t **)a, - *ob = *(const ucl_object_t **)b; + const struct ucl_hash_elt *oa = (const struct ucl_hash_elt *)a, + *ob = (const struct ucl_hash_elt *)b; - if (oa->keylen == ob->keylen) { - return memcmp (oa->key, ob->key, oa->keylen); + if (oa->obj->keylen == ob->obj->keylen) { + return memcmp (oa->obj->key, ob->obj->key, oa->obj->keylen); } - return ((int)(oa->keylen)) - ob->keylen; + return ((int)(oa->obj->keylen)) - ob->obj->keylen; } void @@ -619,18 +611,18 @@ ucl_hash_sort (ucl_hash_t *hashlin, enum ucl_object_keys_sort_flags fl) { if (fl & UCL_SORT_KEYS_ICASE) { - qsort (hashlin->ar.a, hashlin->ar.n, sizeof (ucl_object_t *), - ucl_hash_cmp_icase); + DL_SORT(hashlin->head, ucl_hash_cmp_icase); } else { - qsort (hashlin->ar.a, hashlin->ar.n, sizeof (ucl_object_t *), - ucl_hash_cmp_case_sens); + DL_SORT(hashlin->head, ucl_hash_cmp_case_sens); } if (fl & UCL_SORT_KEYS_RECURSIVE) { - for (size_t i = 0; i < hashlin->ar.n; i ++) { - if (ucl_object_type (hashlin->ar.a[i]) == UCL_OBJECT) { - ucl_hash_sort (hashlin->ar.a[i]->value.ov, fl); + struct ucl_hash_elt *elt; + + DL_FOREACH(hashlin->head, elt) { + if (ucl_object_type (elt->obj) == UCL_OBJECT) { + ucl_hash_sort (elt->obj->value.ov, fl); } } } diff --git a/src/ucl_parser.c b/src/ucl_parser.c index 23f5bce3056f..6be16d12169c 100644 --- a/src/ucl_parser.c +++ b/src/ucl_parser.c @@ -47,6 +47,9 @@ struct ucl_parser_saved_state { */ #define ucl_chunk_skipc(chunk, p) \ do { \ + if (p == chunk->end) { \ + break; \ + } \ if (*(p) == '\n') { \ (chunk)->line ++; \ (chunk)->column = 0; \ @@ -176,7 +179,7 @@ start: if (!quoted) { if (*p == '*') { ucl_chunk_skipc (chunk, p); - if (*p == '/') { + if (chunk->remain > 0 && *p == '/') { comments_nested --; if (comments_nested == 0) { if (parser->flags & UCL_PARSER_SAVE_COMMENTS) { @@ -345,8 +348,9 @@ ucl_check_variable_safe (struct ucl_parser *parser, const char *ptr, size_t rema /* Call generic handler */ if (parser->var_handler (ptr, remain, &dst, &dstlen, &need_free, parser->var_data)) { - *out_len = dstlen; *found = true; + *out_len = dstlen; + if (need_free) { free (dst); } @@ -395,6 +399,9 @@ ucl_check_variable (struct ucl_parser *parser, const char *ptr, } p ++; } + if(p == end) { + (*out_len) ++; + } } else if (*ptr != '$') { /* Not count escaped dollar sign */ @@ -418,13 +425,14 @@ ucl_check_variable (struct ucl_parser *parser, const char *ptr, * Expand a single variable * @param parser * @param ptr - * @param remain + * @param in_len * @param dest + * @param out_len * @return */ static const char * ucl_expand_single_variable (struct ucl_parser *parser, const char *ptr, - size_t remain, unsigned char **dest) + size_t in_len, unsigned char **dest, size_t out_len) { unsigned char *d = *dest, *dst; const char *p = ptr + 1, *ret; @@ -435,7 +443,8 @@ ucl_expand_single_variable (struct ucl_parser *parser, const char *ptr, bool strict = false; ret = ptr + 1; - remain --; + /* For the $ sign */ + in_len --; if (*p == '$') { *d++ = *p++; @@ -444,39 +453,53 @@ ucl_expand_single_variable (struct ucl_parser *parser, const char *ptr, } else if (*p == '{') { p ++; + in_len --; strict = true; ret += 2; - remain -= 2; } LL_FOREACH (parser->variables, var) { - if (remain >= var->var_len) { + if (out_len >= var->value_len && in_len >= (var->var_len + (strict ? 1 : 0))) { if (memcmp (p, var->var, var->var_len) == 0) { - memcpy (d, var->value, var->value_len); - ret += var->var_len; - d += var->value_len; - found = true; - break; + if (!strict || p[var->var_len] == '}') { + memcpy (d, var->value, var->value_len); + ret += var->var_len; + d += var->value_len; + found = true; + break; + } } } } + if (!found) { if (strict && parser->var_handler != NULL) { - if (parser->var_handler (p, remain, &dst, &dstlen, &need_free, + dstlen = out_len; + + if (parser->var_handler (p, in_len, &dst, &dstlen, &need_free, parser->var_data)) { - memcpy (d, dst, dstlen); - ret += remain; - d += dstlen; - found = true; - if (need_free) { - free (dst); + if (dstlen > out_len) { + /* We do not have enough space! */ + if (need_free) { + free (dst); + } + } + else { + memcpy(d, dst, dstlen); + ret += in_len; + d += dstlen; + found = true; + + if (need_free) { + free(dst); + } } } } - /* Leave variable as is */ + /* Leave variable as is, in this case we use dest */ if (!found) { - if (strict) { + if (strict && out_len >= 2) { /* Copy '${' */ memcpy (d, ptr, 2); d += 2; @@ -506,7 +529,7 @@ ucl_expand_variable (struct ucl_parser *parser, unsigned char **dst, const char *src, size_t in_len) { const char *p, *end = src + in_len; - unsigned char *d; + unsigned char *d, *d_end; size_t out_len = 0; bool vars_found = false; @@ -517,7 +540,7 @@ ucl_expand_variable (struct ucl_parser *parser, unsigned char **dst, p = src; while (p != end) { - if (*p == '$') { + if (*p == '$' && p + 1 != end) { p = ucl_check_variable (parser, p + 1, end - p - 1, &out_len, &vars_found); } else { @@ -538,10 +561,11 @@ ucl_expand_variable (struct ucl_parser *parser, unsigned char **dst, } d = *dst; + d_end = d + out_len; p = src; - while (p != end) { - if (*p == '$') { - p = ucl_expand_single_variable (parser, p, end - p, &d); + while (p != end && d != d_end) { + if (*p == '$' && p + 1 != end) { + p = ucl_expand_single_variable (parser, p, end - p, &d, d_end - d); } else { *d++ = *p++; @@ -686,6 +710,8 @@ ucl_parser_add_container (ucl_object_t *obj, struct ucl_parser *parser, ucl_object_unref (obj); } + UCL_FREE(sizeof (struct ucl_stack), st); + return NULL; } @@ -722,13 +748,13 @@ ucl_maybe_parse_number (ucl_object_t *obj, const char *p = start, *c = start; char *endptr; bool got_dot = false, got_exp = false, need_double = false, - is_time = false, valid_start = false, is_hex = false, - is_neg = false; + is_time = false, valid_start = false, is_hex = false; + int is_neg = 0; double dv = 0; int64_t lv = 0; if (*p == '-') { - is_neg = true; + is_neg = 1; c ++; p ++; } @@ -744,6 +770,7 @@ ucl_maybe_parse_number (ucl_object_t *obj, is_hex = true; allow_double = false; c = p + 1; + p ++; } else if (allow_double) { if (p == c) { @@ -792,26 +819,46 @@ ucl_maybe_parse_number (ucl_object_t *obj, break; } } + else if (!allow_double && *p == '.') { + /* Unexpected dot */ + *pos = start; + return EINVAL; + } else { break; } } - if (!valid_start) { + if (!valid_start || p == c) { + *pos = start; + return EINVAL; + } + + char numbuf[128]; + + if ((size_t)(p - c + 1) >= sizeof(numbuf)) { *pos = start; return EINVAL; } + if (is_neg) { + numbuf[0] = '-'; + ucl_strlcpy (&numbuf[1], c, p - c + 1); + } + else { + ucl_strlcpy (numbuf, c, p - c + 1); + } + errno = 0; if (need_double) { - dv = strtod (c, &endptr); + dv = strtod (numbuf, &endptr); } else { if (is_hex) { - lv = strtoimax (c, &endptr, 16); + lv = strtoimax (numbuf, &endptr, 16); } else { - lv = strtoimax (c, &endptr, 10); + lv = strtoimax (numbuf, &endptr, 10); } } if (errno == ERANGE) { @@ -819,7 +866,15 @@ ucl_maybe_parse_number (ucl_object_t *obj, return ERANGE; } - /* Now check endptr */ + /* Now check endptr and move it from numbuf to the real ending */ + if (endptr != NULL) { + long shift = endptr - numbuf - is_neg; + endptr = (char *)c + shift; + } + if (endptr >= end) { + p = end; + goto set_obj; + } if (endptr == NULL || ucl_lex_is_atom_end (*endptr) || *endptr == '\0') { p = endptr; goto set_obj; @@ -849,6 +904,10 @@ ucl_maybe_parse_number (ucl_object_t *obj, dv *= ucl_lex_num_multiplier (*p, false); } p += 2; + if (end - p > 0 && !ucl_lex_is_atom_end (*p)) { + *pos = start; + return EINVAL; + } goto set_obj; } else if (number_bytes || (p[1] == 'b' || p[1] == 'B')) { @@ -859,6 +918,10 @@ ucl_maybe_parse_number (ucl_object_t *obj, } lv *= ucl_lex_num_multiplier (*p, true); p += 2; + if (end - p > 0 && !ucl_lex_is_atom_end (*p)) { + *pos = start; + return EINVAL; + } goto set_obj; } else if (ucl_lex_is_atom_end (p[1])) { @@ -883,6 +946,10 @@ ucl_maybe_parse_number (ucl_object_t *obj, is_time = true; dv *= 60.; p += 3; + if (end - p > 0 && !ucl_lex_is_atom_end (*p)) { + *pos = start; + return EINVAL; + } goto set_obj; } } @@ -895,6 +962,10 @@ ucl_maybe_parse_number (ucl_object_t *obj, lv *= ucl_lex_num_multiplier (*p, number_bytes); } p ++; + if (end - p > 0 && !ucl_lex_is_atom_end (*p)) { + *pos = start; + return EINVAL; + } goto set_obj; } break; @@ -943,7 +1014,7 @@ ucl_maybe_parse_number (ucl_object_t *obj, } else if (endptr == end) { /* Just a number at the end of chunk */ - p = endptr; + p = end; goto set_obj; } @@ -959,11 +1030,11 @@ set_obj: else { obj->type = UCL_TIME; } - obj->value.dv = is_neg ? (-dv) : dv; + obj->value.dv = dv; } else { obj->type = UCL_INT; - obj->value.iv = is_neg ? (-lv) : lv; + obj->value.iv = lv; } } *pos = p; @@ -1037,13 +1108,13 @@ ucl_lex_json_string (struct ucl_parser *parser, } else if (c == '\\') { ucl_chunk_skipc (chunk, p); - c = *p; if (p >= chunk->end) { ucl_set_err (parser, UCL_ESYNTAX, "unfinished escape character", &parser->err); return false; } - else if (ucl_test_character (c, UCL_CHARACTER_ESCAPE)) { + c = *p; + if (ucl_test_character (c, UCL_CHARACTER_ESCAPE)) { if (c == 'u') { ucl_chunk_skipc (chunk, p); for (i = 0; i < 4 && p < chunk->end; i ++) { @@ -1289,24 +1360,20 @@ ucl_parser_process_object_element (struct ucl_parser *parser, ucl_object_t *nobj */ static bool ucl_parse_key (struct ucl_parser *parser, struct ucl_chunk *chunk, - bool *next_key, bool *end_of_object) + bool *next_key, bool *end_of_object, bool *got_content) { const unsigned char *p, *c = NULL, *end, *t; const char *key = NULL; bool got_quote = false, got_eq = false, got_semicolon = false, need_unescape = false, ucl_escape = false, var_expand = false, - got_content = false, got_sep = false; + got_sep = false; ucl_object_t *nobj; ssize_t keylen; p = chunk->pos; - if (*p == '.') { - /* It is macro actually */ - if (!(parser->flags & UCL_PARSER_DISABLE_MACRO)) { - ucl_chunk_skipc (chunk, p); - } - + if (*p == '.' && !(parser->flags & UCL_PARSER_DISABLE_MACRO)) { + ucl_chunk_skipc (chunk, p); parser->prev_state = parser->state; parser->state = UCL_STATE_MACRO_NAME; *end_of_object = false; @@ -1330,13 +1397,13 @@ ucl_parse_key (struct ucl_parser *parser, struct ucl_chunk *chunk, /* The first symbol */ c = p; ucl_chunk_skipc (chunk, p); - got_content = true; + *got_content = true; } else if (*p == '"') { /* JSON style key */ c = p + 1; got_quote = true; - got_content = true; + *got_content = true; ucl_chunk_skipc (chunk, p); } else if (*p == '}') { @@ -1344,7 +1411,7 @@ ucl_parse_key (struct ucl_parser *parser, struct ucl_chunk *chunk, *end_of_object = true; return true; } - else if (*p == '.') { + else if (*p == '.' && !(parser->flags & UCL_PARSER_DISABLE_MACRO)) { ucl_chunk_skipc (chunk, p); parser->prev_state = parser->state; parser->state = UCL_STATE_MACRO_NAME; @@ -1361,7 +1428,7 @@ ucl_parse_key (struct ucl_parser *parser, struct ucl_chunk *chunk, /* Parse the body of a key */ if (!got_quote) { if (ucl_test_character (*p, UCL_CHARACTER_KEY)) { - got_content = true; + *got_content = true; ucl_chunk_skipc (chunk, p); } else if (ucl_test_character (*p, UCL_CHARACTER_KEY_SEP)) { @@ -1387,11 +1454,11 @@ ucl_parse_key (struct ucl_parser *parser, struct ucl_chunk *chunk, } } - if (p >= chunk->end && got_content) { + if (p >= chunk->end && *got_content) { ucl_set_err (parser, UCL_ESYNTAX, "unfinished key", &parser->err); return false; } - else if (!got_content) { + else if (!*got_content) { return true; } *end_of_object = false; @@ -1752,6 +1819,9 @@ ucl_parse_value (struct ucl_parser *parser, struct ucl_chunk *chunk) case '{': obj = ucl_parser_get_container (parser); if (obj == NULL) { + parser->state = UCL_STATE_ERROR; + ucl_set_err(parser, UCL_ESYNTAX, "object value must be a part of an object", + &parser->err); return false; } /* We have a new object */ @@ -1773,6 +1843,9 @@ ucl_parse_value (struct ucl_parser *parser, struct ucl_chunk *chunk) case '[': obj = ucl_parser_get_container (parser); if (obj == NULL) { + parser->state = UCL_STATE_ERROR; + ucl_set_err(parser, UCL_ESYNTAX, "array value must be a part of an object", + &parser->err); return false; } /* We have a new array */ @@ -1804,6 +1877,12 @@ ucl_parse_value (struct ucl_parser *parser, struct ucl_chunk *chunk) break; case '<': obj = ucl_parser_get_container (parser); + if (obj == NULL) { + parser->state = UCL_STATE_ERROR; + ucl_set_err(parser, UCL_ESYNTAX, "multiline value must be a part of an object", + &parser->err); + return false; + } /* We have something like multiline value, which must be <<[A-Z]+\n */ if (chunk->end - p > 3) { if (memcmp (p, "<<", 2) == 0) { @@ -1812,6 +1891,11 @@ ucl_parse_value (struct ucl_parser *parser, struct ucl_chunk *chunk) while (p < chunk->end && *p >= 'A' && *p <= 'Z') { p ++; } + if(p == chunk->end) { + ucl_set_err (parser, UCL_ESYNTAX, + "unterminated multiline value", &parser->err); + return false; + } if (*p =='\n') { /* Set chunk positions and start multiline parsing */ chunk->remain -= p - c + 1; @@ -1850,6 +1934,13 @@ parse_string: obj = ucl_parser_get_container (parser); } + if (obj == NULL) { + parser->state = UCL_STATE_ERROR; + ucl_set_err(parser, UCL_ESYNTAX, "value must be a part of an object", + &parser->err); + return false; + } + /* Parse atom */ if (ucl_test_character (*p, UCL_CHARACTER_VALUE_DIGIT_START)) { if (!ucl_lex_number (parser, chunk, obj)) { @@ -2339,7 +2430,7 @@ ucl_state_machine (struct ucl_parser *parser) unsigned char *macro_escaped; size_t macro_len = 0; struct ucl_macro *macro = NULL; - bool next_key = false, end_of_object = false, ret; + bool next_key = false, end_of_object = false, got_content = false, ret; if (parser->top_obj == NULL) { parser->state = UCL_STATE_INIT; @@ -2428,7 +2519,10 @@ ucl_state_machine (struct ucl_parser *parser) parser->state = UCL_STATE_ERROR; return false; } - if (!ucl_parse_key (parser, chunk, &next_key, &end_of_object)) { + + got_content = false; + + if (!ucl_parse_key (parser, chunk, &next_key, &end_of_object, &got_content)) { parser->prev_state = parser->state; parser->state = UCL_STATE_ERROR; return false; @@ -2451,7 +2545,8 @@ ucl_state_machine (struct ucl_parser *parser) return false; } } - else { + else if (got_content) { + /* Do not switch state if we have not read any content */ parser->state = UCL_STATE_VALUE; } } @@ -2617,6 +2712,9 @@ ucl_state_machine (struct ucl_parser *parser) return false; } break; + case UCL_STATE_ERROR: + /* Already in the error state */ + return false; default: ucl_set_err (parser, UCL_EINTERNAL, "internal error: parser is in an unknown state", &parser->err); @@ -2889,7 +2987,9 @@ ucl_parser_add_chunk_full (struct ucl_parser *parser, const unsigned char *data, if (!special_handler->handler (parser, data, len, &ndata, &nlen, special_handler->user_data)) { + UCL_FREE(sizeof (struct ucl_chunk), chunk); ucl_create_err (&parser->err, "call for external handler failed"); + return false; } @@ -2909,7 +3009,7 @@ ucl_parser_add_chunk_full (struct ucl_parser *parser, const unsigned char *data, if (parse_type == UCL_PARSE_AUTO && len > 0) { /* We need to detect parse type by the first symbol */ - if ((*data & 0x80) == 0x80 && (*data >= 0xdc && *data <= 0xdf)) { + if ((*data & 0x80) == 0x80) { parse_type = UCL_PARSE_MSGPACK; } else if (*data == '(') { diff --git a/src/ucl_schema.c b/src/ucl_schema.c index 68f01187e375..f4ec0ed3284a 100644 --- a/src/ucl_schema.c +++ b/src/ucl_schema.c @@ -39,6 +39,7 @@ #ifdef HAVE_MATH_H #include <math.h> #endif +#include <inttypes.h> static bool ucl_schema_validate (const ucl_object_t *schema, const ucl_object_t *obj, bool try_array, diff --git a/src/ucl_util.c b/src/ucl_util.c index b00a34787e5a..8f97c20db503 100644 --- a/src/ucl_util.c +++ b/src/ucl_util.c @@ -67,7 +67,7 @@ typedef kvec_t(ucl_object_t *) ucl_array_t; #include <fetch.h> #endif -#if defined(_MSC_VER) +#if defined(_WIN32) #include <windows.h> #include <io.h> #include <direct.h> @@ -889,44 +889,49 @@ ucl_fetch_file (const unsigned char *filename, unsigned char **buf, size_t *bufl { int fd; struct stat st; + if ((fd = open (filename, O_RDONLY)) == -1) { + ucl_create_err (err, "cannot open file %s: %s", + filename, strerror (errno)); + return false; + } - if (stat (filename, &st) == -1) { + if (fstat (fd, &st) == -1) { if (must_exist || errno == EPERM) { ucl_create_err (err, "cannot stat file %s: %s", filename, strerror (errno)); } + close (fd); + return false; } if (!S_ISREG (st.st_mode)) { if (must_exist) { ucl_create_err (err, "file %s is not a regular file", filename); } + close (fd); return false; } + if (st.st_size == 0) { /* Do not map empty files */ *buf = NULL; *buflen = 0; } else { - if ((fd = open (filename, O_RDONLY)) == -1) { - ucl_create_err (err, "cannot open file %s: %s", - filename, strerror (errno)); - return false; - } - if ((*buf = ucl_mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) { - close (fd); - ucl_create_err (err, "cannot mmap file %s: %s", - filename, strerror (errno)); + if ((*buf = ucl_mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) { + close(fd); + ucl_create_err(err, "cannot mmap file %s: %s", + filename, strerror(errno)); *buf = NULL; return false; } *buflen = st.st_size; - close (fd); } + close (fd); + return true; } @@ -1017,6 +1022,9 @@ ucl_include_url (const unsigned char *data, size_t len, snprintf (urlbuf, sizeof (urlbuf), "%.*s", (int)len, data); if (!ucl_fetch_url (urlbuf, &buf, &buflen, &parser->err, params->must_exist)) { + if (!params->must_exist) { + ucl_parser_clear_error (parser); + } return !params->must_exist; } @@ -1092,6 +1100,11 @@ ucl_include_file_single (const unsigned char *data, size_t len, ucl_hash_t *container = NULL; struct ucl_stack *st = NULL; + if (parser->state == UCL_STATE_ERROR) { + /* Return immediately if we are in the error state... */ + return false; + } + snprintf (filebuf, sizeof (filebuf), "%.*s", (int)len, data); if (ucl_realpath (filebuf, realbuf) == NULL) { if (params->soft_fail) { @@ -1128,6 +1141,8 @@ ucl_include_file_single (const unsigned char *data, size_t len, return false; } + ucl_parser_clear_error (parser); + return true; } @@ -1138,6 +1153,10 @@ ucl_include_file_single (const unsigned char *data, size_t len, /* We need to check signature first */ snprintf (filebuf, sizeof (filebuf), "%s.sig", realbuf); if (!ucl_fetch_file (filebuf, &sigbuf, &siglen, &parser->err, true)) { + if (buf) { + ucl_munmap (buf, buflen); + } + return false; } if (!ucl_sig_check (buf, buflen, sigbuf, siglen, parser)) { @@ -1147,8 +1166,13 @@ ucl_include_file_single (const unsigned char *data, size_t len, if (sigbuf) { ucl_munmap (sigbuf, siglen); } + if (buf) { + ucl_munmap (buf, buflen); + } + return false; } + if (sigbuf) { ucl_munmap (sigbuf, siglen); } @@ -1257,6 +1281,8 @@ ucl_include_file_single (const unsigned char *data, size_t len, ucl_munmap (buf, buflen); } + ucl_object_unref (new_obj); + return false; } nest_obj->prev = nest_obj; @@ -1576,11 +1602,6 @@ ucl_include_common (const unsigned char *data, size_t len, else if (param->type == UCL_INT) { if (strncmp (param->key, "priority", param->keylen) == 0) { params.priority = ucl_object_toint (param); - if (params.priority > UCL_PRIORITY_MAX) { - ucl_create_err (&parser->err, "Invalid priority value in macro: %d", - params.priority); - return false; - } } } } @@ -1719,9 +1740,8 @@ ucl_priority_handler (const unsigned char *data, size_t len, if (len > 0) { value = malloc(len + 1); ucl_strlcpy(value, (const char *)data, len + 1); - errno = 0; - priority = strtoul(value, &leftover, 10); - if (errno != 0 || *leftover != '\0' || priority > UCL_PRIORITY_MAX) { + priority = strtol(value, &leftover, 10); + if (*leftover != '\0') { ucl_create_err (&parser->err, "Invalid priority value in macro: %s", value); free(value); @@ -1842,6 +1862,10 @@ ucl_load_handler (const unsigned char *data, size_t len, !try_load)) { free (load_file); + if (try_load) { + ucl_parser_clear_error (parser); + } + return (try_load || false); } @@ -1919,7 +1943,7 @@ ucl_inherit_handler (const unsigned char *data, size_t len, /* Some sanity checks */ if (parent == NULL || ucl_object_type (parent) != UCL_OBJECT) { - ucl_create_err (&parser->err, "Unable to find inherited object %*.s", + ucl_create_err (&parser->err, "Unable to find inherited object %.*s", (int)len, data); return false; } @@ -2177,7 +2201,7 @@ ucl_strnstr (const char *s, const char *find, int len) mlen = strlen (find); do { do { - if ((sc = *s++) == 0 || len-- == 0) + if ((sc = *s++) == 0 || len-- < mlen) return (NULL); } while (sc != c); } while (strncmp (s, find, mlen) != 0); @@ -2596,6 +2620,7 @@ ucl_object_merge (ucl_object_t *top, ucl_object_t *elt, bool copy) if (!ucl_object_merge (found, cp, copy)) { return false; } + ucl_object_unref (cp); } else { ucl_hash_replace (top->value.ov, found, cp); @@ -2627,6 +2652,7 @@ ucl_object_merge (ucl_object_t *top, ucl_object_t *elt, bool copy) if (!ucl_object_merge (found, cp, copy)) { return false; } + ucl_object_unref (cp); } else { ucl_hash_replace (top->value.ov, found, cp); @@ -3068,13 +3094,13 @@ ucl_object_type (const ucl_object_t *obj) ucl_object_t* ucl_object_fromstring (const char *str) { - return ucl_object_fromstring_common (str, 0, UCL_STRING_ESCAPE); + return ucl_object_fromstring_common (str, 0, UCL_STRING_RAW); } ucl_object_t * ucl_object_fromlstring (const char *str, size_t len) { - return ucl_object_fromstring_common (str, len, UCL_STRING_ESCAPE); + return ucl_object_fromstring_common (str, len, UCL_STRING_RAW); } ucl_object_t * @@ -3594,9 +3620,11 @@ ucl_object_copy_internal (const ucl_object_t *other, bool allow_array) /* deep copy of values stored */ if (other->trash_stack[UCL_TRASH_KEY] != NULL) { - new->trash_stack[UCL_TRASH_KEY] = - strdup (other->trash_stack[UCL_TRASH_KEY]); + new->trash_stack[UCL_TRASH_KEY] = NULL; if (other->key == (const char *)other->trash_stack[UCL_TRASH_KEY]) { + new->trash_stack[UCL_TRASH_KEY] = malloc(other->keylen + 1); + memcpy(new->trash_stack[UCL_TRASH_KEY], other->trash_stack[UCL_TRASH_KEY], other->keylen); + new->trash_stack[UCL_TRASH_KEY][other->keylen] = '\0'; new->key = new->trash_stack[UCL_TRASH_KEY]; } } @@ -3666,13 +3694,6 @@ ucl_object_compare (const ucl_object_t *o1, const ucl_object_t *o2) ucl_object_iter_t iter = NULL; int ret = 0; - // Must check for NULL or code will segfault - if ((o1 == NULL) || (o2 == NULL)) - { - // The only way this could be true is of both are NULL - return (o1 == NULL) && (o2 == NULL); - } - if (o1->type != o2->type) { return (o1->type) - (o2->type); } diff --git a/tests/schema/definitions.json b/tests/schema/definitions.json deleted file mode 100644 index 1ab9b2163c44..000000000000 --- a/tests/schema/definitions.json +++ /dev/null @@ -1,32 +0,0 @@ -[ - { - "description": "valid definition", - "schema": {"$ref": "http://highsecure.ru/ucl-schema/schema#"}, - "tests": [ - { - "description": "valid definition schema", - "data": { - "definitions": { - "foo": {"type": "integer"} - } - }, - "valid": true - } - ] - }, - { - "description": "invalid definition", - "schema": {"$ref": "http://highsecure.ru/ucl-schema/schema#"}, - "tests": [ - { - "description": "invalid definition schema", - "data": { - "definitions": { - "foo": {"type": 1} - } - }, - "valid": false - } - ] - } -] diff --git a/tests/schema/ref.json b/tests/schema/ref.json index 1767769cd845..d8214bc2b30c 100644 --- a/tests/schema/ref.json +++ b/tests/schema/ref.json @@ -124,21 +124,5 @@ "valid": false } ] - }, - { - "description": "remote ref, containing refs itself", - "schema": {"$ref": "http://highsecure.ru/ucl-schema/schema#"}, - "tests": [ - { - "description": "remote ref valid", - "data": {"minLength": 1}, - "valid": true - }, - { - "description": "remote ref invalid", - "data": {"minLength": -1}, - "valid": false - } - ] } ] diff --git a/tests/schema/refRemote.json b/tests/schema/refRemote.json deleted file mode 100644 index 067c666b0ec8..000000000000 --- a/tests/schema/refRemote.json +++ /dev/null @@ -1,76 +0,0 @@ -[ - { - "description": "remote ref", - "schema": {"$ref": "http://highsecure.ru/ucl-schema/remotes/integer.json"}, - "tests": [ - { - "description": "remote ref valid", - "data": 1, - "valid": true - }, - { - "description": "remote ref invalid", - "data": "a", - "valid": false - } - ] - }, - { - "description": "fragment within remote ref", - "schema": {"$ref": "http://highsecure.ru/ucl-schema/remotes/subSchemas.json#/integer"}, - "tests": [ - { - "description": "remote fragment valid", - "data": 1, - "valid": true - }, - { - "description": "remote fragment invalid", - "data": "a", - "valid": false - } - ] - }, - { - "description": "ref within remote ref", - "schema": { - "$ref": "http://highsecure.ru/ucl-schema/remotes/subSchemas.json#/refToInteger" - }, - "tests": [ - { - "description": "ref within ref valid", - "data": 1, - "valid": true - }, - { - "description": "ref within ref invalid", - "data": "a", - "valid": false - } - ] - } -/* - { - "description": "change resolution scope", - "schema": { - "id": "http://highsecure.ru/ucl-schema/remotes/", - "items": { - "id": "folder/", - "items": {"$ref": "folderInteger.json"} - } - }, - "tests": [ - { - "description": "changed scope ref valid", - "data": [[1]], - "valid": true - }, - { - "description": "changed scope ref invalid", - "data": [["a"]], - "valid": false - } - ] - } -*/ -] diff --git a/tests/test_speed.c b/tests/test_speed.c index 56f2e5abc6c7..51476c94940b 100644 --- a/tests/test_speed.c +++ b/tests/test_speed.c @@ -44,7 +44,7 @@ get_ticks (void) { double res; -#ifdef __APPLE__ +#if defined(__APPLE__) && defined(HAVE_MACH_MACH_TIME_H) res = mach_absolute_time () / 1000000000.; #else struct timespec ts; diff --git a/tests/test_streamline.c b/tests/test_streamline.c index 4c56c4cdcffd..37fe14f9fb97 100644 --- a/tests/test_streamline.c +++ b/tests/test_streamline.c @@ -26,6 +26,10 @@ #include <assert.h> #include "ucl.h" +#include <sys/types.h> +#include <fcntl.h> +#include <unistd.h> + int main (int argc, char **argv) { @@ -34,7 +38,28 @@ main (int argc, char **argv) const char *fname_out = NULL; struct ucl_emitter_context *ctx; struct ucl_emitter_functions *f; - int ret = 0; + int ret = 0, opt, json = 0, compact = 0, yaml = 0; + + while ((opt = getopt(argc, argv, "jcy")) != -1) { + switch (opt) { + case 'j': + json = 1; + break; + case 'c': + compact = 1; + break; + case 'y': + yaml = 1; + break; + default: /* '?' */ + fprintf (stderr, "Usage: %s [-jcy] [out]\n", + argv[0]); + exit (EXIT_FAILURE); + } + } + + argc -= optind; + argv += optind; switch (argc) { case 2: @@ -63,7 +88,21 @@ main (int argc, char **argv) ucl_object_insert_key (obj, cur, "key3", 0, false); f = ucl_object_emit_file_funcs (out); - ctx = ucl_object_emit_streamline_new (obj, UCL_EMIT_CONFIG, f); + + if (yaml) { + ctx = ucl_object_emit_streamline_new (obj, UCL_EMIT_YAML, f); + } + else if (json) { + if (compact) { + ctx = ucl_object_emit_streamline_new (obj, UCL_EMIT_JSON_COMPACT, f); + } + else { + ctx = ucl_object_emit_streamline_new (obj, UCL_EMIT_JSON, f); + } + } + else { + ctx = ucl_object_emit_streamline_new (obj, UCL_EMIT_CONFIG, f); + } assert (ctx != NULL); diff --git a/uthash/utlist.h b/uthash/utlist.h index c82dd916e2ed..08fc59ae6bef 100644 --- a/uthash/utlist.h +++ b/uthash/utlist.h @@ -1,5 +1,5 @@ /* -Copyright (c) 2007-2013, Troy D. Hanson http://troydhanson.github.com/uthash/ +Copyright (c) 2007-2022, Troy D. Hanson https://troydhanson.github.io/uthash/ All rights reserved. Redistribution and use in source and binary forms, with or without @@ -24,11 +24,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef UTLIST_H #define UTLIST_H -#define UTLIST_VERSION 1.9.8 +#define UTLIST_VERSION 2.3.0 #include <assert.h> -/* +/* * This file contains macros to manipulate singly and doubly-linked lists. * * 1. LL_ macros: singly-linked lists. @@ -38,7 +38,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * To use singly-linked lists, your structure must have a "next" pointer. * To use doubly-linked lists, your structure must "prev" and "next" pointers. * Either way, the pointer to the head of the list must be initialized to NULL. - * + * * ----------------.EXAMPLE ------------------------- * struct item { * int id; @@ -61,41 +61,46 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. /* These macros use decltype or the earlier __typeof GNU extension. As decltype is only available in newer compilers (VS2010 or gcc 4.3+ - when compiling c++ code), this code uses whatever method is needed + when compiling c++ source) this code uses whatever method is needed or, for VS2008 where neither is available, uses casting workarounds. */ -#ifdef _MSC_VER /* MS compiler */ +#if !defined(LDECLTYPE) && !defined(NO_DECLTYPE) +#if defined(_MSC_VER) /* MS compiler */ #if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ #define LDECLTYPE(x) decltype(x) -#else /* VS2008 or older (or VS2010 in C mode) */ +#else /* VS2008 or older (or VS2010 in C mode) */ #define NO_DECLTYPE -#define LDECLTYPE(x) char* #endif -#elif defined(__ICCARM__) +#elif defined(__MCST__) /* Elbrus C Compiler */ +#define LDECLTYPE(x) __typeof(x) +#elif defined(__BORLANDC__) || defined(__ICCARM__) || defined(__LCC__) || defined(__WATCOMC__) #define NO_DECLTYPE -#define LDECLTYPE(x) char* -#else /* GNU, Sun and other compilers */ +#else /* GNU, Sun and other compilers */ #define LDECLTYPE(x) __typeof(x) #endif +#endif /* for VS2008 we use some workarounds to get around the lack of decltype, * namely, we always reassign our tmp variable to the list head if we need * to dereference its prev/next pointers, and save/restore the real head.*/ #ifdef NO_DECLTYPE -#define _SV(elt,list) _tmp = (char*)(list); {char **_alias = (char**)&(list); *_alias = (elt); } -#define _NEXT(elt,list,next) ((char*)((list)->next)) -#define _NEXTASGN(elt,list,to,next) { char **_alias = (char**)&((list)->next); *_alias=(char*)(to); } -/* #define _PREV(elt,list,prev) ((char*)((list)->prev)) */ -#define _PREVASGN(elt,list,to,prev) { char **_alias = (char**)&((list)->prev); *_alias=(char*)(to); } -#define _RS(list) { char **_alias = (char**)&(list); *_alias=_tmp; } -#define _CASTASGN(a,b) { char **_alias = (char**)&(a); *_alias=(char*)(b); } -#else -#define _SV(elt,list) -#define _NEXT(elt,list,next) ((elt)->next) -#define _NEXTASGN(elt,list,to,next) ((elt)->next)=(to) -/* #define _PREV(elt,list,prev) ((elt)->prev) */ -#define _PREVASGN(elt,list,to,prev) ((elt)->prev)=(to) -#define _RS(list) -#define _CASTASGN(a,b) (a)=(b) +#define IF_NO_DECLTYPE(x) x +#define LDECLTYPE(x) char* +#define UTLIST_SV(elt,list) _tmp = (char*)(list); {char **_alias = (char**)&(list); *_alias = (elt); } +#define UTLIST_NEXT(elt,list,next) ((char*)((list)->next)) +#define UTLIST_NEXTASGN(elt,list,to,next) { char **_alias = (char**)&((list)->next); *_alias=(char*)(to); } +/* #define UTLIST_PREV(elt,list,prev) ((char*)((list)->prev)) */ +#define UTLIST_PREVASGN(elt,list,to,prev) { char **_alias = (char**)&((list)->prev); *_alias=(char*)(to); } +#define UTLIST_RS(list) { char **_alias = (char**)&(list); *_alias=_tmp; } +#define UTLIST_CASTASGN(a,b) { char **_alias = (char**)&(a); *_alias=(char*)(b); } +#else +#define IF_NO_DECLTYPE(x) +#define UTLIST_SV(elt,list) +#define UTLIST_NEXT(elt,list,next) ((elt)->next) +#define UTLIST_NEXTASGN(elt,list,to,next) ((elt)->next)=(to) +/* #define UTLIST_PREV(elt,list,prev) ((elt)->prev) */ +#define UTLIST_PREVASGN(elt,list,to,prev) ((elt)->prev)=(to) +#define UTLIST_RS(list) +#define UTLIST_CASTASGN(a,b) (a)=(b) #endif /****************************************************************************** @@ -111,13 +116,14 @@ do { LDECLTYPE(list) _ls_q; \ LDECLTYPE(list) _ls_e; \ LDECLTYPE(list) _ls_tail; \ + IF_NO_DECLTYPE(LDECLTYPE(list) _tmp;) \ int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ if (list) { \ _ls_insize = 1; \ _ls_looping = 1; \ while (_ls_looping) { \ - _CASTASGN(_ls_p,list); \ - list = NULL; \ + UTLIST_CASTASGN(_ls_p,list); \ + (list) = NULL; \ _ls_tail = NULL; \ _ls_nmerges = 0; \ while (_ls_p) { \ @@ -126,35 +132,35 @@ do { _ls_psize = 0; \ for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ _ls_psize++; \ - _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list,next); _RS(list); \ + UTLIST_SV(_ls_q,list); _ls_q = UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); \ if (!_ls_q) break; \ } \ _ls_qsize = _ls_insize; \ while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ if (_ls_psize == 0) { \ - _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \ - _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \ + _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ + UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ } else if (_ls_qsize == 0 || !_ls_q) { \ - _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \ - _NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \ + _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ + UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ } else if (cmp(_ls_p,_ls_q) <= 0) { \ - _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \ - _NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \ + _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ + UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ } else { \ - _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \ - _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \ + _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ + UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ } \ if (_ls_tail) { \ - _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e,next); _RS(list); \ + UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,_ls_e,next); UTLIST_RS(list); \ } else { \ - _CASTASGN(list,_ls_e); \ + UTLIST_CASTASGN(list,_ls_e); \ } \ _ls_tail = _ls_e; \ } \ _ls_p = _ls_q; \ } \ if (_ls_tail) { \ - _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,NULL,next); _RS(list); \ + UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,NULL,next); UTLIST_RS(list); \ } \ if (_ls_nmerges <= 1) { \ _ls_looping=0; \ @@ -174,13 +180,14 @@ do { LDECLTYPE(list) _ls_q; \ LDECLTYPE(list) _ls_e; \ LDECLTYPE(list) _ls_tail; \ + IF_NO_DECLTYPE(LDECLTYPE(list) _tmp;) \ int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ if (list) { \ _ls_insize = 1; \ _ls_looping = 1; \ while (_ls_looping) { \ - _CASTASGN(_ls_p,list); \ - list = NULL; \ + UTLIST_CASTASGN(_ls_p,list); \ + (list) = NULL; \ _ls_tail = NULL; \ _ls_nmerges = 0; \ while (_ls_p) { \ @@ -189,36 +196,36 @@ do { _ls_psize = 0; \ for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ _ls_psize++; \ - _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list,next); _RS(list); \ + UTLIST_SV(_ls_q,list); _ls_q = UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); \ if (!_ls_q) break; \ } \ _ls_qsize = _ls_insize; \ - while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ + while ((_ls_psize > 0) || ((_ls_qsize > 0) && _ls_q)) { \ if (_ls_psize == 0) { \ - _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \ - _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \ - } else if (_ls_qsize == 0 || !_ls_q) { \ - _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \ - _NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \ + _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ + UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ + } else if ((_ls_qsize == 0) || (!_ls_q)) { \ + _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ + UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ } else if (cmp(_ls_p,_ls_q) <= 0) { \ - _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \ - _NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \ + _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ + UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ } else { \ - _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \ - _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \ + _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ + UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ } \ if (_ls_tail) { \ - _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e,next); _RS(list); \ + UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,_ls_e,next); UTLIST_RS(list); \ } else { \ - _CASTASGN(list,_ls_e); \ + UTLIST_CASTASGN(list,_ls_e); \ } \ - _SV(_ls_e,list); _PREVASGN(_ls_e,list,_ls_tail,prev); _RS(list); \ + UTLIST_SV(_ls_e,list); UTLIST_PREVASGN(_ls_e,list,_ls_tail,prev); UTLIST_RS(list); \ _ls_tail = _ls_e; \ } \ _ls_p = _ls_q; \ } \ - _CASTASGN(list->prev, _ls_tail); \ - _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,NULL,next); _RS(list); \ + UTLIST_CASTASGN((list)->prev, _ls_tail); \ + UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,NULL,next); UTLIST_RS(list); \ if (_ls_nmerges <= 1) { \ _ls_looping=0; \ } \ @@ -243,9 +250,9 @@ do { _ls_insize = 1; \ _ls_looping = 1; \ while (_ls_looping) { \ - _CASTASGN(_ls_p,list); \ - _CASTASGN(_ls_oldhead,list); \ - list = NULL; \ + UTLIST_CASTASGN(_ls_p,list); \ + UTLIST_CASTASGN(_ls_oldhead,list); \ + (list) = NULL; \ _ls_tail = NULL; \ _ls_nmerges = 0; \ while (_ls_p) { \ @@ -254,47 +261,47 @@ do { _ls_psize = 0; \ for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ _ls_psize++; \ - _SV(_ls_q,list); \ - if (_NEXT(_ls_q,list,next) == _ls_oldhead) { \ + UTLIST_SV(_ls_q,list); \ + if (UTLIST_NEXT(_ls_q,list,next) == _ls_oldhead) { \ _ls_q = NULL; \ } else { \ - _ls_q = _NEXT(_ls_q,list,next); \ + _ls_q = UTLIST_NEXT(_ls_q,list,next); \ } \ - _RS(list); \ + UTLIST_RS(list); \ if (!_ls_q) break; \ } \ _ls_qsize = _ls_insize; \ while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ if (_ls_psize == 0) { \ - _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \ - _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \ + _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ + UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \ } else if (_ls_qsize == 0 || !_ls_q) { \ - _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \ - _NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \ + _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ + UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \ } else if (cmp(_ls_p,_ls_q) <= 0) { \ - _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \ - _NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \ + _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ + UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \ } else { \ - _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \ - _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \ + _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ + UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \ } \ if (_ls_tail) { \ - _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e,next); _RS(list); \ + UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,_ls_e,next); UTLIST_RS(list); \ } else { \ - _CASTASGN(list,_ls_e); \ + UTLIST_CASTASGN(list,_ls_e); \ } \ - _SV(_ls_e,list); _PREVASGN(_ls_e,list,_ls_tail,prev); _RS(list); \ + UTLIST_SV(_ls_e,list); UTLIST_PREVASGN(_ls_e,list,_ls_tail,prev); UTLIST_RS(list); \ _ls_tail = _ls_e; \ } \ _ls_p = _ls_q; \ } \ - _CASTASGN(list->prev,_ls_tail); \ - _CASTASGN(_tmp,list); \ - _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_tmp,next); _RS(list); \ + UTLIST_CASTASGN((list)->prev,_ls_tail); \ + UTLIST_CASTASGN(_tmp,list); \ + UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,_tmp,next); UTLIST_RS(list); \ if (_ls_nmerges <= 1) { \ _ls_looping=0; \ } \ @@ -311,8 +318,8 @@ do { #define LL_PREPEND2(head,add,next) \ do { \ - (add)->next = head; \ - head = add; \ + (add)->next = (head); \ + (head) = (add); \ } while (0) #define LL_CONCAT(head1,head2) \ @@ -322,7 +329,7 @@ do { do { \ LDECLTYPE(head1) _tmp; \ if (head1) { \ - _tmp = head1; \ + _tmp = (head1); \ while (_tmp->next) { _tmp = _tmp->next; } \ _tmp->next=(head2); \ } else { \ @@ -338,7 +345,7 @@ do { LDECLTYPE(head) _tmp; \ (add)->next=NULL; \ if (head) { \ - _tmp = head; \ + _tmp = (head); \ while (_tmp->next) { _tmp = _tmp->next; } \ _tmp->next=(add); \ } else { \ @@ -346,96 +353,76 @@ do { } \ } while (0) -#define LL_DELETE(head,del) \ - LL_DELETE2(head,del,next) +#define LL_INSERT_INORDER(head,add,cmp) \ + LL_INSERT_INORDER2(head,add,cmp,next) -#define LL_DELETE2(head,del,next) \ +#define LL_INSERT_INORDER2(head,add,cmp,next) \ do { \ LDECLTYPE(head) _tmp; \ - if ((head) == (del)) { \ - (head)=(head)->next; \ + if (head) { \ + LL_LOWER_BOUND2(head, _tmp, add, cmp, next); \ + LL_APPEND_ELEM2(head, _tmp, add, next); \ } else { \ - _tmp = head; \ - while (_tmp->next && (_tmp->next != (del))) { \ - _tmp = _tmp->next; \ - } \ - if (_tmp->next) { \ - _tmp->next = ((del)->next); \ - } \ + (head) = (add); \ + (head)->next = NULL; \ } \ } while (0) -/* Here are VS2008 replacements for LL_APPEND and LL_DELETE */ -#define LL_APPEND_VS2008(head,add) \ - LL_APPEND2_VS2008(head,add,next) +#define LL_LOWER_BOUND(head,elt,like,cmp) \ + LL_LOWER_BOUND2(head,elt,like,cmp,next) -#define LL_APPEND2_VS2008(head,add,next) \ -do { \ - if (head) { \ - (add)->next = head; /* use add->next as a temp variable */ \ - while ((add)->next->next) { (add)->next = (add)->next->next; } \ - (add)->next->next=(add); \ - } else { \ - (head)=(add); \ - } \ - (add)->next=NULL; \ -} while (0) +#define LL_LOWER_BOUND2(head,elt,like,cmp,next) \ + do { \ + if ((head) == NULL || (cmp(head, like)) >= 0) { \ + (elt) = NULL; \ + } else { \ + for ((elt) = (head); (elt)->next != NULL; (elt) = (elt)->next) { \ + if (cmp((elt)->next, like) >= 0) { \ + break; \ + } \ + } \ + } \ + } while (0) -#define LL_DELETE_VS2008(head,del) \ - LL_DELETE2_VS2008(head,del,next) +#define LL_DELETE(head,del) \ + LL_DELETE2(head,del,next) -#define LL_DELETE2_VS2008(head,del,next) \ +#define LL_DELETE2(head,del,next) \ do { \ + LDECLTYPE(head) _tmp; \ if ((head) == (del)) { \ (head)=(head)->next; \ } else { \ - char *_tmp = (char*)(head); \ - while ((head)->next && ((head)->next != (del))) { \ - head = (head)->next; \ - } \ - if ((head)->next) { \ - (head)->next = ((del)->next); \ + _tmp = (head); \ + while (_tmp->next && (_tmp->next != (del))) { \ + _tmp = _tmp->next; \ } \ - { \ - char **_head_alias = (char**)&(head); \ - *_head_alias = _tmp; \ + if (_tmp->next) { \ + _tmp->next = (del)->next; \ } \ } \ } while (0) -#ifdef NO_DECLTYPE -#undef LL_APPEND -#define LL_APPEND LL_APPEND_VS2008 -#undef LL_DELETE -#define LL_DELETE LL_DELETE_VS2008 -#undef LL_DELETE2 -#define LL_DELETE2 LL_DELETE2_VS2008 -#undef LL_APPEND2 -#define LL_APPEND2 LL_APPEND2_VS2008 -#undef LL_CONCAT /* no LL_CONCAT_VS2008 */ -#undef DL_CONCAT /* no DL_CONCAT_VS2008 */ -#endif -/* end VS2008 replacements */ #define LL_COUNT(head,el,counter) \ LL_COUNT2(head,el,counter,next) \ #define LL_COUNT2(head,el,counter,next) \ -{ \ - counter = 0; \ - LL_FOREACH2(head,el,next){ ++counter; } \ -} +do { \ + (counter) = 0; \ + LL_FOREACH2(head,el,next) { ++(counter); } \ +} while (0) #define LL_FOREACH(head,el) \ LL_FOREACH2(head,el,next) #define LL_FOREACH2(head,el,next) \ - for(el=head;el;el=(el)->next) + for ((el) = (head); el; (el) = (el)->next) #define LL_FOREACH_SAFE(head,el,tmp) \ LL_FOREACH_SAFE2(head,el,tmp,next) #define LL_FOREACH_SAFE2(head,el,tmp,next) \ - for((el)=(head);(el) && (tmp = (el)->next, 1); (el) = tmp) + for ((el) = (head); (el) && ((tmp) = (el)->next, 1); (el) = (tmp)) #define LL_SEARCH_SCALAR(head,out,field,val) \ LL_SEARCH_SCALAR2(head,out,field,val,next) @@ -445,7 +432,7 @@ do { LL_FOREACH2(head,out,next) { \ if ((out)->field == (val)) break; \ } \ -} while(0) +} while (0) #define LL_SEARCH(head,out,elt,cmp) \ LL_SEARCH2(head,out,elt,cmp,next) @@ -455,19 +442,19 @@ do { LL_FOREACH2(head,out,next) { \ if ((cmp(out,elt))==0) break; \ } \ -} while(0) +} while (0) -#define LL_REPLACE_ELEM(head, el, add) \ +#define LL_REPLACE_ELEM2(head, el, add, next) \ do { \ LDECLTYPE(head) _tmp; \ - assert(head != NULL); \ - assert(el != NULL); \ - assert(add != NULL); \ + assert((head) != NULL); \ + assert((el) != NULL); \ + assert((add) != NULL); \ (add)->next = (el)->next; \ if ((head) == (el)) { \ (head) = (add); \ } else { \ - _tmp = head; \ + _tmp = (head); \ while (_tmp->next && (_tmp->next != (el))) { \ _tmp = _tmp->next; \ } \ @@ -477,26 +464,158 @@ do { } \ } while (0) +#define LL_REPLACE_ELEM(head, el, add) \ + LL_REPLACE_ELEM2(head, el, add, next) + +#define LL_PREPEND_ELEM2(head, el, add, next) \ +do { \ + if (el) { \ + LDECLTYPE(head) _tmp; \ + assert((head) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el); \ + if ((head) == (el)) { \ + (head) = (add); \ + } else { \ + _tmp = (head); \ + while (_tmp->next && (_tmp->next != (el))) { \ + _tmp = _tmp->next; \ + } \ + if (_tmp->next) { \ + _tmp->next = (add); \ + } \ + } \ + } else { \ + LL_APPEND2(head, add, next); \ + } \ +} while (0) \ + #define LL_PREPEND_ELEM(head, el, add) \ + LL_PREPEND_ELEM2(head, el, add, next) + +#define LL_APPEND_ELEM2(head, el, add, next) \ do { \ - LDECLTYPE(head) _tmp; \ - assert(head != NULL); \ - assert(el != NULL); \ - assert(add != NULL); \ - (add)->next = (el); \ - if ((head) == (el)) { \ - (head) = (add); \ + if (el) { \ + assert((head) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el)->next; \ + (el)->next = (add); \ } else { \ - _tmp = head; \ - while (_tmp->next && (_tmp->next != (el))) { \ - _tmp = _tmp->next; \ + LL_PREPEND2(head, add, next); \ + } \ +} while (0) \ + +#define LL_APPEND_ELEM(head, el, add) \ + LL_APPEND_ELEM2(head, el, add, next) + +#ifdef NO_DECLTYPE +/* Here are VS2008 / NO_DECLTYPE replacements for a few functions */ + +#undef LL_CONCAT2 +#define LL_CONCAT2(head1,head2,next) \ +do { \ + char *_tmp; \ + if (head1) { \ + _tmp = (char*)(head1); \ + while ((head1)->next) { (head1) = (head1)->next; } \ + (head1)->next = (head2); \ + UTLIST_RS(head1); \ + } else { \ + (head1)=(head2); \ } \ - if (_tmp->next) { \ - _tmp->next = (add); \ +} while (0) + +#undef LL_APPEND2 +#define LL_APPEND2(head,add,next) \ +do { \ + if (head) { \ + (add)->next = head; /* use add->next as a temp variable */ \ + while ((add)->next->next) { (add)->next = (add)->next->next; } \ + (add)->next->next=(add); \ + } else { \ + (head)=(add); \ + } \ + (add)->next=NULL; \ +} while (0) + +#undef LL_INSERT_INORDER2 +#define LL_INSERT_INORDER2(head,add,cmp,next) \ +do { \ + if ((head) == NULL || (cmp(head, add)) >= 0) { \ + (add)->next = (head); \ + (head) = (add); \ + } else { \ + char *_tmp = (char*)(head); \ + while ((head)->next != NULL && (cmp((head)->next, add)) < 0) { \ + (head) = (head)->next; \ + } \ + (add)->next = (head)->next; \ + (head)->next = (add); \ + UTLIST_RS(head); \ + } \ +} while (0) + +#undef LL_DELETE2 +#define LL_DELETE2(head,del,next) \ +do { \ + if ((head) == (del)) { \ + (head)=(head)->next; \ + } else { \ + char *_tmp = (char*)(head); \ + while ((head)->next && ((head)->next != (del))) { \ + (head) = (head)->next; \ + } \ + if ((head)->next) { \ + (head)->next = ((del)->next); \ + } \ + UTLIST_RS(head); \ + } \ +} while (0) + +#undef LL_REPLACE_ELEM2 +#define LL_REPLACE_ELEM2(head, el, add, next) \ +do { \ + assert((head) != NULL); \ + assert((el) != NULL); \ + assert((add) != NULL); \ + if ((head) == (el)) { \ + (head) = (add); \ + } else { \ + (add)->next = head; \ + while ((add)->next->next && ((add)->next->next != (el))) { \ + (add)->next = (add)->next->next; \ + } \ + if ((add)->next->next) { \ + (add)->next->next = (add); \ + } \ + } \ + (add)->next = (el)->next; \ +} while (0) + +#undef LL_PREPEND_ELEM2 +#define LL_PREPEND_ELEM2(head, el, add, next) \ +do { \ + if (el) { \ + assert((head) != NULL); \ + assert((add) != NULL); \ + if ((head) == (el)) { \ + (head) = (add); \ + } else { \ + (add)->next = (head); \ + while ((add)->next->next && ((add)->next->next != (el))) { \ + (add)->next = (add)->next->next; \ + } \ + if ((add)->next->next) { \ + (add)->next->next = (add); \ + } \ + } \ + (add)->next = (el); \ + } else { \ + LL_APPEND2(head, add, next); \ } \ - } \ } while (0) \ +#endif /* NO_DECLTYPE */ /****************************************************************************** * doubly linked list macros (non-circular) * @@ -506,7 +625,7 @@ do { #define DL_PREPEND2(head,add,prev,next) \ do { \ - (add)->next = head; \ + (add)->next = (head); \ if (head) { \ (add)->prev = (head)->prev; \ (head)->prev = (add); \ @@ -531,7 +650,39 @@ do { (head)->prev = (head); \ (head)->next = NULL; \ } \ -} while (0) +} while (0) + +#define DL_INSERT_INORDER(head,add,cmp) \ + DL_INSERT_INORDER2(head,add,cmp,prev,next) + +#define DL_INSERT_INORDER2(head,add,cmp,prev,next) \ +do { \ + LDECLTYPE(head) _tmp; \ + if (head) { \ + DL_LOWER_BOUND2(head, _tmp, add, cmp, next); \ + DL_APPEND_ELEM2(head, _tmp, add, prev, next); \ + } else { \ + (head) = (add); \ + (head)->prev = (head); \ + (head)->next = NULL; \ + } \ +} while (0) + +#define DL_LOWER_BOUND(head,elt,like,cmp) \ + DL_LOWER_BOUND2(head,elt,like,cmp,next) + +#define DL_LOWER_BOUND2(head,elt,like,cmp,next) \ +do { \ + if ((head) == NULL || (cmp(head, like)) >= 0) { \ + (elt) = NULL; \ + } else { \ + for ((elt) = (head); (elt)->next != NULL; (elt) = (elt)->next) { \ + if ((cmp((elt)->next, like)) >= 0) { \ + break; \ + } \ + } \ + } \ +} while (0) #define DL_CONCAT(head1,head2) \ DL_CONCAT2(head1,head2,prev,next) @@ -541,25 +692,27 @@ do { LDECLTYPE(head1) _tmp; \ if (head2) { \ if (head1) { \ - _tmp = (head2)->prev; \ + UTLIST_CASTASGN(_tmp, (head2)->prev); \ (head2)->prev = (head1)->prev; \ (head1)->prev->next = (head2); \ - (head1)->prev = _tmp; \ + UTLIST_CASTASGN((head1)->prev, _tmp); \ } else { \ (head1)=(head2); \ } \ } \ -} while (0) +} while (0) #define DL_DELETE(head,del) \ DL_DELETE2(head,del,prev,next) #define DL_DELETE2(head,del,prev,next) \ do { \ + assert((head) != NULL); \ assert((del)->prev != NULL); \ if ((del)->prev == (del)) { \ (head)=NULL; \ - } else if ((del)==(head)) { \ + } else if ((del) == (head)) { \ + assert((del)->next != NULL); \ (del)->next->prev = (del)->prev; \ (head) = (del)->next; \ } else { \ @@ -570,29 +723,29 @@ do { (head)->prev = (del)->prev; \ } \ } \ -} while (0) +} while (0) #define DL_COUNT(head,el,counter) \ DL_COUNT2(head,el,counter,next) \ #define DL_COUNT2(head,el,counter,next) \ -{ \ - counter = 0; \ - DL_FOREACH2(head,el,next){ ++counter; } \ -} +do { \ + (counter) = 0; \ + DL_FOREACH2(head,el,next) { ++(counter); } \ +} while (0) #define DL_FOREACH(head,el) \ DL_FOREACH2(head,el,next) #define DL_FOREACH2(head,el,next) \ - for(el=head;el;el=(el)->next) + for ((el) = (head); el; (el) = (el)->next) /* this version is safe for deleting the elements during iteration */ #define DL_FOREACH_SAFE(head,el,tmp) \ DL_FOREACH_SAFE2(head,el,tmp,next) #define DL_FOREACH_SAFE2(head,el,tmp,next) \ - for((el)=(head);(el) && (tmp = (el)->next, 1); (el) = tmp) + for ((el) = (head); (el) && ((tmp) = (el)->next, 1); (el) = (tmp)) /* these are identical to their singly-linked list counterparts */ #define DL_SEARCH_SCALAR LL_SEARCH_SCALAR @@ -600,11 +753,11 @@ do { #define DL_SEARCH_SCALAR2 LL_SEARCH_SCALAR2 #define DL_SEARCH2 LL_SEARCH2 -#define DL_REPLACE_ELEM(head, el, add) \ +#define DL_REPLACE_ELEM2(head, el, add, prev, next) \ do { \ - assert(head != NULL); \ - assert(el != NULL); \ - assert(add != NULL); \ + assert((head) != NULL); \ + assert((el) != NULL); \ + assert((add) != NULL); \ if ((head) == (el)) { \ (head) = (add); \ (add)->next = (el)->next; \ @@ -626,25 +779,104 @@ do { } \ } while (0) +#define DL_REPLACE_ELEM(head, el, add) \ + DL_REPLACE_ELEM2(head, el, add, prev, next) + +#define DL_PREPEND_ELEM2(head, el, add, prev, next) \ +do { \ + if (el) { \ + assert((head) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el); \ + (add)->prev = (el)->prev; \ + (el)->prev = (add); \ + if ((head) == (el)) { \ + (head) = (add); \ + } else { \ + (add)->prev->next = (add); \ + } \ + } else { \ + DL_APPEND2(head, add, prev, next); \ + } \ +} while (0) \ + #define DL_PREPEND_ELEM(head, el, add) \ + DL_PREPEND_ELEM2(head, el, add, prev, next) + +#define DL_APPEND_ELEM2(head, el, add, prev, next) \ do { \ - assert(head != NULL); \ - assert(el != NULL); \ - assert(add != NULL); \ - (add)->next = (el); \ - (add)->prev = (el)->prev; \ - (el)->prev = (add); \ - if ((head) == (el)) { \ - (head) = (add); \ + if (el) { \ + assert((head) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el)->next; \ + (add)->prev = (el); \ + (el)->next = (add); \ + if ((add)->next) { \ + (add)->next->prev = (add); \ + } else { \ + (head)->prev = (add); \ + } \ } else { \ - (add)->prev->next = (add); \ + DL_PREPEND2(head, add, prev, next); \ } \ } while (0) \ +#define DL_APPEND_ELEM(head, el, add) \ + DL_APPEND_ELEM2(head, el, add, prev, next) + +#ifdef NO_DECLTYPE +/* Here are VS2008 / NO_DECLTYPE replacements for a few functions */ + +#undef DL_INSERT_INORDER2 +#define DL_INSERT_INORDER2(head,add,cmp,prev,next) \ +do { \ + if ((head) == NULL) { \ + (add)->prev = (add); \ + (add)->next = NULL; \ + (head) = (add); \ + } else if ((cmp(head, add)) >= 0) { \ + (add)->prev = (head)->prev; \ + (add)->next = (head); \ + (head)->prev = (add); \ + (head) = (add); \ + } else { \ + char *_tmp = (char*)(head); \ + while ((head)->next && (cmp((head)->next, add)) < 0) { \ + (head) = (head)->next; \ + } \ + (add)->prev = (head); \ + (add)->next = (head)->next; \ + (head)->next = (add); \ + UTLIST_RS(head); \ + if ((add)->next) { \ + (add)->next->prev = (add); \ + } else { \ + (head)->prev = (add); \ + } \ + } \ +} while (0) +#endif /* NO_DECLTYPE */ /****************************************************************************** * circular doubly linked list macros * *****************************************************************************/ +#define CDL_APPEND(head,add) \ + CDL_APPEND2(head,add,prev,next) + +#define CDL_APPEND2(head,add,prev,next) \ +do { \ + if (head) { \ + (add)->prev = (head)->prev; \ + (add)->next = (head); \ + (head)->prev = (add); \ + (add)->prev->next = (add); \ + } else { \ + (add)->prev = (add); \ + (add)->next = (add); \ + (head) = (add); \ + } \ +} while (0) + #define CDL_PREPEND(head,add) \ CDL_PREPEND2(head,add,prev,next) @@ -659,7 +891,39 @@ do { (add)->prev = (add); \ (add)->next = (add); \ } \ -(head)=(add); \ + (head) = (add); \ +} while (0) + +#define CDL_INSERT_INORDER(head,add,cmp) \ + CDL_INSERT_INORDER2(head,add,cmp,prev,next) + +#define CDL_INSERT_INORDER2(head,add,cmp,prev,next) \ +do { \ + LDECLTYPE(head) _tmp; \ + if (head) { \ + CDL_LOWER_BOUND2(head, _tmp, add, cmp, next); \ + CDL_APPEND_ELEM2(head, _tmp, add, prev, next); \ + } else { \ + (head) = (add); \ + (head)->next = (head); \ + (head)->prev = (head); \ + } \ +} while (0) + +#define CDL_LOWER_BOUND(head,elt,like,cmp) \ + CDL_LOWER_BOUND2(head,elt,like,cmp,next) + +#define CDL_LOWER_BOUND2(head,elt,like,cmp,next) \ +do { \ + if ((head) == NULL || (cmp(head, like)) >= 0) { \ + (elt) = NULL; \ + } else { \ + for ((elt) = (head); (elt)->next != (head); (elt) = (elt)->next) { \ + if ((cmp((elt)->next, like)) >= 0) { \ + break; \ + } \ + } \ + } \ } while (0) #define CDL_DELETE(head,del) \ @@ -667,37 +931,37 @@ do { #define CDL_DELETE2(head,del,prev,next) \ do { \ - if ( ((head)==(del)) && ((head)->next == (head))) { \ - (head) = 0L; \ + if (((head)==(del)) && ((head)->next == (head))) { \ + (head) = NULL; \ } else { \ (del)->next->prev = (del)->prev; \ (del)->prev->next = (del)->next; \ if ((del) == (head)) (head)=(del)->next; \ } \ -} while (0) +} while (0) #define CDL_COUNT(head,el,counter) \ CDL_COUNT2(head,el,counter,next) \ #define CDL_COUNT2(head, el, counter,next) \ -{ \ - counter = 0; \ - CDL_FOREACH2(head,el,next){ ++counter; } \ -} +do { \ + (counter) = 0; \ + CDL_FOREACH2(head,el,next) { ++(counter); } \ +} while (0) #define CDL_FOREACH(head,el) \ CDL_FOREACH2(head,el,next) #define CDL_FOREACH2(head,el,next) \ - for(el=head;el;el=((el)->next==head ? 0L : (el)->next)) + for ((el)=(head);el;(el)=(((el)->next==(head)) ? NULL : (el)->next)) #define CDL_FOREACH_SAFE(head,el,tmp1,tmp2) \ CDL_FOREACH_SAFE2(head,el,tmp1,tmp2,prev,next) #define CDL_FOREACH_SAFE2(head,el,tmp1,tmp2,prev,next) \ - for((el)=(head), ((tmp1)=(head)?((head)->prev):NULL); \ - (el) && ((tmp2)=(el)->next, 1); \ - ((el) = (((el)==(tmp1)) ? 0L : (tmp2)))) + for ((el) = (head), (tmp1) = (head) ? (head)->prev : NULL; \ + (el) && ((tmp2) = (el)->next, 1); \ + (el) = ((el) == (tmp1) ? NULL : (tmp2))) #define CDL_SEARCH_SCALAR(head,out,field,val) \ CDL_SEARCH_SCALAR2(head,out,field,val,next) @@ -707,7 +971,7 @@ do { CDL_FOREACH2(head,out,next) { \ if ((out)->field == (val)) break; \ } \ -} while(0) +} while (0) #define CDL_SEARCH(head,out,elt,cmp) \ CDL_SEARCH2(head,out,elt,cmp,next) @@ -717,13 +981,13 @@ do { CDL_FOREACH2(head,out,next) { \ if ((cmp(out,elt))==0) break; \ } \ -} while(0) +} while (0) -#define CDL_REPLACE_ELEM(head, el, add) \ +#define CDL_REPLACE_ELEM2(head, el, add, prev, next) \ do { \ - assert(head != NULL); \ - assert(el != NULL); \ - assert(add != NULL); \ + assert((head) != NULL); \ + assert((el) != NULL); \ + assert((add) != NULL); \ if ((el)->next == (el)) { \ (add)->next = (add); \ (add)->prev = (add); \ @@ -739,19 +1003,74 @@ do { } \ } while (0) +#define CDL_REPLACE_ELEM(head, el, add) \ + CDL_REPLACE_ELEM2(head, el, add, prev, next) + +#define CDL_PREPEND_ELEM2(head, el, add, prev, next) \ +do { \ + if (el) { \ + assert((head) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el); \ + (add)->prev = (el)->prev; \ + (el)->prev = (add); \ + (add)->prev->next = (add); \ + if ((head) == (el)) { \ + (head) = (add); \ + } \ + } else { \ + CDL_APPEND2(head, add, prev, next); \ + } \ +} while (0) + #define CDL_PREPEND_ELEM(head, el, add) \ + CDL_PREPEND_ELEM2(head, el, add, prev, next) + +#define CDL_APPEND_ELEM2(head, el, add, prev, next) \ do { \ - assert(head != NULL); \ - assert(el != NULL); \ - assert(add != NULL); \ - (add)->next = (el); \ - (add)->prev = (el)->prev; \ - (el)->prev = (add); \ - (add)->prev->next = (add); \ - if ((head) == (el)) { \ - (head) = (add); \ + if (el) { \ + assert((head) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el)->next; \ + (add)->prev = (el); \ + (el)->next = (add); \ + (add)->next->prev = (add); \ + } else { \ + CDL_PREPEND2(head, add, prev, next); \ } \ -} while (0) \ +} while (0) -#endif /* UTLIST_H */ +#define CDL_APPEND_ELEM(head, el, add) \ + CDL_APPEND_ELEM2(head, el, add, prev, next) + +#ifdef NO_DECLTYPE +/* Here are VS2008 / NO_DECLTYPE replacements for a few functions */ +#undef CDL_INSERT_INORDER2 +#define CDL_INSERT_INORDER2(head,add,cmp,prev,next) \ +do { \ + if ((head) == NULL) { \ + (add)->prev = (add); \ + (add)->next = (add); \ + (head) = (add); \ + } else if ((cmp(head, add)) >= 0) { \ + (add)->prev = (head)->prev; \ + (add)->next = (head); \ + (add)->prev->next = (add); \ + (head)->prev = (add); \ + (head) = (add); \ + } else { \ + char *_tmp = (char*)(head); \ + while ((char*)(head)->next != _tmp && (cmp((head)->next, add)) < 0) { \ + (head) = (head)->next; \ + } \ + (add)->prev = (head); \ + (add)->next = (head)->next; \ + (add)->next->prev = (add); \ + (head)->next = (add); \ + UTLIST_RS(head); \ + } \ +} while (0) +#endif /* NO_DECLTYPE */ + +#endif /* UTLIST_H */ |