Update libucl to latest version

While here correctly link libucl to libm and register the dependency on libm
for static building
This commit is contained in:
bapt 2014-11-29 00:45:09 +00:00
commit 7d08b91413
46 changed files with 3172 additions and 276 deletions

23
contrib/libucl/COPYING Normal file
View File

@ -0,0 +1,23 @@
Copyright (c) 2013-2014, Vsevolod Stakhov <vsevolod@highsecure.ru>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -4,3 +4,19 @@
- Streamline emitter has been added, so it is now possible to output partial `ucl` objects
- Emitter now is more flexible due to emitter_context structure
### 0.5.1
- Fixed number of bugs and memory leaks
### 0.5.2
- Allow userdata objects to be emitted and destructed
- Use userdata objects to store lua function references
### Libucl 0.6
- Reworked macro interface
### Libucl 0.6.1
- Various utilities fixes

View File

@ -4,4 +4,8 @@ EXTRA_DIST = uthash README.md
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libucl.pc
SUBDIRS = src tests utils doc
if LUA_SUB
LUA_SUBDIR = lua
endif
SUBDIRS = src tests utils doc $(LUA_SUBDIR)

View File

@ -33,6 +33,7 @@ OBJECTS = $(OBJDIR)/ucl_hash.o \
$(OBJDIR)/ucl_util.o \
$(OBJDIR)/ucl_parser.o \
$(OBJDIR)/ucl_emitter.o \
$(OBJDIR)/ucl_emitter_utils.o \
$(OBJDIR)/ucl_schema.o \
$(OBJDIR)/xxhash.o
@ -51,6 +52,8 @@ $(OBJDIR)/ucl_parser.o: $(SRCDIR)/ucl_parser.c $(HDEPS)
$(CC) -o $(OBJDIR)/ucl_parser.o $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) -c $(SRCDIR)/ucl_parser.c
$(OBJDIR)/ucl_emitter.o: $(SRCDIR)/ucl_emitter.c $(HDEPS)
$(CC) -o $(OBJDIR)/ucl_emitter.o $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) -c $(SRCDIR)/ucl_emitter.c
$(OBJDIR)/ucl_emitter_utils.o: $(SRCDIR)/ucl_emitter_utils.c $(HDEPS)
$(CC) -o $(OBJDIR)/ucl_emitter_utils.o $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) -c $(SRCDIR)/ucl_emitter_utils.c
$(OBJDIR)/ucl_hash.o: $(SRCDIR)/ucl_hash.c $(HDEPS)
$(CC) -o $(OBJDIR)/ucl_hash.o $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) -c $(SRCDIR)/ucl_hash.c
$(OBJDIR)/ucl_schema.o: $(SRCDIR)/ucl_schema.c $(HDEPS)
@ -61,7 +64,7 @@ $(OBJDIR)/xxhash.o: $(SRCDIR)/xxhash.c $(HDEPS)
clean:
$(RM) $(OBJDIR)/*.o $(OBJDIR)/$(SONAME) $(OBJDIR)/$(SONAME) $(OBJDIR)/chargen $(OBJDIR)/test_basic $(OBJDIR)/test_speed $(OBJDIR)/objdump $(OBJDIR)/test_generate
$(RMDIR) $(OBJDIR)
# Utils
chargen: utils/chargen.c $(OBJDIR)/$(SONAME)
@ -75,7 +78,7 @@ test: $(OBJDIR) $(OBJDIR)/$(SONAME) $(OBJDIR)/test_basic $(OBJDIR)/test_speed $(
run-test: test
TEST_DIR=$(TESTDIR) $(TESTDIR)/run_tests.sh $(OBJDIR)/test_basic $(OBJDIR)/test_speed $(OBJDIR)/test_generate
$(OBJDIR)/test_basic: $(TESTDIR)/test_basic.c $(OBJDIR)/$(SONAME)
$(CC) -o $(OBJDIR)/test_basic $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) $(LDFLAGS) $(TESTDIR)/test_basic.c $(LD_UCL_FLAGS)
$(OBJDIR)/test_speed: $(TESTDIR)/test_speed.c $(OBJDIR)/$(SONAME)

View File

@ -223,15 +223,57 @@ UCL supports external macros both multiline and single line ones:
....
};
```
There are two internal macros provided by UCL:
* `include` - read a file `/path/to/file` or an url `http://example.com/file` and include it to the current place of
UCL configuration;
* `try\_include` - try to read a file or url and include it but do not create a fatal error if a file or url is not accessible;
* `includes` - read a file or an url like the previous macro, but fetch and check the signature file (which is obtained
by `.sig` suffix appending).
Moreover, each macro can accept an optional list of arguments in braces. These
arguments themselves are the UCL object that is parsed and passed to a macro as
options:
Public keys which are used for the last command are specified by the concrete UCL user.
```nginx
.macro(param=value) "something";
.macro(param={key=value}) "something";
.macro(.include "params.conf") "something";
.macro(#this is multiline macro
param = [value1, value2]) "something";
.macro(key="()") "something";
```
UCL also provide a convenient `include` macro to load content from another files
to the current UCL object. This macro accepts either path to file:
```nginx
.include "/full/path.conf"
.include "./relative/path.conf"
.include "${CURDIR}/path.conf"
```
or URL (if ucl is built with url support provided by either `libcurl` or `libfetch`):
.include "http://example.com/file.conf"
`.include` macro supports a set of options:
* `try` (default: **false**) - if this option is `true` than UCL treats errors on loading of
this file as non-fatal. For example, such a file can be absent but it won't stop the parsing
of the top-level document.
* `sign` (default: **false**) - if this option is `true` UCL loads and checks the signature for
a file from path named `<FILEPATH>.sig`. Trusted public keys should be provided for UCL API after
parser is created but before any configurations are parsed.
* `glob` (default: **false**) - if this option is `true` UCL treats the filename as GLOB pattern and load
all files that matches the specified pattern (normally the format of patterns is defined in `glob` manual page
for your operating system). This option is meaningless for URL includes.
* `url` (default: **true**) - allow URL includes.
* `priority` (default: 0) - specify priority for the include (see below).
Priorities are used by UCL parser to manage the policy of objects rewriting during including other files
as following:
* If we have two objects with the same priority then we form an implicit array
* If a new object has bigger priority then we overwrite an old one
* If a new object has lower priority then we ignore it
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.
### Variables support
@ -317,7 +359,7 @@ ucl: emitted compact json in 0.0991 seconds
ucl: emitted yaml in 0.1354 seconds
```
You can do your own benchmarks by running `make test` in libucl top directory.
You can do your own benchmarks by running `make check` in libucl top directory.
## Conclusion

View File

@ -1,8 +1,8 @@
PROJECT(libucl C)
SET(LIBUCL_VERSION_MAJOR 0)
SET(LIBUCL_VERSION_MINOR 2)
SET(LIBUCL_VERSION_PATCH 9)
SET(LIBUCL_VERSION_MINOR 5)
SET(LIBUCL_VERSION_PATCH 0)
SET(LIBUCL_VERSION "${LIBUCL_VERSION_MAJOR}.${LIBUCL_VERSION_MINOR}.${LIBUCL_VERSION_PATCH}")
@ -86,6 +86,8 @@ INCLUDE_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}/../uthash")
SET(UCLSRC ../src/ucl_util.c
../src/ucl_parser.c
../src/ucl_emitter.c
../src/ucl_emitter_streamline.c
../src/ucl_emitter_utils.c
../src/ucl_hash.c
../src/ucl_schema.c
../src/xxhash.c)
@ -98,6 +100,18 @@ ENDIF (BUILD_SHARED_LIBS)
ADD_LIBRARY(ucl ${LIB_TYPE} ${UCLSRC})
SET_TARGET_PROPERTIES(ucl PROPERTIES VERSION ${LIBUCL_VERSION} SOVERSION ${LIBUCL_VERSION_MAJOR})
IF(WITH_LUA)
SET(UCL_LUA_SRC ../lua/lua_ucl.c)
ADD_LIBRARY(lua-ucl ${LIB_TYPE} ${UCL_LUA_SRC})
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})
ENDIF(WITH_LUA)
IF(HAVE_FETCH_H)
TARGET_LINK_LIBRARIES(ucl fetch)
ELSE(HAVE_FETCH_H)

View File

@ -1,12 +1,13 @@
m4_define([maj_ver], [0])
m4_define([med_ver], [5])
m4_define([min_ver], [0])
m4_define([so_version], [2:0:0])
m4_define([med_ver], [6])
m4_define([min_ver], [1])
m4_define([so_version], [3:0:1])
m4_define([ucl_version], [maj_ver.med_ver.min_ver])
AC_INIT([libucl],[ucl_version],[https://github.com/vstakhov/libucl],[libucl])
AC_CONFIG_SRCDIR([configure.ac])
AM_INIT_AUTOMAKE([1.11 foreign silent-rules -Wall -Wportability no-dist-gzip dist-xz])
AM_INIT_AUTOMAKE([1.11 foreign -Wall -Wportability no-dist-gzip dist-xz])
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
UCL_VERSION=ucl_version
SO_VERSION=so_version
@ -57,6 +58,9 @@ AC_ARG_ENABLE([regex], AS_HELP_STRING([--enable-regex],
AC_ARG_ENABLE([signatures], AS_HELP_STRING([--enable-signatures],
[Enable signatures check (requires openssl) @<:@default=no@:>@]), [],
[enable_signatures=no])
AC_ARG_ENABLE([lua], AS_HELP_STRING([--enable-lua],
[Enable lua API build (requires lua libraries and headers) @<:@default=no@:>@]), [],
[enable_lua=no])
AC_ARG_ENABLE([utils],
AS_HELP_STRING([--enable-utils], [Build and install utils @<:@default=no@:>@]),
[case "${enableval}" in
@ -99,6 +103,21 @@ AS_IF([test "x$enable_regex" = "xyes"], [
])
AC_SUBST(LIBREGEX_LIB)
AS_IF([test "x$enable_lua" = "xyes"], [
AX_PROG_LUA([5.1], [], [
AX_LUA_HEADERS([
AX_LUA_LIBS([
AC_DEFINE(HAVE_LUA, 1, [Define to 1 for lua support.])
with_lua="yes"
], [AC_MSG_ERROR([unable to find the lua libraries])
])
], [AC_MSG_ERROR([unable to find the lua header files])
])
], [AC_MSG_ERROR([unable to find the lua interpreter])])
], [with_lua="no"])
AM_CONDITIONAL([LUA_SUB], [test "$with_lua" = "yes"])
AS_IF([test "x$enable_urls" = "xyes"], [
AC_CHECK_HEADER([fetch.h], [
AC_DEFINE(HAVE_FETCH_H, 1, [Define to 1 if you have the <fetch.h> header file.])
@ -155,9 +174,11 @@ AC_LINK_IFELSE([
AC_CONFIG_FILES(Makefile \
src/Makefile \
lua/Makefile
tests/Makefile \
utils/Makefile \
doc/Makefile \
lua/libucl.rockspec \
libucl.pc)
AC_CONFIG_FILES([stamp-h], [echo timestamp > stamp-h])
AC_OUTPUT

View File

@ -0,0 +1,194 @@
## Module `ucl`
This lua module allows to parse objects from strings and to store data into
ucl objects. It uses `libucl` C library to parse and manipulate with ucl objects.
Example:
~~~lua
local ucl = require("ucl")
local parser = ucl.parser()
local res,err = parser:parse_string('{key=value}')
if not res then
print('parser error: ' .. err)
else
local obj = parser:get_object()
local got = ucl.to_format(obj, 'json')
endif
local table = {
str = 'value',
num = 100500,
null = ucl.null,
func = function ()
return 'huh'
end
print(ucl.to_format(table, 'ucl'))
-- Output:
--[[
num = 100500;
str = "value";
null = null;
func = "huh";
--]]
~~~
###Brief content:
**Functions**:
> [`ucl_object_push_lua(L, obj, allow_array)`](#function-ucl_object_push_lual-obj-allow_array)
> [`ucl.to_format(var, format)`](#function-uclto_formatvar-format)
**Methods**:
> [`parser:parse_file(name)`](#method-parserparse_filename)
> [`parser:parse_string(input)`](#method-parserparse_stringinput)
> [`parser:get_object()`](#method-parserget_object)
## Functions
The module `ucl` defines the following functions.
### Function `ucl_object_push_lua(L, obj, allow_array)`
This is a `C` function to push `UCL` object as lua variable. This function
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
**Parameters:**
- `L {lua_State}`: lua state pointer
- `obj {ucl_object_t}`: object to push
- `allow_array {bool}`: expand implicit arrays (should be true for all but partial arrays)
**Returns:**
- `{int}`: `1` if an object is pushed to lua
Back to [module description](#module-ucl).
### Function `ucl.to_format(var, format)`
Converts lua variable `var` to the specified `format`. Formats supported are:
- `json` - fine printed json
- `json-compact` - compacted json
- `config` - fine printed configuration
- `ucl` - same as `config`
- `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.
**Parameters:**
- `var {variant}`: any sort of lua variable (if userdata then metafield `__to_ucl` is searched for output)
- `format {string}`: any available format
**Returns:**
- `{string}`: string representation of `var` in the specific `format`.
Example:
~~~lua
local table = {
str = 'value',
num = 100500,
null = ucl.null,
func = function ()
return 'huh'
end
print(ucl.to_format(table, 'ucl'))
-- Output:
--[[
num = 100500;
str = "value";
null = null;
func = "huh";
--]]
~~~
Back to [module description](#module-ucl).
## Methods
The module `ucl` defines the following methods.
### Method `parser:parse_file(name)`
Parse UCL object from file.
**Parameters:**
- `name {string}`: filename to parse
**Returns:**
- `{bool[, string]}`: if res is `true` then file has been parsed successfully, otherwise an error string is also returned
Example:
~~~lua
local parser = ucl.parser()
local res,err = parser:parse_file('/some/file.conf')
if not res then
print('parser error: ' .. err)
else
-- Do something with object
end
~~~
Back to [module description](#module-ucl).
### Method `parser:parse_string(input)`
Parse UCL object from file.
**Parameters:**
- `input {string}`: string to parse
**Returns:**
- `{bool[, string]}`: if res is `true` then file has been parsed successfully, otherwise an error string is also returned
Back to [module description](#module-ucl).
### Method `parser:get_object()`
Get top object from parser and export it to lua representation.
**Parameters:**
nothing
**Returns:**
- `{variant or nil}`: ucl object as lua native variable
Back to [module description](#module-ucl).
Back to [top](#).

View File

@ -0,0 +1,69 @@
/* Copyright (c) 2014, Vsevolod Stakhov
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef LUA_UCL_H_
#define LUA_UCL_H_
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
#include "ucl.h"
/**
* Closure structure for lua function storing inside UCL
*/
struct ucl_lua_funcdata {
lua_State *L;
int idx;
char *ret;
};
/**
* Initialize lua UCL API
*/
UCL_EXTERN int luaopen_ucl (lua_State *L);
/**
* Import UCL object from lua state
* @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 (lua_State *L, int idx);
/**
* Push an object to lua
* @param L lua state
* @param obj object to push
* @param allow_array traverse over implicit arrays
*/
UCL_EXTERN int ucl_object_push_lua (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);
#endif /* LUA_UCL_H_ */

View File

@ -147,7 +147,8 @@ typedef enum ucl_emitter {
typedef enum ucl_parser_flags {
UCL_PARSER_KEY_LOWERCASE = 0x1, /**< Convert all keys to lower case */
UCL_PARSER_ZEROCOPY = 0x2, /**< Parse input in zero-copy mode if possible */
UCL_PARSER_NO_TIME = 0x4 /**< Do not parse time and treat time values as strings */
UCL_PARSER_NO_TIME = 0x4, /**< Do not parse time and treat time values as strings */
UCL_PARSER_NO_IMPLICIT_ARRAYS = 0x8 /** Create explicit arrays instead of implicit ones */
} ucl_parser_flags_t;
/**
@ -171,9 +172,12 @@ typedef enum ucl_string_flags {
* Basic flags for an object
*/
typedef enum ucl_object_flags {
UCL_OBJECT_ALLOCATED_KEY = 1, /**< An object has key allocated internally */
UCL_OBJECT_ALLOCATED_VALUE = 2, /**< An object has a string value allocated internally */
UCL_OBJECT_NEED_KEY_ESCAPE = 4 /**< The key of an object need to be escaped on output */
UCL_OBJECT_ALLOCATED_KEY = 0x1, /**< An object has key allocated internally */
UCL_OBJECT_ALLOCATED_VALUE = 0x2, /**< An object has a string value allocated internally */
UCL_OBJECT_NEED_KEY_ESCAPE = 0x4, /**< The key of an object need to be escaped on output */
UCL_OBJECT_EPHEMERAL = 0x8, /**< Temporary object that does not need to be freed really */
UCL_OBJECT_MULTILINE = 0x10, /**< String should be displayed as multiline string */
UCL_OBJECT_MULTIVALUE = 0x20 /**< Object is a key with multiple values */
} ucl_object_flags_t;
/**
@ -195,14 +199,21 @@ typedef struct ucl_object_s {
const char *key; /**< Key of an object */
struct ucl_object_s *next; /**< Array handle */
struct ucl_object_s *prev; /**< Array handle */
unsigned char* trash_stack[2]; /**< Pointer to allocated chunks */
unsigned keylen; /**< Lenght of a key */
unsigned len; /**< Size of an object */
enum ucl_type type; /**< Real type */
uint16_t ref; /**< Reference count */
uint32_t keylen; /**< Lenght of a key */
uint32_t len; /**< Size of an object */
uint32_t ref; /**< Reference count */
uint16_t flags; /**< Object flags */
uint16_t type; /**< Real type */
unsigned char* trash_stack[2]; /**< Pointer to allocated chunks */
} ucl_object_t;
/**
* Destructor type for userdata objects
* @param ud user specified data pointer
*/
typedef void (*ucl_userdata_dtor)(void *ud);
typedef const char* (*ucl_userdata_emitter)(void *ud);
/** @} */
/**
@ -238,6 +249,31 @@ UCL_EXTERN ucl_object_t* ucl_object_new (void) UCL_WARN_UNUSED_RESULT;
*/
UCL_EXTERN ucl_object_t* ucl_object_typed_new (ucl_type_t type) UCL_WARN_UNUSED_RESULT;
/**
* Create new object with type and priority specified
* @param type type of a new object
* @param priority priority of an object
* @return new object
*/
UCL_EXTERN ucl_object_t* ucl_object_new_full (ucl_type_t type, unsigned priority)
UCL_WARN_UNUSED_RESULT;
/**
* Create new object with userdata dtor
* @param dtor destructor function
* @return new object
*/
UCL_EXTERN ucl_object_t* ucl_object_new_userdata (ucl_userdata_dtor dtor,
ucl_userdata_emitter emitter) UCL_WARN_UNUSED_RESULT;
/**
* Perform deep copy of an object copying everything
* @param other object to copy
* @return new object with refcount equal to 1
*/
UCL_EXTERN ucl_object_t * ucl_object_copy (const ucl_object_t *other)
UCL_WARN_UNUSED_RESULT;
/**
* Return the type of an object
* @return the object type
@ -293,7 +329,7 @@ UCL_EXTERN ucl_object_t* ucl_object_frombool (bool bv) UCL_WARN_UNUSED_RESULT;
/**
* Insert a object 'elt' to the hash 'top' and associate it with key 'key'
* @param top destination object (will be created automatically if top is NULL)
* @param top destination object (must be of type UCL_OBJECT)
* @param elt element to insert (must NOT be NULL)
* @param key key to associate with this object (either const or preallocated)
* @param keylen length of the key (or 0 for NULL terminated keys)
@ -306,7 +342,7 @@ UCL_EXTERN bool ucl_object_insert_key (ucl_object_t *top, ucl_object_t *elt,
/**
* Replace a object 'elt' to the hash 'top' and associate it with key 'key', old object will be unrefed,
* if no object has been found this function works like ucl_object_insert_key()
* @param top destination object (will be created automatically if top is NULL)
* @param top destination object (must be of type UCL_OBJECT)
* @param elt element to insert (must NOT be NULL)
* @param key key to associate with this object (either const or preallocated)
* @param keylen length of the key (or 0 for NULL terminated keys)
@ -316,6 +352,15 @@ UCL_EXTERN bool ucl_object_insert_key (ucl_object_t *top, ucl_object_t *elt,
UCL_EXTERN bool ucl_object_replace_key (ucl_object_t *top, ucl_object_t *elt,
const char *key, size_t keylen, bool copy_key);
/**
* Merge the keys from one object to another object. Overwrite on conflict
* @param top destination object (must be of type UCL_OBJECT)
* @param elt element to insert (must be of type UCL_OBJECT)
* @param copy copy rather than reference the elements
* @return true if all keys have been merged
*/
UCL_EXTERN bool ucl_object_merge (ucl_object_t *top, ucl_object_t *elt, bool copy);
/**
* Delete a object associated with key 'key', old object will be unrefered,
* @param top object
@ -335,8 +380,9 @@ UCL_EXTERN bool ucl_object_delete_key (ucl_object_t *top,
/**
* Delete key from `top` object returning the object deleted. This object is not
* released
* Removes `key` from `top` object, returning the object that was removed. This
* object is not released, caller must unref the returned object when it is no
* longer needed.
* @param top object
* @param key key to remove
* @param keylen length of the key (or 0 for NULL terminated keys)
@ -346,8 +392,9 @@ UCL_EXTERN ucl_object_t* ucl_object_pop_keyl (ucl_object_t *top, const char *key
size_t keylen) UCL_WARN_UNUSED_RESULT;
/**
* Delete key from `top` object returning the object deleted. This object is not
* released
* Removes `key` from `top` object returning the object that was removed. This
* object is not released, caller must unref the returned object when it is no
* longer needed.
* @param top object
* @param key key to remove
* @return removed object or NULL if object has not been found
@ -356,9 +403,9 @@ UCL_EXTERN ucl_object_t* ucl_object_pop_key (ucl_object_t *top, const char *key)
UCL_WARN_UNUSED_RESULT;
/**
* Insert a object 'elt' to the hash 'top' and associate it with key 'key', if the specified key exist,
* try to merge its content
* @param top destination object (will be created automatically if top is NULL)
* Insert a object 'elt' to the hash 'top' and associate it with key 'key', if
* the specified key exist, try to merge its content
* @param top destination object (must be of type UCL_OBJECT)
* @param elt element to insert (must NOT be NULL)
* @param key key to associate with this object (either const or preallocated)
* @param keylen length of the key (or 0 for NULL terminated keys)
@ -369,8 +416,8 @@ UCL_EXTERN bool ucl_object_insert_key_merged (ucl_object_t *top, ucl_object_t *e
const char *key, size_t keylen, bool copy_key);
/**
* Append an element to the front of array object
* @param top destination object (will be created automatically if top is NULL)
* Append an element to the end of array object
* @param top destination object (must NOT be NULL)
* @param elt element to append (must NOT be NULL)
* @return true if value has been inserted
*/
@ -379,7 +426,7 @@ UCL_EXTERN bool ucl_array_append (ucl_object_t *top,
/**
* Append an element to the start of array object
* @param top destination object (will be created automatically if top is NULL)
* @param top destination object (must NOT be NULL)
* @param elt element to append (must NOT be NULL)
* @return true if value has been inserted
*/
@ -387,8 +434,19 @@ UCL_EXTERN bool ucl_array_prepend (ucl_object_t *top,
ucl_object_t *elt);
/**
* Removes an element `elt` from the array `top`. Caller must unref the returned object when it is not
* needed.
* Merge all elements of second array into the first array
* @param top destination array (must be of type UCL_ARRAY)
* @param elt array to copy elements from (must be of type UCL_ARRAY)
* @param copy copy elements instead of referencing them
* @return true if arrays were merged
*/
UCL_EXTERN bool ucl_array_merge (ucl_object_t *top, ucl_object_t *elt,
bool copy);
/**
* Removes an element `elt` from the array `top`, returning the object that was
* removed. This object is not released, caller must unref the returned object
* when it is no longer needed.
* @param top array ucl object
* @param elt element to remove
* @return removed element or NULL if `top` is NULL or not an array
@ -411,35 +469,50 @@ UCL_EXTERN const ucl_object_t* ucl_array_head (const ucl_object_t *top);
UCL_EXTERN const ucl_object_t* ucl_array_tail (const ucl_object_t *top);
/**
* Removes the last element from the array `top`. Caller must unref the returned object when it is not
* needed.
* Removes the last element from the array `top`, returning the object that was
* removed. This object is not released, caller must unref the returned object
* when it is no longer needed.
* @param top array ucl object
* @return removed element or NULL if `top` is NULL or not an array
*/
UCL_EXTERN ucl_object_t* ucl_array_pop_last (ucl_object_t *top);
/**
* Return object identified by an index of the array `top`
* @param obj object to get a key from (must be of type UCL_ARRAY)
* @param index index to return
* @return object at the specified index or NULL if index is not found
*/
UCL_EXTERN const ucl_object_t* ucl_array_find_index (const ucl_object_t *top,
unsigned int index);
/**
* Removes the first element from the array `top`. Caller must unref the returned object when it is not
* needed.
* Removes the first element from the array `top`, returning the object that was
* removed. This object is not released, caller must unref the returned object
* when it is no longer needed.
* @param top array ucl object
* @return removed element or NULL if `top` is NULL or not an array
*/
UCL_EXTERN ucl_object_t* ucl_array_pop_first (ucl_object_t *top);
/**
* Return object identified by index of the array `top`
* @param top object to get a key from (must be of type UCL_ARRAY)
* @param index array index to return
* @return object at the specified index or NULL if index is not found
*/
UCL_EXTERN const ucl_object_t* ucl_array_find_index (const ucl_object_t *top,
unsigned int index);
/**
* Replace an element in an array with a different element, returning the object
* that was replaced. This object is not released, caller must unref the
* returned object when it is no longer needed.
* @param top destination object (must be of type UCL_ARRAY)
* @param elt element to append (must NOT be NULL)
* @param index array index in destination to overwrite with elt
* @return object that was replaced or NULL if index is not found
*/
ucl_object_t *
ucl_array_replace_index (ucl_object_t *top, ucl_object_t *elt,
unsigned int index);
/**
* Append a element to another element forming an implicit array
* @param head head to append (may be NULL)
* @param elt new element
* @return true if element has been inserted
* @return the new implicit array
*/
UCL_EXTERN ucl_object_t * ucl_elt_append (ucl_object_t *head,
ucl_object_t *elt);
@ -533,7 +606,7 @@ UCL_EXTERN const char* ucl_object_tolstring (const ucl_object_t *obj, size_t *tl
* Return object identified by a key in the specified object
* @param obj object to get a key from (must be of type UCL_OBJECT)
* @param key key to search
* @return object matched the specified key or NULL if key is not found
* @return object matching the specified key or NULL if key was not found
*/
UCL_EXTERN const ucl_object_t* ucl_object_find_key (const ucl_object_t *obj,
const char *key);
@ -543,7 +616,7 @@ UCL_EXTERN const ucl_object_t* ucl_object_find_key (const ucl_object_t *obj,
* @param obj object to get a key from (must be of type UCL_OBJECT)
* @param key key to search
* @param klen length of a key
* @return object matched the specified key or NULL if key is not found
* @return object matching the specified key or NULL if key was not found
*/
UCL_EXTERN const ucl_object_t* ucl_object_find_keyl (const ucl_object_t *obj,
const char *key, size_t klen);
@ -575,6 +648,7 @@ UCL_EXTERN const char* ucl_object_keyl (const ucl_object_t *obj, size_t *len);
/**
* Increase reference count for an object
* @param obj object to ref
* @return the referenced object
*/
UCL_EXTERN ucl_object_t* ucl_object_ref (const ucl_object_t *obj);
@ -611,6 +685,21 @@ UCL_EXTERN int ucl_object_compare (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));
/**
* Get the priority for specific UCL object
* @param obj any ucl object
* @return priority of an object
*/
UCL_EXTERN unsigned int ucl_object_get_priority (const ucl_object_t *obj);
/**
* Set explicit priority of an object.
* @param obj any ucl object
* @param priority new priroity value (only 4 least significant bits are considred)
*/
UCL_EXTERN void ucl_object_set_priority (ucl_object_t *obj,
unsigned int priority);
/**
* Opaque iterator object
*/
@ -640,11 +729,14 @@ UCL_EXTERN const ucl_object_t* ucl_iterate_object (const ucl_object_t *obj,
* Macro handler for a parser
* @param data the content of macro
* @param len the length of content
* @param arguments arguments object
* @param ud opaque user data
* @param err error pointer
* @return true if macro has been parsed
*/
typedef bool (*ucl_macro_handler) (const unsigned char *data, size_t len, void* ud);
typedef bool (*ucl_macro_handler) (const unsigned char *data, size_t len,
const ucl_object_t *arguments,
void* ud);
/* Opaque parser */
struct ucl_parser;
@ -702,12 +794,23 @@ UCL_EXTERN void ucl_parser_set_variables_handler (struct ucl_parser *parser,
* @param parser parser structure
* @param data the pointer to the beginning of a chunk
* @param len the length of a chunk
* @param err if *err is NULL it is set to parser error
* @return true if chunk has been added and false in case of error
*/
UCL_EXTERN bool ucl_parser_add_chunk (struct ucl_parser *parser,
const unsigned char *data, size_t len);
/**
* Load new chunk to a parser with the specified priority
* @param parser parser structure
* @param data the pointer to the beginning of a chunk
* @param len the length of a chunk
* @param priority the desired priority of a chunk (only 4 least significant bits
* are considered for this parameter)
* @return true if chunk has been added and false in case of error
*/
UCL_EXTERN bool ucl_parser_add_chunk_priority (struct ucl_parser *parser,
const unsigned char *data, size_t len, unsigned priority);
/**
* Load ucl object from a string
* @param parser parser structure
@ -835,7 +938,7 @@ struct ucl_emitter_context {
/** A set of output operations */
const struct ucl_emitter_operations *ops;
/** Current amount of indent tabs */
unsigned int ident;
unsigned int indent;
/** Top level object */
const ucl_object_t *top;
/** The rest of context */

View File

@ -7,5 +7,5 @@ Name: LibUCL
Description: Universal configuration library
Version: @UCL_VERSION@
Libs: -L${libdir} -lucl
Libs.private: @LIBS_EXTRA@
Libs.private: @LIBS_EXTRA@ @LUA_LIB@
Cflags: -I${includedir}/

View File

@ -0,0 +1,26 @@
ucl_common_cflags= -I$(top_srcdir)/src \
-I$(top_srcdir)/include \
-I$(top_srcdir)/uthash \
-Wall -W -Wno-unused-parameter -Wno-pointer-sign
luaexec_LTLIBRARIES= ucl.la
ucl_la_SOURCES= lua_ucl.c
ucl_la_CFLAGS= $(ucl_common_cflags) \
@LUA_INCLUDE@
ucl_la_LDFLAGS = -module -export-dynamic -avoid-version
ucl_la_LIBADD= $(top_srcdir)/src/libucl.la \
@LIBFETCH_LIBS@ \
@LIBCRYPTO_LIB@ \
@LIBREGEX_LIB@ \
@CURL_LIBS@ \
@LUA_LIB@
include_HEADERS= $(top_srcdir)/include/lua_ucl.h
ROCKSPEC = $(PACKAGE)-$(VERSION)-1.rockspec
EXTRA_DIST = $(PACKAGE).rockspec.in \
test.lua
DISTCLEANFILES = $(PACKAGE).rockspec
$(ROCKSPEC): $(PACKAGE).rockspec dist
sed -e 's/@MD5@/'`$(MD5SUM) $(distdir).tar.gz | \
cut -d " " -f 1`'/g' < $(PACKAGE).rockspec > $@

View File

@ -0,0 +1,26 @@
package="@PACKAGE@"
version="@VERSION@-1"
source = {
url = "https://github.com/downloads/vstakhov/@PACKAGE@/@PACKAGE@-@VERSION@.tar.gz",
md5 = "@MD5@",
dir = "@PACKAGE@-@VERSION@"
}
description = {
summary = "UCL - json like configuration language",
detailed = [[
UCL is heavily infused by nginx configuration as the example
of a convenient configuration system.
However, UCL is fully compatible with JSON format and is able
to parse json files.
]],
homepage = "http://github.com/vstakhov/@PACKAGE@/",
license = ""
}
dependencies = {
"lua >= 5.1"
}
build = {
type = "command",
build_command = "LUA=$(LUA) CPPFLAGS=-I$(LUA_INCDIR) ./configure --prefix=$(PREFIX) --libdir=$(LIBDIR) --datadir=$(LUADIR) && make clean && make",
install_command = "make install"
}

View File

@ -0,0 +1,820 @@
/* Copyright (c) 2014, Vsevolod Stakhov
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @file lua ucl bindings
*/
#include "ucl.h"
#include "ucl_internal.h"
#include "lua_ucl.h"
#include <strings.h>
/***
* @module ucl
* This lua module allows to parse objects from strings and to store data into
* ucl objects. It uses `libucl` C library to parse and manipulate with ucl objects.
* @example
local ucl = require("ucl")
local parser = ucl.parser()
local res,err = parser:parse_string('{key=value}')
if not res then
print('parser error: ' .. err)
else
local obj = parser:get_object()
local got = ucl.to_format(obj, 'json')
endif
local table = {
str = 'value',
num = 100500,
null = ucl.null,
func = function ()
return 'huh'
end
}
print(ucl.to_format(table, 'ucl'))
-- Output:
--[[
num = 100500;
str = "value";
null = null;
func = "huh";
--]]
*/
#define PARSER_META "ucl.parser.meta"
#define EMITTER_META "ucl.emitter.meta"
#define NULL_META "null.emitter.meta"
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 void *ucl_null;
/**
* Push a single element of an object to lua
* @param L
* @param key
* @param obj
*/
static void
ucl_object_lua_push_element (lua_State *L, const char *key,
const ucl_object_t *obj)
{
lua_pushstring (L, key);
ucl_object_push_lua (L, obj, true);
lua_settable (L, -3);
}
static void
lua_ucl_userdata_dtor (void *ud)
{
struct ucl_lua_funcdata *fd = (struct ucl_lua_funcdata *)ud;
luaL_unref (fd->L, LUA_REGISTRYINDEX, fd->idx);
if (fd->ret != NULL) {
free (fd->ret);
}
free (fd);
}
static const char *
lua_ucl_userdata_emitter (void *ud)
{
struct ucl_lua_funcdata *fd = (struct ucl_lua_funcdata *)ud;
const char *out = "";
lua_rawgeti (fd->L, LUA_REGISTRYINDEX, fd->idx);
lua_pcall (fd->L, 0, 1, 0);
out = lua_tostring (fd->L, -1);
if (out != NULL) {
/* We need to store temporary string in a more appropriate place */
if (fd->ret) {
free (fd->ret);
}
fd->ret = strdup (out);
}
lua_settop (fd->L, 0);
return fd->ret;
}
/**
* Push a single object to lua
* @param L
* @param obj
* @return
*/
static int
ucl_object_lua_push_object (lua_State *L, const ucl_object_t *obj,
bool allow_array)
{
const ucl_object_t *cur;
ucl_object_iter_t it = NULL;
int nelt = 0;
if (allow_array && obj->next != NULL) {
/* Actually we need to push this as an array */
return ucl_object_lua_push_array (L, obj);
}
/* Optimize allocation by preallocation of table */
while (ucl_iterate_object (obj, &it, true) != NULL) {
nelt ++;
}
lua_createtable (L, 0, nelt);
it = NULL;
while ((cur = ucl_iterate_object (obj, &it, true)) != NULL) {
ucl_object_lua_push_element (L, ucl_object_key (cur), cur);
}
return 1;
}
/**
* Push an array to lua as table indexed by integers
* @param L
* @param obj
* @return
*/
static int
ucl_object_lua_push_array (lua_State *L, const ucl_object_t *obj)
{
const ucl_object_t *cur;
int i = 1, nelt = 0;
/* Optimize allocation by preallocation of table */
LL_FOREACH (obj, cur) {
nelt ++;
}
lua_createtable (L, nelt, 0);
LL_FOREACH (obj, cur) {
ucl_object_push_lua (L, cur, false);
lua_rawseti (L, -2, i);
i ++;
}
return 1;
}
/**
* Push a simple object to lua depending on its actual type
*/
static int
ucl_object_lua_push_scalar (lua_State *L, const ucl_object_t *obj,
bool allow_array)
{
struct ucl_lua_funcdata *fd;
if (allow_array && obj->next != NULL) {
/* Actually we need to push this as an array */
return ucl_object_lua_push_array (L, obj);
}
switch (obj->type) {
case UCL_BOOLEAN:
lua_pushboolean (L, ucl_obj_toboolean (obj));
break;
case UCL_STRING:
lua_pushstring (L, ucl_obj_tostring (obj));
break;
case UCL_INT:
#if LUA_VERSION_NUM >= 501
lua_pushinteger (L, ucl_obj_toint (obj));
#else
lua_pushnumber (L, ucl_obj_toint (obj));
#endif
break;
case UCL_FLOAT:
case UCL_TIME:
lua_pushnumber (L, ucl_obj_todouble (obj));
break;
case UCL_NULL:
lua_getfield (L, LUA_REGISTRYINDEX, "ucl.null");
break;
case UCL_USERDATA:
fd = (struct ucl_lua_funcdata *)obj->value.ud;
lua_rawgeti (L, LUA_REGISTRYINDEX, fd->idx);
break;
default:
lua_pushnil (L);
break;
}
return 1;
}
/***
* @function ucl_object_push_lua(L, obj, allow_array)
* This is a `C` function to push `UCL` object as lua variable. This function
* 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
* @param {lua_State} L lua state pointer
* @param {ucl_object_t} obj object to push
* @param {bool} allow_array expand implicit arrays (should be true for all but partial arrays)
* @return {int} `1` if an object is pushed to lua
*/
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->value.av);
default:
return ucl_object_lua_push_scalar (L, obj, allow_array);
}
}
/**
* Parse lua table into object top
* @param L
* @param top
* @param idx
*/
static ucl_object_t *
ucl_object_lua_fromtable (lua_State *L, int idx)
{
ucl_object_t *obj, *top = NULL;
size_t keylen;
const char *k;
bool is_array = true;
int max = INT_MIN;
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;
}
}
else {
/* Keys are not integer */
lua_pop (L, 2);
is_array = false;
break;
}
}
else {
/* Keys are not numeric */
lua_pop (L, 2);
is_array = false;
break;
}
lua_pop (L, 1);
}
/* Table iterate */
if (is_array) {
int i;
top = ucl_object_typed_new (UCL_ARRAY);
for (i = 1; i <= max; i ++) {
lua_pushinteger (L, i);
lua_gettable (L, idx);
obj = ucl_object_lua_fromelt (L, lua_gettop (L));
if (obj != NULL) {
ucl_array_append (top, obj);
}
}
}
else {
lua_pushnil (L);
top = ucl_object_typed_new (UCL_OBJECT);
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));
if (obj != NULL) {
ucl_object_insert_key (top, obj, k, keylen, true);
}
lua_pop (L, 1);
}
}
return top;
}
/**
* Get a single element from lua to object obj
* @param L
* @param obj
* @param idx
*/
static ucl_object_t *
ucl_object_lua_fromelt (lua_State *L, int idx)
{
int type;
double num;
ucl_object_t *obj = NULL;
struct ucl_lua_funcdata *fd;
type = lua_type (L, idx);
switch (type) {
case LUA_TSTRING:
obj = ucl_object_fromstring_common (lua_tostring (L, idx), 0, 0);
break;
case LUA_TNUMBER:
num = lua_tonumber (L, idx);
if (num == (int64_t)num) {
obj = ucl_object_fromint (num);
}
else {
obj = ucl_object_fromdouble (num);
}
break;
case LUA_TBOOLEAN:
obj = ucl_object_frombool (lua_toboolean (L, idx));
break;
case LUA_TUSERDATA:
if (lua_topointer (L, idx) == ucl_null) {
obj = ucl_object_typed_new (UCL_NULL);
}
break;
case LUA_TTABLE:
case LUA_TFUNCTION:
case LUA_TTHREAD:
if (luaL_getmetafield (L, idx, "__gen_ucl")) {
if (lua_isfunction (L, -1)) {
lua_settop (L, 3); /* gen, obj, func */
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);
}
lua_pop (L, 2);
}
else {
if (type == LUA_TTABLE) {
obj = ucl_object_lua_fromtable (L, idx);
}
else if (type == LUA_TFUNCTION) {
fd = malloc (sizeof (*fd));
if (fd != NULL) {
lua_pushvalue (L, idx);
fd->L = L;
fd->ret = NULL;
fd->idx = luaL_ref (L, LUA_REGISTRYINDEX);
obj = ucl_object_new_userdata (lua_ucl_userdata_dtor,
lua_ucl_userdata_emitter);
obj->type = UCL_USERDATA;
obj->value.ud = (void *)fd;
}
}
}
break;
}
return obj;
}
/**
* @function ucl_object_lua_import(L, idx)
* Extracts ucl object from lua variable at `idx` position,
* @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 (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);
break;
default:
obj = ucl_object_lua_fromelt (L, idx);
break;
}
return obj;
}
static int
lua_ucl_parser_init (lua_State *L)
{
struct ucl_parser *parser, **pparser;
int flags = 0;
if (lua_gettop (L) >= 1) {
flags = lua_tonumber (L, 1);
}
parser = ucl_parser_new (flags);
if (parser == NULL) {
lua_pushnil (L);
}
pparser = lua_newuserdata (L, sizeof (parser));
*pparser = parser;
luaL_getmetatable (L, PARSER_META);
lua_setmetatable (L, -2);
return 1;
}
static struct ucl_parser *
lua_ucl_parser_get (lua_State *L, int index)
{
return *((struct ucl_parser **) luaL_checkudata(L, index, PARSER_META));
}
/***
* @method parser:parse_file(name)
* Parse UCL object from file.
* @param {string} name filename to parse
* @return {bool[, string]} if res is `true` then file has been parsed successfully, otherwise an error string is also returned
@example
local parser = ucl.parser()
local res,err = parser:parse_file('/some/file.conf')
if not res then
print('parser error: ' .. err)
else
-- Do something with object
end
*/
static int
lua_ucl_parser_parse_file (lua_State *L)
{
struct ucl_parser *parser;
const char *file;
int ret = 2;
parser = lua_ucl_parser_get (L, 1);
file = luaL_checkstring (L, 2);
if (parser != NULL && file != NULL) {
if (ucl_parser_add_file (parser, file)) {
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:parse_string(input)
* Parse UCL object from file.
* @param {string} input string 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_string (lua_State *L)
{
struct ucl_parser *parser;
const char *string;
size_t llen;
int ret = 2;
parser = lua_ucl_parser_get (L, 1);
string = luaL_checklstring (L, 2, &llen);
if (parser != NULL && string != NULL) {
if (ucl_parser_add_chunk (parser, (const unsigned char *)string, llen)) {
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.
* @return {variant or nil} ucl object as lua native variable
*/
static int
lua_ucl_parser_get_object (lua_State *L)
{
struct ucl_parser *parser;
ucl_object_t *obj;
int ret = 1;
parser = lua_ucl_parser_get (L, 1);
obj = ucl_parser_get_object (parser);
if (obj != NULL) {
ret = ucl_object_push_lua (L, obj, false);
/* no need to keep reference */
ucl_object_unref (obj);
}
else {
lua_pushnil (L);
}
return ret;
}
static int
lua_ucl_parser_gc (lua_State *L)
{
struct ucl_parser *parser;
parser = lua_ucl_parser_get (L, 1);
ucl_parser_free (parser);
return 0;
}
static void
lua_ucl_parser_mt (lua_State *L)
{
luaL_newmetatable (L, PARSER_META);
lua_pushvalue(L, -1);
lua_setfield(L, -2, "__index");
lua_pushcfunction (L, lua_ucl_parser_gc);
lua_setfield (L, -2, "__gc");
lua_pushcfunction (L, lua_ucl_parser_parse_file);
lua_setfield (L, -2, "parse_file");
lua_pushcfunction (L, lua_ucl_parser_parse_string);
lua_setfield (L, -2, "parse_string");
lua_pushcfunction (L, lua_ucl_parser_get_object);
lua_setfield (L, -2, "get_object");
lua_pop (L, 1);
}
static int
lua_ucl_to_string (lua_State *L, const ucl_object_t *obj, enum ucl_emitter type)
{
unsigned char *result;
result = ucl_object_emit (obj, type);
if (result != NULL) {
lua_pushstring (L, (const char *)result);
free (result);
}
else {
lua_pushnil (L);
}
return 1;
}
static int
lua_ucl_to_json (lua_State *L)
{
ucl_object_t *obj;
int format = UCL_EMIT_JSON;
if (lua_gettop (L) > 1) {
if (lua_toboolean (L, 2)) {
format = UCL_EMIT_JSON_COMPACT;
}
}
obj = ucl_object_lua_import (L, 1);
if (obj != NULL) {
lua_ucl_to_string (L, obj, format);
ucl_object_unref (obj);
}
else {
lua_pushnil (L);
}
return 1;
}
static int
lua_ucl_to_config (lua_State *L)
{
ucl_object_t *obj;
obj = ucl_object_lua_import (L, 1);
if (obj != NULL) {
lua_ucl_to_string (L, obj, UCL_EMIT_CONFIG);
ucl_object_unref (obj);
}
else {
lua_pushnil (L);
}
return 1;
}
/***
* @function ucl.to_format(var, format)
* Converts lua variable `var` to the specified `format`. Formats supported are:
*
* - `json` - fine printed json
* - `json-compact` - compacted json
* - `config` - fine printed configuration
* - `ucl` - same as `config`
* - `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.
* @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`.
* @example
local table = {
str = 'value',
num = 100500,
null = ucl.null,
func = function ()
return 'huh'
end
}
print(ucl.to_format(table, 'ucl'))
-- Output:
--[[
num = 100500;
str = "value";
null = null;
func = "huh";
--]]
*/
static int
lua_ucl_to_format (lua_State *L)
{
ucl_object_t *obj;
int format = UCL_EMIT_JSON;
if (lua_gettop (L) > 1) {
if (lua_type (L, 2) == LUA_TNUMBER) {
format = lua_tonumber (L, 2);
if (format < 0 || format >= UCL_EMIT_YAML) {
lua_pushnil (L);
return 1;
}
}
else if (lua_type (L, 2) == LUA_TSTRING) {
const char *strtype = lua_tostring (L, 2);
if (strcasecmp (strtype, "json") == 0) {
format = UCL_EMIT_JSON;
}
else if (strcasecmp (strtype, "json-compact") == 0) {
format = UCL_EMIT_JSON_COMPACT;
}
else if (strcasecmp (strtype, "yaml") == 0) {
format = UCL_EMIT_YAML;
}
else if (strcasecmp (strtype, "config") == 0 ||
strcasecmp (strtype, "ucl") == 0) {
format = UCL_EMIT_CONFIG;
}
}
}
obj = ucl_object_lua_import (L, 1);
if (obj != NULL) {
lua_ucl_to_string (L, obj, format);
ucl_object_unref (obj);
}
else {
lua_pushnil (L);
}
return 1;
}
static int
lua_ucl_null_tostring (lua_State* L)
{
lua_pushstring (L, "null");
return 1;
}
static void
lua_ucl_null_mt (lua_State *L)
{
luaL_newmetatable (L, NULL_META);
lua_pushcfunction (L, lua_ucl_null_tostring);
lua_setfield (L, -2, "__tostring");
lua_pop (L, 1);
}
int
luaopen_ucl (lua_State *L)
{
lua_ucl_parser_mt (L);
lua_ucl_null_mt (L);
/* Create the refs weak table: */
lua_createtable (L, 0, 2);
lua_pushliteral (L, "v"); /* tbl, "v" */
lua_setfield (L, -2, "__mode");
lua_pushvalue (L, -1); /* tbl, tbl */
lua_setmetatable (L, -2); /* tbl */
lua_setfield (L, LUA_REGISTRYINDEX, "ucl.refs");
lua_newtable (L);
lua_pushcfunction (L, lua_ucl_parser_init);
lua_setfield (L, -2, "parser");
lua_pushcfunction (L, lua_ucl_to_json);
lua_setfield (L, -2, "to_json");
lua_pushcfunction (L, lua_ucl_to_config);
lua_setfield (L, -2, "to_config");
lua_pushcfunction (L, lua_ucl_to_format);
lua_setfield (L, -2, "to_format");
ucl_null = lua_newuserdata (L, 0);
luaL_getmetatable (L, NULL_META);
lua_setmetatable (L, -2);
lua_pushvalue (L, -1);
lua_setfield (L, LUA_REGISTRYINDEX, "ucl.null");
lua_setfield (L, -2, "null");
return 1;
}
struct ucl_lua_funcdata*
ucl_object_toclosure (const ucl_object_t *obj)
{
if (obj == NULL || obj->type != UCL_USERDATA) {
return NULL;
}
return (struct ucl_lua_funcdata*)obj->value.ud;
}

View File

@ -0,0 +1,48 @@
local ucl = require("ucl")
function test_simple()
local expect =
'['..
'"float",1.5,'..
'"integer",5,'..
'"true",true,'..
'"false",false,'..
'"null",null,'..
'"string","hello",'..
'"array",[1,2],'..
'"object",{"key":"value"}'..
']'
-- Input to to_value matches the output of to_string:
local parser = ucl.parser()
local res,err = parser:parse_string(expect)
if not res then
print('parser error: ' .. err)
return 1
end
local obj = parser:get_object()
local got = ucl.to_json(obj, true)
if expect == got then
return 0
else
print(expect .. " == " .. tostring(got))
return 1
end
end
test_simple()
local table = {
str = 'value',
num = 100500,
null = ucl.null,
func = function ()
return 'huh'
end,
badfunc = function()
print("I'm bad")
end
}
print(ucl.to_format(table, 'ucl'))

4
contrib/libucl/m4/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

606
contrib/libucl/m4/ax_lua.m4 Normal file
View File

@ -0,0 +1,606 @@
# ===========================================================================
# http://www.gnu.org/software/autoconf-archive/ax_lua.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_PROG_LUA[([MINIMUM-VERSION], [TOO-BIG-VERSION], [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])]
# AX_LUA_HEADERS[([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])]
# AX_LUA_LIBS[([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])]
# AX_LUA_READLINE[([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])]
#
# DESCRIPTION
#
# Detect a Lua interpreter, optionally specifying a minimum and maximum
# version number. Set up important Lua paths, such as the directories in
# which to install scripts and modules (shared libraries).
#
# Also detect Lua headers and libraries. The Lua version contained in the
# header is checked to match the Lua interpreter version exactly. When
# searching for Lua libraries, the version number is used as a suffix.
# This is done with the goal of supporting multiple Lua installs (5.1 and
# 5.2 side-by-side).
#
# A note on compatibility with previous versions: This file has been
# mostly rewritten for serial 18. Most developers should be able to use
# these macros without needing to modify configure.ac. Care has been taken
# to preserve each macro's behavior, but there are some differences:
#
# 1) AX_WITH_LUA is deprecated; it now expands to the exact same thing as
# AX_PROG_LUA with no arguments.
#
# 2) AX_LUA_HEADERS now checks that the version number defined in lua.h
# matches the interpreter version. AX_LUA_HEADERS_VERSION is therefore
# unnecessary, so it is deprecated and does not expand to anything.
#
# 3) The configure flag --with-lua-suffix no longer exists; the user
# should instead specify the LUA precious variable on the command line.
# See the AX_PROG_LUA description for details.
#
# Please read the macro descriptions below for more information.
#
# This file was inspired by Andrew Dalke's and James Henstridge's
# python.m4 and Tom Payne's, Matthieu Moy's, and Reuben Thomas's ax_lua.m4
# (serial 17). Basically, this file is a mash-up of those two files. I
# like to think it combines the best of the two!
#
# AX_PROG_LUA: Search for the Lua interpreter, and set up important Lua
# paths. Adds precious variable LUA, which may contain the path of the Lua
# interpreter. If LUA is blank, the user's path is searched for an
# suitable interpreter.
#
# If MINIMUM-VERSION is supplied, then only Lua interpreters with a
# version number greater or equal to MINIMUM-VERSION will be accepted. If
# TOO-BIG- VERSION is also supplied, then only Lua interpreters with a
# version number greater or equal to MINIMUM-VERSION and less than
# TOO-BIG-VERSION will be accepted.
#
# The Lua version number, LUA_VERSION, is found from the interpreter, and
# substituted. LUA_PLATFORM is also found, but not currently supported (no
# standard representation).
#
# Finally, the macro finds four paths:
#
# luadir Directory to install Lua scripts.
# pkgluadir $luadir/$PACKAGE
# luaexecdir Directory to install Lua modules.
# pkgluaexecdir $luaexecdir/$PACKAGE
#
# These paths a found based on $prefix, $exec_prefix, Lua's package.path,
# and package.cpath. The first path of package.path beginning with $prefix
# is selected as luadir. The first path of package.cpath beginning with
# $exec_prefix is used as luaexecdir. This should work on all reasonable
# Lua installations. If a path cannot be determined, a default path is
# used. Of course, the user can override these later when invoking make.
#
# luadir Default: $prefix/share/lua/$LUA_VERSION
# luaexecdir Default: $exec_prefix/lib/lua/$LUA_VERSION
#
# These directories can be used by Automake as install destinations. The
# variable name minus 'dir' needs to be used as a prefix to the
# appropriate Automake primary, e.g. lua_SCRIPS or luaexec_LIBRARIES.
#
# If an acceptable Lua interpreter is found, then ACTION-IF-FOUND is
# performed, otherwise ACTION-IF-NOT-FOUND is preformed. If ACTION-IF-NOT-
# FOUND is blank, then it will default to printing an error. To prevent
# the default behavior, give ':' as an action.
#
# AX_LUA_HEADERS: Search for Lua headers. Requires that AX_PROG_LUA be
# expanded before this macro. Adds precious variable LUA_INCLUDE, which
# may contain Lua specific include flags, e.g. -I/usr/include/lua5.1. If
# LUA_INCLUDE is blank, then this macro will attempt to find suitable
# flags.
#
# LUA_INCLUDE can be used by Automake to compile Lua modules or
# executables with embedded interpreters. The *_CPPFLAGS variables should
# be used for this purpose, e.g. myprog_CPPFLAGS = $(LUA_INCLUDE).
#
# This macro searches for the header lua.h (and others). The search is
# performed with a combination of CPPFLAGS, CPATH, etc, and LUA_INCLUDE.
# If the search is unsuccessful, then some common directories are tried.
# If the headers are then found, then LUA_INCLUDE is set accordingly.
#
# The paths automatically searched are:
#
# * /usr/include/luaX.Y
# * /usr/include/lua/X.Y
# * /usr/include/luaXY
# * /usr/local/include/luaX.Y
# * /usr/local/include/lua-X.Y
# * /usr/local/include/lua/X.Y
# * /usr/local/include/luaXY
#
# (Where X.Y is the Lua version number, e.g. 5.1.)
#
# The Lua version number found in the headers is always checked to match
# the Lua interpreter's version number. Lua headers with mismatched
# version numbers are not accepted.
#
# If headers are found, then ACTION-IF-FOUND is performed, otherwise
# ACTION-IF-NOT-FOUND is performed. If ACTION-IF-NOT-FOUND is blank, then
# it will default to printing an error. To prevent the default behavior,
# set the action to ':'.
#
# AX_LUA_LIBS: Search for Lua libraries. Requires that AX_PROG_LUA be
# expanded before this macro. Adds precious variable LUA_LIB, which may
# contain Lua specific linker flags, e.g. -llua5.1. If LUA_LIB is blank,
# then this macro will attempt to find suitable flags.
#
# LUA_LIB can be used by Automake to link Lua modules or executables with
# embedded interpreters. The *_LIBADD and *_LDADD variables should be used
# for this purpose, e.g. mymod_LIBADD = $(LUA_LIB).
#
# This macro searches for the Lua library. More technically, it searches
# for a library containing the function lua_load. The search is performed
# with a combination of LIBS, LIBRARY_PATH, and LUA_LIB.
#
# If the search determines that some linker flags are missing, then those
# flags will be added to LUA_LIB.
#
# If libraries are found, then ACTION-IF-FOUND is performed, otherwise
# ACTION-IF-NOT-FOUND is performed. If ACTION-IF-NOT-FOUND is blank, then
# it will default to printing an error. To prevent the default behavior,
# set the action to ':'.
#
# AX_LUA_READLINE: Search for readline headers and libraries. Requires the
# AX_LIB_READLINE macro, which is provided by ax_lib_readline.m4 from the
# Autoconf Archive.
#
# If a readline compatible library is found, then ACTION-IF-FOUND is
# performed, otherwise ACTION-IF-NOT-FOUND is performed.
#
# LICENSE
#
# Copyright (c) 2014 Reuben Thomas <rrt@sc3d.org>
# Copyright (c) 2013 Tim Perkins <tprk77@gmail.com>
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation, either version 3 of the License, or (at your
# option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
# As a special exception, the respective Autoconf Macro's copyright owner
# gives unlimited permission to copy, distribute and modify the configure
# scripts that are the output of Autoconf when processing the Macro. You
# need not follow the terms of the GNU General Public License when using
# or distributing such scripts, even though portions of the text of the
# Macro appear in them. The GNU General Public License (GPL) does govern
# all other use of the material that constitutes the Autoconf Macro.
#
# This special exception to the GPL applies to versions of the Autoconf
# Macro released by the Autoconf Archive. When you make and distribute a
# modified version of the Autoconf Macro, you may extend this special
# exception to the GPL to apply to your modified version as well.
#serial 23
dnl =========================================================================
dnl AX_PROG_LUA([MINIMUM-VERSION], [TOO-BIG-VERSION],
dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
dnl =========================================================================
AC_DEFUN([AX_PROG_LUA],
[
dnl Make LUA a precious variable.
AC_ARG_VAR([LUA], [The Lua interpreter, e.g. /usr/bin/lua5.1])
dnl Find a Lua interpreter.
m4_define_default([_AX_LUA_INTERPRETER_LIST],
[lua lua5.2 lua52 lua5.1 lua51 lua50])
m4_if([$1], [],
[ dnl No version check is needed. Find any Lua interpreter.
AS_IF([test "x$LUA" = 'x'],
[AC_PATH_PROGS([LUA], [_AX_LUA_INTERPRETER_LIST], [:])])
ax_display_LUA='lua'
dnl At least check if this is a Lua interpreter.
AC_MSG_CHECKING([if $LUA is a Lua interpreter])
_AX_LUA_CHK_IS_INTRP([$LUA],
[AC_MSG_RESULT([yes])],
[ AC_MSG_RESULT([no])
AC_MSG_ERROR([not a Lua interpreter])
])
],
[ dnl A version check is needed.
AS_IF([test "x$LUA" != 'x'],
[ dnl Check if this is a Lua interpreter.
AC_MSG_CHECKING([if $LUA is a Lua interpreter])
_AX_LUA_CHK_IS_INTRP([$LUA],
[AC_MSG_RESULT([yes])],
[ AC_MSG_RESULT([no])
AC_MSG_ERROR([not a Lua interpreter])
])
dnl Check the version.
m4_if([$2], [],
[_ax_check_text="whether $LUA version >= $1"],
[_ax_check_text="whether $LUA version >= $1, < $2"])
AC_MSG_CHECKING([$_ax_check_text])
_AX_LUA_CHK_VER([$LUA], [$1], [$2],
[AC_MSG_RESULT([yes])],
[ AC_MSG_RESULT([no])
AC_MSG_ERROR([version is out of range for specified LUA])])
ax_display_LUA=$LUA
],
[ dnl Try each interpreter until we find one that satisfies VERSION.
m4_if([$2], [],
[_ax_check_text="for a Lua interpreter with version >= $1"],
[_ax_check_text="for a Lua interpreter with version >= $1, < $2"])
AC_CACHE_CHECK([$_ax_check_text],
[ax_cv_pathless_LUA],
[ for ax_cv_pathless_LUA in _AX_LUA_INTERPRETER_LIST none; do
test "x$ax_cv_pathless_LUA" = 'xnone' && break
_AX_LUA_CHK_IS_INTRP([$ax_cv_pathless_LUA], [], [continue])
_AX_LUA_CHK_VER([$ax_cv_pathless_LUA], [$1], [$2], [break])
done
])
dnl Set $LUA to the absolute path of $ax_cv_pathless_LUA.
AS_IF([test "x$ax_cv_pathless_LUA" = 'xnone'],
[LUA=':'],
[AC_PATH_PROG([LUA], [$ax_cv_pathless_LUA])])
ax_display_LUA=$ax_cv_pathless_LUA
])
])
AS_IF([test "x$LUA" = 'x:'],
[ dnl Run any user-specified action, or abort.
m4_default([$4], [AC_MSG_ERROR([cannot find suitable Lua interpreter])])
],
[ dnl Query Lua for its version number.
AC_CACHE_CHECK([for $ax_display_LUA version], [ax_cv_lua_version],
[ ax_cv_lua_version=`$LUA -e 'print(_VERSION:match "(%d+%.%d+)")'` ])
AS_IF([test "x$ax_cv_lua_version" = 'x'],
[AC_MSG_ERROR([invalid Lua version number])])
AC_SUBST([LUA_VERSION], [$ax_cv_lua_version])
AC_SUBST([LUA_SHORT_VERSION], [`echo "$LUA_VERSION" | sed 's|\.||'`])
dnl The following check is not supported:
dnl At times (like when building shared libraries) you may want to know
dnl which OS platform Lua thinks this is.
AC_CACHE_CHECK([for $ax_display_LUA platform], [ax_cv_lua_platform],
[ax_cv_lua_platform=`$LUA -e "print('unknown')"`])
AC_SUBST([LUA_PLATFORM], [$ax_cv_lua_platform])
dnl Use the values of $prefix and $exec_prefix for the corresponding
dnl values of LUA_PREFIX and LUA_EXEC_PREFIX. These are made distinct
dnl variables so they can be overridden if need be. However, the general
dnl consensus is that you shouldn't need this ability.
AC_SUBST([LUA_PREFIX], ['${prefix}'])
AC_SUBST([LUA_EXEC_PREFIX], ['${exec_prefix}'])
dnl Lua provides no way to query the script directory, and instead
dnl provides LUA_PATH. However, we should be able to make a safe educated
dnl guess. If the built-in search path contains a directory which is
dnl prefixed by $prefix, then we can store scripts there. The first
dnl matching path will be used.
AC_CACHE_CHECK([for $ax_display_LUA script directory],
[ax_cv_lua_luadir],
[ AS_IF([test "x$prefix" = 'xNONE'],
[ax_lua_prefix=$ac_default_prefix],
[ax_lua_prefix=$prefix])
dnl Initialize to the default path.
ax_cv_lua_luadir="$LUA_PREFIX/share/lua/$LUA_VERSION"
dnl Try to find a path with the prefix.
_AX_LUA_FND_PRFX_PTH([$LUA], [$ax_lua_prefix], [package.path])
AS_IF([test "x$ax_lua_prefixed_path" != 'x'],
[ dnl Fix the prefix.
_ax_strip_prefix=`echo "$ax_lua_prefix" | sed 's|.|.|g'`
ax_cv_lua_luadir=`echo "$ax_lua_prefixed_path" | \
sed "s,^$_ax_strip_prefix,$LUA_PREFIX,"`
])
])
AC_SUBST([luadir], [$ax_cv_lua_luadir])
AC_SUBST([pkgluadir], [\${luadir}/$PACKAGE])
dnl Lua provides no way to query the module directory, and instead
dnl provides LUA_PATH. However, we should be able to make a safe educated
dnl guess. If the built-in search path contains a directory which is
dnl prefixed by $exec_prefix, then we can store modules there. The first
dnl matching path will be used.
AC_CACHE_CHECK([for $ax_display_LUA module directory],
[ax_cv_lua_luaexecdir],
[ AS_IF([test "x$exec_prefix" = 'xNONE'],
[ax_lua_exec_prefix=$ax_lua_prefix],
[ax_lua_exec_prefix=$exec_prefix])
dnl Initialize to the default path.
ax_cv_lua_luaexecdir="$LUA_EXEC_PREFIX/lib/lua/$LUA_VERSION"
dnl Try to find a path with the prefix.
_AX_LUA_FND_PRFX_PTH([$LUA],
[$ax_lua_exec_prefix], [package.cpathd])
AS_IF([test "x$ax_lua_prefixed_path" != 'x'],
[ dnl Fix the prefix.
_ax_strip_prefix=`echo "$ax_lua_exec_prefix" | sed 's|.|.|g'`
ax_cv_lua_luaexecdir=`echo "$ax_lua_prefixed_path" | \
sed "s,^$_ax_strip_prefix,$LUA_EXEC_PREFIX,"`
])
])
AC_SUBST([luaexecdir], [$ax_cv_lua_luaexecdir])
AC_SUBST([pkgluaexecdir], [\${luaexecdir}/$PACKAGE])
dnl Run any user specified action.
$3
])
])
dnl AX_WITH_LUA is now the same thing as AX_PROG_LUA.
AC_DEFUN([AX_WITH_LUA],
[
AC_MSG_WARN([[$0 is deprecated, please use AX_PROG_LUA]])
AX_PROG_LUA
])
dnl =========================================================================
dnl _AX_LUA_CHK_IS_INTRP(PROG, [ACTION-IF-TRUE], [ACTION-IF-FALSE])
dnl =========================================================================
AC_DEFUN([_AX_LUA_CHK_IS_INTRP],
[
dnl Just print _VERSION because all Lua interpreters have this global.
AS_IF([$1 -e "print('Hello ' .. _VERSION .. '!')" &>/dev/null],
[$2], [$3])
])
dnl =========================================================================
dnl _AX_LUA_CHK_VER(PROG, MINIMUM-VERSION, [TOO-BIG-VERSION],
dnl [ACTION-IF-TRUE], [ACTION-IF-FALSE])
dnl =========================================================================
AC_DEFUN([_AX_LUA_CHK_VER],
[
AS_IF([$1 2>/dev/null -e '
function norm (v) i,j=v:match "(%d+)%.(%d+)" return 100 * i + j end
v=norm (_VERSION)
os.exit ((v >= norm ("$2") and ("$3" == "" or v < norm ("$3"))) and 0 or 1)'],
[$4], [$5])
])
dnl =========================================================================
dnl _AX_LUA_FND_PRFX_PTH(PROG, PREFIX, LUA-PATH-VARIABLE)
dnl =========================================================================
AC_DEFUN([_AX_LUA_FND_PRFX_PTH],
[
dnl Invokes the Lua interpreter PROG to print the path variable
dnl LUA-PATH-VARIABLE, usually package.path or package.cpath. Paths are
dnl then matched against PREFIX. The first path to begin with PREFIX is set
dnl to ax_lua_prefixed_path.
ax_lua_prefixed_path=''
_ax_package_paths=`$1 -e 'print($3)' 2>/dev/null | sed 's|;|\n|g'`
dnl Try the paths in order, looking for the prefix.
for _ax_package_path in $_ax_package_paths; do
dnl Copy the path, up to the use of a Lua wildcard.
_ax_path_parts=`echo "$_ax_package_path" | sed 's|/|\n|g'`
_ax_reassembled=''
for _ax_path_part in $_ax_path_parts; do
echo "$_ax_path_part" | grep '\?' >/dev/null && break
_ax_reassembled="$_ax_reassembled/$_ax_path_part"
done
dnl Check the path against the prefix.
_ax_package_path=$_ax_reassembled
if echo "$_ax_package_path" | grep "^$2" >/dev/null; then
dnl Found it.
ax_lua_prefixed_path=$_ax_package_path
break
fi
done
])
dnl =========================================================================
dnl AX_LUA_HEADERS([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
dnl =========================================================================
AC_DEFUN([AX_LUA_HEADERS],
[
dnl Check for LUA_VERSION.
AC_MSG_CHECKING([if LUA_VERSION is defined])
AS_IF([test "x$LUA_VERSION" != 'x'],
[AC_MSG_RESULT([yes])],
[ AC_MSG_RESULT([no])
AC_MSG_ERROR([cannot check Lua headers without knowing LUA_VERSION])
])
dnl Make LUA_INCLUDE a precious variable.
AC_ARG_VAR([LUA_INCLUDE], [The Lua includes, e.g. -I/usr/include/lua5.1])
dnl Some default directories to search.
LUA_SHORT_VERSION=`echo "$LUA_VERSION" | sed 's|\.||'`
m4_define_default([_AX_LUA_INCLUDE_LIST],
[ /usr/include/lua$LUA_VERSION \
/usr/include/lua/$LUA_VERSION \
/usr/include/lua$LUA_SHORT_VERSION \
/usr/local/include/lua$LUA_VERSION \
/usr/local/include/lua-$LUA_VERSION \
/usr/local/include/lua/$LUA_VERSION \
/usr/local/include/lua$LUA_SHORT_VERSION \
])
dnl Try to find the headers.
_ax_lua_saved_cppflags=$CPPFLAGS
CPPFLAGS="$CPPFLAGS $LUA_INCLUDE"
AC_CHECK_HEADERS([lua.h lualib.h lauxlib.h luaconf.h])
CPPFLAGS=$_ax_lua_saved_cppflags
dnl Try some other directories if LUA_INCLUDE was not set.
AS_IF([test "x$LUA_INCLUDE" = 'x' &&
test "x$ac_cv_header_lua_h" != 'xyes'],
[ dnl Try some common include paths.
for _ax_include_path in _AX_LUA_INCLUDE_LIST; do
test ! -d "$_ax_include_path" && continue
AC_MSG_CHECKING([for Lua headers in])
AC_MSG_RESULT([$_ax_include_path])
AS_UNSET([ac_cv_header_lua_h])
AS_UNSET([ac_cv_header_lualib_h])
AS_UNSET([ac_cv_header_lauxlib_h])
AS_UNSET([ac_cv_header_luaconf_h])
_ax_lua_saved_cppflags=$CPPFLAGS
CPPFLAGS="$CPPFLAGS -I$_ax_include_path"
AC_CHECK_HEADERS([lua.h lualib.h lauxlib.h luaconf.h])
CPPFLAGS=$_ax_lua_saved_cppflags
AS_IF([test "x$ac_cv_header_lua_h" = 'xyes'],
[ LUA_INCLUDE="-I$_ax_include_path"
break
])
done
])
AS_IF([test "x$ac_cv_header_lua_h" = 'xyes' && test "x$cross_compiling" != 'xyes'],
[ dnl Make a program to print LUA_VERSION defined in the header.
dnl TODO This probably shouldn't be a runtime test.
AC_CACHE_CHECK([for Lua header version],
[ax_cv_lua_header_version],
[ _ax_lua_saved_cppflags=$CPPFLAGS
CPPFLAGS="$CPPFLAGS $LUA_INCLUDE"
AC_RUN_IFELSE(
[ AC_LANG_SOURCE([[
#include <lua.h>
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char ** argv)
{
if(argc > 1) printf("%s", LUA_VERSION);
exit(EXIT_SUCCESS);
}
]])
],
[ ax_cv_lua_header_version=`./conftest$EXEEXT p | \
sed "s|^Lua \(.*\)|\1|" | \
grep -o "^@<:@0-9@:>@\+\\.@<:@0-9@:>@\+"`
],
[ax_cv_lua_header_version='unknown'])
CPPFLAGS=$_ax_lua_saved_cppflags
])
dnl Compare this to the previously found LUA_VERSION.
AC_MSG_CHECKING([if Lua header version matches $LUA_VERSION])
AS_IF([test "x$ax_cv_lua_header_version" = "x$LUA_VERSION"],
[ AC_MSG_RESULT([yes])
ax_header_version_match='yes'
],
[ AC_MSG_RESULT([no])
ax_header_version_match='no'
])
],
[
ax_header_version_match='yes'
])
dnl Was LUA_INCLUDE specified?
AS_IF([test "x$ax_header_version_match" != 'xyes' &&
test "x$LUA_INCLUDE" != 'x'],
[AC_MSG_ERROR([cannot find headers for specified LUA_INCLUDE])])
dnl Test the final result and run user code.
AS_IF([test "x$ax_header_version_match" = 'xyes'], [$1],
[m4_default([$2], [AC_MSG_ERROR([cannot find Lua includes])])])
])
dnl AX_LUA_HEADERS_VERSION no longer exists, use AX_LUA_HEADERS.
AC_DEFUN([AX_LUA_HEADERS_VERSION],
[
AC_MSG_WARN([[$0 is deprecated, please use AX_LUA_HEADERS]])
])
dnl =========================================================================
dnl AX_LUA_LIBS([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
dnl =========================================================================
AC_DEFUN([AX_LUA_LIBS],
[
dnl TODO Should this macro also check various -L flags?
dnl Check for LUA_VERSION.
AC_MSG_CHECKING([if LUA_VERSION is defined])
AS_IF([test "x$LUA_VERSION" != 'x'],
[AC_MSG_RESULT([yes])],
[ AC_MSG_RESULT([no])
AC_MSG_ERROR([cannot check Lua libs without knowing LUA_VERSION])
])
dnl Make LUA_LIB a precious variable.
AC_ARG_VAR([LUA_LIB], [The Lua library, e.g. -llua5.1])
AS_IF([test "x$LUA_LIB" != 'x'],
[ dnl Check that LUA_LIBS works.
_ax_lua_saved_libs=$LIBS
LIBS="$LIBS $LUA_LIB"
AC_SEARCH_LIBS([lua_load], [],
[_ax_found_lua_libs='yes'],
[_ax_found_lua_libs='no'])
LIBS=$_ax_lua_saved_libs
dnl Check the result.
AS_IF([test "x$_ax_found_lua_libs" != 'xyes'],
[AC_MSG_ERROR([cannot find libs for specified LUA_LIB])])
],
[ dnl First search for extra libs.
_ax_lua_extra_libs=''
_ax_lua_saved_libs=$LIBS
LIBS="$LIBS $LUA_LIB"
AC_SEARCH_LIBS([exp], [m])
AC_SEARCH_LIBS([dlopen], [dl])
LIBS=$_ax_lua_saved_libs
AS_IF([test "x$ac_cv_search_exp" != 'xno' &&
test "x$ac_cv_search_exp" != 'xnone required'],
[_ax_lua_extra_libs="$_ax_lua_extra_libs $ac_cv_search_exp"])
AS_IF([test "x$ac_cv_search_dlopen" != 'xno' &&
test "x$ac_cv_search_dlopen" != 'xnone required'],
[_ax_lua_extra_libs="$_ax_lua_extra_libs $ac_cv_search_dlopen"])
dnl Try to find the Lua libs.
_ax_lua_saved_libs=$LIBS
LIBS="$LIBS $LUA_LIB"
AC_SEARCH_LIBS([lua_load],
[ lua$LUA_VERSION \
lua$LUA_SHORT_VERSION \
lua-$LUA_VERSION \
lua-$LUA_SHORT_VERSION \
lua],
[_ax_found_lua_libs='yes'],
[_ax_found_lua_libs='no'],
[$_ax_lua_extra_libs])
LIBS=$_ax_lua_saved_libs
AS_IF([test "x$ac_cv_search_lua_load" != 'xno' &&
test "x$ac_cv_search_lua_load" != 'xnone required'],
[LUA_LIB="$ac_cv_search_lua_load $_ax_lua_extra_libs"])
])
dnl Test the result and run user code.
AS_IF([test "x$_ax_found_lua_libs" = 'xyes'], [$1],
[m4_default([$2], [AC_MSG_ERROR([cannot find Lua libs])])])
])
dnl =========================================================================
dnl AX_LUA_READLINE([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
dnl =========================================================================
AC_DEFUN([AX_LUA_READLINE],
[
AX_LIB_READLINE
AS_IF([test "x$ac_cv_header_readline_readline_h" != 'x' &&
test "x$ac_cv_header_readline_history_h" != 'x'],
[ LUA_LIBS_CFLAGS="-DLUA_USE_READLINE $LUA_LIBS_CFLAGS"
$1
],
[$2])
])

View File

@ -130,6 +130,19 @@ ucl_emitter_print_key (bool print_key, struct ucl_emitter_context *ctx,
func->ucl_emitter_append_character (' ', 1, func->ud);
}
}
else if (ctx->id == UCL_EMIT_YAML) {
if (obj->keylen > 0 && (obj->flags & UCL_OBJECT_NEED_KEY_ESCAPE)) {
ucl_elt_string_write_json (obj->key, obj->keylen, ctx);
}
else if (obj->keylen > 0) {
func->ucl_emitter_append_len (obj->key, obj->keylen, func->ud);
}
else {
func->ucl_emitter_append_len ("null", 4, func->ud);
}
func->ucl_emitter_append_len (": ", 2, func->ud);
}
else {
if (obj->keylen > 0) {
ucl_elt_string_write_json (obj->key, obj->keylen, ctx);
@ -182,7 +195,7 @@ ucl_emitter_common_end_object (struct ucl_emitter_context *ctx,
const struct ucl_emitter_functions *func = ctx->func;
if (UCL_EMIT_IDENT_TOP_OBJ(ctx, obj)) {
ctx->ident --;
ctx->indent --;
if (compact) {
func->ucl_emitter_append_character ('}', 1, func->ud);
}
@ -191,7 +204,7 @@ ucl_emitter_common_end_object (struct ucl_emitter_context *ctx,
/* newline is already added for this format */
func->ucl_emitter_append_character ('\n', 1, func->ud);
}
ucl_add_tabs (func, ctx->ident, compact);
ucl_add_tabs (func, ctx->indent, compact);
func->ucl_emitter_append_character ('}', 1, func->ud);
}
}
@ -210,7 +223,7 @@ ucl_emitter_common_end_array (struct ucl_emitter_context *ctx,
{
const struct ucl_emitter_functions *func = ctx->func;
ctx->ident --;
ctx->indent --;
if (compact) {
func->ucl_emitter_append_character (']', 1, func->ud);
}
@ -219,7 +232,7 @@ ucl_emitter_common_end_array (struct ucl_emitter_context *ctx,
/* newline is already added for this format */
func->ucl_emitter_append_character ('\n', 1, func->ud);
}
ucl_add_tabs (func, ctx->ident, compact);
ucl_add_tabs (func, ctx->indent, compact);
func->ucl_emitter_append_character (']', 1, func->ud);
}
@ -249,7 +262,7 @@ ucl_emitter_common_start_array (struct ucl_emitter_context *ctx,
func->ucl_emitter_append_len ("[\n", 2, func->ud);
}
ctx->ident ++;
ctx->indent ++;
if (obj->type == UCL_ARRAY) {
/* explicit array */
@ -294,7 +307,7 @@ ucl_emitter_common_start_object (struct ucl_emitter_context *ctx,
else {
func->ucl_emitter_append_len ("{\n", 2, func->ud);
}
ctx->ident ++;
ctx->indent ++;
}
while ((cur = ucl_hash_iterate (obj->value.ov, &it))) {
@ -315,7 +328,7 @@ ucl_emitter_common_start_object (struct ucl_emitter_context *ctx,
func->ucl_emitter_append_len (",\n", 2, func->ud);
}
}
ucl_add_tabs (func, ctx->ident, compact);
ucl_add_tabs (func, ctx->indent, compact);
ucl_emitter_common_start_array (ctx, cur, true, compact);
ucl_emitter_common_end_array (ctx, cur, compact);
}
@ -342,17 +355,23 @@ ucl_emitter_common_elt (struct ucl_emitter_context *ctx,
{
const struct ucl_emitter_functions *func = ctx->func;
bool flag;
struct ucl_object_userdata *ud;
const char *ud_out = "";
if (ctx->id != UCL_EMIT_CONFIG && !first) {
if (compact) {
func->ucl_emitter_append_character (',', 1, func->ud);
}
else {
func->ucl_emitter_append_len (",\n", 2, func->ud);
if (ctx->id == UCL_EMIT_YAML && ctx->indent == 0) {
func->ucl_emitter_append_len ("\n", 1, func->ud);
} else {
func->ucl_emitter_append_len (",\n", 2, func->ud);
}
}
}
ucl_add_tabs (func, ctx->ident, compact);
ucl_add_tabs (func, ctx->indent, compact);
switch (obj->type) {
case UCL_INT:
@ -379,7 +398,12 @@ ucl_emitter_common_elt (struct ucl_emitter_context *ctx,
break;
case UCL_STRING:
ucl_emitter_print_key (print_key, ctx, obj, compact);
ucl_elt_string_write_json (obj->value.sv, obj->len, ctx);
if (ctx->id == UCL_EMIT_CONFIG && ucl_maybe_long_string (obj)) {
ucl_elt_string_write_multiline (obj->value.sv, obj->len, ctx);
}
else {
ucl_elt_string_write_json (obj->value.sv, obj->len, ctx);
}
ucl_emitter_finish_object (ctx, obj, compact, !print_key);
break;
case UCL_NULL:
@ -396,6 +420,16 @@ ucl_emitter_common_elt (struct ucl_emitter_context *ctx,
ucl_emitter_common_end_array (ctx, obj, compact);
break;
case UCL_USERDATA:
ud = (struct ucl_object_userdata *)obj;
ucl_emitter_print_key (print_key, ctx, obj, compact);
if (ud->emitter) {
ud_out = ud->emitter (obj->value.ud);
if (ud_out == NULL) {
ud_out = "null";
}
}
ucl_elt_string_write_json (ud_out, strlen (ud_out), ctx);
ucl_emitter_finish_object (ctx, obj, compact, !print_key);
break;
}
}
@ -425,10 +459,10 @@ ucl_emitter_common_elt (struct ucl_emitter_context *ctx,
ucl_emitter_common_end_array (ctx, obj, (compact)); \
}
UCL_EMIT_TYPE_IMPL(json, false);
UCL_EMIT_TYPE_IMPL(json_compact, true);
UCL_EMIT_TYPE_IMPL(config, false);
UCL_EMIT_TYPE_IMPL(yaml, false);
UCL_EMIT_TYPE_IMPL(json, false)
UCL_EMIT_TYPE_IMPL(json_compact, true)
UCL_EMIT_TYPE_IMPL(config, false)
UCL_EMIT_TYPE_IMPL(yaml, false)
unsigned char *
ucl_object_emit (const ucl_object_t *obj, enum ucl_emitter emit_type)
@ -461,7 +495,7 @@ ucl_object_emit_full (const ucl_object_t *obj, enum ucl_emitter emit_type,
if (ctx != NULL) {
memcpy (&my_ctx, ctx, sizeof (my_ctx));
my_ctx.func = emitter;
my_ctx.ident = 0;
my_ctx.indent = 0;
my_ctx.top = obj;
my_ctx.ops->ucl_emitter_write_elt (&my_ctx, obj, true, false);

View File

@ -108,9 +108,8 @@ ucl_object_emit_streamline_start_container (struct ucl_emitter_context *ctx,
st->is_array = false;
sctx->ops->ucl_emitter_start_object (ctx, obj, print_key);
}
LL_PREPEND (sctx->containers, st);
}
LL_PREPEND (sctx->containers, st);
}
void

View File

@ -93,9 +93,7 @@ ucl_elt_string_write_json (const char *str, size_t size,
size_t len = 0;
const struct ucl_emitter_functions *func = ctx->func;
if (ctx->id != UCL_EMIT_YAML) {
func->ucl_emitter_append_character ('"', 1, func->ud);
}
func->ucl_emitter_append_character ('"', 1, func->ud);
while (size) {
if (ucl_test_character (*p, UCL_CHARACTER_JSON_UNSAFE)) {
@ -137,9 +135,18 @@ ucl_elt_string_write_json (const char *str, size_t size,
if (len > 0) {
func->ucl_emitter_append_len (c, len, func->ud);
}
if (ctx->id != UCL_EMIT_YAML) {
func->ucl_emitter_append_character ('"', 1, 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)
{
const struct ucl_emitter_functions *func = ctx->func;
func->ucl_emitter_append_len ("<<EOD\n", sizeof ("<<EOD\n") - 1, func->ud);
func->ucl_emitter_append_len (str, size, func->ud);
func->ucl_emitter_append_len ("\nEOD", sizeof ("\nEOD") - 1, func->ud);
}
/*
@ -154,7 +161,7 @@ ucl_utstring_append_character (unsigned char c, size_t len, void *ud)
utstring_append_c (buf, c);
}
else {
utstring_reserve (buf, len);
utstring_reserve (buf, len + 1);
memset (&buf->d[buf->i], c, len);
buf->i += len;
buf->d[buf->i] = '\0';
@ -267,19 +274,23 @@ ucl_fd_append_character (unsigned char c, size_t len, void *ud)
unsigned char *buf;
if (len == 1) {
write (fd, &c, 1);
return write (fd, &c, 1);
}
else {
buf = malloc (len);
if (buf == NULL) {
/* Fallback */
while (len --) {
write (fd, &c, 1);
if (write (fd, &c, 1) == -1) {
return -1;
}
}
}
else {
memset (buf, c, len);
write (fd, buf, len);
if (write (fd, buf, len) == -1) {
return -1;
}
free (buf);
}
}
@ -292,9 +303,7 @@ ucl_fd_append_len (const unsigned char *str, size_t len, void *ud)
{
int fd = *(int *)ud;
write (fd, str, len);
return 0;
return write (fd, str, len);
}
static int
@ -304,9 +313,7 @@ ucl_fd_append_int (int64_t val, void *ud)
char intbuf[64];
snprintf (intbuf, sizeof (intbuf), "%jd", (intmax_t)val);
write (fd, intbuf, strlen (intbuf));
return 0;
return write (fd, intbuf, strlen (intbuf));
}
static int
@ -327,9 +334,7 @@ ucl_fd_append_double (double val, void *ud)
snprintf (nbuf, sizeof (nbuf), "%lf", val);
}
write (fd, nbuf, strlen (nbuf));
return 0;
return write (fd, nbuf, strlen (nbuf));
}
struct ucl_emitter_functions*
@ -464,3 +469,18 @@ ucl_object_emit_single_json (const ucl_object_t *obj)
return res;
}
#define LONG_STRING_LIMIT 80
bool
ucl_maybe_long_string (const ucl_object_t *obj)
{
if (obj->len > LONG_STRING_LIMIT || (obj->flags & UCL_OBJECT_MULTILINE)) {
/* String is long enough, so search for newline characters in it */
if (memchr (obj->value.sv, '\n', obj->len) != NULL) {
return true;
}
}
return false;
}

View File

@ -66,6 +66,20 @@ ucl_hash_insert (ucl_hash_t* hashlin, const ucl_object_t *obj,
HASH_ADD_KEYPTR (hh, hashlin->buckets, key, keylen, node);
}
void ucl_hash_replace (ucl_hash_t* hashlin, const ucl_object_t *old,
const ucl_object_t *new)
{
ucl_hash_node_t *node;
HASH_FIND (hh, hashlin->buckets, old->key, old->keylen, node);
if (node != NULL) {
/* Direct replacement */
node->data = new;
node->hh.key = new->key;
node->hh.keylen = new->keylen;
}
}
const void*
ucl_hash_iterate (ucl_hash_t *hashlin, ucl_hash_iter_t *iter)
{
@ -122,5 +136,6 @@ ucl_hash_delete (ucl_hash_t* hashlin, const ucl_object_t *obj)
if (found) {
HASH_DELETE (hh, hashlin->buckets, found);
UCL_FREE (sizeof (ucl_hash_node_t), found);
}
}

View File

@ -65,6 +65,12 @@ void ucl_hash_destroy (ucl_hash_t* hashlin, ucl_hash_free_func *func);
void ucl_hash_insert (ucl_hash_t* hashlin, const ucl_object_t *obj, const char *key,
unsigned keylen);
/**
* Replace element in the hash
*/
void ucl_hash_replace (ucl_hash_t* hashlin, const ucl_object_t *old,
const ucl_object_t *new);
/**
* Delete an element from the the hashtable.
*/

View File

@ -163,6 +163,7 @@ struct ucl_chunk {
size_t remain;
unsigned int line;
unsigned int column;
unsigned priority;
struct ucl_chunk *next;
};
@ -182,7 +183,7 @@ struct ucl_variable {
char *value;
size_t var_len;
size_t value_len;
struct ucl_variable *next;
struct ucl_variable *prev, *next;
};
struct ucl_parser {
@ -192,6 +193,7 @@ struct ucl_parser {
int flags;
ucl_object_t *top_obj;
ucl_object_t *cur_obj;
char *cur_file;
struct ucl_macro *macroes;
struct ucl_stack *stack;
struct ucl_chunk *chunks;
@ -202,6 +204,12 @@ struct ucl_parser {
UT_string *err;
};
struct ucl_object_userdata {
ucl_object_t obj;
ucl_userdata_dtor dtor;
ucl_userdata_emitter emitter;
};
/**
* Unescape json string inplace
* @param str
@ -216,9 +224,11 @@ size_t ucl_unescape_json_string (char *str, size_t len);
* @param err error ptr
* @return
*/
bool ucl_include_handler (const unsigned char *data, size_t len, void* ud);
bool ucl_include_handler (const unsigned char *data, size_t len,
const ucl_object_t *args, void* ud);
bool ucl_try_include_handler (const unsigned char *data, size_t len, void* ud);
bool ucl_try_include_handler (const unsigned char *data, size_t len,
const ucl_object_t *args, void* ud);
/**
* Handle includes macro
@ -228,7 +238,8 @@ bool ucl_try_include_handler (const unsigned char *data, size_t len, void* ud);
* @param err error ptr
* @return
*/
bool ucl_includes_handler (const unsigned char *data, size_t len, void* ud);
bool ucl_includes_handler (const unsigned char *data, size_t len,
const ucl_object_t *args, void* ud);
size_t ucl_strlcpy (char *dst, const char *src, size_t siz);
size_t ucl_strlcpy_unsafe (char *dst, const char *src, size_t siz);
@ -264,7 +275,7 @@ ucl_create_err (UT_string **err, const char *fmt, ...)
static inline bool
ucl_maybe_parse_boolean (ucl_object_t *obj, const unsigned char *start, size_t len)
{
const unsigned char *p = start;
const char *p = (const char *)start;
bool ret = false, val = false;
if (len == 5) {
@ -351,13 +362,22 @@ const struct ucl_emitter_context *
ucl_emit_get_standard_context (enum ucl_emitter emit_type);
/**
* Serialise string
* Serialize string as JSON string
* @param str string to emit
* @param buf target buffer
*/
void ucl_elt_string_write_json (const char *str, size_t size,
struct ucl_emitter_context *ctx);
/**
* Write multiline string using `EOD` as string terminator
* @param str
* @param size
* @param ctx
*/
void ucl_elt_string_write_multiline (const char *str, size_t size,
struct ucl_emitter_context *ctx);
/**
* Emit a single object to string
* @param obj
@ -365,4 +385,12 @@ void ucl_elt_string_write_json (const char *str, size_t size,
*/
unsigned char * ucl_object_emit_single_json (const ucl_object_t *obj);
/**
* Check whether a specified string is long and should be likely printed in
* multiline mode
* @param obj
* @return
*/
bool ucl_maybe_long_string (const ucl_object_t *obj);
#endif /* UCL_INTERNAL_H_ */

View File

@ -26,8 +26,8 @@
#include "ucl_chartable.h"
/**
* @file rcl_parser.c
* The implementation of rcl parser
* @file ucl_parser.c
* The implementation of ucl parser
*/
struct ucl_parser_saved_state {
@ -56,20 +56,33 @@ struct ucl_parser_saved_state {
} while (0)
static inline void
ucl_set_err (struct ucl_chunk *chunk, int code, const char *str, UT_string **err)
ucl_set_err (struct ucl_parser *parser, int code, const char *str, UT_string **err)
{
if (chunk->pos < chunk->end) {
if (isgraph (*chunk->pos)) {
ucl_create_err (err, "error on line %d at column %d: '%s', character: '%c'",
chunk->line, chunk->column, str, *chunk->pos);
}
else {
ucl_create_err (err, "error on line %d at column %d: '%s', character: '0x%02x'",
chunk->line, chunk->column, str, (int)*chunk->pos);
}
const char *fmt_string, *filename;
struct ucl_chunk *chunk = parser->chunks;
if (parser->cur_file) {
filename = parser->cur_file;
}
else {
ucl_create_err (err, "error at the end of chunk: %s", str);
filename = "<unknown>";
}
if (chunk->pos < chunk->end) {
if (isgraph (*chunk->pos)) {
fmt_string = "error while parsing %s: "
"line: %d, column: %d - '%s', character: '%c'";
}
else {
fmt_string = "error while parsing %s: "
"line: %d, column: %d - '%s', character: '0x%02x'";
}
ucl_create_err (err, fmt_string,
filename, chunk->line, chunk->column,
str, *chunk->pos);
}
else {
ucl_create_err (err, "error while parsing %s: at the end of chunk: %s",
filename, str);
}
}
@ -84,11 +97,12 @@ ucl_skip_comments (struct ucl_parser *parser)
struct ucl_chunk *chunk = parser->chunks;
const unsigned char *p;
int comments_nested = 0;
bool quoted = false;
p = chunk->pos;
start:
if (*p == '#') {
if (chunk->remain > 0 && *p == '#') {
if (parser->state != UCL_STATE_SCOMMENT &&
parser->state != UCL_STATE_MCOMMENT) {
while (p < chunk->end) {
@ -100,34 +114,41 @@ ucl_skip_comments (struct ucl_parser *parser)
}
}
}
else if (*p == '/' && chunk->remain >= 2) {
else if (chunk->remain >= 2 && *p == '/') {
if (p[1] == '*') {
ucl_chunk_skipc (chunk, p);
comments_nested ++;
ucl_chunk_skipc (chunk, p);
while (p < chunk->end) {
if (*p == '*') {
ucl_chunk_skipc (chunk, p);
if (*p == '/') {
comments_nested --;
if (comments_nested == 0) {
ucl_chunk_skipc (chunk, p);
goto start;
}
}
ucl_chunk_skipc (chunk, p);
if (*p == '"' && *(p - 1) != '\\') {
quoted = !quoted;
}
else if (p[0] == '/' && chunk->remain >= 2 && p[1] == '*') {
comments_nested ++;
ucl_chunk_skipc (chunk, p);
ucl_chunk_skipc (chunk, p);
continue;
if (!quoted) {
if (*p == '*') {
ucl_chunk_skipc (chunk, p);
if (*p == '/') {
comments_nested --;
if (comments_nested == 0) {
ucl_chunk_skipc (chunk, p);
goto start;
}
}
ucl_chunk_skipc (chunk, p);
}
else if (p[0] == '/' && chunk->remain >= 2 && p[1] == '*') {
comments_nested ++;
ucl_chunk_skipc (chunk, p);
ucl_chunk_skipc (chunk, p);
continue;
}
}
ucl_chunk_skipc (chunk, p);
}
if (comments_nested != 0) {
ucl_set_err (chunk, UCL_ENESTED, "unfinished multiline comment", &parser->err);
ucl_set_err (parser, UCL_ENESTED,
"unfinished multiline comment", &parser->err);
return false;
}
}
@ -492,7 +513,8 @@ ucl_copy_or_store_ptr (struct ucl_parser *parser,
/* Copy string */
*dst = UCL_ALLOC (in_len + 1);
if (*dst == NULL) {
ucl_set_err (parser->chunks, 0, "cannot allocate memory for a string", &parser->err);
ucl_set_err (parser, 0, "cannot allocate memory for a string",
&parser->err);
return false;
}
if (need_lowercase) {
@ -514,6 +536,10 @@ ucl_copy_or_store_ptr (struct ucl_parser *parser,
*dst = tmp;
ret = tret;
}
else {
/* Free unexpanded value */
UCL_FREE (in_len + 1, tmp);
}
}
*dst_const = *dst;
}
@ -539,7 +565,7 @@ ucl_add_parser_stack (ucl_object_t *obj, struct ucl_parser *parser, bool is_arra
if (!is_array) {
if (obj == NULL) {
obj = ucl_object_typed_new (UCL_OBJECT);
obj = ucl_object_new_full (UCL_OBJECT, parser->chunks->priority);
}
else {
obj->type = UCL_OBJECT;
@ -549,7 +575,7 @@ ucl_add_parser_stack (ucl_object_t *obj, struct ucl_parser *parser, bool is_arra
}
else {
if (obj == NULL) {
obj = ucl_object_typed_new (UCL_ARRAY);
obj = ucl_object_new_full (UCL_ARRAY, parser->chunks->priority);
}
else {
obj->type = UCL_ARRAY;
@ -559,7 +585,9 @@ ucl_add_parser_stack (ucl_object_t *obj, struct ucl_parser *parser, bool is_arra
st = UCL_ALLOC (sizeof (struct ucl_stack));
if (st == NULL) {
ucl_set_err (parser->chunks, 0, "cannot allocate memory for an object", &parser->err);
ucl_set_err (parser, 0, "cannot allocate memory for an object",
&parser->err);
ucl_object_unref (obj);
return NULL;
}
st->obj = obj;
@ -676,8 +704,7 @@ ucl_maybe_parse_number (ucl_object_t *obj,
}
/* Now check endptr */
if (endptr == NULL || ucl_lex_is_atom_end (*endptr) || *endptr == '\0' ||
ucl_test_character (*endptr, UCL_CHARACTER_WHITESPACE_UNSAFE)) {
if (endptr == NULL || ucl_lex_is_atom_end (*endptr) || *endptr == '\0') {
p = endptr;
goto set_obj;
}
@ -788,8 +815,21 @@ ucl_maybe_parse_number (ucl_object_t *obj,
goto set_obj;
}
break;
case '\t':
case ' ':
while (p < end && ucl_test_character(*p, UCL_CHARACTER_WHITESPACE)) {
p++;
}
if (ucl_lex_is_atom_end(*p))
goto set_obj;
break;
}
}
else if (endptr == end) {
/* Just a number at the end of chunk */
p = endptr;
goto set_obj;
}
*pos = c;
return EINVAL;
@ -835,7 +875,7 @@ ucl_lex_number (struct ucl_parser *parser,
return true;
}
else if (ret == ERANGE) {
ucl_set_err (chunk, ERANGE, "numeric value out of range", &parser->err);
ucl_set_err (parser, ERANGE, "numeric value out of range", &parser->err);
}
return false;
@ -860,10 +900,12 @@ ucl_lex_json_string (struct ucl_parser *parser,
if (c < 0x1F) {
/* Unmasked control character */
if (c == '\n') {
ucl_set_err (chunk, UCL_ESYNTAX, "unexpected newline", &parser->err);
ucl_set_err (parser, UCL_ESYNTAX, "unexpected newline",
&parser->err);
}
else {
ucl_set_err (chunk, UCL_ESYNTAX, "unexpected control character", &parser->err);
ucl_set_err (parser, UCL_ESYNTAX, "unexpected control character",
&parser->err);
}
return false;
}
@ -871,7 +913,8 @@ ucl_lex_json_string (struct ucl_parser *parser,
ucl_chunk_skipc (chunk, p);
c = *p;
if (p >= chunk->end) {
ucl_set_err (chunk, UCL_ESYNTAX, "unfinished escape character", &parser->err);
ucl_set_err (parser, UCL_ESYNTAX, "unfinished escape character",
&parser->err);
return false;
}
else if (ucl_test_character (c, UCL_CHARACTER_ESCAPE)) {
@ -879,13 +922,15 @@ ucl_lex_json_string (struct ucl_parser *parser,
ucl_chunk_skipc (chunk, p);
for (i = 0; i < 4 && p < chunk->end; i ++) {
if (!isxdigit (*p)) {
ucl_set_err (chunk, UCL_ESYNTAX, "invalid utf escape", &parser->err);
ucl_set_err (parser, UCL_ESYNTAX, "invalid utf escape",
&parser->err);
return false;
}
ucl_chunk_skipc (chunk, p);
}
if (p >= chunk->end) {
ucl_set_err (chunk, UCL_ESYNTAX, "unfinished escape character", &parser->err);
ucl_set_err (parser, UCL_ESYNTAX, "unfinished escape character",
&parser->err);
return false;
}
}
@ -910,10 +955,42 @@ ucl_lex_json_string (struct ucl_parser *parser,
ucl_chunk_skipc (chunk, p);
}
ucl_set_err (chunk, UCL_ESYNTAX, "no quote at the end of json string", &parser->err);
ucl_set_err (parser, UCL_ESYNTAX, "no quote at the end of json string",
&parser->err);
return false;
}
static void
ucl_parser_append_elt (struct ucl_parser *parser, ucl_hash_t *cont,
ucl_object_t *top,
ucl_object_t *elt)
{
ucl_object_t *nobj;
if ((parser->flags & UCL_PARSER_NO_IMPLICIT_ARRAYS) == 0) {
/* Implicit array */
top->flags |= UCL_OBJECT_MULTIVALUE;
DL_APPEND (top, elt);
}
else {
if ((top->flags & UCL_OBJECT_MULTIVALUE) != 0) {
/* Just add to the explicit array */
DL_APPEND (top->value.av, elt);
}
else {
/* Convert to an array */
ucl_hash_delete (cont, top);
nobj = ucl_object_typed_new (UCL_ARRAY);
nobj->key = top->key;
nobj->keylen = top->keylen;
nobj->flags |= UCL_OBJECT_MULTIVALUE;
DL_APPEND (nobj->value.av, top);
DL_APPEND (nobj->value.av, elt);
ucl_hash_insert (cont, nobj, nobj->key, nobj->keylen);
}
}
}
/**
* Parse a key in an object
* @param parser
@ -981,7 +1058,8 @@ ucl_parse_key (struct ucl_parser *parser, struct ucl_chunk *chunk, bool *next_ke
}
else {
/* Invalid identifier */
ucl_set_err (chunk, UCL_ESYNTAX, "key must begin with a letter", &parser->err);
ucl_set_err (parser, UCL_ESYNTAX, "key must begin with a letter",
&parser->err);
return false;
}
}
@ -997,7 +1075,8 @@ ucl_parse_key (struct ucl_parser *parser, struct ucl_chunk *chunk, bool *next_ke
break;
}
else {
ucl_set_err (chunk, UCL_ESYNTAX, "invalid character in a key", &parser->err);
ucl_set_err (parser, UCL_ESYNTAX, "invalid character in a key",
&parser->err);
return false;
}
}
@ -1015,7 +1094,7 @@ ucl_parse_key (struct ucl_parser *parser, struct ucl_chunk *chunk, bool *next_ke
}
if (p >= chunk->end && got_content) {
ucl_set_err (chunk, UCL_ESYNTAX, "unfinished key", &parser->err);
ucl_set_err (parser, UCL_ESYNTAX, "unfinished key", &parser->err);
return false;
}
else if (!got_content) {
@ -1033,7 +1112,8 @@ ucl_parse_key (struct ucl_parser *parser, struct ucl_chunk *chunk, bool *next_ke
got_eq = true;
}
else {
ucl_set_err (chunk, UCL_ESYNTAX, "unexpected '=' character", &parser->err);
ucl_set_err (parser, UCL_ESYNTAX, "unexpected '=' character",
&parser->err);
return false;
}
}
@ -1043,7 +1123,8 @@ ucl_parse_key (struct ucl_parser *parser, struct ucl_chunk *chunk, bool *next_ke
got_semicolon = true;
}
else {
ucl_set_err (chunk, UCL_ESYNTAX, "unexpected ':' character", &parser->err);
ucl_set_err (parser, UCL_ESYNTAX, "unexpected ':' character",
&parser->err);
return false;
}
}
@ -1061,7 +1142,7 @@ ucl_parse_key (struct ucl_parser *parser, struct ucl_chunk *chunk, bool *next_ke
}
if (p >= chunk->end && got_content) {
ucl_set_err (chunk, UCL_ESYNTAX, "unfinished key", &parser->err);
ucl_set_err (parser, UCL_ESYNTAX, "unfinished key", &parser->err);
return false;
}
@ -1096,7 +1177,7 @@ ucl_parse_key (struct ucl_parser *parser, struct ucl_chunk *chunk, bool *next_ke
}
/* Create a new object */
nobj = ucl_object_new ();
nobj = ucl_object_new_full (UCL_NULL, parser->chunks->priority);
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);
if (keylen == -1) {
@ -1104,7 +1185,7 @@ ucl_parse_key (struct ucl_parser *parser, struct ucl_chunk *chunk, bool *next_ke
return false;
}
else if (keylen == 0) {
ucl_set_err (chunk, UCL_ESYNTAX, "empty keys are not allowed", &parser->err);
ucl_set_err (parser, UCL_ESYNTAX, "empty keys are not allowed", &parser->err);
ucl_object_unref (nobj);
return false;
}
@ -1120,7 +1201,27 @@ ucl_parse_key (struct ucl_parser *parser, struct ucl_chunk *chunk, bool *next_ke
parser->stack->obj->len ++;
}
else {
DL_APPEND (tobj, nobj);
/*
* The logic here is the following:
*
* - if we have two objects with the same priority, then we form an
* implicit or explicit array
* - if a new object has bigger priority, then we overwrite an old one
* - if a new object has lower priority, then we ignore it
*/
unsigned priold = ucl_object_get_priority (tobj),
prinew = ucl_object_get_priority (nobj);
if (priold == prinew) {
ucl_parser_append_elt (parser, container, tobj, nobj);
}
else if (priold > prinew) {
ucl_object_unref (nobj);
return true;
}
else {
ucl_hash_replace (container, tobj, nobj);
ucl_object_unref (tobj);
}
}
if (ucl_escape) {
@ -1197,11 +1298,6 @@ ucl_parse_string_value (struct ucl_parser *parser,
ucl_chunk_skipc (chunk, p);
}
if (p >= chunk->end) {
ucl_set_err (chunk, UCL_ESYNTAX, "unfinished value", &parser->err);
return false;
}
return true;
}
@ -1219,7 +1315,7 @@ ucl_parse_multiline_string (struct ucl_parser *parser,
int term_len, unsigned char const **beg,
bool *var_expand)
{
const unsigned char *p, *c;
const unsigned char *p, *c, *tend;
bool newline = false;
int len = 0;
@ -1232,7 +1328,13 @@ ucl_parse_multiline_string (struct ucl_parser *parser,
if (chunk->end - p < term_len) {
return 0;
}
else if (memcmp (p, term, term_len) == 0 && (p[term_len] == '\n' || p[term_len] == '\r')) {
else if (memcmp (p, term, term_len) == 0) {
tend = p + term_len;
if (*tend != '\n' && *tend != ';' && *tend != ',') {
/* Incomplete terminator */
ucl_chunk_skipc (chunk, p);
continue;
}
len = p - c;
chunk->remain -= term_len;
chunk->pos = p + term_len;
@ -1263,7 +1365,7 @@ ucl_get_value_object (struct ucl_parser *parser)
if (parser->stack->obj->type == UCL_ARRAY) {
/* Object must be allocated */
obj = ucl_object_new ();
obj = ucl_object_new_full (UCL_NULL, parser->chunks->priority);
t = parser->stack->obj->value.av;
DL_APPEND (t, obj);
parser->cur_obj = obj;
@ -1378,7 +1480,8 @@ ucl_parse_value (struct ucl_parser *parser, struct ucl_chunk *chunk)
chunk->line ++;
if ((str_len = ucl_parse_multiline_string (parser, chunk, c,
p - c, &c, &var_expand)) == 0) {
ucl_set_err (chunk, UCL_ESYNTAX, "unterminated multiline value", &parser->err);
ucl_set_err (parser, UCL_ESYNTAX,
"unterminated multiline value", &parser->err);
return false;
}
obj->type = UCL_STRING;
@ -1423,7 +1526,8 @@ ucl_parse_value (struct ucl_parser *parser, struct ucl_chunk *chunk)
}
str_len = chunk->pos - c - stripped_spaces;
if (str_len <= 0) {
ucl_set_err (chunk, 0, "string value must not be empty", &parser->err);
ucl_set_err (parser, 0, "string value must not be empty",
&parser->err);
return false;
}
else if (str_len == 4 && memcmp (c, "null", 4) == 0) {
@ -1482,7 +1586,9 @@ ucl_parse_after_value (struct ucl_parser *parser, struct ucl_chunk *chunk)
else if (ucl_test_character (*p, UCL_CHARACTER_VALUE_END)) {
if (*p == '}' || *p == ']') {
if (parser->stack == NULL) {
ucl_set_err (chunk, UCL_ESYNTAX, "end of array or object detected without corresponding start", &parser->err);
ucl_set_err (parser, UCL_ESYNTAX,
"end of array or object detected without corresponding start",
&parser->err);
return false;
}
if ((*p == '}' && parser->stack->obj->type == UCL_OBJECT) ||
@ -1503,7 +1609,9 @@ ucl_parse_after_value (struct ucl_parser *parser, struct ucl_chunk *chunk)
}
}
else {
ucl_set_err (chunk, UCL_ESYNTAX, "unexpected terminating symbol detected", &parser->err);
ucl_set_err (parser, UCL_ESYNTAX,
"unexpected terminating symbol detected",
&parser->err);
return false;
}
@ -1525,7 +1633,8 @@ ucl_parse_after_value (struct ucl_parser *parser, struct ucl_chunk *chunk)
else {
/* Anything else */
if (!got_sep) {
ucl_set_err (chunk, UCL_ESYNTAX, "delimiter is missing", &parser->err);
ucl_set_err (parser, UCL_ESYNTAX, "delimiter is missing",
&parser->err);
return false;
}
return true;
@ -1612,6 +1721,120 @@ ucl_parse_macro_value (struct ucl_parser *parser,
return true;
}
/**
* Parse macro arguments as UCL object
* @param parser parser structure
* @param chunk the current data chunk
* @return
*/
static ucl_object_t *
ucl_parse_macro_arguments (struct ucl_parser *parser,
struct ucl_chunk *chunk)
{
ucl_object_t *res = NULL;
struct ucl_parser *params_parser;
int obraces = 1, ebraces = 0, state = 0;
const unsigned char *p, *c;
size_t args_len = 0;
struct ucl_parser_saved_state saved;
saved.column = chunk->column;
saved.line = chunk->line;
saved.pos = chunk->pos;
saved.remain = chunk->remain;
p = chunk->pos;
if (*p != '(' || chunk->remain < 2) {
return NULL;
}
/* Set begin and start */
ucl_chunk_skipc (chunk, p);
c = p;
while ((p) < (chunk)->end) {
switch (state) {
case 0:
/* Parse symbols and check for '(', ')' and '"' */
if (*p == '(') {
obraces ++;
}
else if (*p == ')') {
ebraces ++;
}
else if (*p == '"') {
state = 1;
}
/* Check pairing */
if (obraces == ebraces) {
state = 99;
}
else {
args_len ++;
}
/* Check overflow */
if (chunk->remain == 0) {
goto restore_chunk;
}
ucl_chunk_skipc (chunk, p);
break;
case 1:
/* We have quote character, so skip all but quotes */
if (*p == '"' && *(p - 1) != '\\') {
state = 0;
}
if (chunk->remain == 0) {
goto restore_chunk;
}
ucl_chunk_skipc (chunk, p);
break;
case 99:
/*
* We have read the full body of arguments, so we need to parse and set
* object from that
*/
params_parser = ucl_parser_new (parser->flags);
if (!ucl_parser_add_chunk (params_parser, c, args_len)) {
ucl_set_err (parser, UCL_ESYNTAX, "macro arguments parsing error",
&parser->err);
}
else {
res = ucl_parser_get_object (params_parser);
}
ucl_parser_free (params_parser);
return res;
break;
}
}
return res;
restore_chunk:
chunk->column = saved.column;
chunk->line = saved.line;
chunk->pos = saved.pos;
chunk->remain = saved.remain;
return NULL;
}
#define SKIP_SPACES_COMMENTS(parser, chunk, p) do { \
while ((p) < (chunk)->end) { \
if (!ucl_test_character (*(p), UCL_CHARACTER_WHITESPACE_UNSAFE)) { \
if ((chunk)->remain >= 2 && ucl_lex_is_comment ((p)[0], (p)[1])) { \
if (!ucl_skip_comments (parser)) { \
return false; \
} \
p = (chunk)->pos; \
} \
break; \
} \
ucl_chunk_skipc (chunk, p); \
} \
} while(0)
/**
* Handle the main states of rcl parser
* @param parser parser structure
@ -1622,13 +1845,13 @@ ucl_parse_macro_value (struct ucl_parser *parser,
static bool
ucl_state_machine (struct ucl_parser *parser)
{
ucl_object_t *obj;
ucl_object_t *obj, *macro_args;
struct ucl_chunk *chunk = parser->chunks;
const unsigned char *p, *c = NULL, *macro_start = NULL;
unsigned char *macro_escaped;
size_t macro_len = 0;
struct ucl_macro *macro = NULL;
bool next_key = false, end_of_object = false;
bool next_key = false, end_of_object = false, ret;
if (parser->top_obj == NULL) {
if (*chunk->pos == '[') {
@ -1654,7 +1877,6 @@ ucl_state_machine (struct ucl_parser *parser)
* if we got [ or { correspondingly or can just treat new data as
* a key of newly created object
*/
obj = parser->cur_obj;
if (!ucl_skip_comments (parser)) {
parser->prev_state = parser->state;
parser->state = UCL_STATE_ERROR;
@ -1691,7 +1913,7 @@ ucl_state_machine (struct ucl_parser *parser)
}
if (parser->stack == NULL) {
/* No objects are on stack, but we want to parse a key */
ucl_set_err (chunk, UCL_ESYNTAX, "top object is finished but the parser "
ucl_set_err (parser, UCL_ESYNTAX, "top object is finished but the parser "
"expects a key", &parser->err);
parser->prev_state = parser->state;
parser->state = UCL_STATE_ERROR;
@ -1757,7 +1979,8 @@ ucl_state_machine (struct ucl_parser *parser)
p = chunk->pos;
break;
case UCL_STATE_MACRO_NAME:
if (!ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE)) {
if (!ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE) &&
*p != '(') {
ucl_chunk_skipc (chunk, p);
}
else if (p - c > 0) {
@ -1772,48 +1995,51 @@ ucl_state_machine (struct ucl_parser *parser)
return false;
}
/* Now we need to skip all spaces */
while (p < chunk->end) {
if (!ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE)) {
if (chunk->remain >= 2 && ucl_lex_is_comment (p[0], p[1])) {
/* Skip comment */
if (!ucl_skip_comments (parser)) {
return false;
}
p = chunk->pos;
}
break;
}
ucl_chunk_skipc (chunk, p);
}
SKIP_SPACES_COMMENTS(parser, chunk, p);
parser->state = UCL_STATE_MACRO;
}
break;
case UCL_STATE_MACRO:
if (*chunk->pos == '(') {
macro_args = ucl_parse_macro_arguments (parser, chunk);
p = chunk->pos;
if (macro_args) {
SKIP_SPACES_COMMENTS(parser, chunk, p);
}
}
else {
macro_args = NULL;
}
if (!ucl_parse_macro_value (parser, chunk, macro,
&macro_start, &macro_len)) {
parser->prev_state = parser->state;
parser->state = UCL_STATE_ERROR;
return false;
}
macro_len = ucl_expand_variable (parser, &macro_escaped, macro_start, macro_len);
macro_len = ucl_expand_variable (parser, &macro_escaped,
macro_start, macro_len);
parser->state = parser->prev_state;
if (macro_escaped == NULL) {
if (!macro->handler (macro_start, macro_len, macro->ud)) {
return false;
}
ret = macro->handler (macro_start, macro_len, macro_args,
macro->ud);
}
else {
if (!macro->handler (macro_escaped, macro_len, macro->ud)) {
UCL_FREE (macro_len + 1, macro_escaped);
return false;
}
ret = macro->handler (macro_escaped, macro_len, macro_args,
macro->ud);
UCL_FREE (macro_len + 1, macro_escaped);
}
p = chunk->pos;
if (macro_args) {
ucl_object_unref (macro_args);
}
if (!ret) {
return false;
}
break;
default:
/* TODO: add all states */
ucl_set_err (chunk, UCL_EINTERNAL, "internal error: parser is in an unknown state", &parser->err);
ucl_set_err (parser, UCL_EINTERNAL,
"internal error: parser is in an unknown state", &parser->err);
parser->state = UCL_STATE_ERROR;
return false;
}
@ -1888,7 +2114,7 @@ ucl_parser_register_variable (struct ucl_parser *parser, const char *var,
if (new != NULL) {
/* Remove variable */
LL_DELETE (parser->variables, new);
DL_DELETE (parser->variables, new);
free (new->var);
free (new->value);
UCL_FREE (sizeof (struct ucl_variable), new);
@ -1910,7 +2136,7 @@ ucl_parser_register_variable (struct ucl_parser *parser, const char *var,
new->value = strdup (value);
new->value_len = strlen (value);
LL_PREPEND (parser->variables, new);
DL_APPEND (parser->variables, new);
}
else {
free (new->value);
@ -1929,15 +2155,19 @@ ucl_parser_set_variables_handler (struct ucl_parser *parser,
}
bool
ucl_parser_add_chunk (struct ucl_parser *parser, const unsigned char *data,
size_t len)
ucl_parser_add_chunk_priority (struct ucl_parser *parser, const unsigned char *data,
size_t len, unsigned priority)
{
struct ucl_chunk *chunk;
if (data == NULL || len == 0) {
if (data == NULL) {
ucl_create_err (&parser->err, "invalid chunk added");
return false;
}
if (len == 0) {
parser->top_obj = ucl_object_new_full (UCL_OBJECT, priority);
return true;
}
if (parser->state != UCL_STATE_ERROR) {
chunk = UCL_ALLOC (sizeof (struct ucl_chunk));
if (chunk == NULL) {
@ -1950,6 +2180,7 @@ ucl_parser_add_chunk (struct ucl_parser *parser, const unsigned char *data,
chunk->end = chunk->begin + len;
chunk->line = 1;
chunk->column = 0;
chunk->priority = priority;
LL_PREPEND (parser->chunks, chunk);
parser->recursion ++;
if (parser->recursion > UCL_MAX_RECURSION) {
@ -1965,6 +2196,13 @@ ucl_parser_add_chunk (struct ucl_parser *parser, const unsigned char *data,
return false;
}
bool
ucl_parser_add_chunk (struct ucl_parser *parser, const unsigned char *data,
size_t len)
{
return ucl_parser_add_chunk_priority (parser, data, len, 0);
}
bool
ucl_parser_add_string (struct ucl_parser *parser, const char *data,
size_t len)

View File

@ -25,6 +25,8 @@
#include "ucl_internal.h"
#include "ucl_chartable.h"
#include <glob.h>
#ifdef HAVE_LIBGEN_H
#include <libgen.h> /* For dirname */
#endif
@ -129,11 +131,6 @@ static char* ucl_realpath(const char *path, char *resolved_path) {
#define ucl_realpath realpath
#endif
/**
* @file rcl_util.c
* Utilities for rcl parsing
*/
typedef void (*ucl_object_dtor) (ucl_object_t *obj);
static void ucl_object_free_internal (ucl_object_t *obj, bool allow_rec,
ucl_object_dtor dtor);
@ -148,7 +145,19 @@ ucl_object_dtor_free (ucl_object_t *obj)
if (obj->trash_stack[UCL_TRASH_VALUE] != NULL) {
UCL_FREE (obj->len, obj->trash_stack[UCL_TRASH_VALUE]);
}
UCL_FREE (sizeof (ucl_object_t), obj);
/* Do not free ephemeral objects */
if ((obj->flags & UCL_OBJECT_EPHEMERAL) == 0) {
if (obj->type != UCL_USERDATA) {
UCL_FREE (sizeof (ucl_object_t), obj);
}
else {
struct ucl_object_userdata *ud = (struct ucl_object_userdata *)obj;
if (ud->dtor) {
ud->dtor (obj->value.ud);
}
UCL_FREE (sizeof (*ud), obj);
}
}
}
/*
@ -423,7 +432,11 @@ ucl_parser_free (struct ucl_parser *parser)
}
if (parser->err != NULL) {
utstring_free(parser->err);
utstring_free (parser->err);
}
if (parser->cur_file) {
free (parser->cur_file);
}
UCL_FREE (sizeof (struct ucl_parser), parser);
@ -701,7 +714,8 @@ ucl_sig_check (const unsigned char *data, size_t datalen,
*/
static bool
ucl_include_url (const unsigned char *data, size_t len,
struct ucl_parser *parser, bool check_signature, bool must_exist)
struct ucl_parser *parser, bool check_signature, bool must_exist,
unsigned priority)
{
bool res;
@ -744,7 +758,7 @@ ucl_include_url (const unsigned char *data, size_t len,
prev_state = parser->state;
parser->state = UCL_STATE_INIT;
res = ucl_parser_add_chunk (parser, buf, buflen);
res = ucl_parser_add_chunk_priority (parser, buf, buflen, priority);
if (res == true) {
/* Remove chunk from the stack */
chunk = parser->chunks;
@ -761,23 +775,30 @@ ucl_include_url (const unsigned char *data, size_t len,
}
/**
* Include a file to configuration
* Include a single file to the parser
* @param data
* @param len
* @param parser
* @param err
* @param check_signature
* @param must_exist
* @param allow_glob
* @param priority
* @return
*/
static bool
ucl_include_file (const unsigned char *data, size_t len,
struct ucl_parser *parser, bool check_signature, bool must_exist)
ucl_include_file_single (const unsigned char *data, size_t len,
struct ucl_parser *parser, bool check_signature, bool must_exist,
unsigned priority)
{
bool res;
struct ucl_chunk *chunk;
unsigned char *buf = NULL;
char *old_curfile;
size_t buflen;
char filebuf[PATH_MAX], realbuf[PATH_MAX];
int prev_state;
struct ucl_variable *cur_var, *tmp_var, *old_curdir = NULL,
*old_filename = NULL;
snprintf (filebuf, sizeof (filebuf), "%.*s", (int)len, data);
if (ucl_realpath (filebuf, realbuf) == NULL) {
@ -790,6 +811,13 @@ ucl_include_file (const unsigned char *data, size_t len,
return false;
}
if (parser->cur_file && strcmp (realbuf, parser->cur_file) == 0) {
/* We are likely including the file itself */
ucl_create_err (&parser->err, "trying to include the file %s from itself",
realbuf);
return false;
}
if (!ucl_fetch_file (realbuf, &buf, &buflen, &parser->err, must_exist)) {
return (!must_exist || false);
}
@ -818,19 +846,66 @@ ucl_include_file (const unsigned char *data, size_t len,
#endif
}
old_curfile = parser->cur_file;
parser->cur_file = strdup (realbuf);
/* Store old file vars */
DL_FOREACH_SAFE (parser->variables, cur_var, tmp_var) {
if (strcmp (cur_var->var, "CURDIR") == 0) {
old_curdir = cur_var;
DL_DELETE (parser->variables, cur_var);
}
else if (strcmp (cur_var->var, "FILENAME") == 0) {
old_filename = cur_var;
DL_DELETE (parser->variables, cur_var);
}
}
ucl_parser_set_filevars (parser, realbuf, false);
prev_state = parser->state;
parser->state = UCL_STATE_INIT;
res = ucl_parser_add_chunk (parser, buf, buflen);
if (res == true) {
/* Remove chunk from the stack */
chunk = parser->chunks;
if (chunk != NULL) {
parser->chunks = chunk->next;
UCL_FREE (sizeof (struct ucl_chunk), chunk);
res = ucl_parser_add_chunk_priority (parser, buf, buflen, priority);
if (!res && !must_exist) {
/* Free error */
utstring_free (parser->err);
parser->err = NULL;
parser->state = UCL_STATE_AFTER_VALUE;
}
/* Remove chunk from the stack */
chunk = parser->chunks;
if (chunk != NULL) {
parser->chunks = chunk->next;
UCL_FREE (sizeof (struct ucl_chunk), chunk);
parser->recursion --;
}
/* Restore old file vars */
parser->cur_file = old_curfile;
DL_FOREACH_SAFE (parser->variables, cur_var, tmp_var) {
if (strcmp (cur_var->var, "CURDIR") == 0 && old_curdir) {
DL_DELETE (parser->variables, cur_var);
free (cur_var->var);
free (cur_var->value);
UCL_FREE (sizeof (struct ucl_variable), cur_var);
}
else if (strcmp (cur_var->var, "FILENAME") == 0 && old_filename) {
DL_DELETE (parser->variables, cur_var);
free (cur_var->var);
free (cur_var->value);
UCL_FREE (sizeof (struct ucl_variable), cur_var);
}
}
if (old_filename) {
DL_APPEND (parser->variables, old_filename);
}
if (old_curdir) {
DL_APPEND (parser->variables, old_curdir);
}
if (old_curfile) {
free (old_curfile);
}
parser->state = prev_state;
@ -842,6 +917,138 @@ ucl_include_file (const unsigned char *data, size_t len,
return res;
}
/**
* Include a file to configuration
* @param data
* @param len
* @param parser
* @param err
* @return
*/
static bool
ucl_include_file (const unsigned char *data, size_t len,
struct ucl_parser *parser, bool check_signature, bool must_exist,
bool allow_glob, unsigned priority)
{
const unsigned char *p = data, *end = data + len;
bool need_glob = false;
int cnt = 0;
glob_t globbuf;
char glob_pattern[PATH_MAX];
size_t i;
if (!allow_glob) {
return ucl_include_file_single (data, len, parser, check_signature,
must_exist, priority);
}
else {
/* Check for special symbols in a filename */
while (p != end) {
if (*p == '*' || *p == '?') {
need_glob = true;
break;
}
p ++;
}
if (need_glob) {
memset (&globbuf, 0, sizeof (globbuf));
ucl_strlcpy (glob_pattern, (const char *)data, sizeof (glob_pattern));
if (glob (glob_pattern, 0, NULL, &globbuf) != 0) {
return (!must_exist || false);
}
for (i = 0; i < globbuf.gl_pathc; i ++) {
if (!ucl_include_file_single ((unsigned char *)globbuf.gl_pathv[i],
strlen (globbuf.gl_pathv[i]), parser, check_signature,
must_exist, priority)) {
globfree (&globbuf);
return false;
}
cnt ++;
}
globfree (&globbuf);
if (cnt == 0 && must_exist) {
ucl_create_err (&parser->err, "cannot match any files for pattern %s",
glob_pattern);
return false;
}
}
else {
return ucl_include_file_single (data, len, parser, check_signature,
must_exist, priority);
}
}
return true;
}
/**
* Common function to handle .*include* macros
* @param data
* @param len
* @param args
* @param parser
* @param default_try
* @param default_sign
* @return
*/
static bool
ucl_include_common (const unsigned char *data, size_t len,
const ucl_object_t *args, struct ucl_parser *parser,
bool default_try,
bool default_sign)
{
bool try_load, allow_glob, allow_url, need_sign;
unsigned priority;
const ucl_object_t *param;
ucl_object_iter_t it = NULL;
/* Default values */
try_load = default_try;
allow_glob = false;
allow_url = true;
need_sign = default_sign;
priority = 0;
/* Process arguments */
if (args != NULL && args->type == UCL_OBJECT) {
while ((param = ucl_iterate_object (args, &it, true)) != NULL) {
if (param->type == UCL_BOOLEAN) {
if (strcmp (param->key, "try") == 0) {
try_load = ucl_object_toboolean (param);
}
else if (strcmp (param->key, "sign") == 0) {
need_sign = ucl_object_toboolean (param);
}
else if (strcmp (param->key, "glob") == 0) {
allow_glob = ucl_object_toboolean (param);
}
else if (strcmp (param->key, "url") == 0) {
allow_url = ucl_object_toboolean (param);
}
}
else if (param->type == UCL_INT) {
if (strcmp (param->key, "priority") == 0) {
priority = ucl_object_toint (param);
}
}
}
}
if (*data == '/' || *data == '.') {
/* Try to load a file */
return ucl_include_file (data, len, parser, need_sign, !try_load,
allow_glob, priority);
}
else if (allow_url) {
/* Globbing is not used for URL's */
return ucl_include_url (data, len, parser, need_sign, !try_load,
priority);
}
return false;
}
/**
* Handle include macro
* @param data include data
@ -851,16 +1058,12 @@ ucl_include_file (const unsigned char *data, size_t len,
* @return
*/
UCL_EXTERN bool
ucl_include_handler (const unsigned char *data, size_t len, void* ud)
ucl_include_handler (const unsigned char *data, size_t len,
const ucl_object_t *args, void* ud)
{
struct ucl_parser *parser = ud;
if (*data == '/' || *data == '.') {
/* Try to load a file */
return ucl_include_file (data, len, parser, false, true);
}
return ucl_include_url (data, len, parser, false, true);
return ucl_include_common (data, len, args, parser, false, false);
}
/**
@ -872,30 +1075,22 @@ ucl_include_handler (const unsigned char *data, size_t len, void* ud)
* @return
*/
UCL_EXTERN bool
ucl_includes_handler (const unsigned char *data, size_t len, void* ud)
ucl_includes_handler (const unsigned char *data, size_t len,
const ucl_object_t *args, void* ud)
{
struct ucl_parser *parser = ud;
if (*data == '/' || *data == '.') {
/* Try to load a file */
return ucl_include_file (data, len, parser, true, true);
}
return ucl_include_url (data, len, parser, true, true);
return ucl_include_common (data, len, args, parser, false, true);
}
UCL_EXTERN bool
ucl_try_include_handler (const unsigned char *data, size_t len, void* ud)
ucl_try_include_handler (const unsigned char *data, size_t len,
const ucl_object_t *args, void* ud)
{
struct ucl_parser *parser = ud;
if (*data == '/' || *data == '.') {
/* Try to load a file */
return ucl_include_file (data, len, parser, false, false);
}
return ucl_include_url (data, len, parser, false, false);
return ucl_include_common (data, len, args, parser, true, false);
}
UCL_EXTERN bool
@ -947,6 +1142,10 @@ ucl_parser_add_file (struct ucl_parser *parser, const char *filename)
return false;
}
if (parser->cur_file) {
free (parser->cur_file);
}
parser->cur_file = strdup (realbuf);
ucl_parser_set_filevars (parser, realbuf, false);
ret = ucl_parser_add_chunk (parser, buf, len);
@ -957,6 +1156,39 @@ ucl_parser_add_file (struct ucl_parser *parser, const char *filename)
return ret;
}
UCL_EXTERN bool
ucl_parser_add_fd (struct ucl_parser *parser, int fd)
{
unsigned char *buf;
size_t len;
bool ret;
struct stat st;
if (fstat (fd, &st) == -1) {
ucl_create_err (&parser->err, "cannot stat fd %d: %s",
fd, strerror (errno));
return false;
}
if ((buf = ucl_mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
ucl_create_err (&parser->err, "cannot mmap fd %d: %s",
fd, strerror (errno));
return false;
}
if (parser->cur_file) {
free (parser->cur_file);
}
parser->cur_file = NULL;
len = st.st_size;
ret = ucl_parser_add_chunk (parser, buf, len);
if (len > 0) {
ucl_munmap (buf, len);
}
return ret;
}
size_t
ucl_strlcpy (char *dst, const char *src, size_t siz)
{
@ -1176,6 +1408,15 @@ ucl_object_insert_key_common (ucl_object_t *top, ucl_object_t *elt,
}
}
/* workaround for some use cases */
if (elt->trash_stack[UCL_TRASH_KEY] != NULL &&
key != (const char *)elt->trash_stack[UCL_TRASH_KEY]) {
/* Remove copied key */
free (elt->trash_stack[UCL_TRASH_KEY]);
elt->trash_stack[UCL_TRASH_KEY] = NULL;
elt->flags &= ~UCL_OBJECT_ALLOCATED_KEY;
}
elt->key = key;
elt->keylen = keylen;
@ -1185,9 +1426,8 @@ ucl_object_insert_key_common (ucl_object_t *top, ucl_object_t *elt,
found = __DECONST (ucl_object_t *, ucl_hash_search_obj (top->value.ov, elt));
if (!found) {
if (found == NULL) {
top->value.ov = ucl_hash_insert_object (top->value.ov, elt);
DL_APPEND (found, elt);
top->len ++;
if (replace) {
ret = false;
@ -1195,11 +1435,8 @@ ucl_object_insert_key_common (ucl_object_t *top, ucl_object_t *elt,
}
else {
if (replace) {
ucl_hash_delete (top->value.ov, found);
ucl_hash_replace (top->value.ov, found, elt);
ucl_object_unref (found);
top->value.ov = ucl_hash_insert_object (top->value.ov, elt);
found = NULL;
DL_APPEND (found, elt);
}
else if (merge) {
if (found->type != UCL_OBJECT && elt->type == UCL_OBJECT) {
@ -1310,6 +1547,40 @@ ucl_object_replace_key (ucl_object_t *top, ucl_object_t *elt,
return ucl_object_insert_key_common (top, elt, key, keylen, copy_key, false, true);
}
bool
ucl_object_merge (ucl_object_t *top, ucl_object_t *elt, bool copy)
{
ucl_object_t *cur = NULL, *cp = NULL, *found = NULL;
ucl_object_iter_t iter = NULL;
if (top == NULL || top->type != UCL_OBJECT || elt == NULL || elt->type != UCL_OBJECT) {
return false;
}
/* Mix two hashes */
while ((cur = (ucl_object_t*)ucl_hash_iterate (elt->value.ov, &iter))) {
if (copy) {
cp = ucl_object_copy (cur);
}
else {
cp = ucl_object_ref (cur);
}
found = __DECONST(ucl_object_t *, ucl_hash_search (top->value.ov, cp->key, cp->keylen));
if (found == NULL) {
/* The key does not exist */
top->value.ov = ucl_hash_insert_object (top->value.ov, cp);
top->len ++;
}
else {
/* The key already exists, replace it */
ucl_hash_replace (top->value.ov, found, cp);
ucl_object_unref (found);
}
}
return true;
}
const ucl_object_t *
ucl_object_find_keyl (const ucl_object_t *obj, const char *key, size_t klen)
{
@ -1372,9 +1643,6 @@ ucl_iterate_object (const ucl_object_t *obj, ucl_object_iter_t *iter, bool expan
elt = *iter;
if (elt == NULL) {
elt = obj;
if (elt == NULL) {
return NULL;
}
}
else if (elt == obj) {
return NULL;
@ -1442,29 +1710,59 @@ ucl_lookup_path (const ucl_object_t *top, const char *path_in) {
ucl_object_t *
ucl_object_new (void)
{
ucl_object_t *new;
new = malloc (sizeof (ucl_object_t));
if (new != NULL) {
memset (new, 0, sizeof (ucl_object_t));
new->ref = 1;
new->type = UCL_NULL;
}
return new;
return ucl_object_typed_new (UCL_NULL);
}
ucl_object_t *
ucl_object_typed_new (ucl_type_t type)
{
return ucl_object_new_full (type, 0);
}
ucl_object_t *
ucl_object_new_full (ucl_type_t type, unsigned priority)
{
ucl_object_t *new;
new = malloc (sizeof (ucl_object_t));
if (new != NULL) {
memset (new, 0, sizeof (ucl_object_t));
new->ref = 1;
new->type = (type <= UCL_NULL ? type : UCL_NULL);
if (type != UCL_USERDATA) {
new = UCL_ALLOC (sizeof (ucl_object_t));
if (new != NULL) {
memset (new, 0, sizeof (ucl_object_t));
new->ref = 1;
new->type = (type <= UCL_NULL ? type : UCL_NULL);
new->next = NULL;
new->prev = new;
ucl_object_set_priority (new, priority);
}
}
else {
new = ucl_object_new_userdata (NULL, NULL);
ucl_object_set_priority (new, priority);
}
return new;
}
ucl_object_t*
ucl_object_new_userdata (ucl_userdata_dtor dtor, ucl_userdata_emitter emitter)
{
struct ucl_object_userdata *new;
size_t nsize = sizeof (*new);
new = UCL_ALLOC (nsize);
if (new != NULL) {
memset (new, 0, nsize);
new->obj.ref = 1;
new->obj.type = UCL_USERDATA;
new->obj.next = NULL;
new->obj.prev = (ucl_object_t *)new;
new->dtor = dtor;
new->emitter = emitter;
}
return (ucl_object_t *)new;
}
ucl_type_t
ucl_object_type (const ucl_object_t *obj)
{
@ -1576,6 +1874,30 @@ ucl_array_prepend (ucl_object_t *top, ucl_object_t *elt)
return true;
}
bool
ucl_array_merge (ucl_object_t *top, ucl_object_t *elt, bool copy)
{
ucl_object_t *cur, *tmp, *cp;
if (elt == NULL || top == NULL || top->type != UCL_ARRAY || elt->type != UCL_ARRAY) {
return false;
}
DL_FOREACH_SAFE (elt->value.av, cur, tmp) {
if (copy) {
cp = ucl_object_copy (cur);
}
else {
cp = ucl_object_ref (cur);
}
if (cp != NULL) {
ucl_array_append (top, cp);
}
}
return true;
}
ucl_object_t *
ucl_array_delete (ucl_object_t *top, ucl_object_t *elt)
{
@ -1660,6 +1982,28 @@ ucl_array_find_index (const ucl_object_t *top, unsigned int index)
return NULL;
}
ucl_object_t *
ucl_array_replace_index (ucl_object_t *top, ucl_object_t *elt,
unsigned int index)
{
ucl_object_t *cur, *tmp;
if (top == NULL || top->type != UCL_ARRAY || elt == NULL ||
top->len == 0 || (index + 1) > top->len) {
return NULL;
}
DL_FOREACH_SAFE (top->value.av, cur, tmp) {
if (index == 0) {
DL_REPLACE_ELEM (top->value.av, cur, elt);
return cur;
}
--index;
}
return NULL;
}
ucl_object_t *
ucl_elt_append (ucl_object_t *head, ucl_object_t *elt)
{
@ -1849,16 +2193,99 @@ ucl_object_ref (const ucl_object_t *obj)
ucl_object_t *res = NULL;
if (obj != NULL) {
res = __DECONST (ucl_object_t *, obj);
if (obj->flags & UCL_OBJECT_EPHEMERAL) {
/*
* Use deep copy for ephemeral objects, note that its refcount
* is NOT increased, since ephemeral objects does not need refcount
* at all
*/
res = ucl_object_copy (obj);
}
else {
res = __DECONST (ucl_object_t *, obj);
#ifdef HAVE_ATOMIC_BUILTINS
(void)__sync_add_and_fetch (&res->ref, 1);
(void)__sync_add_and_fetch (&res->ref, 1);
#else
res->ref ++;
res->ref ++;
#endif
}
}
return res;
}
static ucl_object_t *
ucl_object_copy_internal (const ucl_object_t *other, bool allow_array)
{
ucl_object_t *new;
ucl_object_iter_t it = NULL;
const ucl_object_t *cur;
new = malloc (sizeof (*new));
if (new != NULL) {
memcpy (new, other, sizeof (*new));
if (other->flags & UCL_OBJECT_EPHEMERAL) {
/* Copied object is always non ephemeral */
new->flags &= ~UCL_OBJECT_EPHEMERAL;
}
new->ref = 1;
/* Unlink from others */
new->next = NULL;
new->prev = new;
/* deep copy of values stored */
if (other->trash_stack[UCL_TRASH_KEY] != NULL) {
new->trash_stack[UCL_TRASH_KEY] =
strdup (other->trash_stack[UCL_TRASH_KEY]);
if (other->key == (const char *)other->trash_stack[UCL_TRASH_KEY]) {
new->key = new->trash_stack[UCL_TRASH_KEY];
}
}
if (other->trash_stack[UCL_TRASH_VALUE] != NULL) {
new->trash_stack[UCL_TRASH_VALUE] =
strdup (other->trash_stack[UCL_TRASH_VALUE]);
if (new->type == UCL_STRING) {
new->value.sv = new->trash_stack[UCL_TRASH_VALUE];
}
}
if (other->type == UCL_ARRAY || other->type == UCL_OBJECT) {
/* reset old value */
memset (&new->value, 0, sizeof (new->value));
while ((cur = ucl_iterate_object (other, &it, true)) != NULL) {
if (other->type == UCL_ARRAY) {
ucl_array_append (new, ucl_object_copy_internal (cur, false));
}
else {
ucl_object_t *cp = ucl_object_copy_internal (cur, true);
if (cp != NULL) {
ucl_object_insert_key (new, cp, cp->key, cp->keylen,
false);
}
}
}
}
else if (allow_array && other->next != NULL) {
LL_FOREACH (other->next, cur) {
ucl_object_t *cp = ucl_object_copy_internal (cur, false);
if (cp != NULL) {
DL_APPEND (new, cp);
}
}
}
}
return new;
}
ucl_object_t *
ucl_object_copy (const ucl_object_t *other)
{
return ucl_object_copy_internal (other, true);
}
void
ucl_object_unref (ucl_object_t *obj)
{
@ -1956,3 +2383,25 @@ ucl_object_array_sort (ucl_object_t *ar,
DL_SORT (ar->value.av, cmp);
}
#define PRIOBITS 4
unsigned int
ucl_object_get_priority (const ucl_object_t *obj)
{
if (obj == NULL) {
return 0;
}
return (obj->flags >> ((sizeof (obj->flags) * NBBY) - PRIOBITS));
}
void
ucl_object_set_priority (ucl_object_t *obj,
unsigned int priority)
{
if (obj != NULL) {
priority &= (0x1 << PRIOBITS) - 1;
obj->flags |= priority << ((sizeof (obj->flags) * NBBY) - PRIOBITS);
}
}

View File

@ -1,4 +1,4 @@
EXTRA_DIST = $(TESTS) basic schema generate.res rcl_test.json.xz
EXTRA_DIST = $(TESTS) basic schema generate.res streamline.res rcl_test.json.xz
TESTS = basic.test \
generate.test \

View File

@ -0,0 +1,2 @@
key1: 12 ,
key2: 12 value

View File

@ -0,0 +1,3 @@
key1 = 12;
key2 = "12 value";

View File

@ -0,0 +1,9 @@
key = value_orig;
# test glob
.include(glob=true) "${CURDIR}/include_dir/test*.conf"
.include(priority=1) "${CURDIR}/include_dir/pri1.conf"
.include(priority=2) "${CURDIR}/include_dir/pri2.conf"
.include(try=true) "${CURDIR}/include_dir/invalid.conf"

View File

@ -0,0 +1,8 @@
key = "value_orig";
key = "value1";
key = "value2";
key = "value3";
key_pri = "priority2";
key_trace1 = "pri1";
key_trace2 = "pri2";

View File

@ -10,7 +10,14 @@ licenses [
"BSD",
]
flatsize = 60523;
desc = "pkgconf is a program which helps to configure compiler and linker flags for\ndevelopment frameworks. It is similar to pkg-config, but was written from\nscratch in Summer of 2011 to replace pkg-config, which now needs itself to build\nitself.\n\nWWW: https://github.com/pkgconf/pkgconf";
desc = <<EOD
pkgconf is a program which helps to configure compiler and linker flags for
development frameworks. It is similar to pkg-config, but was written from
scratch in Summer of 2011 to replace pkg-config, which now needs itself to build
itself.
WWW: https://github.com/pkgconf/pkgconf
EOD;
categories [
"devel",
]
@ -31,6 +38,17 @@ scripts {
pre-deinstall = "cd /usr/local\nn";
post-deinstall = "cd /usr/local\nn";
}
multiline-key = "test\ntest\ntest\\n\n/* comment like */\n# Some invalid endings\n EOD\nEOD \nEOF\n# Valid ending + empty string\n";
multiline-key = <<EOD
test
test
test\n
/* comment like */
# Some invalid endings
EOD
EOD
EOF
# Valid ending + empty string
EOD;
normal-key = "<<EODnot";

View File

@ -0,0 +1,25 @@
# This test is intended to check various comments in ucl
obj {
key = value
key = "/* value"
/*
key = value
*/
# Nested comments
key = nested
/*
adasdasdads
/* asdasdasd */asjdasjldaskd
/* asdsadasd */
/* /* /* /* /* */ */ */ */ */
# some
*/
key = quotes # quoted
# Quotes
/*
key = "/* value"
key = "*/value"
*/
}

View File

@ -0,0 +1,7 @@
obj {
key = "value";
key = "/* value";
key = "nested";
key = "quotes";
}

View File

@ -0,0 +1 @@
@@@@ BAD UCL ~~~~

View File

@ -0,0 +1,2 @@
key_pri = priority1;
key_trace1 = pri1;

View File

@ -0,0 +1,2 @@
key_pri = priority2;
key_trace2 = pri2;

View File

@ -0,0 +1 @@
key = value1;

View File

@ -0,0 +1 @@
key = value2;

View File

@ -0,0 +1 @@
key = value3;

View File

@ -1,3 +1,4 @@
key0 = 0.100000;
key1 = "test string";
key2 = "test \\nstring";
key3 = " test string \n";

View File

@ -90,7 +90,7 @@ main (int argc, char **argv)
inlen = strlen (inbuf);
test_in = malloc (inlen);
memcpy (test_in, inbuf, inlen);
ucl_parser_add_chunk (parser, test_in, inlen);
ucl_parser_add_chunk (parser, (const unsigned char *)test_in, inlen);
}
fclose (in);
@ -126,7 +126,7 @@ main (int argc, char **argv)
ucl_parser_free (parser);
ucl_object_unref (obj);
parser2 = ucl_parser_new (UCL_PARSER_KEY_LOWERCASE);
ucl_parser_add_string (parser2, emitted, 0);
ucl_parser_add_string (parser2, (const char *)emitted, 0);
if (ucl_parser_get_error(parser2) != NULL) {
fprintf (out, "Error occurred: %s\n", ucl_parser_get_error(parser2));

View File

@ -54,6 +54,13 @@ main (int argc, char **argv)
}
obj = ucl_object_typed_new (UCL_OBJECT);
/* Keys replacing */
cur = ucl_object_fromstring_common ("value1", 0, UCL_STRING_TRIM);
ucl_object_insert_key (obj, cur, "key0", 0, false);
cur = ucl_object_fromdouble (0.1);
ucl_object_replace_key (obj, cur, "key0", 0, false);
/* Create some strings */
cur = ucl_object_fromstring_common (" test string ", 0, UCL_STRING_TRIM);
ucl_object_insert_key (obj, cur, "key1", 0, false);

View File

@ -61,7 +61,7 @@ static bool
perform_test (const ucl_object_t *schema, const ucl_object_t *obj,
struct ucl_schema_error *err)
{
const const ucl_object_t *valid, *data, *description;
const ucl_object_t *valid, *data, *description;
bool match;
data = ucl_object_find_key (obj, "data");

View File

@ -53,6 +53,7 @@ ucl_obj_dump (const ucl_object_t *obj, unsigned int shift)
if (obj->type == UCL_OBJECT) {
printf ("%stype: UCL_OBJECT\n", pre);
printf ("%svalue: %p\n", pre, obj->value.ov);
it_obj = NULL;
while ((cur = ucl_iterate_object (obj, &it_obj, true))) {
ucl_obj_dump (cur, shift + 2);
}

View File

@ -16,6 +16,8 @@ SRCS= ucl_emitter_streamline.c \
.PATH: ${LIBUCL}/src
LIBADD= m
WARNS= 1
CFLAGS+= -I${LIBUCL}/include \
-I${LIBUCL}/src \

View File

@ -220,6 +220,7 @@ _DP_pam+= ypclnt
_DP_krb5+= asn1 com_err crypt crypto hx509 roken wind heimbase heimipcc \
pthread
_DP_gssapi_krb5+= gssapi krb5 crypto roken asn1 com_err
_DP_ucl= m
# Define spacial cases
LDADD_supcplusplus= -lsupc++
@ -243,10 +244,6 @@ LDADD_${_l}+= ${LDADD_${_d}}
.endif
.endfor
# ucl needs and exposes libm
DPADD_ucl+= ${DPADD_m}
LDADD_ucl+= ${LDADD_m}
DPADD_sqlite3+= ${DPADD_pthread}
LDADD_sqlite3+= ${LDADD_pthread}