libucl: import latest snapshot from 2021-03-14

This commit is contained in:
Baptiste Daroussin 2021-03-22 15:07:18 +01:00
parent 8392e70f8a
commit 3c319408d0
50 changed files with 2828 additions and 637 deletions

View File

@ -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()

View File

@ -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

View File

@ -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);

View File

@ -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])

View File

@ -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);
~~~

View File

@ -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[]

View File

@ -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:**

View File

@ -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_ */

View File

@ -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*/ > > &macros,
std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND)
{
return parse (in, std::map<std::string, std::string>(), macros, err, duplicate_strategy);
}
//Macro handler will receive a macro_userdata_s as void *ud
static Ucl parse (const std::string &in, const std::map<std::string, std::string> &vars,
std::vector< std::tuple< std::string /*name*/, macro_handler_s, void * /*userdata*/ > > &macros,
std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND)
{
//Preserve macro_userdata_s memory for later use in parse_with_strategy_function()
std::vector<macro_userdata_s> userdata_list;
userdata_list.reserve (macros.size());
auto config_func = [&userdata_list, &vars, &macros] (ucl_parser *parser) {
for (const auto & item : vars) {
ucl_parser_register_variable (parser, item.first.c_str (), item.second.c_str ());
}
}
for (auto & macro : macros) {
userdata_list.push_back ({parser, std::get<2>(macro)});
if (std::get<1>(macro).handler != NULL) {
ucl_parser_register_macro (parser,
std::get<0>(macro).c_str(),
std::get<1>(macro).handler,
reinterpret_cast<void*>(&userdata_list.back()));
}
else if (std::get<1>(macro).ctx_handler != NULL) {
ucl_parser_register_context_macro (parser,
std::get<0>(macro).c_str(),
std::get<1>(macro).ctx_handler,
reinterpret_cast<void*>(&userdata_list.back()));
}
}
};
auto parse_func = [&in] (ucl_parser *parser) {
return ucl_parser_add_chunk (parser, (unsigned char *)in.data (), in.size ());
auto parse_func = [&in, &duplicate_strategy] (struct ucl_parser *parser) -> bool {
return ucl_parser_add_chunk_full (parser,
(unsigned char *) in.data (),
in.size (),
(unsigned int)ucl_parser_get_default_priority (parser),
duplicate_strategy,
UCL_PARSE_UCL);
};
return parse_with_strategy_function (config_func, parse_func, err);
}
static Ucl parse (const std::string &in, const variable_replacer &replacer, std::string &err)
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*/ > > &macros,
std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND)
{
//Preserve macro_userdata_s memory for later use in parse_with_strategy_function()
std::vector<macro_userdata_s> userdata_list;
userdata_list.reserve (macros.size());
auto config_func = [&userdata_list, &replacer, &macros] (ucl_parser *parser) {
ucl_parser_set_variables_handler (parser, ucl_variable_replacer, &const_cast<variable_replacer &>(replacer));
for (auto & macro : macros) {
userdata_list.push_back ({parser, std::get<2>(macro)});
if (std::get<1>(macro).handler != NULL) {
ucl_parser_register_macro (parser,
std::get<0>(macro).c_str(),
std::get<1>(macro).handler,
reinterpret_cast<void*>(&userdata_list.back()));
}
else if (std::get<1>(macro).ctx_handler != NULL) {
ucl_parser_register_context_macro (parser,
std::get<0>(macro).c_str(),
std::get<1>(macro).ctx_handler,
reinterpret_cast<void*>(&userdata_list.back()));
}
}
};
auto parse_func = [&in] (ucl_parser *parser) {
return ucl_parser_add_chunk (parser, (unsigned char *) in.data (), in.size ());
auto parse_func = [&in, &duplicate_strategy] (struct ucl_parser *parser) -> bool {
return ucl_parser_add_chunk_full (parser,
(unsigned char *) in.data (),
in.size (),
(unsigned int)ucl_parser_get_default_priority (parser),
duplicate_strategy,
UCL_PARSE_UCL);
};
return parse_with_strategy_function (config_func, parse_func, err);
}
static Ucl parse (const char *in, std::string &err)
static Ucl parse (const char *in, std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND)
{
return parse (in, std::map<std::string, std::string>(), err);
return parse (in, std::map<std::string, std::string>(), err, duplicate_strategy);
}
static Ucl parse (const char *in, const std::map<std::string, std::string> &vars, std::string &err)
@ -480,13 +574,46 @@ 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*/ > > &macros,
std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND)
{
return parse (in, std::map<std::string, std::string>(), macros, err, duplicate_strategy);
}
//Macro handler will receive a macro_userdata_s as void *ud
static Ucl parse (const char *in, const std::map<std::string, std::string> &vars,
std::vector< std::tuple< std::string /*name*/, macro_handler_s, void * /*userdata*/ > > &macros,
std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND)
{
if (!in) {
err = "null input";
return nullptr;
}
return parse (std::string(in), replacer, err);
return parse (std::string (in), vars, macros, err, duplicate_strategy);
}
static Ucl parse (const char *in, const variable_replacer &replacer,
std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND)
{
if (!in) {
err = "null input";
return nullptr;
}
return parse (std::string(in), replacer, err, duplicate_strategy);
}
//Macro handler will receive a macro_userdata_s as void *ud
static Ucl parse (const char *in, const variable_replacer &replacer,
std::vector< std::tuple< std::string /*name*/, macro_handler_s, void * /*userdata*/ > > &macros,
std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND)
{
if (!in) {
err = "null input";
return nullptr;
}
return parse (std::string (in), replacer, macros, err, duplicate_strategy);
}
static Ucl parse_from_file (const std::string &filename, std::string &err)
@ -556,7 +683,7 @@ 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)

View File

@ -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_ */

View File

@ -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 */

View File

@ -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
python/MANIFEST.in Normal file
View File

@ -0,0 +1,5 @@
include COPYING
recursive-include include *.h
recursive-include src *.h
recursive-include klib *.h
recursive-include uthash *.h

View File

@ -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")

View File

@ -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) {

View 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)

View File

@ -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)

View File

@ -35,7 +35,7 @@
Random and Pseudorandom Number Generators for Cryptographic
Applications (version 2.2.1) with 1000 bitstreams each containing
1M bits. MUM hashing is also faster Spooky64 and City64 on small
strings (at least upto 512-bit) on Haswell and Power7. The MUM bulk
strings (at least up to 512-bit) on Haswell and Power7. The MUM bulk
speed (speed on very long data) is bigger than Spooky and City on
Power7. On Haswell the bulk speed is bigger than Spooky one and
close to City speed. */
@ -172,7 +172,7 @@ _mum_le (uint64_t v) {
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
return _mum_bswap64 (v);
#else
#error "Unknown endianess"
#error "Unknown endianness"
#endif
}
@ -183,7 +183,7 @@ _mum_le32 (uint32_t v) {
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
return _mum_bswap32 (v);
#else
#error "Unknown endianess"
#error "Unknown endianness"
#endif
}
@ -396,7 +396,7 @@ mum_hash64 (uint64_t key, uint64_t seed) {
}
/* Hash data KEY of length LEN and SEED. The hash depends on the
target endianess and the unroll factor. */
target endianness and the unroll factor. */
static inline uint64_t
mum_hash (const void *key, size_t len, uint64_t seed) {
#if defined(__x86_64__) && defined(_MUM_FRESH_GCC)

View File

@ -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 /* . */,

View File

@ -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);

View File

@ -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;
}

View File

@ -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);
}
}
}
}

View File

@ -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

View File

@ -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_ */

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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

2
tests/.gitignore vendored
View File

@ -4,5 +4,7 @@
test_basic
test_generate
test_msgpack
test_schema
test_speed
test_streamline

View File

@ -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

View File

@ -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"

View File

@ -1,2 +0,0 @@
# issue 112
[[0

View File

@ -1,5 +0,0 @@
[
[
0,
]
]

View File

@ -1,2 +0,0 @@
[9
{0 [[0

View File

@ -1,10 +0,0 @@
[
9,
{
0 [
[
0,
]
]
}
]

View File

@ -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"

View File

@ -18,9 +18,6 @@ array [
]
array1 [
10,
{
key1 = "value";
}
]
prefix {
key1 = "value";
@ -31,4 +28,9 @@ prefix2 [
key1 = "value";
}
]
prefix3 [
{
key1 = "value";
}
]

8
tests/basic/squote.in Normal file
View File

@ -0,0 +1,8 @@
a = 'b';
b = 'b\n\'a'
c = ''
d = '\
aaa';
e = '"';
f = '\0\e\\\\\\\\\
\'';

7
tests/basic/squote.res Normal file
View File

@ -0,0 +1,7 @@
a = 'b';
b = 'b\n\'a';
c = '';
d = 'aaa';
e = '"';
f = '\0\e\\\\\\\\\'';

View 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;
}

View 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;
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 {

View File

@ -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);

View File

@ -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
utils/CMakeLists.txt Normal file
View 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)

View File

@ -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;
}

View File

@ -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);
}