aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBaptiste Daroussin <bapt@FreeBSD.org>2021-03-22 14:07:18 +0000
committerBaptiste Daroussin <bapt@FreeBSD.org>2021-03-22 14:07:18 +0000
commit3c319408d0de2d2de0c19b24e1a41c0a0e4a823b (patch)
treede0b27939e96ed7a8b3554a149b321ef719cc875
parent8392e70f8a07e517fab31f8300cfaf5f02bea0f5 (diff)
downloadsrc-3c319408d0de2d2de0c19b24e1a41c0a0e4a823b.tar.gz
src-3c319408d0de2d2de0c19b24e1a41c0a0e4a823b.zip
libucl: import latest snapshot from 2021-03-14vendor/libucl/20210314
-rw-r--r--CMakeLists.txt105
-rw-r--r--ChangeLog.md38
-rw-r--r--README.md64
-rw-r--r--configure.ac8
-rw-r--r--doc/api.md17
-rw-r--r--doc/libucl.326
-rw-r--r--doc/lua_api.md4
-rw-r--r--include/lua_ucl.h20
-rw-r--r--include/ucl++.h191
-rw-r--r--include/ucl.h191
-rw-r--r--klib/kvec.h78
-rw-r--r--lua/lua_ucl.c450
-rw-r--r--python/MANIFEST.in5
-rw-r--r--python/setup.py42
-rw-r--r--python/src/uclmodule.c3
-rw-r--r--python/tests/test_example.py59
-rw-r--r--python/tests/test_load.py17
-rw-r--r--src/mum.h8
-rw-r--r--src/ucl_chartable.h4
-rw-r--r--src/ucl_emitter.c12
-rw-r--r--src/ucl_emitter_utils.c57
-rw-r--r--src/ucl_hash.c218
-rw-r--r--src/ucl_hash.h20
-rw-r--r--src/ucl_internal.h105
-rw-r--r--src/ucl_msgpack.c82
-rw-r--r--src/ucl_parser.c552
-rw-r--r--src/ucl_schema.c29
-rw-r--r--src/ucl_util.c780
-rw-r--r--tests/.gitignore2
-rwxr-xr-xtests/basic.test2
-rw-r--r--tests/basic/13.in2
-rw-r--r--tests/basic/20.in2
-rw-r--r--tests/basic/20.res5
-rw-r--r--tests/basic/21.in2
-rw-r--r--tests/basic/21.res10
-rw-r--r--tests/basic/9.in2
-rw-r--r--tests/basic/9.res8
-rw-r--r--tests/basic/squote.in8
-rw-r--r--tests/basic/squote.res7
-rw-r--r--tests/fuzzers/ucl_add_string_fuzzer.c25
-rw-r--r--tests/fuzzers/ucl_msgpack_fuzzer.c29
-rwxr-xr-xtests/generate.test2
-rwxr-xr-xtests/run_tests.sh4
-rwxr-xr-xtests/streamline.test2
-rw-r--r--tests/test_basic.c11
-rw-r--r--tests/test_generate.c15
-rw-r--r--tests/test_msgpack.c1
-rw-r--r--utils/CMakeLists.txt12
-rw-r--r--utils/objdump.c17
-rw-r--r--utils/ucl-tool.c100
50 files changed, 2822 insertions, 631 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7b55faf8243f..5fe772a30f88 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -9,13 +9,16 @@ SET(LIBUCL_VERSION
"${LIBUCL_VERSION_MAJOR}.${LIBUCL_VERSION_MINOR}.${LIBUCL_VERSION_PATCH}")
INCLUDE(CheckCCompilerFlag)
+INCLUDE(CheckCSourceCompiles)
INCLUDE(FindOpenSSL)
+INCLUDE(GNUInstallDirs)
OPTION(ENABLE_URL_INCLUDE "Enable urls in ucl includes (requires libcurl or libfetch) [default: OFF]" OFF)
OPTION(ENABLE_URL_SIGN "Enable signatures check in ucl includes (requires openssl) [default: OFF]" OFF)
OPTION(BUILD_SHARED_LIBS "Build Shared Libraries [default: OFF]" OFF)
OPTION(ENABLE_LUA "Enable lua support [default: OFF]" OFF)
OPTION(ENABLE_LUAJIT "Enable luajit support [default: OFF]" OFF)
+OPTION(ENABLE_UTILS "Enable building utility binaries [default: OFF]" OFF)
# Find lua installation
MACRO(FindLua)
@@ -150,40 +153,47 @@ IF(ENABLE_URL_INCLUDE MATCHES "ON")
DOC "Path to libfetch header")
ELSE(LIBFETCH_LIBRARY)
# Try to find libcurl
- ProcessPackage(CURL libcurl)
+ FIND_PACKAGE(CURL)
IF(NOT CURL_FOUND)
MESSAGE(WARNING "Neither libcurl nor libfetch were found, no support of URL includes in configuration")
ENDIF(NOT CURL_FOUND)
ENDIF(LIBFETCH_LIBRARY)
ENDIF(ENABLE_URL_INCLUDE MATCHES "ON")
+set(SYNC_BUILTINS_TEST_SOURCE [====[
+int main()
+{
+ unsigned long val;
+
+ __sync_bool_compare_and_swap(&val, 0, 1);
+ __sync_add_and_fetch(&val, 1);
+ __sync_fetch_and_add(&val, 0);
+ __sync_sub_and_fetch(&val, 1);
+
+ return 0;
+}
+]====])
+
+CHECK_C_SOURCE_COMPILES("${SYNC_BUILTINS_TEST_SOURCE}" HAVE_ATOMIC_BUILTINS)
+IF(NOT HAVE_ATOMIC_BUILTINS)
+ MESSAGE(WARNING "Libucl references could be thread-unsafe because atomic builtins are missing")
+ENDIF(NOT HAVE_ATOMIC_BUILTINS)
+
SET(CMAKE_C_WARN_FLAGS "")
-CHECK_C_COMPILER_FLAG(-Wall SUPPORT_WALL)
CHECK_C_COMPILER_FLAG(-W SUPPORT_W)
-CHECK_C_COMPILER_FLAG(-Wno-unused-parameter SUPPORT_WPARAM)
CHECK_C_COMPILER_FLAG(-Wno-pointer-sign SUPPORT_WPOINTER_SIGN)
-CHECK_C_COMPILER_FLAG(-Wstrict-prototypes SUPPORT_WSTRICT_PROTOTYPES)
-IF(NOT "${CMAKE_C_COMPILER_ID}" MATCHES SunPro)
- CHECK_C_COMPILER_FLAG("-std=c99" SUPPORT_STD_FLAG)
-ENDIF(NOT "${CMAKE_C_COMPILER_ID}" MATCHES SunPro)
+CHECK_C_COMPILER_FLAG(-Wno-unused-parameter SUPPORT_WUNUSED_PARAMETER)
IF(SUPPORT_W)
- SET(CMAKE_C_WARN_FLAGS "${CMAKE_C_WARN_FLAGS} -W")
+ SET(CMAKE_C_WARN_FLAGS "${CMAKE_C_WARN_FLAGS} -W")
ENDIF(SUPPORT_W)
-IF(SUPPORT_WALL)
- SET(CMAKE_C_WARN_FLAGS "${CMAKE_C_WARN_FLAGS} -Wall")
-ENDIF(SUPPORT_WALL)
-IF(SUPPORT_WPARAM)
- SET(CMAKE_C_WARN_FLAGS "${CMAKE_C_WARN_FLAGS} -Wno-unused-parameter")
-ENDIF(SUPPORT_WPARAM)
IF(SUPPORT_WPOINTER_SIGN)
SET(CMAKE_C_WARN_FLAGS "${CMAKE_C_WARN_FLAGS} -Wno-pointer-sign")
ENDIF(SUPPORT_WPOINTER_SIGN)
-IF(SUPPORT_WSTRICT_PROTOTYPES)
- SET(CMAKE_C_WARN_FLAGS "${CMAKE_C_WARN_FLAGS} -Wstrict-prototypes")
-ENDIF(SUPPORT_WSTRICT_PROTOTYPES)
-IF(SUPPORT_STD_FLAG)
- SET(CMAKE_C_WARN_FLAGS "${CMAKE_C_WARN_FLAGS} -std=c99")
-ENDIF(SUPPORT_STD_FLAG)
+IF(SUPPORT_WUNUSED_PARAMETER)
+ SET(CMAKE_C_WARN_FLAGS "${CMAKE_C_WARN_FLAGS} -Wno-unused-parameter")
+ENDIF(SUPPORT_WUNUSED_PARAMETER)
+
+SET( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${CMAKE_C_WARN_FLAGS}" )
IF(ENABLE_URL_SIGN MATCHES "ON")
IF(OPENSSL_FOUND)
@@ -192,10 +202,19 @@ IF(ENABLE_URL_SIGN MATCHES "ON")
ENDIF(OPENSSL_FOUND)
ENDIF(ENABLE_URL_SIGN MATCHES "ON")
-INCLUDE_DIRECTORIES("src")
-INCLUDE_DIRECTORIES("include")
-INCLUDE_DIRECTORIES("uthash")
-INCLUDE_DIRECTORIES("klib")
+SET(UCL_COMPILE_DEFS)
+IF(HAVE_FETCH_H)
+ LIST(APPEND UCL_COMPILE_DEFS -DHAVE_FETCH_H=1)
+ENDIF(HAVE_FETCH_H)
+IF(CURL_FOUND)
+ LIST(APPEND UCL_COMPILE_DEFS -DCURL_FOUND=1)
+ENDIF(CURL_FOUND)
+IF(HAVE_OPENSSL)
+ LIST(APPEND UCL_COMPILE_DEFS -DHAVE_OPENSSL=1)
+ENDIF(HAVE_OPENSSL)
+IF(HAVE_ATOMIC_BUILTINS)
+ LIST(APPEND UCL_COMPILE_DEFS -DHAVE_ATOMIC_BUILTINS=1)
+ENDIF(HAVE_ATOMIC_BUILTINS)
SET(UCLSRC src/ucl_util.c
src/ucl_parser.c
@@ -207,13 +226,27 @@ SET(UCLSRC src/ucl_util.c
src/ucl_msgpack.c
src/ucl_sexp.c)
+SET(UCLHDR include/ucl.h
+ include/ucl++.h)
SET (LIB_TYPE STATIC)
IF (BUILD_SHARED_LIBS)
SET (LIB_TYPE SHARED)
ENDIF (BUILD_SHARED_LIBS)
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)
+TARGET_COMPILE_DEFINITIONS(ucl
+ PRIVATE
+ ${UCL_COMPILE_DEFS}
+)
IF(ENABLE_LUA MATCHES "ON")
IF(ENABLE_LUAJIT MATCHES "ON")
@@ -236,13 +269,20 @@ IF(ENABLE_LUA MATCHES "ON")
ENDIF(ENABLE_LUAJIT MATCHES "ON")
SET(UCL_LUA_SRC lua/lua_ucl.c)
ADD_LIBRARY(lua-ucl ${LIB_TYPE} ${UCL_LUA_SRC})
+ ADD_LIBRARY(ucl::lua ALIAS lua-ucl)
IF(ENABLE_LUAJIT MATCHES "ON")
TARGET_LINK_LIBRARIES(lua-ucl "${LUAJIT_LIBRARY}")
ELSE(ENABLE_LUAJIT MATCHES "ON")
TARGET_LINK_LIBRARIES(lua-ucl "${LUA_LIBRARY}")
ENDIF(ENABLE_LUAJIT MATCHES "ON")
TARGET_LINK_LIBRARIES(lua-ucl ucl)
- SET_TARGET_PROPERTIES(lua-ucl PROPERTIES VERSION ${LIBUCL_VERSION} SOVERSION ${LIBUCL_VERSION_MAJOR})
+ TARGET_INCLUDE_DIRECTORIES(lua-ucl PUBLIC include PRIVATE src uthash)
+ SET_TARGET_PROPERTIES(lua-ucl PROPERTIES
+ VERSION ${LIBUCL_VERSION}
+ SOVERSION ${LIBUCL_VERSION_MAJOR}
+ PUBLIC_HEADER include/lua_ucl.h)
+ INSTALL(TARGETS lua-ucl DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
ENDIF()
IF(HAVE_FETCH_H)
@@ -257,3 +297,18 @@ IF(ENABLE_URL_SIGN MATCHES "ON")
TARGET_LINK_LIBRARIES(ucl ${OPENSSL_LIBRARIES})
ENDIF(OPENSSL_FOUND)
ENDIF(ENABLE_URL_SIGN MATCHES "ON")
+
+IF(UNIX)
+ TARGET_LINK_LIBRARIES(ucl -lm)
+ENDIF(UNIX)
+
+SET_TARGET_PROPERTIES(ucl PROPERTIES
+ PUBLIC_HEADER "${UCLHDR}")
+
+INSTALL(TARGETS ucl DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
+
+IF(ENABLE_UTILS MATCHES "ON")
+ ADD_SUBDIRECTORY(utils)
+ENDIF()
+
diff --git a/ChangeLog.md b/ChangeLog.md
index e4c1263bccb7..cba29aa9a7b5 100644
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -64,4 +64,40 @@
**Incompatible changes**:
-- `ucl_object_emit_full` now accepts additional argument `comments` that could be used to emit comments with UCL output \ No newline at end of file
+- `ucl_object_emit_full` now accepts additional argument `comments` that could be used to emit comments with UCL output
+
+### Libucl 0.8.1
+
+- Create ucl_parser_add_file_full() to be able to specify merge mode and parser type (by Allan Jude)
+- C++ wrapper improvements (by @ftilde)
+- C++ wrapper: add convenience method at() and lookup() (by Yonghee Kim)
+- C++ wrapper: add assignment operator to Ucl class (by Yonghee Kim)
+- C++ wrapper: support variables in parser (by Yonghee Kim)
+- C++ wrapper: refactoring C++ interface (by Yonghee Kim):
+ - use auto variables (if possible)
+ - remove dangling expressions
+ - use std::set::emplace instead of std::set::insert
+ - not use std::move in return statement; considering copy elision
+- C++ wrapper: fix compilation error and warnings (by Zhe Wang)
+- C++ wrapper: fix iteration over objects in which the first value is `false` (by Zhe Wang)
+- C++ wrapper: Macro helper functions (by Chris Meacham)
+- C++ wrapper: Changing the duplicate strategy in the C++ API (by Chris Meacham)
+- C++ wrapper: Added access functions for the size of a UCL_ARRAY (by Chris Meacham)
+- Fix caseless comparison
+- Fix include when EPERM is issued
+- Fix Windows build
+- Allow to reserve space in arrays and hashes
+- Fix bug with including of empty files
+- Move to mum_hash from xxhash
+- Fix msgpack on non-x86
+- python: Add support to Python 3 (by Denis Volpato Martins)
+- python: Add support for Python 2.6 tests (by Denis Volpato Martins)
+- python: Implement validation function and tests (by Denis Volpato Martins)
+- python: Added UCL_NULL handling and tests (by Denis Volpato Martins)
+- Fix schema validation for patternProperties with object data (by Denis Volpato Martins)
+- Remove the dependency on NBBY, add missing <strings.h> include (by Ed Schouten)
+- Allow to emit msgpack from Lua
+- 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
diff --git a/README.md b/README.md
index 44983c57d643..53d8a651d73b 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# LIBUCL
-[![Build Status](https://travis-ci.org/vstakhov/libucl.svg?branch=master)](https://travis-ci.org/vstakhov/libucl)
+[![CircleCI](https://circleci.com/gh/vstakhov/libucl.svg?style=svg)](https://circleci.com/gh/vstakhov/libucl)
[![Coverity](https://scan.coverity.com/projects/4138/badge.svg)](https://scan.coverity.com/projects/4138)
[![Coverage Status](https://coveralls.io/repos/github/vstakhov/libucl/badge.svg?branch=master)](https://coveralls.io/github/vstakhov/libucl?branch=master)
@@ -18,6 +18,7 @@
- [Macros support](#macros-support)
- [Variables support](#variables-support)
- [Multiline strings](#multiline-strings)
+ - [Single quoted strings](#single-quoted-strings)
- [Emitter](#emitter)
- [Validation](#validation)
- [Performance](#performance)
@@ -65,19 +66,25 @@ section {
```json
{
"param": "value",
- "param1": "value1",
- "flag": true,
- "subsection": {
- "host": [
- {
- "host": "hostname",
- "port": 900
- },
- {
- "host": "hostname",
- "port": 901
+ "section": {
+ "param": "value",
+ "param1": "value1",
+ "flag": true,
+ "number": 10000,
+ "time": "0.2s",
+ "string": "something",
+ "subsection": {
+ "host": [
+ {
+ "host": "hostname",
+ "port": 900
+ },
+ {
+ "host": "hostname",
+ "port": 901
+ }
+ ]
}
- ]
}
}
```
@@ -288,7 +295,22 @@ as following:
By default, the priority of top-level object is set to zero (lowest priority). Currently,
you can define up to 16 priorities (from 0 to 15). Includes with bigger priorities will
-rewrite keys from the objects with lower priorities as specified by the policy.
+rewrite keys from the objects with lower priorities as specified by the policy. The priority
+of the top-level or any other object can be changed with the `.priority` macro, which has no
+options and takes the new priority:
+
+```
+# Default priority: 0.
+foo = 6
+.priority 5
+# The following will have priority 5.
+bar = 6
+baz = 7
+# The following will be included with a priority of 3, 5, and 6 respectively.
+.include(priority=3) "path.conf"
+.include(priority=5) "equivalent-path.conf"
+.include(priority=6) "highpriority-path.conf"
+```
### Variables support
@@ -333,9 +355,21 @@ text
EOD
```
+### Single quoted strings
+
+It is possible to use single quoted strings to simplify escaping rules. All values passed in single quoted strings are *NOT* escaped, with two exceptions: a single `'` character just before `\` character, and a newline character just after `\` character that is ignored.
+
+```
+key = 'value'; # Read as value
+key = 'value\n\'; # Read as value\n\
+key = 'value\''; # Read as value'
+key = 'value\
+bla'; # Read as valuebla
+```
+
## Emitter
-Each UCL object can be serialized to one of the three supported formats:
+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);
diff --git a/configure.ac b/configure.ac
index 6457268854ac..731b7113e689 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], [0])
-m4_define([so_version], [6:0:0])
+m4_define([min_ver], [1])
+m4_define([so_version], [6:0:1])
m4_define([ucl_version], [maj_ver.med_ver.min_ver])
AC_INIT([libucl],[ucl_version],[https://github.com/vstakhov/libucl],[libucl])
@@ -73,11 +73,11 @@ AC_ARG_ENABLE([utils],
AM_CONDITIONAL([UTILS], [test x$utils = xtrue])
AS_IF([test "x$enable_signatures" = "xyes"], [
- AC_SEARCH_LIBS([EVP_MD_CTX_create], [crypto], [
+ AC_SEARCH_LIBS([CRYPTO_new_ex_data], [crypto], [
AC_DEFINE(HAVE_OPENSSL, 1, [Define to 1 if you have the 'crypto' library (-lcrypto).])
LIBCRYPTO_LIB="-lcrypto"
LIBS_EXTRA="${LIBS_EXTRA} -lcrypto"
- ], [AC_MSG_ERROR([unable to find the EVP_MD_CTX_create() function])])
+ ], [AC_MSG_ERROR([unable to find the CRYPTO_new_ex_data() function])])
])
AC_SUBST(LIBCRYPTO_LIB)
AC_PATH_PROG(PANDOC, pandoc, [/non/existent])
diff --git a/doc/api.md b/doc/api.md
index 75b954bb302c..a0d33c0e68a9 100644
--- a/doc/api.md
+++ b/doc/api.md
@@ -243,7 +243,7 @@ return ret;
# Emitting functions
-Libucl can transform UCL objects to a number of tectual formats:
+Libucl can transform UCL objects to a number of textual formats:
- configuration (`UCL_EMIT_CONFIG`) - nginx like human readable configuration file where implicit arrays are transformed to the duplicate keys
- compact json: `UCL_EMIT_JSON_COMPACT` - single line valid json without spaces
@@ -349,7 +349,7 @@ This object should be released by caller.
Libucl provides the functions similar to inverse conversion functions called with the specific C type:
- `ucl_object_fromint` - converts `int64_t` to UCL object
- `ucl_object_fromdouble` - converts `double` to UCL object
-- `ucl_object_fromboolean` - converts `bool` to UCL object
+- `ucl_object_frombool` - converts `bool` to UCL object
- `ucl_object_fromstring` - converts `const char *` to UCL object (this string should be NULL terminated)
- `ucl_object_fromlstring` - converts `const char *` and `size_t` len to UCL object (string does not need to be NULL terminated)
@@ -432,7 +432,8 @@ UCL defines the following functions to manage safe iterators:
- `ucl_object_iterate_new` - creates new safe iterator
- `ucl_object_iterate_reset` - resets iterator to a new object
-- `ucl_object_iterate_safe` - safely iterate the object inside iterator
+- `ucl_object_iterate_safe` - safely iterate the object inside iterator. Note: function may allocate and free memory during its operation. Therefore it returns `NULL` either while trying to access item after the last one or when exception (such as memory allocation failure) happens.
+- `ucl_object_iter_chk_excpn` - check if the last call to `ucl_object_iterate_safe` ended up in unrecoverable exception (e.g. `ENOMEM`).
- `ucl_object_iterate_free` - free memory associated with the safe iterator
Please note that unlike unsafe iterators, safe iterators *must* be explicitly initialized and freed.
@@ -447,6 +448,11 @@ it = ucl_object_iterate_new (obj);
while ((cur = ucl_object_iterate_safe (it, true)) != NULL) {
/* Do something */
}
+/* Check error condition */
+if (ucl_object_iter_chk_excpn (it)) {
+ ucl_object_iterate_free (it);
+ exit (1);
+}
/* Switch to another object */
it = ucl_object_iterate_reset (it, another_obj);
@@ -454,6 +460,11 @@ it = ucl_object_iterate_reset (it, another_obj);
while ((cur = ucl_object_iterate_safe (it, true)) != NULL) {
/* Do something else */
}
+/* Check error condition */
+if (ucl_object_iter_chk_excpn (it)) {
+ ucl_object_iterate_free (it);
+ exit (1);
+}
ucl_object_iterate_free (it);
~~~
diff --git a/doc/libucl.3 b/doc/libucl.3
index ec5046325700..b5fef09f7691 100644
--- a/doc/libucl.3
+++ b/doc/libucl.3
@@ -612,15 +612,23 @@ Iteration\ without\ expansion:
.PP
UCL defines the following functions to manage safe iterators:
.IP \[bu] 2
-\f[C]ucl_object_iterate_new\f[] \- creates new safe iterator
+\f[C]ucl_object_iterate_new\f[] \- creates new safe iterator.
.IP \[bu] 2
-\f[C]ucl_object_iterate_reset\f[] \- resets iterator to a new object
+\f[C]ucl_object_iterate_reset\f[] \- resets iterator to a new object.
.IP \[bu] 2
\f[C]ucl_object_iterate_safe\f[] \- safely iterate the object inside
-iterator
+iterator.
+Note: function may allocate and free memory during its operation.
+Therefore it returns \f[C]NULL\f[] either while trying to access item
+after the last one or when exception (such as memory allocation
+failure) happens.
+.IP \[bu] 2
+\f[C]ucl_object_iter_chk_excpn\f[] \- check if the last call to
+\f[C]ucl_object_iterate_safe\f[] ended up in unrecoverable exception
+(e.g. \f[C]ENOMEM\f[]).
.IP \[bu] 2
\f[C]ucl_object_iterate_free\f[] \- free memory associated with the safe
-iterator
+iterator.
.PP
Please note that unlike unsafe iterators, safe iterators \f[I]must\f[]
be explicitly initialized and freed.
@@ -637,6 +645,11 @@ it\ =\ ucl_object_iterate_new\ (obj);
while\ ((cur\ =\ ucl_object_iterate_safe\ (it,\ true))\ !=\ NULL)\ {
\ \ \ \ /*\ Do\ something\ */
}
+/*\ Check\ error\ condition\ */
+if\ (ucl_object_iter_chk_excpn\ (it))\ {
+\ \ \ \ ucl_object_iterate_free\ (it);
+\ \ \ \ exit\ (1);
+}
/*\ Switch\ to\ another\ object\ */
it\ =\ ucl_object_iterate_reset\ (it,\ another_obj);
@@ -644,6 +657,11 @@ it\ =\ ucl_object_iterate_reset\ (it,\ another_obj);
while\ ((cur\ =\ ucl_object_iterate_safe\ (it,\ true))\ !=\ NULL)\ {
\ \ \ \ /*\ Do\ something\ else\ */
}
+/*\ Check\ error\ condition\ */
+if\ (ucl_object_iter_chk_excpn\ (it))\ {
+\ \ \ \ ucl_object_iterate_free\ (it);
+\ \ \ \ exit\ (1);
+}
ucl_object_iterate_free\ (it);
\f[]
diff --git a/doc/lua_api.md b/doc/lua_api.md
index f7af3caffff4..7da414903b01 100644
--- a/doc/lua_api.md
+++ b/doc/lua_api.md
@@ -69,8 +69,8 @@ converts `obj` to lua representation using the following conversions:
- *scalar* values are directly presented by lua objects
- *userdata* values are converted to lua function objects using `LUA_REGISTRYINDEX`,
this can be used to pass functions from lua to c and vice-versa
-- *arrays* are converted to lua tables with numeric indicies suitable for `ipairs` iterations
-- *objects* are converted to lua tables with string indicies
+- *arrays* are converted to lua tables with numeric indices suitable for `ipairs` iterations
+- *objects* are converted to lua tables with string indices
**Parameters:**
diff --git a/include/lua_ucl.h b/include/lua_ucl.h
index 38e74d3f619d..5b7f88e031e1 100644
--- a/include/lua_ucl.h
+++ b/include/lua_ucl.h
@@ -55,6 +55,14 @@ UCL_EXTERN int luaopen_ucl (lua_State *L);
UCL_EXTERN ucl_object_t* ucl_object_lua_import (lua_State *L, int idx);
/**
+ * Import UCL object from lua state, escaping JSON strings
+ * @param L lua state
+ * @param idx index of object at the lua stack to convert to UCL
+ * @return new UCL object or NULL, the caller should unref object after using
+ */
+UCL_EXTERN ucl_object_t* ucl_object_lua_import_escape (lua_State *L, int idx);
+
+/**
* Push an object to lua
* @param L lua state
* @param obj object to push
@@ -62,8 +70,16 @@ UCL_EXTERN ucl_object_t* ucl_object_lua_import (lua_State *L, int idx);
*/
UCL_EXTERN int ucl_object_push_lua (lua_State *L,
const ucl_object_t *obj, bool allow_array);
+/**
+ * Push an object to lua replacing all ucl.null with `false`
+ * @param L lua state
+ * @param obj object to push
+ * @param allow_array traverse over implicit arrays
+ */
+UCL_EXTERN int ucl_object_push_lua_filter_nil (lua_State *L,
+ const ucl_object_t *obj,
+ bool allow_array);
-UCL_EXTERN struct ucl_lua_funcdata* ucl_object_toclosure (
- const ucl_object_t *obj);
+UCL_EXTERN struct ucl_lua_funcdata* ucl_object_toclosure (const ucl_object_t *obj);
#endif /* LUA_UCL_H_ */
diff --git a/include/ucl++.h b/include/ucl++.h
index 2c2bdde51559..fb63430d400d 100644
--- a/include/ucl++.h
+++ b/include/ucl++.h
@@ -29,6 +29,7 @@
#include <set>
#include <memory>
#include <iostream>
+#include <tuple>
#include "ucl.h"
@@ -106,7 +107,7 @@ private:
static bool ucl_variable_getter(const unsigned char *data, size_t len,
unsigned char ** /*replace*/, size_t * /*replace_len*/, bool *need_free, void* ud)
{
- *need_free = false;
+ *need_free = false;
auto vars = reinterpret_cast<std::set<std::string> *>(ud);
if (vars && data && len != 0) {
@@ -123,17 +124,17 @@ private:
auto replacer = reinterpret_cast<variable_replacer *>(ud);
if (!replacer) {
return false;
- }
+ }
std::string var_name (data, data + len);
if (!replacer->is_variable (var_name)) {
return false;
- }
+ }
std::string var_value = replacer->replace (var_name);
if (var_value.empty ()) {
return false;
- }
+ }
*replace = (unsigned char *)UCL_ALLOC (var_value.size ());
memcpy (*replace, var_value.data (), var_value.size ());
@@ -152,7 +153,8 @@ private:
config_func (parser);
if (!parse_func (parser)) {
- err.assign (ucl_parser_get_error (parser));
+ const char *error = ucl_parser_get_error (parser); //Assigning here without checking result first causes a
+ if( error != NULL ) err.assign(error); // crash if ucl_parser_get_error returns NULL
ucl_parser_free (parser);
return nullptr;
@@ -168,6 +170,16 @@ private:
std::unique_ptr<ucl_object_t, ucl_deleter> obj;
public:
+ struct macro_handler_s {
+ ucl_macro_handler handler;
+ ucl_context_macro_handler ctx_handler;
+ };
+
+ struct macro_userdata_s {
+ ucl_parser *parser;
+ void *userdata;
+ };
+
class const_iterator {
private:
struct ucl_iter_deleter {
@@ -184,7 +196,7 @@ public:
it = std::shared_ptr<void>(ucl_object_iterate_new (obj.obj.get()),
ucl_iter_deleter());
cur.reset (new Ucl(ucl_object_iterate_safe (it.get(), true)));
- if (cur->type() == UCL_NULL) {
+ if (!cur->obj) {
it.reset ();
cur.reset ();
}
@@ -218,7 +230,7 @@ public:
cur.reset (new Ucl(ucl_object_iterate_safe (it.get(), true)));
}
- if (cur && cur->type() == UCL_NULL) {
+ if (cur && !cur->obj) {
it.reset ();
cur.reset ();
}
@@ -330,7 +342,7 @@ public:
return UCL_NULL;
}
- const std::string key () const {
+ std::string key () const {
std::string res;
if (obj->key) {
@@ -373,7 +385,7 @@ public:
return default_val;
}
- const std::string string_value (const std::string& default_val = "") const
+ std::string string_value (const std::string& default_val = "") const
{
const char* res = nullptr;
@@ -384,7 +396,16 @@ public:
return default_val;
}
- const Ucl at (size_t i) const
+ size_t size () const
+ {
+ if (type () == UCL_ARRAY) {
+ return ucl_array_size (obj.get());
+ }
+
+ return 0;
+ }
+
+ Ucl at (size_t i) const
{
if (type () == UCL_ARRAY) {
return Ucl (ucl_array_find_index (obj.get(), i));
@@ -393,7 +414,7 @@ public:
return Ucl (nullptr);
}
- const Ucl lookup (const std::string &key) const
+ Ucl lookup (const std::string &key) const
{
if (type () == UCL_OBJECT) {
return Ucl (ucl_object_lookup_len (obj.get(),
@@ -403,12 +424,12 @@ public:
return Ucl (nullptr);
}
- inline const Ucl operator[] (size_t i) const
+ inline Ucl operator[] (size_t i) const
{
return at(i);
}
- inline const Ucl operator[](const std::string &key) const
+ inline Ucl operator[](const std::string &key) const
{
return lookup(key);
}
@@ -432,43 +453,116 @@ public:
return out;
}
- static Ucl parse (const std::string &in, std::string &err)
+ static Ucl parse (const std::string &in, std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND)
{
- return parse (in, std::map<std::string, std::string>(), err);
+ return parse (in, std::map<std::string, std::string>(), err, duplicate_strategy);
}
- static Ucl parse (const std::string &in, const std::map<std::string, std::string> &vars, std::string &err)
+ static Ucl parse (const std::string &in, const std::map<std::string, std::string> &vars,
+ std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND)
{
- auto config_func = [&vars] (ucl_parser *parser) {
+ std::vector< std::tuple< std::string, macro_handler_s, void * > > emptyVector;
+ return parse ( in, vars, emptyVector, err, duplicate_strategy );
+ }
+
+ //Macro handler will receive a macro_userdata_s as void *ud
+ static Ucl parse (const std::string &in,
+ std::vector< std::tuple< std::string /*name*/, macro_handler_s, void * /*userdata*/ > > &macros,
+ std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND)
+ {
+ return parse (in, std::map<std::string, std::string>(), macros, err, duplicate_strategy);
+ }
+
+ //Macro handler will receive a macro_userdata_s as void *ud
+ static Ucl parse (const std::string &in, const std::map<std::string, std::string> &vars,
+ std::vector< std::tuple< std::string /*name*/, macro_handler_s, void * /*userdata*/ > > &macros,
+ std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND)
+ {
+ //Preserve macro_userdata_s memory for later use in parse_with_strategy_function()
+ std::vector<macro_userdata_s> userdata_list;
+ userdata_list.reserve (macros.size());
+ auto config_func = [&userdata_list, &vars, &macros] (ucl_parser *parser) {
for (const auto & item : vars) {
ucl_parser_register_variable (parser, item.first.c_str (), item.second.c_str ());
- }
+ }
+ for (auto & macro : macros) {
+ userdata_list.push_back ({parser, std::get<2>(macro)});
+ if (std::get<1>(macro).handler != NULL) {
+ ucl_parser_register_macro (parser,
+ std::get<0>(macro).c_str(),
+ std::get<1>(macro).handler,
+ reinterpret_cast<void*>(&userdata_list.back()));
+ }
+ else if (std::get<1>(macro).ctx_handler != NULL) {
+ ucl_parser_register_context_macro (parser,
+ std::get<0>(macro).c_str(),
+ std::get<1>(macro).ctx_handler,
+ reinterpret_cast<void*>(&userdata_list.back()));
+ }
+ }
};
- auto parse_func = [&in] (ucl_parser *parser) {
- return ucl_parser_add_chunk (parser, (unsigned char *)in.data (), in.size ());
+ auto parse_func = [&in, &duplicate_strategy] (struct ucl_parser *parser) -> bool {
+ return ucl_parser_add_chunk_full (parser,
+ (unsigned char *) in.data (),
+ in.size (),
+ (unsigned int)ucl_parser_get_default_priority (parser),
+ duplicate_strategy,
+ UCL_PARSE_UCL);
};
return parse_with_strategy_function (config_func, parse_func, err);
}
- static Ucl parse (const std::string &in, const variable_replacer &replacer, std::string &err)
- {
- auto config_func = [&replacer] (ucl_parser *parser) {
- ucl_parser_set_variables_handler (parser, ucl_variable_replacer,
- &const_cast<variable_replacer &>(replacer));
+ static Ucl parse (const std::string &in, const variable_replacer &replacer,
+ std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND)
+ {
+ std::vector< std::tuple< std::string, macro_handler_s, void * > > emptyVector;
+ return parse ( in, replacer, emptyVector, err, duplicate_strategy );
+ }
+
+ //Macro handler will receive a macro_userdata_s as void *ud
+ static Ucl parse (const std::string &in, const variable_replacer &replacer,
+ std::vector< std::tuple< std::string /*name*/, macro_handler_s, void * /*userdata*/ > > &macros,
+ std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND)
+ {
+ //Preserve macro_userdata_s memory for later use in parse_with_strategy_function()
+ std::vector<macro_userdata_s> userdata_list;
+ userdata_list.reserve (macros.size());
+ auto config_func = [&userdata_list, &replacer, &macros] (ucl_parser *parser) {
+ ucl_parser_set_variables_handler (parser, ucl_variable_replacer, &const_cast<variable_replacer &>(replacer));
+ for (auto & macro : macros) {
+ userdata_list.push_back ({parser, std::get<2>(macro)});
+ if (std::get<1>(macro).handler != NULL) {
+ ucl_parser_register_macro (parser,
+ std::get<0>(macro).c_str(),
+ std::get<1>(macro).handler,
+ reinterpret_cast<void*>(&userdata_list.back()));
+ }
+ else if (std::get<1>(macro).ctx_handler != NULL) {
+ ucl_parser_register_context_macro (parser,
+ std::get<0>(macro).c_str(),
+ std::get<1>(macro).ctx_handler,
+ reinterpret_cast<void*>(&userdata_list.back()));
+ }
+ }
};
- auto parse_func = [&in] (ucl_parser *parser) {
- return ucl_parser_add_chunk (parser, (unsigned char *) in.data (), in.size ());
+ auto parse_func = [&in, &duplicate_strategy] (struct ucl_parser *parser) -> bool {
+ return ucl_parser_add_chunk_full (parser,
+ (unsigned char *) in.data (),
+ in.size (),
+ (unsigned int)ucl_parser_get_default_priority (parser),
+ duplicate_strategy,
+ UCL_PARSE_UCL);
};
return parse_with_strategy_function (config_func, parse_func, err);
}
- static Ucl parse (const char *in, std::string &err)
+ static Ucl parse (const char *in, std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND)
{
- return parse (in, std::map<std::string, std::string>(), err);
+ return parse (in, std::map<std::string, std::string>(), err, duplicate_strategy);
}
static Ucl parse (const char *in, const std::map<std::string, std::string> &vars, std::string &err)
@@ -480,13 +574,46 @@ public:
return parse (std::string (in), vars, err);
}
- static Ucl parse (const char *in, const variable_replacer &replacer, std::string &err)
+ //Macro handler will receive a macro_userdata_s as void *ud
+ static Ucl parse (const char *in,
+ std::vector< std::tuple< std::string /*name*/, macro_handler_s, void * /*userdata*/ > > &macros,
+ std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND)
+ {
+ return parse (in, std::map<std::string, std::string>(), macros, err, duplicate_strategy);
+ }
+
+ //Macro handler will receive a macro_userdata_s as void *ud
+ static Ucl parse (const char *in, const std::map<std::string, std::string> &vars,
+ std::vector< std::tuple< std::string /*name*/, macro_handler_s, void * /*userdata*/ > > &macros,
+ std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND)
{
if (!in) {
err = "null input";
return nullptr;
}
- return parse (std::string(in), replacer, err);
+ return parse (std::string (in), vars, macros, err, duplicate_strategy);
+ }
+
+ static Ucl parse (const char *in, const variable_replacer &replacer,
+ std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND)
+ {
+ if (!in) {
+ err = "null input";
+ return nullptr;
+ }
+ return parse (std::string(in), replacer, err, duplicate_strategy);
+ }
+
+ //Macro handler will receive a macro_userdata_s as void *ud
+ static Ucl parse (const char *in, const variable_replacer &replacer,
+ std::vector< std::tuple< std::string /*name*/, macro_handler_s, void * /*userdata*/ > > &macros,
+ std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND)
+ {
+ if (!in) {
+ err = "null input";
+ return nullptr;
+ }
+ return parse (std::string (in), replacer, macros, err, duplicate_strategy);
}
static Ucl parse_from_file (const std::string &filename, std::string &err)
@@ -556,7 +683,7 @@ public:
std::vector<std::string> result;
std::move (vars.begin (), vars.end (), std::back_inserter (result));
- return std::move (result);
+ return result;
}
Ucl& operator= (Ucl rhs)
diff --git a/include/ucl.h b/include/ucl.h
index fccf6fcb2237..39da2593648d 100644
--- a/include/ucl.h
+++ b/include/ucl.h
@@ -105,10 +105,11 @@ typedef enum ucl_error {
UCL_EIO, /**< IO error occurred during parsing */
UCL_ESTATE, /**< Invalid state machine state */
UCL_ENESTED, /**< Input has too many recursion levels */
+ UCL_EUNPAIRED, /**< Input has too many recursion levels */
UCL_EMACRO, /**< Error processing a macro */
UCL_EINTERNAL, /**< Internal unclassified error */
UCL_ESSL, /**< SSL error */
- UCL_EMERGE /**< A merge error occured */
+ UCL_EMERGE /**< A merge error occurred */
} ucl_error_t;
/**
@@ -177,7 +178,8 @@ typedef enum ucl_string_flags {
} ucl_string_flags_t;
/**
- * Basic flags for an object
+ * Basic flags for an object (can use up to 12 bits as higher 4 bits are used
+ * for priorities)
*/
typedef enum ucl_object_flags {
UCL_OBJECT_ALLOCATED_KEY = (1 << 0), /**< An object has key allocated internally */
@@ -187,7 +189,8 @@ typedef enum ucl_object_flags {
UCL_OBJECT_MULTILINE = (1 << 4), /**< String should be displayed as multiline string */
UCL_OBJECT_MULTIVALUE = (1 << 5), /**< Object is a key with multiple values */
UCL_OBJECT_INHERITED = (1 << 6), /**< Object has been inherited from another */
- UCL_OBJECT_BINARY = (1 << 7) /**< Object contains raw binary data */
+ UCL_OBJECT_BINARY = (1 << 7), /**< Object contains raw binary data */
+ UCL_OBJECT_SQUOTED = (1 << 8) /**< Object has been enclosed in single quotes */
} ucl_object_flags_t;
/**
@@ -463,6 +466,14 @@ UCL_EXTERN bool ucl_object_insert_key_merged (ucl_object_t *top, ucl_object_t *e
const char *key, size_t keylen, bool copy_key);
/**
+ * Reserve space in ucl array or object for `elt` elements
+ * @param obj object to reserve
+ * @param reserved size to reserve in an object
+ * @return 0 on success, -1 on failure (i.e. ENOMEM)
+ */
+UCL_EXTERN bool ucl_object_reserve (ucl_object_t *obj, size_t reserved);
+
+/**
* Append an element to the end of array object
* @param top destination object (must NOT be NULL)
* @param elt element to append (must NOT be NULL)
@@ -534,6 +545,13 @@ UCL_EXTERN ucl_object_t* ucl_array_pop_last (ucl_object_t *top);
UCL_EXTERN ucl_object_t* ucl_array_pop_first (ucl_object_t *top);
/**
+ * Return size of the array `top`
+ * @param top object to get size from (must be of type UCL_ARRAY)
+ * @return size of the array
+ */
+UCL_EXTERN unsigned int ucl_array_size (const ucl_object_t *top);
+
+/**
* Return object identified by index of the array `top`
* @param top object to get a key from (must be of type UCL_ARRAY)
* @param index array index to return
@@ -782,6 +800,19 @@ UCL_EXTERN int ucl_object_compare_qsort (const ucl_object_t **o1,
UCL_EXTERN void ucl_object_array_sort (ucl_object_t *ar,
int (*cmp)(const ucl_object_t **o1, const ucl_object_t **o2));
+enum ucl_object_keys_sort_flags {
+ UCL_SORT_KEYS_DEFAULT = 0,
+ UCL_SORT_KEYS_ICASE = (1u << 0u),
+ UCL_SORT_KEYS_RECURSIVE = (1u << 1u),
+};
+/***
+ * Sorts keys in object in place
+ * @param obj
+ * @param how
+ */
+UCL_EXTERN void ucl_object_sort_keys (ucl_object_t *obj,
+ enum ucl_object_keys_sort_flags how);
+
/**
* Get the priority for specific UCL object
* @param obj any ucl object
@@ -808,11 +839,14 @@ typedef void* ucl_object_iter_t;
* @param iter opaque iterator, must be set to NULL on the first call:
* ucl_object_iter_t it = NULL;
* while ((cur = ucl_iterate_object (obj, &it)) != NULL) ...
+ * @param ep pointer record exception (such as ENOMEM), could be NULL
* @return the next object or NULL
*/
-UCL_EXTERN const ucl_object_t* ucl_object_iterate (const ucl_object_t *obj,
- ucl_object_iter_t *iter, bool expand_values);
+UCL_EXTERN const ucl_object_t* ucl_object_iterate_with_error (const ucl_object_t *obj,
+ ucl_object_iter_t *iter, bool expand_values, int *ep);
+
#define ucl_iterate_object ucl_object_iterate
+#define ucl_object_iterate(ob, it, ev) ucl_object_iterate_with_error((ob), (it), (ev), NULL)
/**
* Create new safe iterator for the specified object
@@ -822,6 +856,15 @@ UCL_EXTERN const ucl_object_t* ucl_object_iterate (const ucl_object_t *obj,
UCL_EXTERN ucl_object_iter_t ucl_object_iterate_new (const ucl_object_t *obj)
UCL_WARN_UNUSED_RESULT;
/**
+ * Check safe iterator object after performing some operations on it
+ * (such as ucl_object_iterate_safe()) to see if operation has encountered
+ * fatal exception while performing that operation (e.g. ENOMEM).
+ * @param iter opaque iterator
+ * @return true if exception has occured, false otherwise
+ */
+UCL_EXTERN bool ucl_object_iter_chk_excpn(ucl_object_iter_t *it);
+
+/**
* Reset initialized iterator to a new object
* @param obj new object to iterate
* @return modified iterator object
@@ -830,7 +873,7 @@ UCL_EXTERN ucl_object_iter_t ucl_object_iterate_reset (ucl_object_iter_t it,
const ucl_object_t *obj);
/**
- * Get the next object from the `obj`. This fucntion iterates over arrays, objects
+ * Get the next object from the `obj`. This function iterates over arrays, objects
* and implicit arrays
* @param iter safe iterator
* @param expand_values expand explicit arrays and objects
@@ -848,7 +891,7 @@ enum ucl_iterate_type {
};
/**
- * Get the next object from the `obj`. This fucntion iterates over arrays, objects
+ * Get the next object from the `obj`. This function iterates over arrays, objects
* and implicit arrays if needed
* @param iter safe iterator
* @param
@@ -912,7 +955,7 @@ struct ucl_parser;
UCL_EXTERN struct ucl_parser* ucl_parser_new (int flags);
/**
- * Sets the default priority for the parser applied to chunks that does not
+ * Sets the default priority for the parser applied to chunks that do not
* specify priority explicitly
* @param parser parser object
* @param prio default priority (0 .. 16)
@@ -921,13 +964,22 @@ UCL_EXTERN struct ucl_parser* ucl_parser_new (int flags);
UCL_EXTERN bool ucl_parser_set_default_priority (struct ucl_parser *parser,
unsigned prio);
/**
+ * Gets the default priority for the parser applied to chunks that do not
+ * specify priority explicitly
+ * @param parser parser object
+ * @return true default priority (0 .. 16), -1 for failure
+ */
+UCL_EXTERN int ucl_parser_get_default_priority (struct ucl_parser *parser);
+
+/**
* Register new handler for a macro
* @param parser parser object
* @param macro macro name (without leading dot)
* @param handler handler (it is called immediately after macro is parsed)
* @param ud opaque user data for a handler
+ * @return true on success, false on failure (i.e. ENOMEM)
*/
-UCL_EXTERN void ucl_parser_register_macro (struct ucl_parser *parser,
+UCL_EXTERN bool ucl_parser_register_macro (struct ucl_parser *parser,
const char *macro,
ucl_macro_handler handler, void* ud);
@@ -937,8 +989,9 @@ UCL_EXTERN void ucl_parser_register_macro (struct ucl_parser *parser,
* @param macro macro name (without leading dot)
* @param handler handler (it is called immediately after macro is parsed)
* @param ud opaque user data for a handler
+ * @return true on success, false on failure (i.e. ENOMEM)
*/
-UCL_EXTERN void ucl_parser_register_context_macro (struct ucl_parser *parser,
+UCL_EXTERN bool ucl_parser_register_context_macro (struct ucl_parser *parser,
const char *macro,
ucl_context_macro_handler handler,
void* ud);
@@ -997,6 +1050,16 @@ UCL_EXTERN bool ucl_parser_add_chunk_priority (struct ucl_parser *parser,
const unsigned char *data, size_t len, unsigned priority);
/**
+ * Insert new chunk to a parser (must have previously processed data with an existing top object)
+ * @param parser parser structure
+ * @param data the pointer to the beginning of a chunk
+ * @param len the length of a chunk
+ * @return true if chunk has been added and false in case of error
+ */
+UCL_EXTERN bool ucl_parser_insert_chunk (struct ucl_parser *parser,
+ const unsigned char *data, size_t len);
+
+/**
* Full version of ucl_add_chunk with priority and duplicate strategy
* @param parser parser structure
* @param data the pointer to the beginning of a chunk
@@ -1019,7 +1082,7 @@ UCL_EXTERN bool ucl_parser_add_chunk_full (struct ucl_parser *parser,
* @return true if string has been added and false in case of error
*/
UCL_EXTERN bool ucl_parser_add_string (struct ucl_parser *parser,
- const char *data,size_t len);
+ const char *data, size_t len);
/**
* Load ucl object from a string
@@ -1125,6 +1188,29 @@ UCL_EXTERN bool ucl_set_include_path (struct ucl_parser *parser,
UCL_EXTERN ucl_object_t* ucl_parser_get_object (struct ucl_parser *parser);
/**
+ * Get the current stack object as stack accessor function for use in macro
+ * functions (refcount is increased)
+ * @param parser parser object
+ * @param depth depth of stack to retrieve (top is 0)
+ * @return current stack object or NULL
+ */
+UCL_EXTERN ucl_object_t* ucl_parser_get_current_stack_object (struct ucl_parser *parser, unsigned int depth);
+
+/**
+ * Peek at the character at the current chunk position
+ * @param parser parser structure
+ * @return current chunk position character
+ */
+UCL_EXTERN unsigned char ucl_parser_chunk_peek (struct ucl_parser *parser);
+
+/**
+ * Skip the character at the current chunk position
+ * @param parser parser structure
+ * @return success boolean
+ */
+UCL_EXTERN bool ucl_parser_chunk_skip (struct ucl_parser *parser);
+
+/**
* Get the error string if parsing has been failed
* @param parser parser object
* @return error description
@@ -1185,7 +1271,7 @@ UCL_EXTERN const ucl_object_t * ucl_comments_find (const ucl_object_t *comments,
* Move comment from `from` object to `to` object
* @param comments comments object
* @param what source object
- * @param whith destination object
+ * @param with destination object
* @return `true` if `from` has comment and it has been moved to `to`
*/
UCL_EXTERN bool ucl_comments_move (ucl_object_t *comments,
@@ -1221,6 +1307,76 @@ UCL_EXTERN bool ucl_parser_pubkey_add (struct ucl_parser *parser,
UCL_EXTERN bool ucl_parser_set_filevars (struct ucl_parser *parser, const char *filename,
bool need_expand);
+/**
+ * Returns current file for the parser
+ * @param parser parser object
+ * @return current file or NULL if parsing memory
+ */
+UCL_EXTERN const char *ucl_parser_get_cur_file (struct ucl_parser *parser);
+
+/**
+ * Defines special handler for certain types of data (identified by magic)
+ */
+typedef bool (*ucl_parser_special_handler_t) (struct ucl_parser *parser,
+ const unsigned char *source, size_t source_len,
+ unsigned char **destination, size_t *dest_len,
+ void *user_data);
+
+/**
+ * Special handler flags
+ */
+enum ucl_special_handler_flags {
+ UCL_SPECIAL_HANDLER_DEFAULT = 0,
+ UCL_SPECIAL_HANDLER_PREPROCESS_ALL = (1u << 0),
+};
+
+/**
+ * Special handler structure
+ */
+struct ucl_parser_special_handler {
+ const unsigned char *magic;
+ size_t magic_len;
+ enum ucl_special_handler_flags flags;
+ ucl_parser_special_handler_t handler;
+ void (*free_function) (unsigned char *data, size_t len, void *user_data);
+ void *user_data;
+ struct ucl_parser_special_handler *next; /* Used internally */
+};
+
+/**
+ * Add special handler for a parser, handles special sequences identified by magic
+ * @param parser parser structure
+ * @param handler handler structure
+ */
+UCL_EXTERN void ucl_parser_add_special_handler (struct ucl_parser *parser,
+ struct ucl_parser_special_handler *handler);
+
+/**
+ * Handler for include traces:
+ * @param parser parser object
+ * @param parent where include is done from
+ * @param args arguments to an include
+ * @param path path of the include
+ * @param pathlen length of the path
+ * @param user_data opaque userdata
+ */
+typedef void (ucl_include_trace_func_t) (struct ucl_parser *parser,
+ const ucl_object_t *parent,
+ const ucl_object_t *args,
+ const char *path,
+ size_t pathlen,
+ void *user_data);
+
+/**
+ * Register trace function for an include handler
+ * @param parser parser object
+ * @param func function to trace includes
+ * @param user_data opaque data
+ */
+UCL_EXTERN void ucl_parser_set_include_tracer (struct ucl_parser *parser,
+ ucl_include_trace_func_t func,
+ void *user_data);
+
/** @} */
/**
@@ -1420,7 +1576,7 @@ enum ucl_schema_error_code {
struct ucl_schema_error {
enum ucl_schema_error_code code; /**< error code */
char msg[128]; /**< error message */
- const ucl_object_t *obj; /**< object where error occured */
+ const ucl_object_t *obj; /**< object where error occurred */
};
/**
@@ -1428,7 +1584,7 @@ struct ucl_schema_error {
* @param schema schema object
* @param obj object to validate
* @param err error pointer, if this parameter is not NULL and error has been
- * occured, then `err` is filled with the exact error definition.
+ * occurred, then `err` is filled with the exact error definition.
* @return true if `obj` is valid using `schema`
*/
UCL_EXTERN bool ucl_object_validate (const ucl_object_t *schema,
@@ -1440,7 +1596,7 @@ UCL_EXTERN bool ucl_object_validate (const ucl_object_t *schema,
* @param obj object to validate
* @param root root schema object
* @param err error pointer, if this parameter is not NULL and error has been
- * occured, then `err` is filled with the exact error definition.
+ * occurred, then `err` is filled with the exact error definition.
* @return true if `obj` is valid using `schema`
*/
UCL_EXTERN bool ucl_object_validate_root (const ucl_object_t *schema,
@@ -1456,7 +1612,7 @@ UCL_EXTERN bool ucl_object_validate_root (const ucl_object_t *schema,
* @param root root schema object
* @param ext_refs external references (might be modified during validation)
* @param err error pointer, if this parameter is not NULL and error has been
- * occured, then `err` is filled with the exact error definition.
+ * occurred, then `err` is filled with the exact error definition.
* @return true if `obj` is valid using `schema`
*/
UCL_EXTERN bool ucl_object_validate_root_ext (const ucl_object_t *schema,
@@ -1491,4 +1647,7 @@ UCL_EXTERN bool ucl_object_validate_root_ext (const ucl_object_t *schema,
#define ucl_obj_ref ucl_object_ref
#define ucl_obj_free ucl_object_free
+#define UCL_PRIORITY_MIN 0
+#define UCL_PRIORITY_MAX 15
+
#endif /* UCL_H_ */
diff --git a/klib/kvec.h b/klib/kvec.h
index b0a7504b2268..ce6a53640df9 100644
--- a/klib/kvec.h
+++ b/klib/kvec.h
@@ -30,11 +30,13 @@
int main() {
kvec_t(int) array;
kv_init(array);
- kv_push(int, array, 10); // append
+ kv_push_safe(int, array, 10, e0); // append
kv_a(int, array, 20) = 5; // dynamic
kv_A(array, 20) = 4; // static
kv_destroy(array);
return 0;
+e0:
+ return 1;
}
*/
@@ -60,8 +62,71 @@ int main() {
#define kv_size(v) ((v).n)
#define kv_max(v) ((v).m)
-#define kv_resize(type, v, s) ((v).m = (s), (v).a = (type*)realloc((v).a, sizeof(type) * (v).m))
+#define kv_resize_safe(type, v, s, el) do { \
+ type *_tp = (type*)realloc((v).a, sizeof(type) * (s)); \
+ if (_tp == NULL) { \
+ goto el; \
+ } else { \
+ (v).a = _tp; \
+ (v).m = (s); \
+ } \
+ } while (0)
+
#define kv_grow_factor 1.5
+#define kv_grow_safe(type, v, el) do { \
+ size_t _ts = ((v).m > 1 ? (v).m * kv_grow_factor : 2); \
+ type *_tp = (type*)realloc((v).a, sizeof(type) * _ts); \
+ if (_tp == NULL) { \
+ goto el; \
+ } else { \
+ (v).a = _tp; \
+ (v).m = _ts; \
+ } \
+ } while (0)
+
+#define kv_copy_safe(type, v1, v0, el) do { \
+ if ((v1).m < (v0).n) kv_resize_safe(type, v1, (v0).n, el); \
+ (v1).n = (v0).n; \
+ memcpy((v1).a, (v0).a, sizeof(type) * (v0).n); \
+ } while (0)
+
+#define kv_push_safe(type, v, x, el) do { \
+ if ((v).n == (v).m) { \
+ kv_grow_safe(type, v, el); \
+ } \
+ (v).a[(v).n++] = (x); \
+ } while (0)
+
+#define kv_prepend_safe(type, v, x, el) do { \
+ if ((v).n == (v).m) { \
+ kv_grow_safe(type, v, el); \
+ } \
+ memmove((v).a + 1, (v).a, sizeof(type) * (v).n); \
+ (v).a[0] = (x); \
+ (v).n ++; \
+ } while (0)
+
+#define kv_concat_safe(type, v1, v0, el) do { \
+ if ((v1).m < (v0).n + (v1).n) \
+ kv_resize_safe(type, v1, (v0).n + (v1).n, el); \
+ memcpy((v1).a + (v1).n, (v0).a, sizeof(type) * (v0).n); \
+ (v1).n = (v0).n + (v1).n; \
+ } while (0)
+
+#define kv_del(type, v, i) do { \
+ if ((i) < (v).n) { \
+ memmove((v).a + (i), (v).a + ((i) + 1), sizeof(type) * ((v).n - (i) - 1)); \
+ (v).n --; \
+ } \
+} while (0)
+
+/*
+ * Old (ENOMEM-unsafe) version of kv_xxx macros. Compat-only, not for use in
+ * the new library code.
+ */
+
+#define kv_resize(type, v, s) ((v).m = (s), (v).a = (type*)realloc((v).a, sizeof(type) * (v).m))
+
#define kv_grow(type, v) ((v).m = ((v).m > 1 ? (v).m * kv_grow_factor : 2), \
(v).a = (type*)realloc((v).a, sizeof(type) * (v).m))
@@ -93,11 +158,4 @@ int main() {
(v1).n = (v0).n + (v1).n; \
} while (0)
-#define kv_del(type, v, i) do { \
- if ((i) < (v).n) { \
- memmove((v).a + (i), (v).a + ((i) + 1), sizeof(type) * ((v).n - (i) - 1)); \
- (v).n --; \
- } \
-} while (0)
-
-#endif
+#endif /* AC_KVEC_H */
diff --git a/lua/lua_ucl.c b/lua/lua_ucl.c
index 62b0652f564a..b34fd56878b8 100644
--- a/lua/lua_ucl.c
+++ b/lua/lua_ucl.c
@@ -68,16 +68,27 @@ func = "huh";
#define PARSER_META "ucl.parser.meta"
#define EMITTER_META "ucl.emitter.meta"
-#define NULL_META "null.emitter.meta"
+#define NULL_META "ucl.null.meta"
#define OBJECT_META "ucl.object.meta"
+#define UCL_OBJECT_TYPE_META "ucl.type.object"
+#define UCL_ARRAY_TYPE_META "ucl.type.array"
+#define UCL_IMPL_ARRAY_TYPE_META "ucl.type.impl_array"
-static int ucl_object_lua_push_array (lua_State *L, const ucl_object_t *obj);
-static int ucl_object_lua_push_scalar (lua_State *L, const ucl_object_t *obj, bool allow_array);
-static ucl_object_t* ucl_object_lua_fromtable (lua_State *L, int idx);
-static ucl_object_t* ucl_object_lua_fromelt (lua_State *L, int idx);
+static int ucl_object_lua_push_array (lua_State *L, const ucl_object_t *obj, int flags);
+static int ucl_object_lua_push_scalar (lua_State *L, const ucl_object_t *obj, int flags);
+static int ucl_object_push_lua_common (lua_State *L, const ucl_object_t *obj, int flags);
+static ucl_object_t* ucl_object_lua_fromtable (lua_State *L, int idx, ucl_string_flags_t flags);
+static ucl_object_t* ucl_object_lua_fromelt (lua_State *L, int idx, ucl_string_flags_t flags);
static void *ucl_null;
+
+enum lua_ucl_push_flags {
+ LUA_UCL_DEFAULT_FLAGS = 0,
+ LUA_UCL_ALLOW_ARRAY = (1u << 0u),
+ LUA_UCL_CONVERT_NIL = (1u << 1u),
+};
+
/**
* Push a single element of an object to lua
* @param L
@@ -86,10 +97,10 @@ static void *ucl_null;
*/
static void
ucl_object_lua_push_element (lua_State *L, const char *key,
- const ucl_object_t *obj)
+ const ucl_object_t *obj, int flags)
{
lua_pushstring (L, key);
- ucl_object_push_lua (L, obj, true);
+ ucl_object_push_lua_common (L, obj, flags|LUA_UCL_ALLOW_ARRAY);
lua_settable (L, -3);
}
@@ -137,29 +148,26 @@ lua_ucl_userdata_emitter (void *ud)
*/
static int
ucl_object_lua_push_object (lua_State *L, const ucl_object_t *obj,
- bool allow_array)
+ int flags)
{
const ucl_object_t *cur;
ucl_object_iter_t it = NULL;
- int nelt = 0;
- if (allow_array && obj->next != NULL) {
+ if ((flags & LUA_UCL_ALLOW_ARRAY) && obj->next != NULL) {
/* Actually we need to push this as an array */
- return ucl_object_lua_push_array (L, obj);
- }
-
- /* Optimize allocation by preallocation of table */
- while (ucl_object_iterate (obj, &it, true) != NULL) {
- nelt ++;
+ return ucl_object_lua_push_array (L, obj, flags);
}
- lua_createtable (L, 0, nelt);
+ lua_createtable (L, 0, obj->len);
it = NULL;
while ((cur = ucl_object_iterate (obj, &it, true)) != NULL) {
- ucl_object_lua_push_element (L, ucl_object_key (cur), cur);
+ ucl_object_lua_push_element (L, ucl_object_key (cur), cur, flags);
}
+ luaL_getmetatable (L, UCL_OBJECT_TYPE_META);
+ lua_setmetatable (L, -2);
+
return 1;
}
@@ -170,7 +178,7 @@ ucl_object_lua_push_object (lua_State *L, const ucl_object_t *obj,
* @return
*/
static int
-ucl_object_lua_push_array (lua_State *L, const ucl_object_t *obj)
+ucl_object_lua_push_array (lua_State *L, const ucl_object_t *obj, int flags)
{
const ucl_object_t *cur;
ucl_object_iter_t it;
@@ -182,11 +190,14 @@ ucl_object_lua_push_array (lua_State *L, const ucl_object_t *obj)
lua_createtable (L, nelt, 0);
while ((cur = ucl_object_iterate_safe (it, true))) {
- ucl_object_push_lua (L, cur, false);
+ ucl_object_push_lua (L, cur, (flags & ~LUA_UCL_ALLOW_ARRAY));
lua_rawseti (L, -2, i);
i ++;
}
+ luaL_getmetatable (L, UCL_ARRAY_TYPE_META);
+ lua_setmetatable (L, -2);
+
ucl_object_iterate_free (it);
}
else {
@@ -198,10 +209,13 @@ ucl_object_lua_push_array (lua_State *L, const ucl_object_t *obj)
lua_createtable (L, nelt, 0);
LL_FOREACH (obj, cur) {
- ucl_object_push_lua (L, cur, false);
+ ucl_object_push_lua (L, cur, (flags & ~LUA_UCL_ALLOW_ARRAY));
lua_rawseti (L, -2, i);
i ++;
}
+
+ luaL_getmetatable (L, UCL_IMPL_ARRAY_TYPE_META);
+ lua_setmetatable (L, -2);
}
return 1;
@@ -212,13 +226,13 @@ ucl_object_lua_push_array (lua_State *L, const ucl_object_t *obj)
*/
static int
ucl_object_lua_push_scalar (lua_State *L, const ucl_object_t *obj,
- bool allow_array)
+ int flags)
{
struct ucl_lua_funcdata *fd;
- if (allow_array && obj->next != NULL) {
+ if ((flags & LUA_UCL_ALLOW_ARRAY) && obj->next != NULL) {
/* Actually we need to push this as an array */
- return ucl_object_lua_push_array (L, obj);
+ return ucl_object_lua_push_array (L, obj, flags);
}
switch (obj->type) {
@@ -240,7 +254,12 @@ ucl_object_lua_push_scalar (lua_State *L, const ucl_object_t *obj,
lua_pushnumber (L, ucl_obj_todouble (obj));
break;
case UCL_NULL:
- lua_getfield (L, LUA_REGISTRYINDEX, "ucl.null");
+ if (flags & LUA_UCL_CONVERT_NIL) {
+ lua_pushboolean (L, false);
+ }
+ else {
+ lua_getfield (L, LUA_REGISTRYINDEX, "ucl.null");
+ }
break;
case UCL_USERDATA:
fd = (struct ucl_lua_funcdata *)obj->value.ud;
@@ -254,6 +273,19 @@ ucl_object_lua_push_scalar (lua_State *L, const ucl_object_t *obj,
return 1;
}
+static int
+ucl_object_push_lua_common (lua_State *L, const ucl_object_t *obj, int flags)
+{
+ switch (obj->type) {
+ case UCL_OBJECT:
+ return ucl_object_lua_push_object (L, obj, flags);
+ case UCL_ARRAY:
+ return ucl_object_lua_push_array (L, obj, flags);
+ default:
+ return ucl_object_lua_push_scalar (L, obj, flags);
+ }
+}
+
/***
* @function ucl_object_push_lua(L, obj, allow_array)
* This is a `C` function to push `UCL` object as lua variable. This function
@@ -272,14 +304,16 @@ ucl_object_lua_push_scalar (lua_State *L, const ucl_object_t *obj,
int
ucl_object_push_lua (lua_State *L, const ucl_object_t *obj, bool allow_array)
{
- switch (obj->type) {
- case UCL_OBJECT:
- return ucl_object_lua_push_object (L, obj, allow_array);
- case UCL_ARRAY:
- return ucl_object_lua_push_array (L, obj);
- default:
- return ucl_object_lua_push_scalar (L, obj, allow_array);
- }
+ return ucl_object_push_lua_common (L, obj,
+ allow_array ? LUA_UCL_ALLOW_ARRAY : LUA_UCL_DEFAULT_FLAGS);
+}
+
+int
+ucl_object_push_lua_filter_nil (lua_State *L, const ucl_object_t *obj, bool allow_array)
+{
+ return ucl_object_push_lua_common (L, obj,
+ allow_array ? (LUA_UCL_ALLOW_ARRAY|LUA_UCL_CONVERT_NIL) :
+ (LUA_UCL_DEFAULT_FLAGS|LUA_UCL_CONVERT_NIL));
}
/**
@@ -289,55 +323,107 @@ ucl_object_push_lua (lua_State *L, const ucl_object_t *obj, bool allow_array)
* @param idx
*/
static ucl_object_t *
-ucl_object_lua_fromtable (lua_State *L, int idx)
+ucl_object_lua_fromtable (lua_State *L, int idx, ucl_string_flags_t flags)
{
- ucl_object_t *obj, *top = NULL;
+ ucl_object_t *obj, *top = NULL, *cur;
size_t keylen;
const char *k;
- bool is_array = true;
- int max = INT_MIN;
+ bool is_array = true, is_implicit = false, found_mt = false;
+ size_t max = 0, nelts = 0;
if (idx < 0) {
/* For negative indicies we want to invert them */
idx = lua_gettop (L) + idx + 1;
}
- /* Check for array */
- lua_pushnil (L);
- while (lua_next (L, idx) != 0) {
- if (lua_type (L, -2) == LUA_TNUMBER) {
- double num = lua_tonumber (L, -2);
- if (num == (int)num) {
- if (num > max) {
- max = num;
+
+ /* First, we check from metatable */
+ if (luaL_getmetafield (L, idx, "class") != 0) {
+
+ if (lua_type (L, -1) == LUA_TSTRING) {
+ const char *classname = lua_tostring (L, -1);
+
+ if (strcmp (classname, UCL_OBJECT_TYPE_META) == 0) {
+ is_array = false;
+ found_mt = true;
+ } else if (strcmp (classname, UCL_ARRAY_TYPE_META) == 0) {
+ is_array = true;
+ found_mt = true;
+#if LUA_VERSION_NUM >= 502
+ max = lua_rawlen (L, idx);
+#else
+ max = lua_objlen (L, idx);
+#endif
+ nelts = max;
+ } else if (strcmp (classname, UCL_IMPL_ARRAY_TYPE_META) == 0) {
+ is_array = true;
+ is_implicit = true;
+ found_mt = true;
+#if LUA_VERSION_NUM >= 502
+ max = lua_rawlen (L, idx);
+#else
+ max = lua_objlen (L, idx);
+#endif
+ nelts = max;
+ }
+ }
+
+ lua_pop (L, 1);
+ }
+
+ if (!found_mt) {
+ /* Check for array (it is all inefficient) */
+ lua_pushnil (L);
+
+ while (lua_next (L, idx) != 0) {
+ lua_pushvalue (L, -2);
+
+ if (lua_type (L, -1) == LUA_TNUMBER) {
+ double num = lua_tonumber (L, -1);
+ if (num == (int) num) {
+ if (num > max) {
+ max = num;
+ }
+ }
+ else {
+ /* Keys are not integer */
+ is_array = false;
}
}
else {
- /* Keys are not integer */
- lua_pop (L, 2);
+ /* Keys are not numeric */
is_array = false;
- break;
}
- }
- else {
- /* Keys are not numeric */
+
lua_pop (L, 2);
- is_array = false;
- break;
+ nelts ++;
}
- lua_pop (L, 1);
}
/* Table iterate */
if (is_array) {
int i;
- top = ucl_object_typed_new (UCL_ARRAY);
+ if (!is_implicit) {
+ top = ucl_object_typed_new (UCL_ARRAY);
+ ucl_object_reserve (top, nelts);
+ }
+ else {
+ top = NULL;
+ }
+
for (i = 1; i <= max; i ++) {
lua_pushinteger (L, i);
lua_gettable (L, idx);
- obj = ucl_object_lua_fromelt (L, lua_gettop (L));
+
+ obj = ucl_object_lua_fromelt (L, lua_gettop (L), flags);
+
if (obj != NULL) {
- ucl_array_append (top, obj);
+ if (is_implicit) {
+ DL_APPEND (top, obj);
+ }
+ else {
+ ucl_array_append (top, obj);
+ }
}
lua_pop (L, 1);
}
@@ -345,15 +431,25 @@ ucl_object_lua_fromtable (lua_State *L, int idx)
else {
lua_pushnil (L);
top = ucl_object_typed_new (UCL_OBJECT);
+ ucl_object_reserve (top, nelts);
+
while (lua_next (L, idx) != 0) {
/* copy key to avoid modifications */
- k = lua_tolstring (L, -2, &keylen);
- obj = ucl_object_lua_fromelt (L, lua_gettop (L));
+ lua_pushvalue (L, -2);
+ k = lua_tolstring (L, -1, &keylen);
+ obj = ucl_object_lua_fromelt (L, lua_gettop (L) - 1, flags);
if (obj != NULL) {
ucl_object_insert_key (top, obj, k, keylen, true);
+
+ DL_FOREACH (obj, cur) {
+ if (cur->keylen == 0) {
+ cur->keylen = obj->keylen;
+ cur->key = obj->key;
+ }
+ }
}
- lua_pop (L, 1);
+ lua_pop (L, 2);
}
}
@@ -367,18 +463,27 @@ ucl_object_lua_fromtable (lua_State *L, int idx)
* @param idx
*/
static ucl_object_t *
-ucl_object_lua_fromelt (lua_State *L, int idx)
+ucl_object_lua_fromelt (lua_State *L, int idx, ucl_string_flags_t flags)
{
int type;
double num;
ucl_object_t *obj = NULL;
struct ucl_lua_funcdata *fd;
+ const char *str;
+ size_t sz;
type = lua_type (L, idx);
switch (type) {
case LUA_TSTRING:
- obj = ucl_object_fromstring_common (lua_tostring (L, idx), 0, 0);
+ str = lua_tolstring (L, idx, &sz);
+
+ if (str) {
+ obj = ucl_object_fromstring_common (str, sz, flags);
+ }
+ else {
+ obj = ucl_object_typed_new (UCL_NULL);
+ }
break;
case LUA_TNUMBER:
num = lua_tonumber (L, idx);
@@ -406,13 +511,13 @@ ucl_object_lua_fromelt (lua_State *L, int idx)
lua_insert (L, 1); /* func, gen, obj */
lua_insert (L, 2); /* func, obj, gen */
lua_call(L, 2, 1);
- obj = ucl_object_lua_fromelt (L, 1);
+ obj = ucl_object_lua_fromelt (L, 1, flags);
}
lua_pop (L, 2);
}
else {
if (type == LUA_TTABLE) {
- obj = ucl_object_lua_fromtable (L, idx);
+ obj = ucl_object_lua_fromtable (L, idx, flags);
}
else if (type == LUA_TFUNCTION) {
fd = malloc (sizeof (*fd));
@@ -451,10 +556,38 @@ 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);
+ obj = ucl_object_lua_fromtable (L, idx, 0);
+ break;
+ default:
+ obj = ucl_object_lua_fromelt (L, idx, 0);
+ break;
+ }
+
+ return obj;
+}
+
+/**
+ * @function ucl_object_lua_import_escape(L, idx)
+ * Extracts ucl object from lua variable at `idx` position escaping JSON strings
+ * @see ucl_object_push_lua for conversion definitions
+ * @param {lua_state} L lua state machine pointer
+ * @param {int} idx index where the source variable is placed
+ * @return {ucl_object_t} new ucl object extracted from lua variable. Reference count of this object is 1,
+ * this object thus needs to be unref'ed after usage.
+ */
+ucl_object_t *
+ucl_object_lua_import_escape (lua_State *L, int idx)
+{
+ ucl_object_t *obj;
+ int t;
+
+ t = lua_type (L, idx);
+ switch (t) {
+ case LUA_TTABLE:
+ obj = ucl_object_lua_fromtable (L, idx, UCL_STRING_RAW);
break;
default:
- obj = ucl_object_lua_fromelt (L, idx);
+ obj = ucl_object_lua_fromelt (L, idx, UCL_STRING_RAW);
break;
}
@@ -492,6 +625,7 @@ 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));
@@ -590,6 +724,76 @@ lua_ucl_parser_parse_file (lua_State *L)
}
/***
+ * @method parser:register_variable(name, value)
+ * Register parser variable
+ * @param {string} name name of variable
+ * @param {string} value value of variable
+ * @return {bool} success
+@example
+local parser = ucl.parser()
+local res = parser:register_variable('CONFDIR', '/etc/foo')
+ */
+static int
+lua_ucl_parser_register_variable (lua_State *L)
+{
+ struct ucl_parser *parser;
+ const char *name, *value;
+ int ret = 2;
+
+ parser = lua_ucl_parser_get (L, 1);
+ name = luaL_checkstring (L, 2);
+ value = luaL_checkstring (L, 3);
+
+ if (parser != NULL && name != NULL && value != NULL) {
+ ucl_parser_register_variable (parser, name, value);
+ lua_pushboolean (L, true);
+ ret = 1;
+ }
+ else {
+ return luaL_error (L, "invalid arguments");
+ }
+
+ return ret;
+}
+
+/***
+ * @method parser:register_variables(vars)
+ * Register parser variables
+ * @param {table} vars names/values of variables
+ * @return {bool} success
+@example
+local parser = ucl.parser()
+local res = parser:register_variables({CONFDIR = '/etc/foo', VARDIR = '/var'})
+ */
+static int
+lua_ucl_parser_register_variables (lua_State *L)
+{
+ struct ucl_parser *parser;
+ const char *name, *value;
+ int ret = 2;
+
+ parser = lua_ucl_parser_get (L, 1);
+
+ if (parser != NULL && lua_type (L, 2) == LUA_TTABLE) {
+ for (lua_pushnil (L); lua_next (L, 2); lua_pop (L, 1)) {
+ lua_pushvalue (L, -2);
+ name = luaL_checkstring (L, -1);
+ value = luaL_checkstring (L, -2);
+ ucl_parser_register_variable (parser, name, value);
+ lua_pop (L, 1);
+ }
+
+ lua_pushboolean (L, true);
+ ret = 1;
+ }
+ else {
+ return luaL_error (L, "invalid arguments");
+ }
+
+ return ret;
+}
+
+/***
* @method parser:parse_string(input)
* Parse UCL object from file.
* @param {string} input string to parse
@@ -630,6 +834,52 @@ 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).
+ * @param {rspamd_text} input text to parse
+ * @return {bool[, string]} if res is `true` then file has been parsed successfully, otherwise an error string is also returned
+ */
+static int
+lua_ucl_parser_parse_text (lua_State *L)
+{
+ struct ucl_parser *parser;
+ struct _rspamd_lua_text *t;
+ enum ucl_parse_type type = UCL_PARSE_UCL;
+ int ret = 2;
+
+ parser = lua_ucl_parser_get (L, 1);
+ t = lua_touserdata (L, 2);
+
+ if (lua_type (L, 3) == LUA_TSTRING) {
+ type = lua_ucl_str_to_parse_type (lua_tostring (L, 3));
+ }
+
+ if (parser != NULL && t != NULL) {
+ if (ucl_parser_add_chunk_full (parser, (const unsigned char *)t->start,
+ t->len, 0, UCL_DUPLICATE_APPEND, type)) {
+ lua_pushboolean (L, true);
+ ret = 1;
+ }
+ else {
+ lua_pushboolean (L, false);
+ lua_pushstring (L, ucl_parser_get_error (parser));
+ }
+ }
+ else {
+ lua_pushboolean (L, false);
+ lua_pushstring (L, "invalid arguments");
+ }
+
+ return ret;
+}
+
/***
* @method parser:get_object()
* Get top object from parser and export it to lua representation.
@@ -977,6 +1227,15 @@ lua_ucl_parser_mt (lua_State *L)
lua_pushcfunction (L, lua_ucl_parser_parse_string);
lua_setfield (L, -2, "parse_string");
+ lua_pushcfunction (L, lua_ucl_parser_parse_text);
+ lua_setfield (L, -2, "parse_text");
+
+ lua_pushcfunction (L, lua_ucl_parser_register_variable);
+ lua_setfield (L, -2, "register_variable");
+
+ lua_pushcfunction (L, lua_ucl_parser_register_variables);
+ lua_setfield (L, -2, "register_variables");
+
lua_pushcfunction (L, lua_ucl_parser_get_object);
lua_setfield (L, -2, "get_object");
@@ -1021,6 +1280,49 @@ lua_ucl_object_mt (lua_State *L)
lua_pop (L, 1);
}
+static void
+lua_ucl_types_mt (lua_State *L)
+{
+ luaL_newmetatable (L, UCL_OBJECT_TYPE_META);
+
+ lua_pushcfunction (L, lua_ucl_object_tostring);
+ lua_setfield (L, -2, "__tostring");
+
+ lua_pushcfunction (L, lua_ucl_object_tostring);
+ lua_setfield (L, -2, "tostring");
+
+ lua_pushstring (L, UCL_OBJECT_TYPE_META);
+ lua_setfield (L, -2, "class");
+
+ lua_pop (L, 1);
+
+ luaL_newmetatable (L, UCL_ARRAY_TYPE_META);
+
+ lua_pushcfunction (L, lua_ucl_object_tostring);
+ lua_setfield (L, -2, "__tostring");
+
+ lua_pushcfunction (L, lua_ucl_object_tostring);
+ lua_setfield (L, -2, "tostring");
+
+ lua_pushstring (L, UCL_ARRAY_TYPE_META);
+ lua_setfield (L, -2, "class");
+
+ lua_pop (L, 1);
+
+ luaL_newmetatable (L, UCL_IMPL_ARRAY_TYPE_META);
+
+ lua_pushcfunction (L, lua_ucl_object_tostring);
+ lua_setfield (L, -2, "__tostring");
+
+ lua_pushcfunction (L, lua_ucl_object_tostring);
+ lua_setfield (L, -2, "tostring");
+
+ lua_pushstring (L, UCL_IMPL_ARRAY_TYPE_META);
+ lua_setfield (L, -2, "class");
+
+ lua_pop (L, 1);
+}
+
static int
lua_ucl_to_json (lua_State *L)
{
@@ -1073,7 +1375,7 @@ lua_ucl_to_config (lua_State *L)
* - `yaml` - embedded yaml
*
* If `var` contains function, they are called during output formatting and if
- * they return string value, then this value is used for ouptut.
+ * they return string value, then this value is used for output.
* @param {variant} var any sort of lua variable (if userdata then metafield `__to_ucl` is searched for output)
* @param {string} format any available format
* @return {string} string representation of `var` in the specific `format`.
@@ -1101,6 +1403,7 @@ lua_ucl_to_format (lua_State *L)
{
ucl_object_t *obj;
int format = UCL_EMIT_JSON;
+ bool sort = false;
if (lua_gettop (L) > 1) {
if (lua_type (L, 2) == LUA_TNUMBER) {
@@ -1130,10 +1433,22 @@ lua_ucl_to_format (lua_State *L)
format = UCL_EMIT_MSGPACK;
}
}
+
+ if (lua_isboolean (L, 3)) {
+ sort = lua_toboolean (L, 3);
+ }
}
obj = ucl_object_lua_import (L, 1);
+
if (obj != NULL) {
+
+ if (sort) {
+ if (ucl_object_type (obj) == UCL_OBJECT) {
+ ucl_object_sort_keys (obj, UCL_SORT_KEYS_RECURSIVE);
+ }
+ }
+
lua_ucl_to_string (L, obj, format);
ucl_object_unref (obj);
}
@@ -1168,6 +1483,7 @@ luaopen_ucl (lua_State *L)
lua_ucl_parser_mt (L);
lua_ucl_null_mt (L);
lua_ucl_object_mt (L);
+ lua_ucl_types_mt (L);
/* Create the refs weak table: */
lua_createtable (L, 0, 2);
diff --git a/python/MANIFEST.in b/python/MANIFEST.in
new file mode 100644
index 000000000000..336a4b3a7a07
--- /dev/null
+++ b/python/MANIFEST.in
@@ -0,0 +1,5 @@
+include COPYING
+recursive-include include *.h
+recursive-include src *.h
+recursive-include klib *.h
+recursive-include uthash *.h
diff --git a/python/setup.py b/python/setup.py
index 9e1151a1323a..8da832bac381 100644
--- a/python/setup.py
+++ b/python/setup.py
@@ -1,11 +1,20 @@
try:
from setuptools import setup, Extension
+ # setuptools doesn't support template param for MANIFEST.in
+ from setuptools.command.egg_info import manifest_maker
+ manifest_maker.template = 'python/MANIFEST.in'
except ImportError:
from distutils.core import setup, Extension
import os
import sys
+LIB_ROOT = os.path.abspath(os.path.join(__file__, os.pardir, os.pardir))
+if os.getcwd() != LIB_ROOT:
+ os.chdir(LIB_ROOT)
+if LIB_ROOT not in sys.path:
+ sys.path.append(LIB_ROOT)
+
tests_require = []
if sys.version < '2.7':
@@ -13,16 +22,35 @@ if sys.version < '2.7':
uclmodule = Extension(
'ucl',
- libraries = ['ucl'],
- sources = ['src/uclmodule.c'],
- language = 'c'
+ libraries=['ucl', 'curl'],
+ sources=['python/src/uclmodule.c'],
+ include_dirs=['include'],
+ language='c',
)
+ucl_lib = {
+ 'sources': ['src/' + fn for fn in os.listdir('src') if fn.endswith('.c')],
+ 'include_dirs': ['include', 'src', 'uthash', 'klib'],
+ 'macros': [('CURL_FOUND', '1')],
+}
+
+# sdist setup() will pull in the *.c files automatically, but not headers
+# MANIFEST.in will include the headers for sdist only
+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:
+ os.link('python/setup.py', 'setup.py')
+
setup(
name = 'ucl',
- version = '0.8',
- description = 'ucl parser and emmitter',
+ version = '0.8.1',
+ description = 'ucl parser and emitter',
ext_modules = [uclmodule],
+ template=template, # no longer supported with setuptools but doesn't hurt
+ libraries = [('ucl', ucl_lib)],
test_suite = 'tests',
tests_require = tests_require,
author = "Eitan Adler, Denis Volpato Martins",
@@ -41,3 +69,7 @@ setup(
"Topic :: Software Development :: Libraries",
]
)
+
+# clean up the trick after the build
+if in_ucl_root:
+ os.unlink("setup.py")
diff --git a/python/src/uclmodule.c b/python/src/uclmodule.c
index fce0dab14a44..d1051fbb0d12 100644
--- a/python/src/uclmodule.c
+++ b/python/src/uclmodule.c
@@ -80,7 +80,8 @@ static PyObject *
_internal_load_ucl (char *uclstr)
{
PyObject *ret;
- struct ucl_parser *parser = ucl_parser_new (UCL_PARSER_NO_TIME);
+ struct ucl_parser *parser =
+ ucl_parser_new (UCL_PARSER_NO_TIME|UCL_PARSER_NO_IMPLICIT_ARRAYS);
bool r = ucl_parser_add_string(parser, uclstr, 0);
if (r) {
diff --git a/python/tests/test_example.py b/python/tests/test_example.py
new file mode 100644
index 000000000000..f0785531f4e2
--- /dev/null
+++ b/python/tests/test_example.py
@@ -0,0 +1,59 @@
+from .compat import unittest
+import json
+import ucl
+
+_ucl_inp = '''
+param = value;
+section {
+ param = value;
+ param1 = value1;
+ flag = true;
+ number = 10k;
+ time = 0.2s;
+ string = "something";
+ subsection {
+ host = {
+ host = "hostname";
+ port = 900;
+ }
+ host = {
+ host = "hostname";
+ port = 901;
+ }
+ }
+}
+'''
+
+_json_res = {
+ 'param': 'value',
+ 'section': {
+ 'param': 'value',
+ 'param1': 'value1',
+ 'flag': True,
+ 'number': 10000,
+ 'time': '0.2s',
+ 'string': 'something',
+ 'subsection': {
+ 'host': [
+ {
+ 'host': 'hostname',
+ 'port': 900,
+ },
+ {
+ 'host': 'hostname',
+ 'port': 901,
+ }
+ ]
+ }
+ }
+}
+
+class TestExample(unittest.TestCase):
+ def test_example(self):
+ # load in sample UCL
+ u = ucl.load(_ucl_inp)
+
+ # Output and read back the JSON
+ uj = json.loads(json.dumps(u))
+
+ self.assertEqual(uj, _json_res)
diff --git a/python/tests/test_load.py b/python/tests/test_load.py
index 786587a67f3d..73d43188f3d5 100644
--- a/python/tests/test_load.py
+++ b/python/tests/test_load.py
@@ -71,7 +71,22 @@ class LoadTest(unittest.TestCase):
self.assertEqual(ucl.load("{/*1*/}"), {})
def test_1_in(self):
- valid = { 'key1': 'value' }
+ valid = {
+ 'key1': [
+ 'value',
+ 'value2',
+ 'value;',
+ 1.0,
+ -0xdeadbeef,
+ '0xdeadbeef.1',
+ '0xreadbeef',
+ -1e-10,
+ 1,
+ True,
+ False,
+ True,
+ ]
+ }
with open("../tests/basic/1.in", "r") as in1:
self.assertEqual(ucl.load(in1.read()), valid)
diff --git a/src/mum.h b/src/mum.h
index 8bc8af8621c5..318efea50268 100644
--- a/src/mum.h
+++ b/src/mum.h
@@ -35,7 +35,7 @@
Random and Pseudorandom Number Generators for Cryptographic
Applications (version 2.2.1) with 1000 bitstreams each containing
1M bits. MUM hashing is also faster Spooky64 and City64 on small
- strings (at least upto 512-bit) on Haswell and Power7. The MUM bulk
+ strings (at least up to 512-bit) on Haswell and Power7. The MUM bulk
speed (speed on very long data) is bigger than Spooky and City on
Power7. On Haswell the bulk speed is bigger than Spooky one and
close to City speed. */
@@ -172,7 +172,7 @@ _mum_le (uint64_t v) {
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
return _mum_bswap64 (v);
#else
-#error "Unknown endianess"
+#error "Unknown endianness"
#endif
}
@@ -183,7 +183,7 @@ _mum_le32 (uint32_t v) {
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
return _mum_bswap32 (v);
#else
-#error "Unknown endianess"
+#error "Unknown endianness"
#endif
}
@@ -396,7 +396,7 @@ mum_hash64 (uint64_t key, uint64_t seed) {
}
/* Hash data KEY of length LEN and SEED. The hash depends on the
- target endianess and the unroll factor. */
+ target endianness and the unroll factor. */
static inline uint64_t
mum_hash (const void *key, size_t len, uint64_t seed) {
#if defined(__x86_64__) && defined(_MUM_FRESH_GCC)
diff --git a/src/ucl_chartable.h b/src/ucl_chartable.h
index db9f02900c02..7571a1d91549 100644
--- a/src/ucl_chartable.h
+++ b/src/ucl_chartable.h
@@ -27,7 +27,7 @@
#include "ucl_internal.h"
static const unsigned int ucl_chartable[256] = {
-UCL_CHARACTER_VALUE_END, UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED,
+UCL_CHARACTER_JSON_UNSAFE|UCL_CHARACTER_VALUE_END|UCL_CHARACTER_UCL_UNSAFE, UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED,
UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED,
UCL_CHARACTER_DENIED, UCL_CHARACTER_DENIED,
UCL_CHARACTER_JSON_UNSAFE|UCL_CHARACTER_UCL_UNSAFE,
@@ -49,7 +49,7 @@ UCL_CHARACTER_VALUE_END /* # */, UCL_CHARACTER_VALUE_STR /* $ */,
UCL_CHARACTER_VALUE_STR /* % */, UCL_CHARACTER_VALUE_STR /* & */,
UCL_CHARACTER_VALUE_STR /* ' */, UCL_CHARACTER_VALUE_STR /* ( */,
UCL_CHARACTER_VALUE_STR /* ) */, UCL_CHARACTER_VALUE_STR /* * */,
-UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* + */,
+UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT|UCL_CHARACTER_UCL_UNSAFE /* + */,
UCL_CHARACTER_VALUE_END /* , */,
UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT_START|UCL_CHARACTER_VALUE_DIGIT /* - */,
UCL_CHARACTER_KEY|UCL_CHARACTER_VALUE_STR|UCL_CHARACTER_VALUE_DIGIT /* . */,
diff --git a/src/ucl_emitter.c b/src/ucl_emitter.c
index a15cd08cfb98..4f4465dfbf4a 100644
--- a/src/ucl_emitter.c
+++ b/src/ucl_emitter.c
@@ -424,8 +424,16 @@ ucl_emitter_common_elt (struct ucl_emitter_context *ctx,
break;
case UCL_STRING:
ucl_emitter_print_key (print_key, ctx, obj, compact);
- if (ctx->id == UCL_EMIT_CONFIG && ucl_maybe_long_string (obj)) {
- ucl_elt_string_write_multiline (obj->value.sv, obj->len, ctx);
+ if (ctx->id == UCL_EMIT_CONFIG) {
+ if (ucl_maybe_long_string (obj)) {
+ ucl_elt_string_write_multiline (obj->value.sv, obj->len, ctx);
+ } else {
+ if (obj->flags & UCL_OBJECT_SQUOTED) {
+ ucl_elt_string_write_squoted (obj->value.sv, obj->len, ctx);
+ } else {
+ ucl_elt_string_write_json (obj->value.sv, obj->len, ctx);
+ }
+ }
}
else {
ucl_elt_string_write_json (obj->value.sv, obj->len, ctx);
diff --git a/src/ucl_emitter_utils.c b/src/ucl_emitter_utils.c
index 3559eb63df92..b9f7de8f0224 100644
--- a/src/ucl_emitter_utils.c
+++ b/src/ucl_emitter_utils.c
@@ -71,6 +71,13 @@ static const struct ucl_emitter_context ucl_standard_emitters[] = {
}
};
+static inline void
+_ucl_emitter_free(void *p)
+{
+
+ free(p);
+}
+
/**
* Get standard emitter context for a specified emit_type
* @param emit_type type of emitter
@@ -102,7 +109,9 @@ ucl_elt_string_write_json (const char *str, size_t size,
func->ucl_emitter_append_character ('"', 1, func->ud);
while (size) {
- if (ucl_test_character (*p, UCL_CHARACTER_JSON_UNSAFE|UCL_CHARACTER_DENIED)) {
+ if (ucl_test_character (*p, (UCL_CHARACTER_JSON_UNSAFE|
+ UCL_CHARACTER_DENIED|
+ UCL_CHARACTER_WHITESPACE_UNSAFE))) {
if (len > 0) {
func->ucl_emitter_append_len (c, len, func->ud);
}
@@ -122,15 +131,21 @@ ucl_elt_string_write_json (const char *str, size_t size,
case '\f':
func->ucl_emitter_append_len ("\\f", 2, func->ud);
break;
+ case '\v':
+ func->ucl_emitter_append_len ("\\u000B", 6, func->ud);
+ break;
case '\\':
func->ucl_emitter_append_len ("\\\\", 2, func->ud);
break;
+ case ' ':
+ func->ucl_emitter_append_character (' ', 1, func->ud);
+ break;
case '"':
func->ucl_emitter_append_len ("\\\"", 2, func->ud);
break;
default:
/* Emit unicode unknown character */
- func->ucl_emitter_append_len ("\\uFFFD", 5, func->ud);
+ func->ucl_emitter_append_len ("\\uFFFD", 6, func->ud);
break;
}
len = 0;
@@ -151,6 +166,40 @@ ucl_elt_string_write_json (const char *str, size_t size,
}
void
+ucl_elt_string_write_squoted (const char *str, size_t size,
+ struct ucl_emitter_context *ctx)
+{
+ const char *p = str, *c = str;
+ size_t len = 0;
+ const struct ucl_emitter_functions *func = ctx->func;
+
+ func->ucl_emitter_append_character ('\'', 1, func->ud);
+
+ while (size) {
+ if (*p == '\'') {
+ if (len > 0) {
+ func->ucl_emitter_append_len (c, len, func->ud);
+ }
+
+ len = 0;
+ c = ++p;
+ func->ucl_emitter_append_len ("\\\'", 2, func->ud);
+ }
+ else {
+ p ++;
+ len ++;
+ }
+ size --;
+ }
+
+ if (len > 0) {
+ func->ucl_emitter_append_len (c, len, func->ud);
+ }
+
+ func->ucl_emitter_append_character ('\'', 1, func->ud);
+}
+
+void
ucl_elt_string_write_multiline (const char *str, size_t size,
struct ucl_emitter_context *ctx)
{
@@ -363,7 +412,7 @@ ucl_object_emit_memory_funcs (void **pmem)
f->ucl_emitter_append_double = ucl_utstring_append_double;
f->ucl_emitter_append_int = ucl_utstring_append_int;
f->ucl_emitter_append_len = ucl_utstring_append_len;
- f->ucl_emitter_free_func = free;
+ f->ucl_emitter_free_func = _ucl_emitter_free;
utstring_new (s);
f->ud = s;
*pmem = s->d;
@@ -412,7 +461,7 @@ ucl_object_emit_fd_funcs (int fd)
f->ucl_emitter_append_double = ucl_fd_append_double;
f->ucl_emitter_append_int = ucl_fd_append_int;
f->ucl_emitter_append_len = ucl_fd_append_len;
- f->ucl_emitter_free_func = free;
+ f->ucl_emitter_free_func = _ucl_emitter_free;
f->ud = ip;
}
diff --git a/src/ucl_hash.c b/src/ucl_hash.c
index bdc7fb486fc4..a74dfcdee68e 100644
--- a/src/ucl_hash.c
+++ b/src/ucl_hash.c
@@ -143,10 +143,10 @@ ucl_hash_caseless_func (const ucl_object_t *o)
u.c.c2 = lc_map[u.c.c2];
u.c.c3 = lc_map[u.c.c3];
u.c.c4 = lc_map[u.c.c4];
- u.c.c1 = lc_map[u.c.c5];
- u.c.c2 = lc_map[u.c.c6];
- u.c.c3 = lc_map[u.c.c7];
- u.c.c4 = lc_map[u.c.c8];
+ u.c.c5 = lc_map[u.c.c5];
+ u.c.c6 = lc_map[u.c.c6];
+ u.c.c7 = lc_map[u.c.c7];
+ u.c.c8 = lc_map[u.c.c8];
r = mum_hash_step (r, u.pp);
}
@@ -154,16 +154,22 @@ ucl_hash_caseless_func (const ucl_object_t *o)
switch (leftover) {
case 7:
u.c.c7 = lc_map[(unsigned char)s[i++]];
+ /* FALLTHRU */
case 6:
u.c.c6 = lc_map[(unsigned char)s[i++]];
+ /* FALLTHRU */
case 5:
u.c.c5 = lc_map[(unsigned char)s[i++]];
+ /* FALLTHRU */
case 4:
u.c.c4 = lc_map[(unsigned char)s[i++]];
+ /* FALLTHRU */
case 3:
u.c.c3 = lc_map[(unsigned char)s[i++]];
+ /* FALLTHRU */
case 2:
u.c.c2 = lc_map[(unsigned char)s[i++]];
+ /* FALLTHRU */
case 1:
u.c.c1 = lc_map[(unsigned char)s[i]];
r = mum_hash_step (r, u.pp);
@@ -177,7 +183,45 @@ static inline int
ucl_hash_caseless_equal (const ucl_object_t *k1, const ucl_object_t *k2)
{
if (k1->keylen == k2->keylen) {
- return memcmp (k1->key, k2->key, k1->keylen) == 0;
+ unsigned fp, i;
+ const char *s = k1->key, *d = k2->key;
+ unsigned char c1, c2, c3, c4;
+ union {
+ unsigned char c[4];
+ uint32_t n;
+ } cmp1, cmp2;
+ size_t leftover = k1->keylen % 4;
+
+ fp = k1->keylen - leftover;
+
+ for (i = 0; i != fp; i += 4) {
+ c1 = s[i], c2 = s[i + 1], c3 = s[i + 2], c4 = s[i + 3];
+ cmp1.c[0] = lc_map[c1];
+ cmp1.c[1] = lc_map[c2];
+ cmp1.c[2] = lc_map[c3];
+ cmp1.c[3] = lc_map[c4];
+
+ c1 = d[i], c2 = d[i + 1], c3 = d[i + 2], c4 = d[i + 3];
+ cmp2.c[0] = lc_map[c1];
+ cmp2.c[1] = lc_map[c2];
+ cmp2.c[2] = lc_map[c3];
+ cmp2.c[3] = lc_map[c4];
+
+ if (cmp1.n != cmp2.n) {
+ return 0;
+ }
+ }
+
+ while (leftover > 0) {
+ if (lc_map[(unsigned char)s[i]] != lc_map[(unsigned char)d[i]]) {
+ return 0;
+ }
+
+ leftover--;
+ i++;
+ }
+
+ return 1;
}
return 0;
@@ -193,17 +237,21 @@ ucl_hash_create (bool ignore_case)
new = UCL_ALLOC (sizeof (ucl_hash_t));
if (new != NULL) {
+ void *h;
kv_init (new->ar);
new->caseless = ignore_case;
if (ignore_case) {
- khash_t(ucl_hash_caseless_node) *h = kh_init (ucl_hash_caseless_node);
- new->hash = (void *)h;
+ h = (void *)kh_init (ucl_hash_caseless_node);
}
else {
- khash_t(ucl_hash_node) *h = kh_init (ucl_hash_node);
- new->hash = (void *)h;
+ h = (void *)kh_init (ucl_hash_node);
}
+ if (h == NULL) {
+ UCL_FREE (sizeof (ucl_hash_t), new);
+ return NULL;
+ }
+ new->hash = h;
}
return new;
}
@@ -249,7 +297,7 @@ void ucl_hash_destroy (ucl_hash_t* hashlin, ucl_hash_free_func func)
UCL_FREE (sizeof (*hashlin), hashlin);
}
-void
+bool
ucl_hash_insert (ucl_hash_t* hashlin, const ucl_object_t *obj,
const char *key, unsigned keylen)
{
@@ -258,7 +306,7 @@ ucl_hash_insert (ucl_hash_t* hashlin, const ucl_object_t *obj,
struct ucl_hash_elt *elt;
if (hashlin == NULL) {
- return;
+ return false;
}
if (hashlin->caseless) {
@@ -267,7 +315,7 @@ ucl_hash_insert (ucl_hash_t* hashlin, const ucl_object_t *obj,
k = kh_put (ucl_hash_caseless_node, h, obj, &ret);
if (ret > 0) {
elt = &kh_value (h, k);
- kv_push (const ucl_object_t *, hashlin->ar, obj);
+ kv_push_safe (const ucl_object_t *, hashlin->ar, obj, e0);
elt->obj = obj;
elt->ar_idx = kv_size (hashlin->ar) - 1;
}
@@ -278,11 +326,16 @@ ucl_hash_insert (ucl_hash_t* hashlin, const ucl_object_t *obj,
k = kh_put (ucl_hash_node, h, obj, &ret);
if (ret > 0) {
elt = &kh_value (h, k);
- kv_push (const ucl_object_t *, hashlin->ar, obj);
+ kv_push_safe (const ucl_object_t *, hashlin->ar, obj, e0);
elt->obj = obj;
elt->ar_idx = kv_size (hashlin->ar) - 1;
+ } else if (ret < 0) {
+ goto e0;
}
}
+ return true;
+e0:
+ return false;
}
void ucl_hash_replace (ucl_hash_t* hashlin, const ucl_object_t *old,
@@ -331,13 +384,16 @@ struct ucl_hash_real_iter {
const ucl_object_t **end;
};
+#define UHI_SETERR(ep, ern) {if (ep != NULL) *ep = (ern);}
+
const void*
-ucl_hash_iterate (ucl_hash_t *hashlin, ucl_hash_iter_t *iter)
+ucl_hash_iterate2 (ucl_hash_t *hashlin, ucl_hash_iter_t *iter, int *ep)
{
struct ucl_hash_real_iter *it = (struct ucl_hash_real_iter *)(*iter);
const ucl_object_t *ret = NULL;
if (hashlin == NULL) {
+ UHI_SETERR(ep, EINVAL);
return NULL;
}
@@ -345,6 +401,7 @@ ucl_hash_iterate (ucl_hash_t *hashlin, ucl_hash_iter_t *iter)
it = UCL_ALLOC (sizeof (*it));
if (it == NULL) {
+ UHI_SETERR(ep, ENOMEM);
return NULL;
}
@@ -352,6 +409,7 @@ ucl_hash_iterate (ucl_hash_t *hashlin, ucl_hash_iter_t *iter)
it->end = it->cur + hashlin->ar.n;
}
+ UHI_SETERR(ep, 0);
if (it->cur < it->end) {
ret = *it->cur++;
}
@@ -418,6 +476,7 @@ 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;
@@ -430,8 +489,15 @@ 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);
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 --;
+ }
}
}
else {
@@ -440,8 +506,132 @@ ucl_hash_delete (ucl_hash_t* hashlin, const ucl_object_t *obj)
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);
kh_del (ucl_hash_node, h, k);
+
+ /* Update subsequent elts */
+ for (; i < hashlin->ar.n; i ++) {
+ elt = &kh_value (h, i);
+ elt->ar_idx --;
+ }
+ }
+ }
+}
+
+bool ucl_hash_reserve (ucl_hash_t *hashlin, size_t sz)
+{
+ if (hashlin == NULL) {
+ return false;
+ }
+
+ if (sz > hashlin->ar.m) {
+ kv_resize_safe (const ucl_object_t *, hashlin->ar, sz, e0);
+
+ if (hashlin->caseless) {
+ khash_t(ucl_hash_caseless_node) *h = (khash_t(
+ ucl_hash_caseless_node) *)
+ hashlin->hash;
+ kh_resize (ucl_hash_caseless_node, h, sz * 2);
+ } else {
+ khash_t(ucl_hash_node) *h = (khash_t(ucl_hash_node) *)
+ hashlin->hash;
+ kh_resize (ucl_hash_node, h, sz * 2);
+ }
+ }
+ return true;
+e0:
+ return false;
+}
+
+static int
+ucl_lc_cmp (const char *s, const char *d, size_t l)
+{
+ unsigned int fp, i;
+ unsigned char c1, c2, c3, c4;
+ union {
+ unsigned char c[4];
+ uint32_t n;
+ } cmp1, cmp2;
+ size_t leftover = l % 4;
+ int ret = 0;
+
+ fp = l - leftover;
+
+ for (i = 0; i != fp; i += 4) {
+ c1 = s[i], c2 = s[i + 1], c3 = s[i + 2], c4 = s[i + 3];
+ cmp1.c[0] = lc_map[c1];
+ cmp1.c[1] = lc_map[c2];
+ cmp1.c[2] = lc_map[c3];
+ cmp1.c[3] = lc_map[c4];
+
+ c1 = d[i], c2 = d[i + 1], c3 = d[i + 2], c4 = d[i + 3];
+ cmp2.c[0] = lc_map[c1];
+ cmp2.c[1] = lc_map[c2];
+ cmp2.c[2] = lc_map[c3];
+ cmp2.c[3] = lc_map[c4];
+
+ if (cmp1.n != cmp2.n) {
+ return cmp1.n - cmp2.n;
+ }
+ }
+
+ while (leftover > 0) {
+ if (lc_map[(unsigned char)s[i]] != lc_map[(unsigned char)d[i]]) {
+ return s[i] - d[i];
+ }
+
+ leftover--;
+ i++;
+ }
+
+ return ret;
+}
+
+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;
+
+ if (oa->keylen == ob->keylen) {
+ return ucl_lc_cmp (oa->key, ob->key, oa->keylen);
+ }
+
+ return ((int)(oa->keylen)) - ob->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;
+
+ if (oa->keylen == ob->keylen) {
+ return memcmp (oa->key, ob->key, oa->keylen);
+ }
+
+ return ((int)(oa->keylen)) - ob->keylen;
+}
+
+void
+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);
+ }
+ else {
+ qsort (hashlin->ar.a, hashlin->ar.n, sizeof (ucl_object_t *),
+ 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);
+ }
}
}
}
diff --git a/src/ucl_hash.h b/src/ucl_hash.h
index 92021e34075e..8f6d69e21a25 100644
--- a/src/ucl_hash.h
+++ b/src/ucl_hash.h
@@ -55,8 +55,9 @@ void ucl_hash_destroy (ucl_hash_t* hashlin, ucl_hash_free_func func);
/**
* Inserts an element in the the hashtable.
+ * @return true on success, false on failure (i.e. ENOMEM)
*/
-void ucl_hash_insert (ucl_hash_t* hashlin, const ucl_object_t *obj, const char *key,
+bool ucl_hash_insert (ucl_hash_t* hashlin, const ucl_object_t *obj, const char *key,
unsigned keylen);
/**
@@ -81,13 +82,28 @@ const ucl_object_t* ucl_hash_search (ucl_hash_t* hashlin, const char *key,
* Iterate over hash table
* @param hashlin hash
* @param iter iterator (must be NULL on first iteration)
+ * @param ep pointer record exception (such as ENOMEM), could be NULL
* @return the next object
*/
-const void* ucl_hash_iterate (ucl_hash_t *hashlin, ucl_hash_iter_t *iter);
+const void* ucl_hash_iterate2 (ucl_hash_t *hashlin, ucl_hash_iter_t *iter, int *ep);
+
+/**
+ * Helper macro to support older code
+ */
+#define ucl_hash_iterate(hl, ip) ucl_hash_iterate2((hl), (ip), NULL)
/**
* Check whether an iterator has next element
*/
bool ucl_hash_iter_has_next (ucl_hash_t *hashlin, ucl_hash_iter_t iter);
+/**
+ * Reserves space in hash
+ * @return true on sucess, false on failure (e.g. ENOMEM)
+ * @param hashlin
+ */
+bool ucl_hash_reserve (ucl_hash_t *hashlin, size_t sz);
+
+void ucl_hash_sort (ucl_hash_t *hashlin, enum ucl_object_keys_sort_flags fl);
+
#endif
diff --git a/src/ucl_internal.h b/src/ucl_internal.h
index 9ae5250cc92e..2c5fdf84fee7 100644
--- a/src/ucl_internal.h
+++ b/src/ucl_internal.h
@@ -63,7 +63,9 @@
#include <sys/stat.h>
#endif
#ifdef HAVE_SYS_PARAM_H
-#include <sys/param.h>
+# ifndef _WIN32
+# include <sys/param.h>
+# endif
#endif
#ifdef HAVE_LIMITS_H
@@ -76,7 +78,9 @@
#include <errno.h>
#endif
#ifdef HAVE_UNISTD_H
-#include <unistd.h>
+# ifndef _WIN32
+# include <unistd.h>
+# endif
#endif
#ifdef HAVE_CTYPE_H
#include <ctype.h>
@@ -91,6 +95,32 @@
#include <strings.h>
#endif
+#if defined(_MSC_VER)
+/* Windows hacks */
+#include <BaseTsd.h>
+#include <inttypes.h>
+typedef SSIZE_T ssize_t;
+#define strdup _strdup
+#define snprintf _snprintf
+#define vsnprintf _vsnprintf
+#define strcasecmp _stricmp
+#define strncasecmp _strnicmp
+#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
+#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
+#if _MSC_VER >= 1900
+#include <../ucrt/stdlib.h>
+#else
+#include <../include/stdlib.h>
+#endif
+#ifndef PATH_MAX
+#define PATH_MAX _MAX_PATH
+#endif
+
+/* Dirname, basename implementations */
+
+
+#endif
+
#include "utlist.h"
#include "utstring.h"
#include "uthash.h"
@@ -119,6 +149,7 @@ enum ucl_parser_state {
UCL_STATE_OBJECT,
UCL_STATE_ARRAY,
UCL_STATE_KEY,
+ UCL_STATE_KEY_OBRACE,
UCL_STATE_VALUE,
UCL_STATE_AFTER_VALUE,
UCL_STATE_ARRAY_VALUE,
@@ -147,7 +178,7 @@ enum ucl_character_type {
struct ucl_macro {
char *name;
- union {
+ union _ucl_macro {
ucl_macro_handler handler;
ucl_context_macro_handler context_handler;
} h;
@@ -156,22 +187,44 @@ struct ucl_macro {
UT_hash_handle hh;
};
+enum ucl_stack_flags {
+ UCL_STACK_HAS_OBRACE = (1u << 0),
+ UCL_STACK_MAX = (1u << 1),
+};
+
struct ucl_stack {
ucl_object_t *obj;
struct ucl_stack *next;
- uint64_t level;
+ union {
+ struct {
+ uint16_t level;
+ uint16_t flags;
+ uint32_t line;
+ } params;
+ uint64_t len;
+ } e;
+ struct ucl_chunk *chunk;
+};
+
+struct ucl_parser_special_handler_chain {
+ unsigned char *begin;
+ size_t len;
+ struct ucl_parser_special_handler *special_handler;
+ struct ucl_parser_special_handler_chain *next;
};
struct ucl_chunk {
const unsigned char *begin;
const unsigned char *end;
const unsigned char *pos;
+ char *fname;
size_t remain;
unsigned int line;
unsigned int column;
unsigned priority;
enum ucl_duplicate_strategy strategy;
enum ucl_parse_type parse_type;
+ struct ucl_parser_special_handler_chain *special_handlers;
struct ucl_chunk *next;
};
@@ -210,6 +263,9 @@ struct ucl_parser {
struct ucl_stack *stack;
struct ucl_chunk *chunks;
struct ucl_pubkey *keys;
+ struct ucl_parser_special_handler *special_handlers;
+ ucl_include_trace_func_t *include_trace_func;
+ void *include_trace_ud;
struct ucl_variable *variables;
ucl_variable_handler var_handler;
void *var_data;
@@ -230,6 +286,13 @@ struct ucl_object_userdata {
*/
size_t ucl_unescape_json_string (char *str, size_t len);
+
+/**
+ * Unescape single quoted string inplace
+ * @param str
+ */
+size_t ucl_unescape_squoted_string (char *str, size_t len);
+
/**
* Handle include macro
* @param data include data
@@ -410,12 +473,24 @@ ucl_hash_insert_object (ucl_hash_t *hashlin,
const ucl_object_t *obj,
bool ignore_case)
{
+ ucl_hash_t *nhp;
+
if (hashlin == NULL) {
- hashlin = ucl_hash_create (ignore_case);
+ nhp = ucl_hash_create (ignore_case);
+ if (nhp == NULL) {
+ return NULL;
+ }
+ } else {
+ nhp = hashlin;
+ }
+ if (!ucl_hash_insert (nhp, obj, obj->key, obj->keylen)) {
+ if (nhp != hashlin) {
+ ucl_hash_destroy(nhp, NULL);
+ }
+ return NULL;
}
- ucl_hash_insert (hashlin, obj, obj->key, obj->keylen);
- return hashlin;
+ return nhp;
}
/**
@@ -434,6 +509,16 @@ ucl_emit_get_standard_context (enum ucl_emitter emit_type);
void ucl_elt_string_write_json (const char *str, size_t size,
struct ucl_emitter_context *ctx);
+
+/**
+ * Serialize string as single quoted string
+ * @param str string to emit
+ * @param buf target buffer
+ */
+void
+ucl_elt_string_write_squoted (const char *str, size_t size,
+ struct ucl_emitter_context *ctx);
+
/**
* Write multiline string using `EOD` as string terminator
* @param str
@@ -573,4 +658,10 @@ bool ucl_parse_msgpack (struct ucl_parser *parser);
bool ucl_parse_csexp (struct ucl_parser *parser);
+/**
+ * Free ucl chunk
+ * @param chunk
+ */
+void ucl_chunk_free (struct ucl_chunk *chunk);
+
#endif /* UCL_INTERNAL_H_ */
diff --git a/src/ucl_msgpack.c b/src/ucl_msgpack.c
index bd7c3a1ce785..3335e39cd9e7 100644
--- a/src/ucl_msgpack.c
+++ b/src/ucl_msgpack.c
@@ -434,7 +434,6 @@ static ssize_t ucl_msgpack_parse_ignore (struct ucl_parser *parser,
#define MSGPACK_FLAG_EXT (1 << 3)
#define MSGPACK_FLAG_ASSOC (1 << 4)
#define MSGPACK_FLAG_KEY (1 << 5)
-#define MSGPACK_CONTAINER_BIT (1ULL << 62)
/*
* Search tree packed in array
@@ -768,7 +767,6 @@ ucl_msgpack_get_container (struct ucl_parser *parser,
assert (obj_parser != NULL);
if (obj_parser->flags & MSGPACK_FLAG_CONTAINER) {
- assert ((len & MSGPACK_CONTAINER_BIT) == 0);
/*
* Insert new container to the stack
*/
@@ -779,6 +777,8 @@ ucl_msgpack_get_container (struct ucl_parser *parser,
ucl_create_err (&parser->err, "no memory");
return NULL;
}
+
+ parser->stack->chunk = parser->chunks;
}
else {
stack = calloc (1, sizeof (struct ucl_stack));
@@ -788,11 +788,12 @@ ucl_msgpack_get_container (struct ucl_parser *parser,
return NULL;
}
+ stack->chunk = parser->chunks;
stack->next = parser->stack;
parser->stack = stack;
}
- parser->stack->level = len | MSGPACK_CONTAINER_BIT;
+ parser->stack->e.len = len;
#ifdef MSGPACK_DEBUG_PARSER
stack = parser->stack;
@@ -823,16 +824,11 @@ ucl_msgpack_get_container (struct ucl_parser *parser,
static bool
ucl_msgpack_is_container_finished (struct ucl_stack *container)
{
- uint64_t level;
-
assert (container != NULL);
- if (container->level & MSGPACK_CONTAINER_BIT) {
- level = container->level & ~MSGPACK_CONTAINER_BIT;
- if (level == 0) {
- return true;
- }
+ if (container->e.len == 0) {
+ return true;
}
return false;
@@ -843,12 +839,11 @@ ucl_msgpack_insert_object (struct ucl_parser *parser,
const unsigned char *key,
size_t keylen, ucl_object_t *obj)
{
- uint64_t level;
struct ucl_stack *container;
container = parser->stack;
assert (container != NULL);
- assert (container->level > 0);
+ assert (container->e.len > 0);
assert (obj != NULL);
assert (container->obj != NULL);
@@ -875,10 +870,7 @@ ucl_msgpack_insert_object (struct ucl_parser *parser,
return false;
}
- if (container->level & MSGPACK_CONTAINER_BIT) {
- level = container->level & ~MSGPACK_CONTAINER_BIT;
- container->level = (level - 1) | MSGPACK_CONTAINER_BIT;
- }
+ container->e.len--;
return true;
}
@@ -887,7 +879,7 @@ static struct ucl_stack *
ucl_msgpack_get_next_container (struct ucl_parser *parser)
{
struct ucl_stack *cur = NULL;
- uint64_t level;
+ uint64_t len;
cur = parser->stack;
@@ -895,17 +887,16 @@ ucl_msgpack_get_next_container (struct ucl_parser *parser)
return NULL;
}
- if (cur->level & MSGPACK_CONTAINER_BIT) {
- level = cur->level & ~MSGPACK_CONTAINER_BIT;
+ len = cur->e.len;
- if (level == 0) {
- /* We need to switch to the previous container */
- parser->stack = cur->next;
- parser->cur_obj = cur->obj;
- free (cur);
+ if (len == 0) {
+ /* We need to switch to the previous container */
+ parser->stack = cur->next;
+ parser->cur_obj = cur->obj;
+ free (cur);
#ifdef MSGPACK_DEBUG_PARSER
- cur = parser->stack;
+ cur = parser->stack;
while (cur) {
fprintf(stderr, "-");
cur = cur->next;
@@ -913,8 +904,7 @@ ucl_msgpack_get_next_container (struct ucl_parser *parser)
fprintf(stderr, "-%s -> %d\n", parser->cur_obj->type == UCL_OBJECT ? "object" : "array", (int)parser->cur_obj->len);
#endif
- return ucl_msgpack_get_next_container (parser);
- }
+ return ucl_msgpack_get_next_container (parser);
}
/*
@@ -1029,6 +1019,8 @@ ucl_msgpack_consume (struct ucl_parser *parser)
}
else {
/* Length is not embedded */
+ remain --;
+
if (remain < obj_parser->len) {
ucl_create_err (&parser->err, "not enough data remain to "
"read object's length: %u remain, %u needed",
@@ -1038,7 +1030,6 @@ ucl_msgpack_consume (struct ucl_parser *parser)
}
p ++;
- remain --;
switch (obj_parser->len) {
case 1:
@@ -1054,8 +1045,10 @@ ucl_msgpack_consume (struct ucl_parser *parser)
len = FROM_BE64 (*(uint64_t *)p);
break;
default:
- assert (0);
- break;
+ ucl_create_err (&parser->err, "invalid length of the length field: %u",
+ (unsigned)obj_parser->len);
+
+ return false;
}
p += obj_parser->len;
@@ -1141,7 +1134,9 @@ ucl_msgpack_consume (struct ucl_parser *parser)
*/
container = parser->stack;
- if (container == NULL) {
+ if (parser->stack == NULL) {
+ ucl_create_err (&parser->err,
+ "read assoc value when no container represented");
return false;
}
@@ -1201,6 +1196,8 @@ ucl_msgpack_consume (struct ucl_parser *parser)
container = parser->stack;
if (container == NULL) {
+ ucl_create_err (&parser->err,
+ "read assoc value when no container represented");
return false;
}
@@ -1212,6 +1209,7 @@ ucl_msgpack_consume (struct ucl_parser *parser)
if (!ucl_msgpack_insert_object (parser, key, keylen,
parser->cur_obj)) {
+
return false;
}
@@ -1247,7 +1245,9 @@ ucl_msgpack_consume (struct ucl_parser *parser)
case start_assoc:
/* Empty container at the end */
if (len != 0) {
- ucl_create_err (&parser->err, "invalid non-empty container at the end");
+ ucl_create_err (&parser->err,
+ "invalid non-empty container at the end; len=%zu",
+ (uintmax_t)len);
return false;
}
@@ -1255,6 +1255,12 @@ ucl_msgpack_consume (struct ucl_parser *parser)
parser->cur_obj = ucl_object_new_full (
state == start_array ? UCL_ARRAY : UCL_OBJECT,
parser->chunks->priority);
+
+ if (parser->stack == NULL) {
+ ucl_create_err (&parser->err,
+ "read assoc value when no container represented");
+ return false;
+ }
/* Insert to the previous level container */
if (!ucl_msgpack_insert_object (parser,
key, keylen, parser->cur_obj)) {
@@ -1281,7 +1287,9 @@ ucl_msgpack_consume (struct ucl_parser *parser)
container = parser->stack;
- if (container == NULL) {
+ if (parser->stack == NULL) {
+ ucl_create_err (&parser->err,
+ "read assoc value when no container represented");
return false;
}
@@ -1311,8 +1319,12 @@ ucl_msgpack_consume (struct ucl_parser *parser)
/* Rewind to the top level container */
ucl_msgpack_get_next_container (parser);
- assert (parser->stack == NULL ||
- (parser->stack->level & MSGPACK_CONTAINER_BIT) == 0);
+
+ if (parser->stack != NULL) {
+ ucl_create_err (&parser->err, "incomplete container");
+
+ return false;
+ }
return true;
}
diff --git a/src/ucl_parser.c b/src/ucl_parser.c
index 9f44de10a6fc..23f5bce3056f 100644
--- a/src/ucl_parser.c
+++ b/src/ucl_parser.c
@@ -21,6 +21,7 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#include <math.h>
#include "ucl.h"
#include "ucl_internal.h"
#include "ucl_chartable.h"
@@ -44,16 +45,17 @@ struct ucl_parser_saved_state {
* @param len
* @return new position in chunk
*/
-#define ucl_chunk_skipc(chunk, p) do{ \
- if (*(p) == '\n') { \
- (chunk)->line ++; \
- (chunk)->column = 0; \
- } \
- else (chunk)->column ++; \
- (p++); \
- (chunk)->pos ++; \
- (chunk)->remain --; \
- } while (0)
+#define ucl_chunk_skipc(chunk, p) \
+do { \
+ if (*(p) == '\n') { \
+ (chunk)->line ++; \
+ (chunk)->column = 0; \
+ } \
+ else (chunk)->column ++; \
+ (p++); \
+ (chunk)->pos ++; \
+ (chunk)->remain --; \
+} while (0)
static inline void
ucl_set_err (struct ucl_parser *parser, int code, const char *str, UT_string **err)
@@ -87,6 +89,7 @@ ucl_set_err (struct ucl_parser *parser, int code, const char *str, UT_string **e
}
parser->err_code = code;
+ parser->state = UCL_STATE_ERROR;
}
static void
@@ -342,7 +345,7 @@ 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;
+ *out_len = dstlen;
*found = true;
if (need_free) {
free (dst);
@@ -459,19 +462,15 @@ ucl_expand_single_variable (struct ucl_parser *parser, const char *ptr,
}
if (!found) {
if (strict && parser->var_handler != NULL) {
- size_t var_len = 0;
- while (var_len < remain && p[var_len] != '}')
- var_len ++;
-
- if (parser->var_handler (p, var_len, &dst, &dstlen, &need_free,
+ if (parser->var_handler (p, remain, &dst, &dstlen, &need_free,
parser->var_data)) {
memcpy (d, dst, dstlen);
- ret += var_len;
+ ret += remain;
d += dstlen;
+ found = true;
if (need_free) {
free (dst);
}
- found = true;
}
}
@@ -564,13 +563,15 @@ ucl_expand_variable (struct ucl_parser *parser, unsigned char **dst,
* @param need_unescape need to unescape source (and copy it)
* @param need_lowercase need to lowercase value (and copy)
* @param need_expand need to expand variables (and copy as well)
+ * @param unescape_squote unescape single quoted string
* @return output length (excluding \0 symbol)
*/
static inline ssize_t
ucl_copy_or_store_ptr (struct ucl_parser *parser,
const unsigned char *src, unsigned char **dst,
const char **dst_const, size_t in_len,
- bool need_unescape, bool need_lowercase, bool need_expand)
+ bool need_unescape, bool need_lowercase, bool need_expand,
+ bool unescape_squote)
{
ssize_t ret = -1, tret;
unsigned char *tmp;
@@ -593,8 +594,14 @@ ucl_copy_or_store_ptr (struct ucl_parser *parser,
}
if (need_unescape) {
- ret = ucl_unescape_json_string (*dst, ret);
+ if (!unescape_squote) {
+ ret = ucl_unescape_json_string (*dst, ret);
+ }
+ else {
+ ret = ucl_unescape_squoted_string (*dst, ret);
+ }
}
+
if (need_expand) {
tmp = *dst;
tret = ret;
@@ -628,47 +635,83 @@ ucl_copy_or_store_ptr (struct ucl_parser *parser,
*/
static inline ucl_object_t *
ucl_parser_add_container (ucl_object_t *obj, struct ucl_parser *parser,
- bool is_array, int level)
+ bool is_array, uint32_t level, bool has_obrace)
{
struct ucl_stack *st;
+ ucl_object_t *nobj;
- if (!is_array) {
- if (obj == NULL) {
- obj = ucl_object_new_full (UCL_OBJECT, parser->chunks->priority);
+ if (obj == NULL) {
+ nobj = ucl_object_new_full (is_array ? UCL_ARRAY : UCL_OBJECT, parser->chunks->priority);
+ if (nobj == NULL) {
+ goto enomem0;
}
- else {
- obj->type = UCL_OBJECT;
- }
- if (obj->value.ov == NULL) {
- obj->value.ov = ucl_hash_create (parser->flags & UCL_PARSER_KEY_LOWERCASE);
+ } else {
+ if (obj->type == (is_array ? UCL_OBJECT : UCL_ARRAY)) {
+ /* Bad combination for merge: array and object */
+ ucl_set_err (parser, UCL_EMERGE,
+ "cannot merge an object with an array",
+ &parser->err);
+
+ return NULL;
}
- parser->state = UCL_STATE_KEY;
+ nobj = obj;
+ nobj->type = is_array ? UCL_ARRAY : UCL_OBJECT;
}
- else {
- if (obj == NULL) {
- obj = ucl_object_new_full (UCL_ARRAY, parser->chunks->priority);
- }
- else {
- obj->type = UCL_ARRAY;
+
+ if (!is_array) {
+ if (nobj->value.ov == NULL) {
+ nobj->value.ov = ucl_hash_create (parser->flags & UCL_PARSER_KEY_LOWERCASE);
+ if (nobj->value.ov == NULL) {
+ goto enomem1;
+ }
}
+ parser->state = UCL_STATE_KEY;
+ } else {
parser->state = UCL_STATE_VALUE;
}
st = UCL_ALLOC (sizeof (struct ucl_stack));
if (st == NULL) {
- ucl_set_err (parser, UCL_EINTERNAL, "cannot allocate memory for an object",
+ goto enomem1;
+ }
+
+ st->obj = nobj;
+
+ if (level >= UINT16_MAX) {
+ ucl_set_err (parser, UCL_ENESTED,
+ "objects are nesting too deep (over 65535 limit)",
&parser->err);
- ucl_object_unref (obj);
+ if (nobj != obj) {
+ ucl_object_unref (obj);
+ }
+
return NULL;
}
- st->obj = obj;
- st->level = level;
+
+ st->e.params.level = level;
+ st->e.params.line = parser->chunks->line;
+ st->chunk = parser->chunks;
+
+ if (has_obrace) {
+ st->e.params.flags = UCL_STACK_HAS_OBRACE;
+ }
+ else {
+ st->e.params.flags = 0;
+ }
+
LL_PREPEND (parser->stack, st);
- parser->cur_obj = obj;
+ parser->cur_obj = nobj;
- return obj;
+ return nobj;
+enomem1:
+ if (nobj != obj)
+ ucl_object_unref (nobj);
+enomem0:
+ ucl_set_err (parser, UCL_EINTERNAL, "cannot allocate memory for an object",
+ &parser->err);
+ return NULL;
}
int
@@ -969,7 +1012,10 @@ ucl_lex_number (struct ucl_parser *parser,
*/
static bool
ucl_lex_json_string (struct ucl_parser *parser,
- struct ucl_chunk *chunk, bool *need_unescape, bool *ucl_escape, bool *var_expand)
+ struct ucl_chunk *chunk,
+ bool *need_unescape,
+ bool *ucl_escape,
+ bool *var_expand)
{
const unsigned char *p = chunk->pos;
unsigned char c;
@@ -1009,7 +1055,8 @@ ucl_lex_json_string (struct ucl_parser *parser,
ucl_chunk_skipc (chunk, p);
}
if (p >= chunk->end) {
- ucl_set_err (parser, UCL_ESYNTAX, "unfinished escape character",
+ ucl_set_err (parser, UCL_ESYNTAX,
+ "unfinished escape character",
&parser->err);
return false;
}
@@ -1035,7 +1082,54 @@ ucl_lex_json_string (struct ucl_parser *parser,
ucl_chunk_skipc (chunk, p);
}
- ucl_set_err (parser, UCL_ESYNTAX, "no quote at the end of json string",
+ ucl_set_err (parser, UCL_ESYNTAX,
+ "no quote at the end of json string",
+ &parser->err);
+ return false;
+}
+
+/**
+ * Process single quoted string
+ * @param parser
+ * @param chunk
+ * @param need_unescape
+ * @return
+ */
+static bool
+ucl_lex_squoted_string (struct ucl_parser *parser,
+ struct ucl_chunk *chunk, bool *need_unescape)
+{
+ const unsigned char *p = chunk->pos;
+ unsigned char c;
+
+ while (p < chunk->end) {
+ c = *p;
+ if (c == '\\') {
+ ucl_chunk_skipc (chunk, p);
+
+ if (p >= chunk->end) {
+ ucl_set_err (parser, UCL_ESYNTAX,
+ "unfinished escape character",
+ &parser->err);
+ return false;
+ }
+ else {
+ ucl_chunk_skipc (chunk, p);
+ }
+
+ *need_unescape = true;
+ continue;
+ }
+ else if (c == '\'') {
+ ucl_chunk_skipc (chunk, p);
+ return true;
+ }
+
+ ucl_chunk_skipc (chunk, p);
+ }
+
+ ucl_set_err (parser, UCL_ESYNTAX,
+ "no quote at the end of single quoted string",
&parser->err);
return false;
}
@@ -1075,15 +1169,26 @@ bool
ucl_parser_process_object_element (struct ucl_parser *parser, ucl_object_t *nobj)
{
ucl_hash_t *container;
- ucl_object_t *tobj;
+ ucl_object_t *tobj = NULL, *cur;
char errmsg[256];
container = parser->stack->obj->value.ov;
- tobj = __DECONST (ucl_object_t *, ucl_hash_search_obj (container, nobj));
+ DL_FOREACH (parser->stack->obj, cur) {
+ tobj = __DECONST (ucl_object_t *, ucl_hash_search_obj (cur->value.ov, nobj));
+
+ if (tobj != NULL) {
+ break;
+ }
+ }
+
+
if (tobj == NULL) {
container = ucl_hash_insert_object (container, nobj,
parser->flags & UCL_PARSER_KEY_LOWERCASE);
+ if (container == NULL) {
+ return false;
+ }
nobj->prev = nobj;
nobj->next = NULL;
parser->stack->obj->len ++;
@@ -1102,8 +1207,6 @@ ucl_parser_process_object_element (struct ucl_parser *parser, ucl_object_t *nobj
* - if a new object has bigger priority, then we overwrite an old one
* - if a new object has lower priority, then we ignore it
*/
-
-
/* Special case for inherited objects */
if (tobj->flags & UCL_OBJECT_INHERITED) {
prinew = priold + 1;
@@ -1369,8 +1472,12 @@ ucl_parse_key (struct ucl_parser *parser, struct ucl_chunk *chunk,
/* Create a new object */
nobj = ucl_object_new_full (UCL_NULL, parser->chunks->priority);
+ if (nobj == NULL) {
+ return false;
+ }
keylen = ucl_copy_or_store_ptr (parser, c, &nobj->trash_stack[UCL_TRASH_KEY],
- &key, end - c, need_unescape, parser->flags & UCL_PARSER_KEY_LOWERCASE, false);
+ &key, end - c, need_unescape, parser->flags & UCL_PARSER_KEY_LOWERCASE,
+ false, false);
if (keylen == -1) {
ucl_object_unref (nobj);
return false;
@@ -1566,7 +1673,7 @@ ucl_parse_value (struct ucl_parser *parser, struct ucl_chunk *chunk)
const unsigned char *p, *c;
ucl_object_t *obj = NULL;
unsigned int stripped_spaces;
- int str_len;
+ ssize_t str_len;
bool need_unescape = false, ucl_escape = false, var_expand = false;
p = chunk->pos;
@@ -1604,20 +1711,57 @@ ucl_parse_value (struct ucl_parser *parser, struct ucl_chunk *chunk)
if ((str_len = ucl_copy_or_store_ptr (parser, c + 1,
&obj->trash_stack[UCL_TRASH_VALUE],
&obj->value.sv, str_len, need_unescape, false,
- var_expand)) == -1) {
+ var_expand, false)) == -1) {
+ return false;
+ }
+
+ obj->len = str_len;
+ parser->state = UCL_STATE_AFTER_VALUE;
+
+ return true;
+ break;
+ case '\'':
+ ucl_chunk_skipc (chunk, p);
+
+ if (!ucl_lex_squoted_string (parser, chunk, &need_unescape)) {
+ return false;
+ }
+
+ obj = ucl_parser_get_container (parser);
+ if (!obj) {
+ return false;
+ }
+
+ str_len = chunk->pos - c - 2;
+ obj->type = UCL_STRING;
+ obj->flags |= UCL_OBJECT_SQUOTED;
+
+ if ((str_len = ucl_copy_or_store_ptr (parser, c + 1,
+ &obj->trash_stack[UCL_TRASH_VALUE],
+ &obj->value.sv, str_len, need_unescape, false,
+ var_expand, true)) == -1) {
return false;
}
+
obj->len = str_len;
parser->state = UCL_STATE_AFTER_VALUE;
- p = chunk->pos;
return true;
break;
case '{':
obj = ucl_parser_get_container (parser);
+ if (obj == NULL) {
+ return false;
+ }
/* We have a new object */
- obj = ucl_parser_add_container (obj, parser, false, parser->stack->level);
+ if (parser->stack) {
+ obj = ucl_parser_add_container (obj, parser, false,
+ parser->stack->e.params.level, true);
+ }
+ else {
+ return false;
+ }
if (obj == NULL) {
return false;
}
@@ -1628,8 +1772,18 @@ ucl_parse_value (struct ucl_parser *parser, struct ucl_chunk *chunk)
break;
case '[':
obj = ucl_parser_get_container (parser);
+ if (obj == NULL) {
+ return false;
+ }
/* We have a new array */
- obj = ucl_parser_add_container (obj, parser, true, parser->stack->level);
+ if (parser->stack) {
+ obj = ucl_parser_add_container (obj, parser, true,
+ parser->stack->e.params.level, true);
+ }
+ else {
+ return false;
+ }
+
if (obj == NULL) {
return false;
}
@@ -1660,8 +1814,8 @@ ucl_parse_value (struct ucl_parser *parser, struct ucl_chunk *chunk)
}
if (*p =='\n') {
/* Set chunk positions and start multiline parsing */
+ chunk->remain -= p - c + 1;
c += 2;
- chunk->remain -= p - c;
chunk->pos = p + 1;
chunk->column = 0;
chunk->line ++;
@@ -1677,7 +1831,7 @@ ucl_parse_value (struct ucl_parser *parser, struct ucl_chunk *chunk)
if ((str_len = ucl_copy_or_store_ptr (parser, c,
&obj->trash_stack[UCL_TRASH_VALUE],
&obj->value.sv, str_len - 1, false,
- false, var_expand)) == -1) {
+ false, var_expand, false)) == -1) {
return false;
}
obj->len = str_len;
@@ -1689,6 +1843,7 @@ ucl_parse_value (struct ucl_parser *parser, struct ucl_chunk *chunk)
}
}
/* Fallback to ordinary strings */
+ /* FALLTHRU */
default:
parse_string:
if (obj == NULL) {
@@ -1729,18 +1884,28 @@ parse_string:
obj->len = 0;
obj->type = UCL_NULL;
}
+ else if (str_len == 3 && memcmp (c, "nan", 3) == 0) {
+ obj->len = 0;
+ obj->type = UCL_FLOAT;
+ obj->value.dv = NAN;
+ }
+ else if (str_len == 3 && memcmp (c, "inf", 3) == 0) {
+ obj->len = 0;
+ obj->type = UCL_FLOAT;
+ obj->value.dv = INFINITY;
+ }
else if (!ucl_maybe_parse_boolean (obj, c, str_len)) {
obj->type = UCL_STRING;
if ((str_len = ucl_copy_or_store_ptr (parser, c,
&obj->trash_stack[UCL_TRASH_VALUE],
&obj->value.sv, str_len, need_unescape,
- false, var_expand)) == -1) {
+ false, var_expand, false)) == -1) {
return false;
}
obj->len = str_len;
}
+
parser->state = UCL_STATE_AFTER_VALUE;
- p = chunk->pos;
return true;
break;
@@ -1792,6 +1957,17 @@ ucl_parse_after_value (struct ucl_parser *parser, struct ucl_chunk *chunk)
/* Pop all nested objects from a stack */
st = parser->stack;
+
+ if (!(st->e.params.flags & UCL_STACK_HAS_OBRACE)) {
+ parser->err_code = UCL_EUNPAIRED;
+ ucl_create_err (&parser->err,
+ "%s:%d object closed with } is not opened with { at line %d",
+ chunk->fname ? chunk->fname : "memory",
+ parser->chunks->line, st->e.params.line);
+
+ return false;
+ }
+
parser->stack = st->next;
UCL_FREE (sizeof (struct ucl_stack), st);
@@ -1802,10 +1978,14 @@ ucl_parse_after_value (struct ucl_parser *parser, struct ucl_chunk *chunk)
while (parser->stack != NULL) {
st = parser->stack;
- if (st->next == NULL || st->next->level == st->level) {
+ if (st->next == NULL) {
+ break;
+ }
+ else if (st->next->e.params.level == st->e.params.level) {
break;
}
+
parser->stack = st->next;
parser->cur_obj = st->obj;
UCL_FREE (sizeof (struct ucl_stack), st);
@@ -2180,6 +2360,8 @@ ucl_state_machine (struct ucl_parser *parser)
return false;
}
else {
+ bool seen_obrace = false;
+
/* Skip any spaces */
while (p < chunk->end && ucl_test_character (*p,
UCL_CHARACTER_WHITESPACE_UNSAFE)) {
@@ -2188,23 +2370,33 @@ ucl_state_machine (struct ucl_parser *parser)
p = chunk->pos;
- if (*p == '[') {
- parser->state = UCL_STATE_VALUE;
- ucl_chunk_skipc (chunk, p);
- }
- else {
- parser->state = UCL_STATE_KEY;
- if (*p == '{') {
+ if (p < chunk->end) {
+ if (*p == '[') {
+ parser->state = UCL_STATE_VALUE;
ucl_chunk_skipc (chunk, p);
+ seen_obrace = true;
+ }
+ else {
+
+ if (*p == '{') {
+ ucl_chunk_skipc (chunk, p);
+ parser->state = UCL_STATE_KEY_OBRACE;
+ seen_obrace = true;
+ }
+ else {
+ parser->state = UCL_STATE_KEY;
+ }
}
}
if (parser->top_obj == NULL) {
if (parser->state == UCL_STATE_VALUE) {
- obj = ucl_parser_add_container (NULL, parser, true, 0);
+ obj = ucl_parser_add_container (NULL, parser, true, 0,
+ seen_obrace);
}
else {
- obj = ucl_parser_add_container (NULL, parser, false, 0);
+ obj = ucl_parser_add_container (NULL, parser, false, 0,
+ seen_obrace);
}
if (obj == NULL) {
@@ -2218,6 +2410,7 @@ ucl_state_machine (struct ucl_parser *parser)
}
break;
case UCL_STATE_KEY:
+ case UCL_STATE_KEY_OBRACE:
/* Skip any spaces */
while (p < chunk->end && ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE)) {
ucl_chunk_skipc (chunk, p);
@@ -2240,6 +2433,7 @@ ucl_state_machine (struct ucl_parser *parser)
parser->state = UCL_STATE_ERROR;
return false;
}
+
if (end_of_object) {
p = chunk->pos;
parser->state = UCL_STATE_AFTER_VALUE;
@@ -2248,8 +2442,11 @@ ucl_state_machine (struct ucl_parser *parser)
else if (parser->state != UCL_STATE_MACRO_NAME) {
if (next_key && parser->stack->obj->type == UCL_OBJECT) {
/* Parse more keys and nest objects accordingly */
- obj = ucl_parser_add_container (parser->cur_obj, parser, false,
- parser->stack->level + 1);
+ obj = ucl_parser_add_container (parser->cur_obj,
+ parser,
+ false,
+ parser->stack->e.params.level + 1,
+ parser->state == UCL_STATE_KEY_OBRACE);
if (obj == NULL) {
return false;
}
@@ -2301,7 +2498,8 @@ ucl_state_machine (struct ucl_parser *parser)
if (!ucl_skip_macro_as_comment (parser, chunk)) {
/* We have invalid macro */
ucl_create_err (&parser->err,
- "error on line %d at column %d: invalid macro",
+ "error at %s:%d at column %d: invalid macro",
+ chunk->fname ? chunk->fname : "memory",
chunk->line,
chunk->column);
parser->state = UCL_STATE_ERROR;
@@ -2324,8 +2522,9 @@ ucl_state_machine (struct ucl_parser *parser)
HASH_FIND (hh, parser->macroes, c, macro_len, macro);
if (macro == NULL) {
ucl_create_err (&parser->err,
- "error on line %d at column %d: "
+ "error at %s:%d at column %d: "
"unknown macro: '%.*s', character: '%c'",
+ chunk->fname ? chunk->fname : "memory",
chunk->line,
chunk->column,
(int) (p - c),
@@ -2341,7 +2540,8 @@ ucl_state_machine (struct ucl_parser *parser)
else {
/* We have invalid macro name */
ucl_create_err (&parser->err,
- "error on line %d at column %d: invalid macro name",
+ "error at %s:%d at column %d: invalid macro name",
+ chunk->fname ? chunk->fname : "memory",
chunk->line,
chunk->column);
parser->state = UCL_STATE_ERROR;
@@ -2440,9 +2640,43 @@ ucl_state_machine (struct ucl_parser *parser)
}
}
+ if (parser->stack != NULL && parser->state != UCL_STATE_ERROR) {
+ struct ucl_stack *st;
+ bool has_error = false;
+
+ LL_FOREACH (parser->stack, st) {
+ if (st->chunk != parser->chunks) {
+ break; /* Not our chunk, give up */
+ }
+ if (st->e.params.flags & UCL_STACK_HAS_OBRACE) {
+ if (parser->err == NULL) {
+ utstring_new (parser->err);
+ }
+
+ utstring_printf (parser->err, "%s:%d unmatched open brace at %d; ",
+ chunk->fname ? chunk->fname : "memory",
+ parser->chunks->line,
+ st->e.params.line);
+
+ has_error = true;
+ }
+ }
+
+ if (has_error) {
+ parser->err_code = UCL_EUNPAIRED;
+
+ return false;
+ }
+ }
+
return true;
}
+#define UPRM_SAFE(fn, a, b, c, el) do { \
+ if (!fn(a, b, c, a)) \
+ goto el; \
+ } while (0)
+
struct ucl_parser*
ucl_parser_new (int flags)
{
@@ -2455,12 +2689,12 @@ ucl_parser_new (int flags)
memset (parser, 0, sizeof (struct ucl_parser));
- ucl_parser_register_macro (parser, "include", ucl_include_handler, parser);
- ucl_parser_register_macro (parser, "try_include", ucl_try_include_handler, parser);
- ucl_parser_register_macro (parser, "includes", ucl_includes_handler, parser);
- ucl_parser_register_macro (parser, "priority", ucl_priority_handler, parser);
- ucl_parser_register_macro (parser, "load", ucl_load_handler, parser);
- ucl_parser_register_context_macro (parser, "inherit", ucl_inherit_handler, parser);
+ UPRM_SAFE(ucl_parser_register_macro, parser, "include", ucl_include_handler, e0);
+ UPRM_SAFE(ucl_parser_register_macro, parser, "try_include", ucl_try_include_handler, e0);
+ UPRM_SAFE(ucl_parser_register_macro, parser, "includes", ucl_includes_handler, e0);
+ UPRM_SAFE(ucl_parser_register_macro, parser, "priority", ucl_priority_handler, e0);
+ UPRM_SAFE(ucl_parser_register_macro, parser, "load", ucl_load_handler, e0);
+ UPRM_SAFE(ucl_parser_register_context_macro, parser, "inherit", ucl_inherit_handler, e0);
parser->flags = flags;
parser->includepaths = NULL;
@@ -2475,6 +2709,9 @@ ucl_parser_new (int flags)
}
return parser;
+e0:
+ ucl_parser_free(parser);
+ return NULL;
}
bool
@@ -2489,49 +2726,69 @@ ucl_parser_set_default_priority (struct ucl_parser *parser, unsigned prio)
return true;
}
-void
+int
+ucl_parser_get_default_priority (struct ucl_parser *parser)
+{
+ if (parser == NULL) {
+ return -1;
+ }
+
+ return parser->default_priority;
+}
+
+bool
ucl_parser_register_macro (struct ucl_parser *parser, const char *macro,
ucl_macro_handler handler, void* ud)
{
struct ucl_macro *new;
if (macro == NULL || handler == NULL) {
- return;
+ return false;
}
new = UCL_ALLOC (sizeof (struct ucl_macro));
if (new == NULL) {
- return;
+ return false;
}
memset (new, 0, sizeof (struct ucl_macro));
new->h.handler = handler;
new->name = strdup (macro);
+ if (new->name == NULL) {
+ UCL_FREE (sizeof (struct ucl_macro), new);
+ return false;
+ }
new->ud = ud;
HASH_ADD_KEYPTR (hh, parser->macroes, new->name, strlen (new->name), new);
+ return true;
}
-void
+bool
ucl_parser_register_context_macro (struct ucl_parser *parser, const char *macro,
ucl_context_macro_handler handler, void* ud)
{
struct ucl_macro *new;
if (macro == NULL || handler == NULL) {
- return;
+ return false;
}
new = UCL_ALLOC (sizeof (struct ucl_macro));
if (new == NULL) {
- return;
+ return false;
}
memset (new, 0, sizeof (struct ucl_macro));
new->h.context_handler = handler;
new->name = strdup (macro);
+ if (new->name == NULL) {
+ UCL_FREE (sizeof (struct ucl_macro), new);
+ return false;
+ }
new->ud = ud;
new->is_context = true;
HASH_ADD_KEYPTR (hh, parser->macroes, new->name, strlen (new->name), new);
+ return true;
}
void
@@ -2602,6 +2859,7 @@ ucl_parser_add_chunk_full (struct ucl_parser *parser, const unsigned char *data,
enum ucl_parse_type parse_type)
{
struct ucl_chunk *chunk;
+ struct ucl_parser_special_handler *special_handler;
if (parser == NULL) {
return false;
@@ -2619,6 +2877,36 @@ ucl_parser_add_chunk_full (struct ucl_parser *parser, const unsigned char *data,
return false;
}
+ memset (chunk, 0, sizeof (*chunk));
+
+ /* Apply all matching handlers from the first to the last */
+ LL_FOREACH (parser->special_handlers, special_handler) {
+ if ((special_handler->flags & UCL_SPECIAL_HANDLER_PREPROCESS_ALL) ||
+ (len >= special_handler->magic_len &&
+ memcmp (data, special_handler->magic, special_handler->magic_len) == 0)) {
+ unsigned char *ndata = NULL;
+ size_t nlen = 0;
+
+ if (!special_handler->handler (parser, data, len, &ndata, &nlen,
+ special_handler->user_data)) {
+ ucl_create_err (&parser->err, "call for external handler failed");
+ return false;
+ }
+
+ struct ucl_parser_special_handler_chain *nchain;
+ nchain = UCL_ALLOC (sizeof (*nchain));
+ nchain->begin = ndata;
+ nchain->len = nlen;
+ nchain->special_handler = special_handler;
+
+ /* Free order is reversed */
+ LL_PREPEND (chunk->special_handlers, nchain);
+
+ data = ndata;
+ len = nlen;
+ }
+ }
+
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)) {
@@ -2641,6 +2929,11 @@ ucl_parser_add_chunk_full (struct ucl_parser *parser, const unsigned char *data,
chunk->priority = priority;
chunk->strategy = strat;
chunk->parse_type = parse_type;
+
+ if (parser->cur_file) {
+ chunk->fname = strdup (parser->cur_file);
+ }
+
LL_PREPEND (parser->chunks, chunk);
parser->recursion ++;
@@ -2707,6 +3000,41 @@ ucl_parser_add_chunk (struct ucl_parser *parser, const unsigned char *data,
}
bool
+ucl_parser_insert_chunk (struct ucl_parser *parser, const unsigned char *data,
+ size_t len)
+{
+ if (parser == NULL || parser->top_obj == NULL) {
+ return false;
+ }
+
+ bool res;
+ struct ucl_chunk *chunk;
+
+ int state = parser->state;
+ parser->state = UCL_STATE_INIT;
+
+ /* Prevent inserted chunks from unintentionally closing the current object */
+ if (parser->stack != NULL && parser->stack->next != NULL) {
+ parser->stack->e.params.level = parser->stack->next->e.params.level;
+ }
+
+ res = ucl_parser_add_chunk_full (parser, data, len, parser->chunks->priority,
+ parser->chunks->strategy, parser->chunks->parse_type);
+
+ /* Remove chunk from the stack */
+ chunk = parser->chunks;
+ if (chunk != NULL) {
+ parser->chunks = chunk->next;
+ ucl_chunk_free (chunk);
+ parser->recursion --;
+ }
+
+ parser->state = state;
+
+ return res;
+}
+
+bool
ucl_parser_add_string_priority (struct ucl_parser *parser, const char *data,
size_t len, unsigned priority)
{
@@ -2755,3 +3083,55 @@ ucl_set_include_path (struct ucl_parser *parser, ucl_object_t *paths)
return true;
}
+
+unsigned char ucl_parser_chunk_peek (struct ucl_parser *parser)
+{
+ if (parser == NULL || parser->chunks == NULL || parser->chunks->pos == NULL || parser->chunks->end == NULL ||
+ parser->chunks->pos == parser->chunks->end) {
+ return 0;
+ }
+
+ return( *parser->chunks->pos );
+}
+
+bool ucl_parser_chunk_skip (struct ucl_parser *parser)
+{
+ if (parser == NULL || parser->chunks == NULL || parser->chunks->pos == NULL || parser->chunks->end == NULL ||
+ parser->chunks->pos == parser->chunks->end) {
+ return false;
+ }
+
+ const unsigned char *p = parser->chunks->pos;
+ ucl_chunk_skipc( parser->chunks, p );
+ if( parser->chunks->pos != NULL ) return true;
+ return false;
+}
+
+ucl_object_t*
+ucl_parser_get_current_stack_object (struct ucl_parser *parser, unsigned int depth)
+{
+ ucl_object_t *obj;
+
+ if (parser == NULL || parser->stack == NULL) {
+ return NULL;
+ }
+
+ struct ucl_stack *stack = parser->stack;
+ if(stack == NULL || stack->obj == NULL || ucl_object_type (stack->obj) != UCL_OBJECT)
+ {
+ return NULL;
+ }
+
+ for( unsigned int i = 0; i < depth; ++i )
+ {
+ stack = stack->next;
+ if(stack == NULL || stack->obj == NULL || ucl_object_type (stack->obj) != UCL_OBJECT)
+ {
+ return NULL;
+ }
+ }
+
+ obj = ucl_object_ref (stack->obj);
+ return obj;
+}
+
diff --git a/src/ucl_schema.c b/src/ucl_schema.c
index c3aa8a780402..68f01187e375 100644
--- a/src/ucl_schema.c
+++ b/src/ucl_schema.c
@@ -49,7 +49,16 @@ static bool ucl_schema_validate (const ucl_object_t *schema,
/*
* Create validation error
*/
-static void
+
+#ifdef __GNUC__
+static inline void
+ucl_schema_create_error (struct ucl_schema_error *err,
+ enum ucl_schema_error_code code, const ucl_object_t *obj,
+ const char *fmt, ...)
+__attribute__ (( format( printf, 4, 5) ));
+#endif
+
+static inline void
ucl_schema_create_error (struct ucl_schema_error *err,
enum ucl_schema_error_code code, const ucl_object_t *obj,
const char *fmt, ...)
@@ -235,20 +244,22 @@ ucl_schema_validate_object (const ucl_object_t *schema,
/* Additional properties */
if (!allow_additional || additional_schema != NULL) {
/* Check if we have exactly the same properties in schema and object */
- iter = NULL;
+ iter = ucl_object_iterate_new (obj);
prop = ucl_object_lookup (schema, "properties");
- while ((elt = ucl_object_iterate (obj, &iter, true)) != NULL) {
+ while ((elt = ucl_object_iterate_safe (iter, true)) != NULL) {
found = ucl_object_lookup (prop, ucl_object_key (elt));
if (found == NULL) {
/* Try patternProperties */
- piter = NULL;
pat = ucl_object_lookup (schema, "patternProperties");
- while ((pelt = ucl_object_iterate (pat, &piter, true)) != NULL) {
+ piter = ucl_object_iterate_new (pat);
+ while ((pelt = ucl_object_iterate_safe (piter, true)) != NULL) {
found = ucl_schema_test_pattern (obj, ucl_object_key (pelt), true);
if (found != NULL) {
break;
}
}
+ ucl_object_iterate_free (piter);
+ piter = NULL;
}
if (found == NULL) {
if (!allow_additional) {
@@ -267,6 +278,8 @@ ucl_schema_validate_object (const ucl_object_t *schema,
}
}
}
+ ucl_object_iterate_free (iter);
+ iter = NULL;
}
/* Required properties */
if (required != NULL) {
@@ -311,7 +324,7 @@ ucl_schema_validate_number (const ucl_object_t *schema,
if (fabs (remainder (val, constraint)) > alpha) {
ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
"number %.4f is not multiple of %.4f, remainder is %.7f",
- val, constraint);
+ val, constraint, remainder (val, constraint));
ret = false;
break;
}
@@ -371,7 +384,7 @@ ucl_schema_validate_string (const ucl_object_t *schema,
constraint = ucl_object_toint (elt);
if (obj->len > constraint) {
ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
- "string is too big: %.3f, maximum is: %.3f",
+ "string is too big: %u, maximum is: %" PRId64,
obj->len, constraint);
ret = false;
break;
@@ -382,7 +395,7 @@ ucl_schema_validate_string (const ucl_object_t *schema,
constraint = ucl_object_toint (elt);
if (obj->len < constraint) {
ucl_schema_create_error (err, UCL_SCHEMA_CONSTRAINT, obj,
- "string is too short: %.3f, minimum is: %.3f",
+ "string is too short: %u, minimum is: %" PRId64,
obj->len, constraint);
ret = false;
break;
diff --git a/src/ucl_util.c b/src/ucl_util.c
index 299e0bca2357..b00a34787e5a 100644
--- a/src/ucl_util.c
+++ b/src/ucl_util.c
@@ -40,7 +40,9 @@
#endif
#ifdef HAVE_LIBGEN_H
-#include <libgen.h> /* For dirname */
+#ifndef _WIN32
+# include <libgen.h> /* For dirname */
+#endif
#endif
typedef kvec_t(ucl_object_t *) ucl_array_t;
@@ -65,8 +67,10 @@ typedef kvec_t(ucl_object_t *) ucl_array_t;
#include <fetch.h>
#endif
-#ifdef _WIN32
+#if defined(_MSC_VER)
#include <windows.h>
+#include <io.h>
+#include <direct.h>
#ifndef PROT_READ
#define PROT_READ 1
@@ -87,6 +91,10 @@ typedef kvec_t(ucl_object_t *) ucl_array_t;
#define MAP_FAILED ((void *) -1)
#endif
+#define getcwd _getcwd
+#define open _open
+#define close _close
+
static void *ucl_mmap(char *addr, size_t length, int prot, int access, int fd, off_t offset)
{
void *map = NULL;
@@ -133,16 +141,46 @@ static int ucl_munmap(void *map,size_t length)
return(0);
}
-static char* ucl_realpath(const char *path, char *resolved_path) {
- char *p;
- char tmp[MAX_PATH + 1];
- strncpy(tmp, path, sizeof(tmp)-1);
- p = tmp;
- while(*p) {
- if (*p == '/') *p = '\\';
- p++;
- }
- return _fullpath(resolved_path, tmp, MAX_PATH);
+static char* ucl_realpath(const char *path, char *resolved_path)
+{
+ char *p;
+ char tmp[MAX_PATH + 1];
+ strncpy(tmp, path, sizeof(tmp)-1);
+ p = tmp;
+ while(*p) {
+ if (*p == '/') *p = '\\';
+ p++;
+ }
+ return _fullpath(resolved_path, tmp, MAX_PATH);
+}
+
+
+char *dirname(char *path)
+{
+ static char path_buffer[_MAX_PATH];
+ char drive[_MAX_DRIVE];
+ char dir[_MAX_DIR];
+ char fname[_MAX_FNAME];
+ char ext[_MAX_EXT];
+
+ _splitpath (path, drive, dir, fname, ext);
+ _makepath(path_buffer, drive, dir, NULL, NULL);
+
+ return path_buffer;
+}
+
+char *basename(char *path)
+{
+ static char path_buffer[_MAX_PATH];
+ char drive[_MAX_DRIVE];
+ char dir[_MAX_DIR];
+ char fname[_MAX_FNAME];
+ char ext[_MAX_EXT];
+
+ _splitpath(path, drive, dir, fname, ext);
+ _makepath(path_buffer, NULL, NULL, fname, ext);
+
+ return path_buffer;
}
#else
#define ucl_mmap mmap
@@ -393,6 +431,69 @@ ucl_unescape_json_string (char *str, size_t len)
return (t - str);
}
+size_t
+ucl_unescape_squoted_string (char *str, size_t len)
+{
+ char *t = str, *h = str;
+
+ if (len <= 1) {
+ return len;
+ }
+
+ /* t is target (tortoise), h is source (hare) */
+
+ while (len) {
+ if (*h == '\\') {
+ h ++;
+
+ if (len == 1) {
+ /*
+ * If \ is last, then do not try to go further
+ * Issue: #74
+ */
+ len --;
+ *t++ = '\\';
+ continue;
+ }
+
+ switch (*h) {
+ case '\'':
+ *t++ = '\'';
+ break;
+ case '\n':
+ /* Ignore \<newline> style stuff */
+ break;
+ case '\r':
+ /* Ignore \r and the following \n if needed */
+ if (len > 1 && h[1] == '\n') {
+ h ++;
+ len --;
+ }
+ break;
+ default:
+ /* Ignore \ */
+ *t++ = '\\';
+ *t++ = *h;
+ break;
+ }
+
+ h ++;
+ len --;
+ }
+ else {
+ *t++ = *h++;
+ }
+
+ if (len > 0) {
+ len --;
+ }
+ }
+
+ *t = '\0';
+
+ return (t - str);
+}
+
char *
ucl_copy_key_trash (const ucl_object_t *obj)
{
@@ -415,6 +516,35 @@ ucl_copy_key_trash (const ucl_object_t *obj)
return obj->trash_stack[UCL_TRASH_KEY];
}
+void
+ucl_chunk_free (struct ucl_chunk *chunk)
+{
+ if (chunk) {
+ struct ucl_parser_special_handler_chain *chain, *tmp;
+
+ LL_FOREACH_SAFE (chunk->special_handlers, chain, tmp) {
+ if (chain->special_handler->free_function) {
+ chain->special_handler->free_function (
+ chain->begin,
+ chain->len,
+ chain->special_handler->user_data);
+ } else {
+ UCL_FREE (chain->len, chain->begin);
+ }
+
+ UCL_FREE (sizeof (*chain), chain);
+ }
+
+ chunk->special_handlers = NULL;
+
+ if (chunk->fname) {
+ free (chunk->fname);
+ }
+
+ UCL_FREE (sizeof (*chunk), chunk);
+ }
+}
+
char *
ucl_copy_value_trash (const ucl_object_t *obj)
{
@@ -500,7 +630,7 @@ ucl_parser_free (struct ucl_parser *parser)
UCL_FREE (sizeof (struct ucl_macro), macro);
}
LL_FOREACH_SAFE (parser->chunks, chunk, ctmp) {
- UCL_FREE (sizeof (struct ucl_chunk), chunk);
+ ucl_chunk_free (chunk);
}
LL_FOREACH_SAFE (parser->keys, key, ktmp) {
UCL_FREE (sizeof (struct ucl_pubkey), key);
@@ -617,6 +747,12 @@ ucl_pubkey_add (struct ucl_parser *parser, const unsigned char *key, size_t len)
return true;
}
+void ucl_parser_add_special_handler (struct ucl_parser *parser,
+ struct ucl_parser_special_handler *handler)
+{
+ LL_APPEND (parser->special_handlers, handler);
+}
+
#ifdef CURL_FOUND
struct ucl_curl_cbdata {
unsigned char *buf;
@@ -730,6 +866,8 @@ ucl_fetch_url (const unsigned char *url, unsigned char **buf, size_t *buflen,
*buf = cbdata.buf;
*buflen = cbdata.buflen;
+ curl_easy_cleanup (curl);
+
return true;
#else
ucl_create_err (err, "URL support is disabled");
@@ -752,13 +890,20 @@ ucl_fetch_file (const unsigned char *filename, unsigned char **buf, size_t *bufl
int fd;
struct stat st;
- if (stat (filename, &st) == -1 || !S_ISREG (st.st_mode)) {
- if (must_exist) {
+ if (stat (filename, &st) == -1) {
+ if (must_exist || errno == EPERM) {
ucl_create_err (err, "cannot stat file %s: %s",
filename, strerror (errno));
}
return false;
}
+ if (!S_ISREG (st.st_mode)) {
+ if (must_exist) {
+ ucl_create_err (err, "file %s is not a regular file", filename);
+ }
+
+ return false;
+ }
if (st.st_size == 0) {
/* Do not map empty files */
*buf = NULL;
@@ -909,7 +1054,7 @@ ucl_include_url (const unsigned char *data, size_t len,
chunk = parser->chunks;
if (chunk != NULL) {
parser->chunks = chunk->next;
- UCL_FREE (sizeof (struct ucl_chunk), chunk);
+ ucl_chunk_free (chunk);
}
}
@@ -952,9 +1097,10 @@ ucl_include_file_single (const unsigned char *data, size_t len,
if (params->soft_fail) {
return false;
}
- if (!params->must_exist) {
+ if (!params->must_exist && errno != EPERM) {
return true;
}
+
ucl_create_err (&parser->err, "cannot open file %s: %s",
filebuf,
strerror (errno));
@@ -977,7 +1123,12 @@ ucl_include_file_single (const unsigned char *data, size_t len,
return false;
}
- return (!params->must_exist || false);
+ if (params->must_exist || parser->err != NULL) {
+ /* The case of fatal errors */
+ return false;
+ }
+
+ return true;
}
if (params->check_signature) {
@@ -1005,7 +1156,7 @@ ucl_include_file_single (const unsigned char *data, size_t len,
}
old_curfile = parser->cur_file;
- parser->cur_file = strdup (realbuf);
+ parser->cur_file = NULL;
/* Store old file vars */
DL_FOREACH_SAFE (parser->variables, cur_var, tmp_var) {
@@ -1040,53 +1191,89 @@ ucl_include_file_single (const unsigned char *data, size_t len,
old_obj = __DECONST (ucl_object_t *, ucl_hash_search (container,
params->prefix, strlen (params->prefix)));
- if (strcasecmp (params->target, "array") == 0 && old_obj == NULL) {
- /* Create an array with key: prefix */
- old_obj = ucl_object_new_full (UCL_ARRAY, params->priority);
- old_obj->key = params->prefix;
- old_obj->keylen = strlen (params->prefix);
- ucl_copy_key_trash(old_obj);
- old_obj->prev = old_obj;
- old_obj->next = NULL;
+ if (strcasecmp (params->target, "array") == 0) {
+ if (old_obj == NULL) {
+ /* Create an array with key: prefix */
+ old_obj = ucl_object_new_full (UCL_ARRAY, params->priority);
+ old_obj->key = params->prefix;
+ old_obj->keylen = strlen (params->prefix);
+ ucl_copy_key_trash (old_obj);
+ old_obj->prev = old_obj;
+ old_obj->next = NULL;
- container = ucl_hash_insert_object (container, old_obj,
- parser->flags & UCL_PARSER_KEY_LOWERCASE);
- parser->stack->obj->len ++;
+ container = ucl_hash_insert_object (container, old_obj,
+ parser->flags & UCL_PARSER_KEY_LOWERCASE);
+ parser->stack->obj->len++;
- nest_obj = ucl_object_new_full (UCL_OBJECT, params->priority);
- nest_obj->prev = nest_obj;
- nest_obj->next = NULL;
+ nest_obj = ucl_object_new_full (UCL_OBJECT, params->priority);
+ nest_obj->prev = nest_obj;
+ nest_obj->next = NULL;
- ucl_array_append (old_obj, nest_obj);
- }
- else if (old_obj == NULL) {
- /* Create an object with key: prefix */
- nest_obj = ucl_object_new_full (UCL_OBJECT, params->priority);
+ ucl_array_append (old_obj, nest_obj);
+ }
+ else {
+ if (ucl_object_type (old_obj) == UCL_ARRAY) {
+ /* Append to the existing array */
+ nest_obj = ucl_object_new_full (UCL_OBJECT,
+ params->priority);
+ if (nest_obj == NULL) {
+ ucl_create_err (&parser->err,
+ "cannot allocate memory for an object");
+ if (buf) {
+ ucl_munmap (buf, buflen);
+ }
- if (nest_obj == NULL) {
- ucl_create_err (&parser->err, "cannot allocate memory for an object");
- if (buf) {
- ucl_munmap (buf, buflen);
+ return false;
+ }
+ nest_obj->prev = nest_obj;
+ nest_obj->next = NULL;
+
+ ucl_array_append (old_obj, nest_obj);
}
+ else {
+ /* Convert the object to an array */
+ new_obj = ucl_object_typed_new (UCL_ARRAY);
+ if (new_obj == NULL) {
+ ucl_create_err (&parser->err,
+ "cannot allocate memory for an object");
+ if (buf) {
+ ucl_munmap (buf, buflen);
+ }
- return false;
- }
+ return false;
+ }
+ new_obj->key = old_obj->key;
+ new_obj->keylen = old_obj->keylen;
+ new_obj->flags |= UCL_OBJECT_MULTIVALUE;
+ new_obj->prev = new_obj;
+ new_obj->next = NULL;
+
+ nest_obj = ucl_object_new_full (UCL_OBJECT,
+ params->priority);
+ if (nest_obj == NULL) {
+ ucl_create_err (&parser->err,
+ "cannot allocate memory for an object");
+ if (buf) {
+ ucl_munmap (buf, buflen);
+ }
- nest_obj->key = params->prefix;
- nest_obj->keylen = strlen (params->prefix);
- ucl_copy_key_trash(nest_obj);
- nest_obj->prev = nest_obj;
- nest_obj->next = NULL;
+ return false;
+ }
+ nest_obj->prev = nest_obj;
+ nest_obj->next = NULL;
- container = ucl_hash_insert_object (container, nest_obj,
- parser->flags & UCL_PARSER_KEY_LOWERCASE);
- parser->stack->obj->len ++;
+ ucl_array_append (new_obj, old_obj);
+ ucl_array_append (new_obj, nest_obj);
+ ucl_hash_replace (container, old_obj, new_obj);
+ }
+ }
}
- else if (strcasecmp (params->target, "array") == 0 ||
- ucl_object_type(old_obj) == UCL_ARRAY) {
- if (ucl_object_type(old_obj) == UCL_ARRAY) {
- /* Append to the existing array */
+ else {
+ /* Case of object */
+ if (old_obj == NULL) {
+ /* Create an object with key: prefix */
nest_obj = ucl_object_new_full (UCL_OBJECT, params->priority);
+
if (nest_obj == NULL) {
ucl_create_err (&parser->err, "cannot allocate memory for an object");
if (buf) {
@@ -1095,64 +1282,39 @@ ucl_include_file_single (const unsigned char *data, size_t len,
return false;
}
+
+ nest_obj->key = params->prefix;
+ nest_obj->keylen = strlen (params->prefix);
+ ucl_copy_key_trash(nest_obj);
nest_obj->prev = nest_obj;
nest_obj->next = NULL;
- ucl_array_append (old_obj, nest_obj);
+ container = ucl_hash_insert_object (container, nest_obj,
+ parser->flags & UCL_PARSER_KEY_LOWERCASE);
+ parser->stack->obj->len ++;
}
else {
- /* Convert the object to an array */
- new_obj = ucl_object_typed_new (UCL_ARRAY);
- if (new_obj == NULL) {
- ucl_create_err (&parser->err, "cannot allocate memory for an object");
- if (buf) {
- ucl_munmap (buf, buflen);
- }
-
- return false;
+ if (ucl_object_type (old_obj) == UCL_OBJECT) {
+ /* Append to existing Object*/
+ nest_obj = old_obj;
}
- new_obj->key = old_obj->key;
- new_obj->keylen = old_obj->keylen;
- new_obj->flags |= UCL_OBJECT_MULTIVALUE;
- new_obj->prev = new_obj;
- new_obj->next = NULL;
-
- nest_obj = ucl_object_new_full (UCL_OBJECT, params->priority);
- if (nest_obj == NULL) {
- ucl_create_err (&parser->err, "cannot allocate memory for an object");
+ else {
+ /* The key is not an object */
+ ucl_create_err (&parser->err,
+ "Conflicting type for key: %s, asked %s, has %s",
+ params->prefix, params->target,
+ ucl_object_type_to_string (ucl_object_type (old_obj)));
if (buf) {
ucl_munmap (buf, buflen);
}
return false;
}
- nest_obj->prev = nest_obj;
- nest_obj->next = NULL;
-
- ucl_array_append (new_obj, old_obj);
- ucl_array_append (new_obj, nest_obj);
- ucl_hash_replace (container, old_obj, new_obj);
}
}
- else {
- if (ucl_object_type (old_obj) == UCL_OBJECT) {
- /* Append to existing Object*/
- nest_obj = old_obj;
- }
- else {
- /* The key is not an object */
- ucl_create_err (&parser->err,
- "Conflicting type for key: %s",
- params->prefix);
- if (buf) {
- ucl_munmap (buf, buflen);
- }
- return false;
- }
- }
- /* Put all of the content of the include inside that object */
+ /* Put all of the content of the include inside that object */
parser->stack->obj->value.ov = container;
st = UCL_ALLOC (sizeof (struct ucl_stack));
@@ -1167,7 +1329,10 @@ ucl_include_file_single (const unsigned char *data, size_t len,
return false;
}
st->obj = nest_obj;
- st->level = parser->stack->level;
+ st->e.params.level = parser->stack->e.params.level;
+ st->e.params.flags = parser->stack->e.params.flags;
+ st->e.params.line = parser->stack->e.params.line;
+ st->chunk = parser->chunks;
LL_PREPEND (parser->stack, st);
parser->cur_obj = nest_obj;
}
@@ -1175,57 +1340,49 @@ ucl_include_file_single (const unsigned char *data, size_t len,
res = ucl_parser_add_chunk_full (parser, buf, buflen, params->priority,
params->strat, params->parse_type);
- if (!res) {
- if (!params->must_exist) {
- /* Free error */
- utstring_free (parser->err);
- parser->err = NULL;
- res = true;
+ if (res) {
+ /* Stop nesting the include, take 1 level off the stack */
+ if (params->prefix != NULL && nest_obj != NULL) {
+ parser->stack = st->next;
+ UCL_FREE (sizeof (struct ucl_stack), st);
}
- }
- /* Stop nesting the include, take 1 level off the stack */
- if (params->prefix != NULL && nest_obj != NULL) {
- parser->stack = st->next;
- UCL_FREE (sizeof (struct ucl_stack), st);
- }
-
- /* Remove chunk from the stack */
- chunk = parser->chunks;
- if (chunk != NULL) {
- parser->chunks = chunk->next;
- UCL_FREE (sizeof (struct ucl_chunk), chunk);
- parser->recursion --;
- }
-
- /* Restore old file vars */
- if (parser->cur_file) {
- free (parser->cur_file);
- }
-
- parser->cur_file = old_curfile;
- DL_FOREACH_SAFE (parser->variables, cur_var, tmp_var) {
- if (strcmp (cur_var->var, "CURDIR") == 0 && old_curdir) {
- DL_DELETE (parser->variables, cur_var);
- free (cur_var->var);
- free (cur_var->value);
- UCL_FREE (sizeof (struct ucl_variable), cur_var);
+ /* Remove chunk from the stack */
+ chunk = parser->chunks;
+ if (chunk != NULL) {
+ parser->chunks = chunk->next;
+ ucl_chunk_free (chunk);
+ parser->recursion--;
+ }
+
+ /* Restore old file vars */
+ if (parser->cur_file) {
+ free (parser->cur_file);
+ }
+
+ parser->cur_file = old_curfile;
+ DL_FOREACH_SAFE (parser->variables, cur_var, tmp_var) {
+ if (strcmp (cur_var->var, "CURDIR") == 0 && old_curdir) {
+ DL_DELETE (parser->variables, cur_var);
+ free (cur_var->var);
+ free (cur_var->value);
+ UCL_FREE (sizeof (struct ucl_variable), cur_var);
+ } else if (strcmp (cur_var->var, "FILENAME") == 0 && old_filename) {
+ DL_DELETE (parser->variables, cur_var);
+ free (cur_var->var);
+ free (cur_var->value);
+ UCL_FREE (sizeof (struct ucl_variable), cur_var);
+ }
}
- else if (strcmp (cur_var->var, "FILENAME") == 0 && old_filename) {
- DL_DELETE (parser->variables, cur_var);
- free (cur_var->var);
- free (cur_var->value);
- UCL_FREE (sizeof (struct ucl_variable), cur_var);
+ if (old_filename) {
+ DL_APPEND (parser->variables, old_filename);
+ }
+ if (old_curdir) {
+ DL_APPEND (parser->variables, old_curdir);
}
- }
- if (old_filename) {
- DL_APPEND (parser->variables, old_filename);
- }
- if (old_curdir) {
- DL_APPEND (parser->variables, old_curdir);
- }
- parser->state = prev_state;
+ parser->state = prev_state;
+ }
if (buflen > 0) {
ucl_munmap (buf, buflen);
@@ -1244,7 +1401,9 @@ ucl_include_file_single (const unsigned char *data, size_t len,
*/
static bool
ucl_include_file (const unsigned char *data, size_t len,
- struct ucl_parser *parser, struct ucl_include_params *params)
+ struct ucl_parser *parser,
+ struct ucl_include_params *params,
+ const ucl_object_t *args)
{
const unsigned char *p = data, *end = data + len;
bool need_glob = false;
@@ -1274,6 +1433,20 @@ ucl_include_file (const unsigned char *data, size_t len,
return (!params->must_exist || false);
}
for (i = 0; i < globbuf.gl_pathc; i ++) {
+
+ if (parser->include_trace_func) {
+ const ucl_object_t *parent = NULL;
+
+ if (parser->stack) {
+ parent = parser->stack->obj;
+ }
+
+ parser->include_trace_func (parser, parent, NULL,
+ globbuf.gl_pathv[i],
+ strlen (globbuf.gl_pathv[i]),
+ parser->include_trace_ud);
+ }
+
if (!ucl_include_file_single ((unsigned char *)globbuf.gl_pathv[i],
strlen (globbuf.gl_pathv[i]), parser, params)) {
if (params->soft_fail) {
@@ -1340,6 +1513,17 @@ ucl_include_common (const unsigned char *data, size_t len,
params.strat = UCL_DUPLICATE_APPEND;
params.must_exist = !default_try;
+ if (parser->include_trace_func) {
+ const ucl_object_t *parent = NULL;
+
+ if (parser->stack) {
+ parent = parser->stack->obj;
+ }
+
+ parser->include_trace_func (parser, parent, args,
+ data, len, parser->include_trace_ud);
+ }
+
/* Process arguments */
if (args != NULL && args->type == UCL_OBJECT) {
while ((param = ucl_object_iterate (args, &it, true)) != NULL) {
@@ -1392,6 +1576,11 @@ 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;
+ }
}
}
}
@@ -1404,7 +1593,7 @@ ucl_include_common (const unsigned char *data, size_t len,
}
else if (data != NULL) {
/* Try to load a file */
- return ucl_include_file (data, len, parser, &params);
+ return ucl_include_file (data, len, parser, &params, args);
}
}
else {
@@ -1419,7 +1608,7 @@ ucl_include_common (const unsigned char *data, size_t len,
snprintf (ipath, sizeof (ipath), "%s/%.*s", ucl_object_tostring(param),
(int)len, data);
if ((search = ucl_include_file (ipath, strlen (ipath),
- parser, &params))) {
+ parser, &params, args))) {
if (!params.allow_glob) {
break;
}
@@ -1530,8 +1719,9 @@ 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);
- priority = strtol(value, &leftover, 10);
- if (*leftover != '\0') {
+ errno = 0;
+ priority = strtoul(value, &leftover, 10);
+ if (errno != 0 || *leftover != '\0' || priority > UCL_PRIORITY_MAX) {
ucl_create_err (&parser->err, "Invalid priority value in macro: %s",
value);
free(value);
@@ -1780,6 +1970,12 @@ ucl_parser_set_filevars (struct ucl_parser *parser, const char *filename, bool n
ucl_strlcpy (realbuf, filename, sizeof (realbuf));
}
+ if (parser->cur_file) {
+ free (parser->cur_file);
+ }
+
+ parser->cur_file = strdup (realbuf);
+
/* Define variables */
ucl_parser_register_variable (parser, "FILENAME", realbuf);
curdir = dirname (realbuf);
@@ -1816,10 +2012,6 @@ ucl_parser_add_file_full (struct ucl_parser *parser, const char *filename,
return false;
}
- if (parser->cur_file) {
- free (parser->cur_file);
- }
- parser->cur_file = strdup (realbuf);
ucl_parser_set_filevars (parser, realbuf, false);
ret = ucl_parser_add_chunk_full (parser, buf, len, priority, strat,
parse_type);
@@ -2057,14 +2249,24 @@ ucl_object_fromstring_common (const char *str, size_t len, enum ucl_string_flags
obj->type = UCL_STRING;
if (flags & UCL_STRING_ESCAPE) {
for (p = start, escaped_len = 0; p < end; p ++, escaped_len ++) {
- if (ucl_test_character (*p, UCL_CHARACTER_JSON_UNSAFE)) {
- escaped_len ++;
+ if (ucl_test_character (*p, UCL_CHARACTER_JSON_UNSAFE | UCL_CHARACTER_WHITESPACE_UNSAFE)) {
+ switch (*p) {
+ case '\v':
+ case '\0':
+ escaped_len += 5;
+ break;
+ case ' ':
+ break;
+ default:
+ escaped_len ++;
+ break;
+ }
}
}
dst = malloc (escaped_len + 1);
if (dst != NULL) {
for (p = start, d = dst; p < end; p ++, d ++) {
- if (ucl_test_character (*p, UCL_CHARACTER_JSON_UNSAFE)) {
+ if (ucl_test_character (*p, UCL_CHARACTER_JSON_UNSAFE | UCL_CHARACTER_WHITESPACE_UNSAFE)) {
switch (*p) {
case '\n':
*d++ = '\\';
@@ -2086,10 +2288,29 @@ ucl_object_fromstring_common (const char *str, size_t len, enum ucl_string_flags
*d++ = '\\';
*d = 'f';
break;
+ case '\0':
+ *d++ = '\\';
+ *d++ = 'u';
+ *d++ = '0';
+ *d++ = '0';
+ *d++ = '0';
+ *d = '0';
+ break;
+ case '\v':
+ *d++ = '\\';
+ *d++ = 'u';
+ *d++ = '0';
+ *d++ = '0';
+ *d++ = '0';
+ *d = 'B';
+ break;
case '\\':
*d++ = '\\';
*d = '\\';
break;
+ case ' ':
+ *d = ' ';
+ break;
case '"':
*d++ = '\\';
*d = '"';
@@ -2229,17 +2450,17 @@ ucl_object_insert_key_common (ucl_object_t *top, ucl_object_t *elt,
while ((cur = ucl_object_iterate (elt, &it, true)) != NULL) {
tmp = ucl_object_ref (cur);
ucl_object_insert_key_common (found, tmp, cur->key,
- cur->keylen, copy_key, false, false);
+ cur->keylen, copy_key, true, false);
}
ucl_object_unref (elt);
}
else {
/* Just make a list of scalars */
- DL_APPEND (found, elt);
+ DL_CONCAT (found, elt);
}
}
else {
- DL_APPEND (found, elt);
+ DL_CONCAT (found, elt);
}
}
@@ -2326,30 +2547,98 @@ ucl_object_merge (ucl_object_t *top, ucl_object_t *elt, bool copy)
ucl_object_t *cur = NULL, *cp = NULL, *found = NULL;
ucl_object_iter_t iter = NULL;
- if (top == NULL || top->type != UCL_OBJECT || elt == NULL || elt->type != UCL_OBJECT) {
+ if (top == NULL || elt == NULL) {
return false;
}
- /* Mix two hashes */
- while ((cur = (ucl_object_t*)ucl_hash_iterate (elt->value.ov, &iter))) {
- if (copy) {
- cp = ucl_object_copy (cur);
+ if (top->type == UCL_ARRAY) {
+ if (elt->type == UCL_ARRAY) {
+ /* Merge two arrays */
+ return ucl_array_merge (top, elt, copy);
}
else {
- cp = ucl_object_ref (cur);
+ if (copy) {
+ ucl_array_append (top, ucl_object_copy (elt));
+
+ return true;
+ }
+ else {
+ ucl_array_append (top, ucl_object_ref (elt));
+
+ return true;
+ }
}
- found = __DECONST(ucl_object_t *, ucl_hash_search (top->value.ov, cp->key, cp->keylen));
- if (found == NULL) {
- /* The key does not exist */
- top->value.ov = ucl_hash_insert_object (top->value.ov, cp, false);
- top->len ++;
+ }
+ else if (top->type == UCL_OBJECT) {
+ if (elt->type == UCL_OBJECT) {
+ /* Mix two hashes */
+ while ((cur = (ucl_object_t *) ucl_hash_iterate (elt->value.ov,
+ &iter))) {
+
+ if (copy) {
+ cp = ucl_object_copy (cur);
+ } else {
+ cp = ucl_object_ref (cur);
+ }
+
+ found = __DECONST(ucl_object_t *,
+ ucl_hash_search (top->value.ov, cp->key, cp->keylen));
+
+ if (found == NULL) {
+ /* The key does not exist */
+ top->value.ov = ucl_hash_insert_object (top->value.ov, cp,
+ false);
+ top->len++;
+ }
+ else {
+ /* The key already exists, merge it recursively */
+ if (found->type == UCL_OBJECT || found->type == UCL_ARRAY) {
+ if (!ucl_object_merge (found, cp, copy)) {
+ return false;
+ }
+ }
+ else {
+ ucl_hash_replace (top->value.ov, found, cp);
+ ucl_object_unref (found);
+ }
+ }
+ }
}
else {
- /* The key already exists, replace it */
- ucl_hash_replace (top->value.ov, found, cp);
- ucl_object_unref (found);
+ if (copy) {
+ cp = ucl_object_copy (elt);
+ }
+ else {
+ cp = ucl_object_ref (elt);
+ }
+
+ found = __DECONST(ucl_object_t *,
+ ucl_hash_search (top->value.ov, cp->key, cp->keylen));
+
+ if (found == NULL) {
+ /* The key does not exist */
+ top->value.ov = ucl_hash_insert_object (top->value.ov, cp,
+ false);
+ top->len++;
+ }
+ else {
+ /* The key already exists, merge it recursively */
+ if (found->type == UCL_OBJECT || found->type == UCL_ARRAY) {
+ if (!ucl_object_merge (found, cp, copy)) {
+ return false;
+ }
+ }
+ else {
+ ucl_hash_replace (top->value.ov, found, cp);
+ ucl_object_unref (found);
+ }
+ }
}
}
+ else {
+ /* Cannot merge trivial objects */
+ return false;
+ }
return true;
}
@@ -2416,7 +2705,8 @@ ucl_object_lookup_any (const ucl_object_t *obj,
}
const ucl_object_t*
-ucl_object_iterate (const ucl_object_t *obj, ucl_object_iter_t *iter, bool expand_values)
+ucl_object_iterate_with_error (const ucl_object_t *obj, ucl_object_iter_t *iter, bool expand_values,
+ int *ep)
{
const ucl_object_t *elt = NULL;
@@ -2427,7 +2717,7 @@ ucl_object_iterate (const ucl_object_t *obj, ucl_object_iter_t *iter, bool expan
if (expand_values) {
switch (obj->type) {
case UCL_OBJECT:
- return (const ucl_object_t*)ucl_hash_iterate (obj->value.ov, iter);
+ return (const ucl_object_t*)ucl_hash_iterate2 (obj->value.ov, iter, ep);
break;
case UCL_ARRAY: {
unsigned int idx;
@@ -2468,9 +2758,18 @@ ucl_object_iterate (const ucl_object_t *obj, ucl_object_iter_t *iter, bool expan
return NULL;
}
-const char safe_iter_magic[4] = {'u', 'i', 't', 'e'};
+enum ucl_safe_iter_flags {
+ UCL_ITERATE_FLAG_UNDEFINED = 0,
+ UCL_ITERATE_FLAG_INSIDE_ARRAY,
+ UCL_ITERATE_FLAG_INSIDE_OBJECT,
+ UCL_ITERATE_FLAG_IMPLICIT,
+ UCL_ITERATE_FLAG_EXCEPTION
+};
+
+static const char safe_iter_magic[4] = {'u', 'i', 't', 'e'};
struct ucl_object_safe_iter {
char magic[4]; /* safety check */
+ uint32_t flags;
const ucl_object_t *impl_it; /* implicit object iteration */
ucl_object_iter_t expl_it; /* explicit iteration */
};
@@ -2489,6 +2788,7 @@ ucl_object_iterate_new (const ucl_object_t *obj)
it = UCL_ALLOC (sizeof (*it));
if (it != NULL) {
memcpy (it->magic, safe_iter_magic, sizeof (it->magic));
+ it->flags = UCL_ITERATE_FLAG_UNDEFINED;
it->expl_it = NULL;
it->impl_it = obj;
}
@@ -2496,6 +2796,15 @@ ucl_object_iterate_new (const ucl_object_t *obj)
return (ucl_object_iter_t)it;
}
+bool
+ucl_object_iter_chk_excpn(ucl_object_iter_t *it)
+{
+ struct ucl_object_safe_iter *rit = UCL_SAFE_ITER (it);
+
+ UCL_SAFE_ITER_CHECK (rit);
+
+ return (rit->flags == UCL_ITERATE_FLAG_EXCEPTION);
+}
ucl_object_iter_t
ucl_object_iterate_reset (ucl_object_iter_t it, const ucl_object_t *obj)
@@ -2505,11 +2814,14 @@ ucl_object_iterate_reset (ucl_object_iter_t it, const ucl_object_t *obj)
UCL_SAFE_ITER_CHECK (rit);
if (rit->expl_it != NULL) {
- UCL_FREE (sizeof (*rit->expl_it), rit->expl_it);
+ if (rit->flags == UCL_ITERATE_FLAG_INSIDE_OBJECT) {
+ UCL_FREE (sizeof (*rit->expl_it), rit->expl_it);
+ }
}
rit->impl_it = obj;
rit->expl_it = NULL;
+ rit->flags = UCL_ITERATE_FLAG_UNDEFINED;
return it;
}
@@ -2526,6 +2838,7 @@ ucl_object_iterate_full (ucl_object_iter_t it, enum ucl_iterate_type type)
{
struct ucl_object_safe_iter *rit = UCL_SAFE_ITER (it);
const ucl_object_t *ret = NULL;
+ int ern;
UCL_SAFE_ITER_CHECK (rit);
@@ -2533,7 +2846,25 @@ ucl_object_iterate_full (ucl_object_iter_t it, enum ucl_iterate_type type)
return NULL;
}
- if (rit->impl_it->type == UCL_OBJECT || rit->impl_it->type == UCL_ARRAY) {
+ if (rit->impl_it->type == UCL_OBJECT) {
+ rit->flags = UCL_ITERATE_FLAG_INSIDE_OBJECT;
+ ret = ucl_object_iterate_with_error (rit->impl_it, &rit->expl_it, true, &ern);
+
+ if (ret == NULL && ern != 0) {
+ rit->flags = UCL_ITERATE_FLAG_EXCEPTION;
+ return NULL;
+ }
+
+ if (ret == NULL && (type & UCL_ITERATE_IMPLICIT)) {
+ /* Need to switch to another implicit object in chain */
+ rit->impl_it = rit->impl_it->next;
+ rit->expl_it = NULL;
+
+ return ucl_object_iterate_safe (it, type);
+ }
+ }
+ else if (rit->impl_it->type == UCL_ARRAY) {
+ rit->flags = UCL_ITERATE_FLAG_INSIDE_ARRAY;
ret = ucl_object_iterate (rit->impl_it, &rit->expl_it, true);
if (ret == NULL && (type & UCL_ITERATE_IMPLICIT)) {
@@ -2546,6 +2877,7 @@ ucl_object_iterate_full (ucl_object_iter_t it, enum ucl_iterate_type type)
}
else {
/* Just iterate over the implicit array */
+ rit->flags = UCL_ITERATE_FLAG_IMPLICIT;
ret = rit->impl_it;
rit->impl_it = rit->impl_it->next;
@@ -2568,7 +2900,9 @@ ucl_object_iterate_free (ucl_object_iter_t it)
UCL_SAFE_ITER_CHECK (rit);
if (rit->expl_it != NULL) {
- UCL_FREE (sizeof (*rit->expl_it), rit->expl_it);
+ if (rit->flags == UCL_ITERATE_FLAG_INSIDE_OBJECT) {
+ UCL_FREE (sizeof (*rit->expl_it), rit->expl_it);
+ }
}
UCL_FREE (sizeof (*rit), it);
@@ -2667,7 +3001,7 @@ ucl_object_new_full (ucl_type_t type, unsigned priority)
UCL_ARRAY_GET (vec, new);
/* Preallocate some space for arrays */
- kv_resize (ucl_object_t *, *vec, 8);
+ kv_resize_safe (ucl_object_t *, *vec, 8, enomem);
}
}
}
@@ -2676,10 +3010,28 @@ ucl_object_new_full (ucl_type_t type, unsigned priority)
new = ucl_object_new_userdata (NULL, NULL, NULL);
ucl_object_set_priority (new, priority);
}
-
+enomem:
return new;
}
+bool ucl_object_reserve (ucl_object_t *obj, size_t reserved)
+{
+ if (obj->type == UCL_ARRAY) {
+ UCL_ARRAY_GET (vec, obj);
+
+ if (vec->m < reserved) {
+ /* Preallocate some space for arrays */
+ kv_resize_safe (ucl_object_t *, *vec, reserved, e0);
+ }
+ }
+ else if (obj->type == UCL_OBJECT) {
+ ucl_hash_reserve (obj->value.ov, reserved);
+ }
+ return true;
+e0:
+ return false;
+}
+
ucl_object_t*
ucl_object_new_userdata (ucl_userdata_dtor dtor,
ucl_userdata_emitter emitter,
@@ -2787,11 +3139,13 @@ ucl_array_append (ucl_object_t *top, ucl_object_t *elt)
top->value.av = (void *)vec;
}
- kv_push (ucl_object_t *, *vec, elt);
+ kv_push_safe (ucl_object_t *, *vec, elt, e0);
top->len ++;
return true;
+e0:
+ return false;
}
bool
@@ -2807,16 +3161,18 @@ ucl_array_prepend (ucl_object_t *top, ucl_object_t *elt)
vec = UCL_ALLOC (sizeof (*vec));
kv_init (*vec);
top->value.av = (void *)vec;
- kv_push (ucl_object_t *, *vec, elt);
+ kv_push_safe (ucl_object_t *, *vec, elt, e0);
}
else {
/* Slow O(n) algorithm */
- kv_prepend (ucl_object_t *, *vec, elt);
+ kv_prepend_safe (ucl_object_t *, *vec, elt, e0);
}
top->len ++;
return true;
+e0:
+ return false;
}
bool
@@ -2841,7 +3197,7 @@ ucl_array_merge (ucl_object_t *top, ucl_object_t *elt, bool copy)
UCL_ARRAY_GET (v2, cp);
if (v1 && v2) {
- kv_concat (ucl_object_t *, *v1, *v2);
+ kv_concat_safe (ucl_object_t *, *v1, *v2, e0);
for (i = v2->n; i < v1->n; i ++) {
obj = &kv_A (*v1, i);
@@ -2853,6 +3209,8 @@ ucl_array_merge (ucl_object_t *top, ucl_object_t *elt, bool copy)
}
return true;
+e0:
+ return false;
}
ucl_object_t *
@@ -2935,6 +3293,22 @@ ucl_array_pop_first (ucl_object_t *top)
return ret;
}
+unsigned int
+ucl_array_size (const ucl_object_t *top)
+{
+ if (top == NULL || top->type != UCL_ARRAY) {
+ return 0;
+ }
+
+ UCL_ARRAY_GET (vec, top);
+
+ if (vec != NULL) {
+ return kv_size(*vec);
+ }
+
+ return 0;
+}
+
const ucl_object_t *
ucl_array_find_index (const ucl_object_t *top, unsigned int index)
{
@@ -3008,7 +3382,7 @@ ucl_object_todouble_safe (const ucl_object_t *obj, double *target)
}
switch (obj->type) {
case UCL_INT:
- *target = obj->value.iv; /* Probaly could cause overflow */
+ *target = obj->value.iv; /* Probably could cause overflow */
break;
case UCL_FLOAT:
case UCL_TIME:
@@ -3042,7 +3416,7 @@ ucl_object_toint_safe (const ucl_object_t *obj, int64_t *target)
break;
case UCL_FLOAT:
case UCL_TIME:
- *target = obj->value.dv; /* Loosing of decimal points */
+ *target = obj->value.dv; /* Losing of decimal points */
break;
default:
return false;
@@ -3292,6 +3666,13 @@ 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);
}
@@ -3389,6 +3770,14 @@ ucl_object_array_sort (ucl_object_t *ar,
(int (*)(const void *, const void *))cmp);
}
+void ucl_object_sort_keys (ucl_object_t *obj,
+ enum ucl_object_keys_sort_flags how)
+{
+ if (obj != NULL && obj->type == UCL_OBJECT) {
+ ucl_hash_sort (obj->value.ov, how);
+ }
+}
+
#define PRIOBITS 4
unsigned int
@@ -3541,3 +3930,18 @@ ucl_comments_add (ucl_object_t *comments, const ucl_object_t *obj,
(const char *)&obj, sizeof (void *), true);
}
}
+
+void
+ucl_parser_set_include_tracer (struct ucl_parser *parser,
+ ucl_include_trace_func_t func,
+ void *user_data)
+{
+ parser->include_trace_func = func;
+ parser->include_trace_ud = user_data;
+}
+
+const char *
+ucl_parser_get_cur_file (struct ucl_parser *parser)
+{
+ return parser->cur_file;
+}
diff --git a/tests/.gitignore b/tests/.gitignore
index 5a48681d39b2..464482434f22 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -4,5 +4,7 @@
test_basic
test_generate
+test_msgpack
test_schema
test_speed
+test_streamline
diff --git a/tests/basic.test b/tests/basic.test
index 174cc8c3f0f1..4ef1a504b2ba 100755
--- a/tests/basic.test
+++ b/tests/basic.test
@@ -16,7 +16,7 @@ for _tin in ${TEST_DIR}/basic/*.in ; do
diff -s $_out $_t.res -u 2>/dev/null
if [ $? -ne 0 ] ; then
rm $_out
- echo "Test: $_t output missmatch"
+ echo "Test: $_t output mismatch"
exit 1
fi
fi
diff --git a/tests/basic/13.in b/tests/basic/13.in
index 6e31e9c4b17f..7c464996d404 100644
--- a/tests/basic/13.in
+++ b/tests/basic/13.in
@@ -6,4 +6,4 @@ key = value_orig;
.include(priority=1) "${CURDIR}/include_dir/pri1.conf"
.include(priority=2) "${CURDIR}/include_dir/pri2.conf"
-.include(try=true) "${CURDIR}/include_dir/invalid.conf"
+# No longer valid! .include(try=true) "${CURDIR}/include_dir/invalid.conf"
diff --git a/tests/basic/20.in b/tests/basic/20.in
deleted file mode 100644
index f9d4088fc20c..000000000000
--- a/tests/basic/20.in
+++ /dev/null
@@ -1,2 +0,0 @@
-# issue 112
-[[0 \ No newline at end of file
diff --git a/tests/basic/20.res b/tests/basic/20.res
deleted file mode 100644
index abfbbf02cfe6..000000000000
--- a/tests/basic/20.res
+++ /dev/null
@@ -1,5 +0,0 @@
-[
- [
- 0,
- ]
-]
diff --git a/tests/basic/21.in b/tests/basic/21.in
deleted file mode 100644
index 8f4b328548bb..000000000000
--- a/tests/basic/21.in
+++ /dev/null
@@ -1,2 +0,0 @@
- [9
-{0 [[0 \ No newline at end of file
diff --git a/tests/basic/21.res b/tests/basic/21.res
deleted file mode 100644
index db091ce39354..000000000000
--- a/tests/basic/21.res
+++ /dev/null
@@ -1,10 +0,0 @@
-[
- 9,
- {
- 0 [
- [
- 0,
- ]
- ]
- }
-]
diff --git a/tests/basic/9.in b/tests/basic/9.in
index 2341445edbf0..630ee9a15d75 100644
--- a/tests/basic/9.in
+++ b/tests/basic/9.in
@@ -16,8 +16,8 @@ array = [10]
array1 = [10]
.include(prefix=true; key="prefix") "$CURDIR/9.inc"
.include(prefix=true; key="prefix2"; target="array"; glob=true) "$CURDIR/9.inc"
+.include(prefix=true; key="prefix3"; target="array"; glob=true) "$CURDIR/9.inc"
.include(prefix=true; key="prefix1"; target="array"; glob=true) "$CURDIR/9.inc"
.include(prefix=true; key="array"; target="array"; glob=true) "$CURDIR/9.inc"
-.include(prefix=true; key="array1"; glob=true) "$CURDIR/9.inc"
.include(prefix=true; key="prefix"; glob=true) "$CURDIR/9.inc"
.try_include "/non/existent"
diff --git a/tests/basic/9.res b/tests/basic/9.res
index 0ed36e84422a..a4eccc8692b4 100644
--- a/tests/basic/9.res
+++ b/tests/basic/9.res
@@ -18,9 +18,6 @@ array [
]
array1 [
10,
- {
- key1 = "value";
- }
]
prefix {
key1 = "value";
@@ -31,4 +28,9 @@ prefix2 [
key1 = "value";
}
]
+prefix3 [
+ {
+ key1 = "value";
+ }
+]
diff --git a/tests/basic/squote.in b/tests/basic/squote.in
new file mode 100644
index 000000000000..2ee9d25d8122
--- /dev/null
+++ b/tests/basic/squote.in
@@ -0,0 +1,8 @@
+a = 'b';
+b = 'b\n\'a'
+c = ''
+d = '\
+aaa';
+e = '"';
+f = '\0\e\\\\\\\\\
+\'';
diff --git a/tests/basic/squote.res b/tests/basic/squote.res
new file mode 100644
index 000000000000..a595269567bd
--- /dev/null
+++ b/tests/basic/squote.res
@@ -0,0 +1,7 @@
+a = 'b';
+b = 'b\n\'a';
+c = '';
+d = 'aaa';
+e = '"';
+f = '\0\e\\\\\\\\\'';
+
diff --git a/tests/fuzzers/ucl_add_string_fuzzer.c b/tests/fuzzers/ucl_add_string_fuzzer.c
new file mode 100644
index 000000000000..388ce5dffebb
--- /dev/null
+++ b/tests/fuzzers/ucl_add_string_fuzzer.c
@@ -0,0 +1,25 @@
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include "ucl.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ // If size is 0 we need a null-terminated string.
+ // We dont null-terminate the string and by the design
+ // of the API passing 0 as size with non null-terminated string
+ // gives undefined behavior.
+ if(size==0){
+ return 0;
+ }
+ struct ucl_parser *parser;
+ parser = ucl_parser_new(0);
+
+ ucl_parser_add_string(parser, (char *)data, size);
+
+ if (ucl_parser_get_error(parser) != NULL) {
+ return 0;
+ }
+
+ ucl_parser_free (parser);
+ return 0;
+}
diff --git a/tests/fuzzers/ucl_msgpack_fuzzer.c b/tests/fuzzers/ucl_msgpack_fuzzer.c
new file mode 100644
index 000000000000..6989ec0e4375
--- /dev/null
+++ b/tests/fuzzers/ucl_msgpack_fuzzer.c
@@ -0,0 +1,29 @@
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include "ucl.h"
+#include "ucl_internal.h"
+#include <ctype.h>
+
+typedef ucl_object_t* (*ucl_msgpack_test)(void);
+
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+
+ if(size<3){
+ return 0;
+ }
+
+ struct ucl_parser *parser;
+
+ ucl_object_t *obj = ucl_object_new_full (UCL_OBJECT, 2);
+ obj->type = UCL_OBJECT;
+
+ parser = ucl_parser_new(UCL_PARSER_KEY_LOWERCASE);
+ parser->stack = NULL;
+
+ bool res = ucl_parser_add_chunk_full(parser, (const unsigned char*)data, size, 0, UCL_DUPLICATE_APPEND, UCL_PARSE_MSGPACK);
+
+ ucl_parser_free (parser);
+ return 0;
+}
diff --git a/tests/generate.test b/tests/generate.test
index ed237a35b017..ee4b6e427bf2 100755
--- a/tests/generate.test
+++ b/tests/generate.test
@@ -6,7 +6,7 @@ $PROG ${TEST_OUT_DIR}/generate.out
diff -s ${TEST_OUT_DIR}/generate.out ${TEST_DIR}/generate.res -u 2>/dev/null
if [ $? -ne 0 ] ; then
rm ${TEST_OUT_DIR}/generate.out
- echo "Test: generate.res output missmatch"
+ echo "Test: generate.res output mismatch"
exit 1
fi
rm ${TEST_OUT_DIR}/generate.out
diff --git a/tests/run_tests.sh b/tests/run_tests.sh
index bc26160d8059..f04f459e015b 100755
--- a/tests/run_tests.sh
+++ b/tests/run_tests.sh
@@ -19,7 +19,7 @@ for _tin in ${TEST_DIR}/*.in ; do
diff -s $_t.out $_t.res -u 2>/dev/null
if [ $? -ne 0 ] ; then
rm $_t.out
- echo "Test: $_t output missmatch"
+ echo "Test: $_t output mismatch"
exit 1
fi
fi
@@ -31,7 +31,7 @@ if [ $# -gt 2 ] ; then
diff -s ${TEST_DIR}/generate.out ${TEST_DIR}/generate.res -u 2>/dev/null
if [ $? -ne 0 ] ; then
rm ${TEST_DIR}/generate.out
- echo "Test: generate.res output missmatch"
+ echo "Test: generate.res output mismatch"
exit 1
fi
rm ${TEST_DIR}/generate.out
diff --git a/tests/streamline.test b/tests/streamline.test
index dbe583622fca..11324da316b7 100755
--- a/tests/streamline.test
+++ b/tests/streamline.test
@@ -6,7 +6,7 @@ $PROG ${TEST_OUT_DIR}/streamline.out
diff -s ${TEST_OUT_DIR}/streamline.out ${TEST_DIR}/streamline.res -u 2>/dev/null
if [ $? -ne 0 ] ; then
rm ${TEST_OUT_DIR}/streamline.out
- echo "Test: streamline.res output missmatch"
+ echo "Test: streamline.res output mismatch"
exit 1
fi
rm ${TEST_OUT_DIR}/streamline.out \ No newline at end of file
diff --git a/tests/test_basic.c b/tests/test_basic.c
index b7acc73b4a69..d199b031471c 100644
--- a/tests/test_basic.c
+++ b/tests/test_basic.c
@@ -40,10 +40,10 @@ main (int argc, char **argv)
const char *fname_in = NULL, *fname_out = NULL;
int ret = 0, opt, json = 0, compact = 0, yaml = 0,
save_comments = 0, skip_macro = 0,
- flags, fd_out, fd_in, use_fd = 0;
+ flags, fd_out, fd_in, use_fd = 0, msgpack_input = 0;
struct ucl_emitter_functions *func;
- while ((opt = getopt(argc, argv, "fjcyCM")) != -1) {
+ while ((opt = getopt(argc, argv, "fjcyCMm")) != -1) {
switch (opt) {
case 'j':
json = 1;
@@ -60,6 +60,9 @@ main (int argc, char **argv)
case 'M':
skip_macro = true;
break;
+ case 'm':
+ msgpack_input = 1;
+ break;
case 'f':
use_fd = true;
break;
@@ -145,7 +148,9 @@ main (int argc, char **argv)
exit (EXIT_FAILURE);
}
- ucl_parser_add_chunk (parser, (const unsigned char *)inbuf, r);
+ ucl_parser_add_chunk_full (parser, (const unsigned char *)inbuf, r,
+ 0, UCL_DUPLICATE_APPEND,
+ msgpack_input ? UCL_PARSE_MSGPACK : UCL_PARSE_UCL);
fclose (in);
}
else {
diff --git a/tests/test_generate.c b/tests/test_generate.c
index a8dc2fafc2bb..302a733441cd 100644
--- a/tests/test_generate.c
+++ b/tests/test_generate.c
@@ -106,7 +106,7 @@ main (int argc, char **argv)
cur = ucl_object_fromstring ("Ебв"); /* UTF8 */
ucl_array_prepend (ar1, cur);
/*
- * This is ususally broken or fragile as utf collate is far from perfect
+ * This is usually broken or fragile as utf collate is far from perfect
cur = ucl_object_fromstring ("ёбв");
ucl_array_prepend (ar1, cur);
cur = ucl_object_fromstring ("Ёбв"); // hello to @bapt
@@ -240,31 +240,40 @@ main (int argc, char **argv)
/* Test iteration */
it = ucl_object_iterate_new (obj);
it_obj = ucl_object_iterate_safe (it, true);
+ assert (!ucl_object_iter_chk_excpn (it));
/* key0 = 0.1 */
assert (ucl_object_type (it_obj) == UCL_FLOAT);
it_obj = ucl_object_iterate_safe (it, true);
+ assert (!ucl_object_iter_chk_excpn (it));
/* key1 = "" */
assert (ucl_object_type (it_obj) == UCL_STRING);
it_obj = ucl_object_iterate_safe (it, true);
+ assert (!ucl_object_iter_chk_excpn (it));
/* key2 = "" */
assert (ucl_object_type (it_obj) == UCL_STRING);
it_obj = ucl_object_iterate_safe (it, true);
+ assert (!ucl_object_iter_chk_excpn (it));
/* key3 = "" */
assert (ucl_object_type (it_obj) == UCL_STRING);
it_obj = ucl_object_iterate_safe (it, true);
+ assert (!ucl_object_iter_chk_excpn (it));
/* key4 = ([float, int, float], boolean) */
ucl_object_iterate_reset (it, it_obj);
it_obj = ucl_object_iterate_safe (it, true);
+ assert (!ucl_object_iter_chk_excpn (it));
assert (ucl_object_type (it_obj) == UCL_FLOAT);
it_obj = ucl_object_iterate_safe (it, true);
+ assert (!ucl_object_iter_chk_excpn (it));
assert (ucl_object_type (it_obj) == UCL_INT);
it_obj = ucl_object_iterate_safe (it, true);
+ assert (!ucl_object_iter_chk_excpn (it));
assert (ucl_object_type (it_obj) == UCL_FLOAT);
it_obj = ucl_object_iterate_safe (it, true);
+ assert (!ucl_object_iter_chk_excpn (it));
assert (ucl_object_type (it_obj) == UCL_BOOLEAN);
ucl_object_iterate_free (it);
- fn = ucl_object_emit_memory_funcs (&emitted);
+ fn = ucl_object_emit_memory_funcs ((void **)&emitted);
assert (ucl_object_emit_full (obj, UCL_EMIT_CONFIG, fn, comments));
fprintf (out, "%s\n", emitted);
ucl_object_emit_funcs_free (fn);
@@ -293,7 +302,7 @@ main (int argc, char **argv)
assert (ucl_parser_get_error_code (parser) == 0);
obj = ucl_parser_get_object (parser);
ucl_parser_free (parser);
- ucl_object_free (obj);
+ ucl_object_unref (obj);
if (emitted != NULL) {
free (emitted);
diff --git a/tests/test_msgpack.c b/tests/test_msgpack.c
index 00f804a0a07e..5415c912d33d 100644
--- a/tests/test_msgpack.c
+++ b/tests/test_msgpack.c
@@ -456,6 +456,7 @@ ucl_test_large_string (void)
res = ucl_object_fromstring_common (str, cur_len % 100000,
UCL_STRING_RAW);
res->flags |= UCL_OBJECT_BINARY;
+ free (str);
return res;
}
diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt
new file mode 100644
index 000000000000..4de95fd7b4b0
--- /dev/null
+++ b/utils/CMakeLists.txt
@@ -0,0 +1,12 @@
+
+PROJECT(libucl-utils C)
+
+FUNCTION(MAKE_UTIL UTIL_NAME UTIL_SRCS)
+ ADD_EXECUTABLE(${UTIL_NAME} ${UTIL_SRCS})
+ TARGET_LINK_LIBRARIES(${UTIL_NAME} ucl)
+ INSTALL(TARGETS ${UTIL_NAME} DESTINATION bin)
+ENDFUNCTION()
+
+MAKE_UTIL(ucl_chargen chargen.c)
+MAKE_UTIL(ucl_objdump objdump.c)
+MAKE_UTIL(ucl_tool ucl-tool.c)
diff --git a/utils/objdump.c b/utils/objdump.c
index 3c60c5569231..416eca7c87e0 100644
--- a/utils/objdump.c
+++ b/utils/objdump.c
@@ -22,8 +22,11 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#include <stdio.h>
-#include <errno.h>
+#if defined(_MSC_VER)
+ #include <BaseTsd.h>
+
+ typedef SSIZE_T ssize_t;
+#endif
#include "ucl.h"
@@ -101,8 +104,8 @@ main(int argc, char **argv)
const char *fn = NULL;
unsigned char *inbuf;
struct ucl_parser *parser;
- int k, ret = 0, r = 0;
- ssize_t bufsize;
+ int k, ret = 0;
+ ssize_t bufsize, r = 0;
ucl_object_t *obj = NULL;
const ucl_object_t *par;
FILE *in;
@@ -114,7 +117,7 @@ main(int argc, char **argv)
if (fn != NULL) {
in = fopen (fn, "r");
if (in == NULL) {
- exit (-errno);
+ exit (EXIT_FAILURE);
}
}
else {
@@ -146,14 +149,14 @@ main(int argc, char **argv)
ucl_parser_add_chunk (parser, inbuf, r);
fclose (in);
if (ucl_parser_get_error(parser)) {
- printf ("Error occured: %s\n", ucl_parser_get_error(parser));
+ printf ("Error occurred: %s\n", ucl_parser_get_error(parser));
ret = 1;
goto end;
}
obj = ucl_parser_get_object (parser);
if (ucl_parser_get_error (parser)) {
- printf ("Error occured: %s\n", ucl_parser_get_error(parser));
+ printf ("Error occurred: %s\n", ucl_parser_get_error(parser));
ret = 1;
goto end;
}
diff --git a/utils/ucl-tool.c b/utils/ucl-tool.c
index feea9c2070d5..9b807d35c092 100644
--- a/utils/ucl-tool.c
+++ b/utils/ucl-tool.c
@@ -20,21 +20,8 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#include <stdio.h>
-#include <getopt.h>
-#include <stdlib.h>
-
#include "ucl.h"
-static struct option opts[] = {
- {"help", no_argument, NULL, 'h'},
- {"in", required_argument, NULL, 'i' },
- {"out", required_argument, NULL, 'o' },
- {"schema", required_argument, NULL, 's'},
- {"format", required_argument, NULL, 'f'},
- {0, 0, 0, 0}
-};
-
void usage(const char *name, FILE *out) {
fprintf(out, "Usage: %s [--help] [-i|--in file] [-o|--out file]\n", name);
fprintf(out, " [-s|--schema file] [-f|--format format]\n\n");
@@ -49,64 +36,75 @@ void usage(const char *name, FILE *out) {
}
int main(int argc, char **argv) {
+ int i;
char ch;
FILE *in = stdin, *out = stdout;
- const char *schema = NULL;
+ const char *schema = NULL, *parm, *val;
unsigned char *buf = NULL;
size_t size = 0, r = 0;
struct ucl_parser *parser = NULL;
ucl_object_t *obj = NULL;
ucl_emitter_t emitter = UCL_EMIT_CONFIG;
- while((ch = getopt_long(argc, argv, "hi:o:s:f:", opts, NULL)) != -1) {
- switch (ch) {
- case 'i':
- in = fopen(optarg, "r");
+ for (i = 1; i < argc; ++i) {
+ parm = argv[i];
+ val = ((i + 1) < argc) ? argv[++i] : NULL;
+
+ if ((strcmp(parm, "--help") == 0) || (strcmp(parm, "-h") == 0)) {
+ usage(argv[0], stdout);
+ exit(0);
+
+ } else if ((strcmp(parm, "--in") == 0) || (strcmp(parm, "-i") == 0)) {
+ if (!val)
+ goto err_val;
+
+ in = fopen(val, "r");
if (in == NULL) {
perror("fopen on input file");
exit(EXIT_FAILURE);
}
- break;
- case 'o':
- out = fopen(optarg, "w");
+ } else if ((strcmp(parm, "--out") == 0) || (strcmp(parm, "-o") == 0)) {
+ if (!val)
+ goto err_val;
+
+ out = fopen(val, "w");
if (out == NULL) {
perror("fopen on output file");
exit(EXIT_FAILURE);
}
- break;
- case 's':
- schema = optarg;
- break;
- case 'f':
- if (strcmp(optarg, "ucl") == 0) {
- emitter = UCL_EMIT_CONFIG;
- } else if (strcmp(optarg, "json") == 0) {
- emitter = UCL_EMIT_JSON;
- } else if (strcmp(optarg, "yaml") == 0) {
- emitter = UCL_EMIT_YAML;
- } else if (strcmp(optarg, "compact_json") == 0) {
- emitter = UCL_EMIT_JSON_COMPACT;
- } else if (strcmp(optarg, "msgpack") == 0) {
- emitter = UCL_EMIT_MSGPACK;
- } else {
- fprintf(stderr, "Unknown output format: %s\n", optarg);
- exit(EXIT_FAILURE);
- }
- break;
- case 'h':
- usage(argv[0], stdout);
- exit(0);
- default:
+ } else if ((strcmp(parm, "--schema") == 0) || (strcmp(parm, "-s") == 0)) {
+ if (!val)
+ goto err_val;
+ schema = val;
+
+ } else if ((strcmp(parm, "--format") == 0) || (strcmp(parm, "-f") == 0)) {
+ if (!val)
+ goto err_val;
+
+ if (strcmp(val, "ucl") == 0) {
+ emitter = UCL_EMIT_CONFIG;
+ } else if (strcmp(val, "json") == 0) {
+ emitter = UCL_EMIT_JSON;
+ } else if (strcmp(val, "yaml") == 0) {
+ emitter = UCL_EMIT_YAML;
+ } else if (strcmp(val, "compact_json") == 0) {
+ emitter = UCL_EMIT_JSON_COMPACT;
+ } else if (strcmp(val, "msgpack") == 0) {
+ emitter = UCL_EMIT_MSGPACK;
+ } else {
+ fprintf(stderr, "Unknown output format: %s\n", val);
+ exit(EXIT_FAILURE);
+ }
+ } else {
usage(argv[0], stderr);
exit(EXIT_FAILURE);
- break;
}
}
parser = ucl_parser_new(0);
buf = malloc(BUFSIZ);
size = BUFSIZ;
- while(!feof(in) && !ferror(in)) {
+ while (!feof(in) && !ferror(in)) {
if (r == size) {
buf = realloc(buf, size*2);
size *= 2;
@@ -155,8 +153,7 @@ int main(int argc, char **argv) {
if (emitter != UCL_EMIT_MSGPACK) {
fprintf(out, "%s\n", ucl_object_emit(obj, emitter));
- }
- else {
+ } else {
size_t len;
unsigned char *res;
@@ -165,4 +162,9 @@ int main(int argc, char **argv) {
}
return 0;
+
+err_val:
+ fprintf(stderr, "Parameter %s is missing mandatory value\n", parm);
+ usage(argv[0], stderr);
+ exit(EXIT_FAILURE);
}