Commit 3c319408 authored by Baptiste Daroussin's avatar Baptiste Daroussin
Browse files

libucl: import latest snapshot from 2021-03-14

parent 8392e70f
......@@ -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
\ No newline at end of file
- `ucl_object_emit_full` now accepts additional argument `comments` that could be used to emit comments with UCL output
### Libucl 0.8.1
- Create ucl_parser_add_file_full() to be able to specify merge mode and parser type (by Allan Jude)
- C++ wrapper improvements (by @ftilde)
- C++ wrapper: add convenience method at() and lookup() (by Yonghee Kim)
- C++ wrapper: add assignment operator to Ucl class (by Yonghee Kim)
- C++ wrapper: support variables in parser (by Yonghee Kim)
- C++ wrapper: refactoring C++ interface (by Yonghee Kim):
- use auto variables (if possible)
- remove dangling expressions
- use std::set::emplace instead of std::set::insert
- not use std::move in return statement; considering copy elision
- C++ wrapper: fix compilation error and warnings (by Zhe Wang)
- C++ wrapper: fix iteration over objects in which the first value is `false` (by Zhe Wang)
- C++ wrapper: Macro helper functions (by Chris Meacham)
- C++ wrapper: Changing the duplicate strategy in the C++ API (by Chris Meacham)
- C++ wrapper: Added access functions for the size of a UCL_ARRAY (by Chris Meacham)
- Fix caseless comparison
- Fix include when EPERM is issued
- Fix Windows build
- Allow to reserve space in arrays and hashes
- Fix bug with including of empty files
- Move to mum_hash from xxhash
- Fix msgpack on non-x86
- python: Add support to Python 3 (by Denis Volpato Martins)
- python: Add support for Python 2.6 tests (by Denis Volpato Martins)
- python: Implement validation function and tests (by Denis Volpato Martins)
- python: Added UCL_NULL handling and tests (by Denis Volpato Martins)
- Fix schema validation for patternProperties with object data (by Denis Volpato Martins)
- Remove the dependency on NBBY, add missing <strings.h> include (by Ed Schouten)
- Allow to emit msgpack from Lua
- Performance improvements in Lua API
- Allow to pass opaque objects in Lua API for transparent C passthrough
- Various bugs fixed
- Couple of memory leaks plugged
\ No newline at end of file
# 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);
......
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)