Radish alpha
H
rad:z3QDZAW2FAfuLvihrhiyDC9fAD8G9
HardenedBSD Package Manager
Radicle
Git
Update to latest libucl
Baptiste Daroussin committed 11 years ago
commit bfbb3278e7ad5c93ff7ff7400f78414334784b9d
parent e7c65cb
19 files changed +807 -133
added external/libucl/COPYING
@@ -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.
modified external/libucl/ChangeLog.md
@@ -12,3 +12,11 @@

- 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
modified external/libucl/README.md
@@ -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

modified external/libucl/configure.ac
@@ -1,7 +1,7 @@
m4_define([maj_ver], [0])
-
m4_define([med_ver], [5])
-
m4_define([min_ver], [4])
-
m4_define([so_version], [2:1:2])
+
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])
modified external/libucl/include/lua_ucl.h
@@ -33,6 +33,15 @@
#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);
@@ -54,4 +63,7 @@ UCL_EXTERN ucl_object_t* ucl_object_lua_import (lua_State *L, int idx);
UCL_EXTERN int ucl_object_push_lua (lua_State *L,
		const ucl_object_t *obj, bool allow_array);

+
UCL_EXTERN struct ucl_lua_funcdata* ucl_object_toclosure (
+
		const ucl_object_t *obj);
+

#endif /* LUA_UCL_H_ */
modified external/libucl/include/ucl.h
@@ -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;

/**
@@ -175,7 +176,8 @@ typedef enum ucl_object_flags {
	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 = 0x16 /**< String should be displayed as multiline string */
+
	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;

/**
@@ -248,6 +250,15 @@ 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
@@ -318,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)
@@ -331,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)
@@ -342,6 +353,15 @@ 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
 * @param key key associated to the object to remove
@@ -360,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)
@@ -371,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
@@ -381,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)
@@ -394,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
 */
@@ -404,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
 */
@@ -412,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
@@ -436,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
+
 * 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);

/**
-
 * Removes the first element from the array `top`. Caller must unref the returned object when it is not
-
 * needed.
-
 * @param top array ucl object
-
 * @return removed element or NULL if `top` is NULL or not an array
+
 * 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_EXTERN ucl_object_t* ucl_array_pop_first (ucl_object_t *top);
+
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);
@@ -558,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);
@@ -568,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);
@@ -600,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);

@@ -637,6 +686,21 @@ 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
 */
typedef void* ucl_object_iter_t;
@@ -665,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;
@@ -727,13 +794,24 @@ 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
 * @param data the pointer to the string
modified external/libucl/lua/lua_ucl.c
@@ -77,12 +77,6 @@ static ucl_object_t* ucl_object_lua_fromelt (lua_State *L, int idx);

static void *ucl_null;

-
struct ucl_lua_funcdata {
-
	lua_State *L;
-
	int idx;
-
	char *ret;
-
};
-

/**
 * Push a single element of an object to lua
 * @param L
@@ -814,3 +808,13 @@ luaopen_ucl (lua_State *L)

	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;
+
}
modified external/libucl/src/ucl_internal.h
@@ -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,7 +193,7 @@ struct ucl_parser {
	int flags;
	ucl_object_t *top_obj;
	ucl_object_t *cur_obj;
-
	const char *cur_file;
+
	char *cur_file;
	struct ucl_macro *macroes;
	struct ucl_stack *stack;
	struct ucl_chunk *chunks;
@@ -223,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
@@ -235,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);
modified external/libucl/src/ucl_parser.c
@@ -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 {
@@ -565,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;
@@ -575,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;
@@ -825,6 +825,11 @@ ucl_maybe_parse_number (ucl_object_t *obj,
			break;
		}
	}
+
	else if (endptr == end) {
+
		/* Just a number at the end of chunk */
+
		p = endptr;
+
		goto set_obj;
+
	}

	*pos = c;
	return EINVAL;
@@ -955,6 +960,37 @@ ucl_lex_json_string (struct ucl_parser *parser,
	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
@@ -1141,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) {
@@ -1165,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) {
@@ -1242,11 +1298,6 @@ ucl_parse_string_value (struct ucl_parser *parser,
		ucl_chunk_skipc (chunk, p);
	}

-
	if (p >= chunk->end) {
-
		ucl_set_err (parser, UCL_ESYNTAX, "unfinished value", &parser->err);
-
		return false;
-
	}
-

	return true;
}

@@ -1314,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;
@@ -1671,6 +1722,120 @@ ucl_parse_macro_value (struct ucl_parser *parser,
}

/**
+
 * 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
 * @param data the pointer to the beginning of a chunk
@@ -1680,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 == '[') {
@@ -1814,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) {
@@ -1829,44 +1995,46 @@ 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 */
@@ -1946,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);
@@ -1968,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);
@@ -1987,8 +2155,8 @@ 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;

@@ -1997,7 +2165,7 @@ ucl_parser_add_chunk (struct ucl_parser *parser, const unsigned char *data,
		return false;
	}
	if (len == 0) {
-
		parser->top_obj = ucl_object_typed_new(UCL_OBJECT);
+
		parser->top_obj = ucl_object_new_full (UCL_OBJECT, priority);
		return true;
	}
	if (parser->state != UCL_STATE_ERROR) {
@@ -2012,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) {
@@ -2028,6 +2197,13 @@ ucl_parser_add_chunk (struct ucl_parser *parser, const unsigned char *data,
}

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)
{
modified external/libucl/src/ucl_util.c
@@ -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
@@ -430,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);
@@ -708,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;
@@ -751,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;
@@ -768,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) {
@@ -797,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);
	}
@@ -825,22 +846,67 @@ ucl_include_file (const unsigned char *data, size_t len,
#endif
	}

-
	parser->cur_file = realbuf;
+
	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);
		}
	}
-
	parser->cur_file = NULL;
+
	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;

@@ -852,6 +918,138 @@ ucl_include_file (const unsigned char *data, size_t len,
}

/**
+
 * 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
 * @param len length of data
@@ -860,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);
}

/**
@@ -881,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
@@ -956,14 +1142,16 @@ ucl_parser_add_file (struct ucl_parser *parser, const char *filename)
		return false;
	}

-
	parser->cur_file = realbuf;
+
	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);

	if (len > 0) {
		ucl_munmap (buf, len);
	}
-
	parser->cur_file = NULL;

	return ret;
}
@@ -987,6 +1175,9 @@ ucl_parser_add_fd (struct ucl_parser *parser, int fd)
		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);
@@ -1356,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)
{
@@ -1491,6 +1716,12 @@ ucl_object_new (void)
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;

	if (type != UCL_USERDATA) {
@@ -1501,10 +1732,12 @@ ucl_object_typed_new (ucl_type_t type)
			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;
@@ -1641,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)
{
@@ -1726,6 +1983,28 @@ ucl_array_find_index (const ucl_object_t *top, unsigned int index)
}

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

@@ -2104,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);
+
	}
+
}
added external/libucl/tests/basic/13.in
@@ -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"
added external/libucl/tests/basic/13.res
@@ -0,0 +1,8 @@
+
key = "value_orig";
+
key = "value1";
+
key = "value2";
+
key = "value3";
+
key_pri = "priority2";
+
key_trace1 = "pri1";
+
key_trace2 = "pri2";
+

added external/libucl/tests/basic/include_dir/invalid.conf
@@ -0,0 +1 @@
+
@@@@ BAD UCL ~~~~
added external/libucl/tests/basic/include_dir/pri1.conf
@@ -0,0 +1,2 @@
+
key_pri = priority1;
+
key_trace1 = pri1;
added external/libucl/tests/basic/include_dir/pri2.conf
@@ -0,0 +1,2 @@
+
key_pri = priority2;
+
key_trace2 = pri2;
added external/libucl/tests/basic/include_dir/test1.conf
@@ -0,0 +1 @@
+
key = value1;
added external/libucl/tests/basic/include_dir/test2.conf
@@ -0,0 +1 @@
+
key = value2;
added external/libucl/tests/basic/include_dir/test3.conf
@@ -0,0 +1 @@
+
key = value3;
modified external/libucl/utils/objdump.c
@@ -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);
			}