libucl: vendor import snapshort 20210314
This commit is contained in:
commit
a040967612
@ -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()
|
||||
|
||||
|
@ -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
|
||||
- `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
|
@ -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);
|
||||
|
@ -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])
|
||||
|
@ -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);
|
||||
~~~
|
||||
|
@ -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[]
|
||||
|
@ -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:**
|
||||
|
||||
|
@ -54,6 +54,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
|
||||
@ -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_ */
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include <set>
|
||||
#include <memory>
|
||||
#include <iostream>
|
||||
#include <tuple>
|
||||
|
||||
#include "ucl.h"
|
||||
|
||||
@ -106,7 +107,7 @@ class Ucl final {
|
||||
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 @@ class Ucl final {
|
||||
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 @@ class Ucl final {
|
||||
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 @@ class Ucl final {
|
||||
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 @@ class Ucl final {
|
||||
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 @@ class Ucl final {
|
||||
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 @@ class Ucl final {
|
||||
return UCL_NULL;
|
||||
}
|
||||
|
||||
const std::string key () const {
|
||||
std::string key () const {
|
||||
std::string res;
|
||||
|
||||
if (obj->key) {
|
||||
@ -373,7 +385,7 @@ class Ucl final {
|
||||
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 @@ class Ucl final {
|
||||
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 @@ class Ucl final {
|
||||
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 @@ class Ucl final {
|
||||
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 @@ class Ucl final {
|
||||
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*/ > > ¯os,
|
||||
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*/ > > ¯os,
|
||||
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, ¯os] (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)
|
||||
static Ucl parse (const std::string &in, const variable_replacer &replacer,
|
||||
std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND)
|
||||
{
|
||||
auto config_func = [&replacer] (ucl_parser *parser) {
|
||||
ucl_parser_set_variables_handler (parser, ucl_variable_replacer,
|
||||
&const_cast<variable_replacer &>(replacer));
|
||||
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*/ > > ¯os,
|
||||
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, ¯os] (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 @@ class Ucl final {
|
||||
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*/ > > ¯os,
|
||||
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*/ > > ¯os,
|
||||
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*/ > > ¯os,
|
||||
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 @@ class Ucl final {
|
||||
|
||||
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)
|
||||
|
@ -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;
|
||||
|
||||
/**
|
||||
@ -462,6 +465,14 @@ UCL_EXTERN ucl_object_t* ucl_object_pop_key (ucl_object_t *top, const char *key)
|
||||
UCL_EXTERN bool ucl_object_insert_key_merged (ucl_object_t *top, ucl_object_t *elt,
|
||||
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)
|
||||
@ -533,6 +544,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)
|
||||
@ -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
|
||||
@ -821,6 +855,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
|
||||
@ -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)
|
||||
@ -920,14 +963,23 @@ 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);
|
||||
@ -996,6 +1049,16 @@ UCL_EXTERN bool ucl_parser_add_chunk (struct ucl_parser *parser,
|
||||
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
|
||||
@ -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
|
||||
@ -1124,6 +1187,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
|
||||
@ -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_ */
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
return ucl_object_lua_push_array (L, obj, flags);
|
||||
}
|
||||
|
||||
/* Optimize allocation by preallocation of table */
|
||||
while (ucl_object_iterate (obj, &it, true) != NULL) {
|
||||
nelt ++;
|
||||
}
|
||||
|
||||
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);
|
||||
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, 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));
|
||||
@ -589,6 +723,76 @@ lua_ucl_parser_parse_file (lua_State *L)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/***
|
||||
* @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.
|
||||
@ -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);
|
||||
|
5
contrib/libucl/python/MANIFEST.in
Normal file
5
contrib/libucl/python/MANIFEST.in
Normal file
@ -0,0 +1,5 @@
|
||||
include COPYING
|
||||
recursive-include include *.h
|
||||
recursive-include src *.h
|
||||
recursive-include klib *.h
|
||||
recursive-include uthash *.h
|
@ -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 @@
|
||||
|
||||
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 @@
|
||||
"Topic :: Software Development :: Libraries",
|
||||
]
|
||||
)
|
||||
|
||||
# clean up the trick after the build
|
||||
if in_ucl_root:
|
||||
os.unlink("setup.py")
|
||||
|
@ -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) {
|
||||
|
59
contrib/libucl/python/tests/test_example.py
Normal file
59
contrib/libucl/python/tests/test_example.py
Normal file
@ -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)
|
@ -71,7 +71,22 @@ def test_comment_ignored(self):
|
||||
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)
|
||||
|
||||
|
@ -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. */
|
||||
@ -174,7 +174,7 @@ _mum_le (uint64_t v) {
|
||||
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
return _mum_bswap64 (v);
|
||||
#else
|
||||
#error "Unknown endianess"
|
||||
#error "Unknown endianness"
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -185,7 +185,7 @@ _mum_le32 (uint32_t v) {
|
||||
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
return _mum_bswap32 (v);
|
||||
#else
|
||||
#error "Unknown endianess"
|
||||
#error "Unknown endianness"
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -398,7 +398,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)
|
||||
|
@ -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 /* . */,
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
@ -150,6 +165,40 @@ ucl_elt_string_write_json (const char *str, size_t size,
|
||||
func->ucl_emitter_append_character ('"', 1, func->ud);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
{
|
||||
if (hashlin == NULL) {
|
||||
hashlin = ucl_hash_create (ignore_case);
|
||||
}
|
||||
ucl_hash_insert (hashlin, obj, obj->key, obj->keylen);
|
||||
ucl_hash_t *nhp;
|
||||
|
||||
return hashlin;
|
||||
if (hashlin == NULL) {
|
||||
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;
|
||||
}
|
||||
|
||||
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_ */
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 (obj == NULL) {
|
||||
nobj = ucl_object_new_full (is_array ? UCL_ARRAY : UCL_OBJECT, parser->chunks->priority);
|
||||
if (nobj == NULL) {
|
||||
goto enomem0;
|
||||
}
|
||||
} 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;
|
||||
}
|
||||
nobj = obj;
|
||||
nobj->type = is_array ? UCL_ARRAY : UCL_OBJECT;
|
||||
}
|
||||
|
||||
if (!is_array) {
|
||||
if (obj == NULL) {
|
||||
obj = ucl_object_new_full (UCL_OBJECT, parser->chunks->priority);
|
||||
}
|
||||
else {
|
||||
obj->type = UCL_OBJECT;
|
||||
}
|
||||
if (obj->value.ov == NULL) {
|
||||
obj->value.ov = ucl_hash_create (parser->flags & UCL_PARSER_KEY_LOWERCASE);
|
||||
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 {
|
||||
if (obj == NULL) {
|
||||
obj = ucl_object_new_full (UCL_ARRAY, parser->chunks->priority);
|
||||
}
|
||||
else {
|
||||
obj->type = UCL_ARRAY;
|
||||
}
|
||||
} 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;
|
||||
LL_PREPEND (parser->stack, st);
|
||||
parser->cur_obj = obj;
|
||||
|
||||
return obj;
|
||||
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 = nobj;
|
||||
|
||||
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 @@ ucl_parse_value (struct ucl_parser *parser, struct ucl_chunk *chunk)
|
||||
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,9 +1978,13 @@ 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;
|
||||
@ -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 ++;
|
||||
|
||||
@ -2706,6 +2999,41 @@ ucl_parser_add_chunk (struct ucl_parser *parser, const unsigned char *data,
|
||||
parser->default_priority, UCL_DUPLICATE_APPEND, UCL_PARSE_UCL);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
|
@ -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"
|
||||
|
@ -1,2 +0,0 @@
|
||||
# issue 112
|
||||
[[0
|
@ -1,5 +0,0 @@
|
||||
[
|
||||
[
|
||||
0,
|
||||
]
|
||||
]
|
@ -1,2 +0,0 @@
|
||||
[9
|
||||
{0 [[0
|
@ -1,10 +0,0 @@
|
||||
[
|
||||
9,
|
||||
{
|
||||
0 [
|
||||
[
|
||||
0,
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
@ -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"
|
||||
|
@ -18,9 +18,6 @@ array [
|
||||
]
|
||||
array1 [
|
||||
10,
|
||||
{
|
||||
key1 = "value";
|
||||
}
|
||||
]
|
||||
prefix {
|
||||
key1 = "value";
|
||||
@ -31,4 +28,9 @@ prefix2 [
|
||||
key1 = "value";
|
||||
}
|
||||
]
|
||||
prefix3 [
|
||||
{
|
||||
key1 = "value";
|
||||
}
|
||||
]
|
||||
|
||||
|
8
contrib/libucl/tests/basic/squote.in
Normal file
8
contrib/libucl/tests/basic/squote.in
Normal file
@ -0,0 +1,8 @@
|
||||
a = 'b';
|
||||
b = 'b\n\'a'
|
||||
c = ''
|
||||
d = '\
|
||||
aaa';
|
||||
e = '"';
|
||||
f = '\0\e\\\\\\\\\
|
||||
\'';
|
7
contrib/libucl/tests/basic/squote.res
Normal file
7
contrib/libucl/tests/basic/squote.res
Normal file
@ -0,0 +1,7 @@
|
||||
a = 'b';
|
||||
b = 'b\n\'a';
|
||||
c = '';
|
||||
d = 'aaa';
|
||||
e = '"';
|
||||
f = '\0\e\\\\\\\\\'';
|
||||
|
25
contrib/libucl/tests/fuzzers/ucl_add_string_fuzzer.c
Normal file
25
contrib/libucl/tests/fuzzers/ucl_add_string_fuzzer.c
Normal file
@ -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;
|
||||
}
|
29
contrib/libucl/tests/fuzzers/ucl_msgpack_fuzzer.c
Normal file
29
contrib/libucl/tests/fuzzers/ucl_msgpack_fuzzer.c
Normal file
@ -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;
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
12
contrib/libucl/utils/CMakeLists.txt
Normal file
12
contrib/libucl/utils/CMakeLists.txt
Normal file
@ -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)
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user