]> git.ipfire.org Git - thirdparty/rspamd.git/commitdiff
Import the recent libucl.
authorVsevolod Stakhov <vsevolod@highsecure.ru>
Tue, 28 Jul 2015 15:57:15 +0000 (16:57 +0100)
committerVsevolod Stakhov <vsevolod@highsecure.ru>
Tue, 28 Jul 2015 15:57:15 +0000 (16:57 +0100)
contrib/libucl/CMakeLists.txt
contrib/libucl/ucl.h
contrib/libucl/ucl_emitter.c
contrib/libucl/ucl_emitter_utils.c
contrib/libucl/ucl_hash.c
contrib/libucl/ucl_internal.h
contrib/libucl/ucl_parser.c
contrib/libucl/ucl_util.c

index 80f50155ee87d90cf8059b33d19ae01c347662ec..a1e91948487b84d701deafa9bf0790d2d729720d 100644 (file)
@@ -5,7 +5,8 @@ SET(UCLSRC            ucl_util.c
                       ucl_emitter_utils.c
                       ucl_hash.c
                       ucl_schema.c
-                      lua_ucl.c)
+                      lua_ucl.c
+                                         ucl_msgpack.c)
 
 
 SET (LIB_TYPE STATIC)
index 0fbd5b822660143e5644ce8fcba3832e37a1b863..4d417eebefc791cee4e16a8c55f5f1a341b0d091 100644 (file)
@@ -135,7 +135,9 @@ typedef enum ucl_emitter {
        UCL_EMIT_JSON = 0, /**< Emit fine formatted JSON */
        UCL_EMIT_JSON_COMPACT, /**< Emit compacted JSON */
        UCL_EMIT_CONFIG, /**< Emit human readable config format */
-       UCL_EMIT_YAML /**< Emit embedded YAML format */
+       UCL_EMIT_YAML, /**< Emit embedded YAML format */
+       UCL_EMIT_MSGPACK, /**< Emit msgpack output */
+       UCL_EMIT_MAX /**< Unsupported emitter type */
 } ucl_emitter_t;
 
 /**
@@ -622,6 +624,19 @@ UCL_EXTERN const char* ucl_object_tolstring (const ucl_object_t *obj, size_t *tl
 UCL_EXTERN const ucl_object_t* ucl_object_find_key (const ucl_object_t *obj,
                const char *key);
 
+/**
+ * Return object identified by a key in the specified object, if the first key is
+ * not found then look for the next one. This process is repeated unless
+ * the next argument in the list is not NULL. So, `ucl_object_find_any_key(obj, key, NULL)`
+ * is equal to `ucl_object_find_key(obj, key)`
+ * @param obj object to get a key from (must be of type UCL_OBJECT)
+ * @param key key to search
+ * @param ... list of alternative keys to search (NULL terminated)
+ * @return object matching the specified key or NULL if key was not found
+ */
+UCL_EXTERN const ucl_object_t* ucl_object_find_any_key (const ucl_object_t *obj,
+               const char *key, ...);
+
 /**
  * Return object identified by a fixed size key in the specified object
  * @param obj object to get a key from (must be of type UCL_OBJECT)
@@ -882,6 +897,18 @@ UCL_EXTERN bool ucl_parser_add_chunk_priority (struct ucl_parser *parser,
 UCL_EXTERN bool ucl_parser_add_string (struct ucl_parser *parser,
                const char *data,size_t len);
 
+/**
+ * Load ucl object from a string
+ * @param parser parser structure
+ * @param data the pointer to the string
+ * @param len the length of the string, if `len` is 0 then `data` must be zero-terminated string
+ * @param priority the desired priority of a chunk (only 4 least significant bits
+ * are considered for this parameter)
+ * @return true if string has been added and false in case of error
+ */
+UCL_EXTERN bool ucl_parser_add_string_priority (struct ucl_parser *parser,
+               const char *data, size_t len, unsigned priority);
+
 /**
  * Load and add data from a file
  * @param parser parser structure
@@ -892,6 +919,18 @@ UCL_EXTERN bool ucl_parser_add_string (struct ucl_parser *parser,
 UCL_EXTERN bool ucl_parser_add_file (struct ucl_parser *parser,
                const char *filename);
 
+/**
+ * Load and add data from a file
+ * @param parser parser structure
+ * @param filename the name of file
+ * @param err if *err is NULL it is set to parser error
+ * @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_file_priority (struct ucl_parser *parser,
+               const char *filename, unsigned priority);
+
 /**
  * Load and add data from a file descriptor
  * @param parser parser structure
@@ -902,6 +941,28 @@ UCL_EXTERN bool ucl_parser_add_file (struct ucl_parser *parser,
 UCL_EXTERN bool ucl_parser_add_fd (struct ucl_parser *parser,
                int fd);
 
+/**
+ * Load and add data from a file descriptor
+ * @param parser parser structure
+ * @param filename the name of file
+ * @param err if *err is NULL it is set to parser error
+ * @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_fd_priority (struct ucl_parser *parser,
+               int fd, unsigned priority);
+
+/**
+ * Provide a UCL_ARRAY of paths to search for include files. The object is
+ * copied so caller must unref the object.
+ * @param parser parser structure
+ * @param paths UCL_ARRAY of paths to search
+ * @return true if the path search array was replaced in the parser
+ */
+UCL_EXTERN bool ucl_set_include_path (struct ucl_parser *parser,
+               ucl_object_t *paths);
+
 /**
  * Get a top object for a parser (refcount is increased)
  * @param parser parser structure
@@ -911,11 +972,33 @@ UCL_EXTERN bool ucl_parser_add_fd (struct ucl_parser *parser,
 UCL_EXTERN ucl_object_t* ucl_parser_get_object (struct ucl_parser *parser);
 
 /**
- * Get the error string if failing
+ * Get the error string if parsing has been failed
  * @param parser parser object
+ * @return error description
  */
 UCL_EXTERN const char *ucl_parser_get_error(struct ucl_parser *parser);
 
+/**
+ * Get the code of the last error
+ * @param parser parser object
+ * @return error code
+ */
+UCL_EXTERN int ucl_parser_get_error_code(struct ucl_parser *parser);
+
+/**
+ * Get the current column number within parser
+ * @param parser parser object
+ * @return current column number
+ */
+UCL_EXTERN unsigned ucl_parser_get_column(struct ucl_parser *parser);
+
+/**
+ * Get the current line number within parser
+ * @param parser parser object
+ * @return current line number
+ */
+UCL_EXTERN unsigned ucl_parser_get_linenum(struct ucl_parser *parser);
+
 /**
  * Clear the error in the parser
  * @param parser parser object
@@ -1023,6 +1106,17 @@ struct ucl_emitter_context {
 UCL_EXTERN unsigned char *ucl_object_emit (const ucl_object_t *obj,
                enum ucl_emitter emit_type);
 
+/**
+ * Emit object to a string that can contain `\0` inside
+ * @param obj object
+ * @param emit_type if type is #UCL_EMIT_JSON then emit json, if type is
+ * #UCL_EMIT_CONFIG then emit config like object
+ * @param len the resulting length
+ * @return dump of an object (must be freed after using) or NULL in case of error
+ */
+UCL_EXTERN unsigned char *ucl_object_emit_len (const ucl_object_t *obj,
+               enum ucl_emitter emit_type, size_t *len);
+
 /**
  * Emit object to a string
  * @param obj object
index 9ddf3584a25b8f6c551a3e98c01ddccefc7e2ffc..12cd31c7d8279903967544844431d7d665c42cf1 100644 (file)
@@ -62,6 +62,7 @@ UCL_EMIT_TYPE_OPS(json);
 UCL_EMIT_TYPE_OPS(json_compact);
 UCL_EMIT_TYPE_OPS(config);
 UCL_EMIT_TYPE_OPS(yaml);
+UCL_EMIT_TYPE_OPS(msgpack);
 
 #define UCL_EMIT_TYPE_CONTENT(type) {  \
        .ucl_emitter_write_elt = ucl_emit_ ## type ## _elt,     \
@@ -71,12 +72,12 @@ UCL_EMIT_TYPE_OPS(yaml);
        .ucl_emitter_end_array = ucl_emit_ ## type ##_end_array \
 }
 
-
 const struct ucl_emitter_operations ucl_standartd_emitter_ops[] = {
        [UCL_EMIT_JSON] = UCL_EMIT_TYPE_CONTENT(json),
        [UCL_EMIT_JSON_COMPACT] = UCL_EMIT_TYPE_CONTENT(json_compact),
        [UCL_EMIT_CONFIG] = UCL_EMIT_TYPE_CONTENT(config),
-       [UCL_EMIT_YAML] = UCL_EMIT_TYPE_CONTENT(yaml)
+       [UCL_EMIT_YAML] = UCL_EMIT_TYPE_CONTENT(yaml),
+       [UCL_EMIT_MSGPACK] = UCL_EMIT_TYPE_CONTENT(msgpack)
 };
 
 /*
@@ -469,19 +470,139 @@ UCL_EMIT_TYPE_IMPL(json_compact, true)
 UCL_EMIT_TYPE_IMPL(config, false)
 UCL_EMIT_TYPE_IMPL(yaml, false)
 
+static void
+ucl_emit_msgpack_elt (struct ucl_emitter_context *ctx,
+               const ucl_object_t *obj, bool first, bool print_key)
+{
+       ucl_object_iter_t it;
+       struct ucl_object_userdata *ud;
+       const char *ud_out;
+       const ucl_object_t *cur, *celt;
+
+       switch (obj->type) {
+       case UCL_INT:
+               ucl_emitter_print_key_msgpack (print_key, ctx, obj);
+               ucl_emitter_print_int_msgpack (ctx, ucl_object_toint (obj));
+               break;
+
+       case UCL_FLOAT:
+       case UCL_TIME:
+               ucl_emitter_print_key_msgpack (print_key, ctx, obj);
+               ucl_emitter_print_double_msgpack (ctx, ucl_object_todouble (obj));
+               break;
+
+       case UCL_BOOLEAN:
+               ucl_emitter_print_key_msgpack (print_key, ctx, obj);
+               ucl_emitter_print_bool_msgpack (ctx, ucl_object_toboolean (obj));
+               break;
+
+       case UCL_STRING:
+               ucl_emitter_print_key_msgpack (print_key, ctx, obj);
+               ucl_emitter_print_string_msgpack (ctx, obj->value.sv, obj->len);
+               break;
+
+       case UCL_NULL:
+               ucl_emitter_print_key_msgpack (print_key, ctx, obj);
+               ucl_emitter_print_null_msgpack (ctx);
+               break;
+
+       case UCL_OBJECT:
+               ucl_emitter_print_key_msgpack (print_key, ctx, obj);
+               ucl_emit_msgpack_start_obj (ctx, obj, print_key);
+               it = ucl_object_iterate_new (obj);
+
+               while ((cur = ucl_object_iterate_safe (it, true)) != NULL) {
+                       LL_FOREACH (cur, celt) {
+                               ucl_emit_msgpack_elt (ctx, celt, false, true);
+                       }
+               }
+
+               ucl_object_iterate_free (it);
+               break;
+
+       case UCL_ARRAY:
+               ucl_emitter_print_key_msgpack (print_key, ctx, obj);
+               ucl_emit_msgpack_start_array (ctx, obj, print_key);
+               it = ucl_object_iterate_new (obj);
+
+               while ((cur = ucl_object_iterate_safe (it, true)) != NULL) {
+                       ucl_emit_msgpack_elt (ctx, cur, false, false);
+               }
+
+               ucl_object_iterate_free (it);
+               break;
+
+       case UCL_USERDATA:
+               ud = (struct ucl_object_userdata *)obj;
+               ucl_emitter_print_key_msgpack (print_key, ctx, obj);
+
+               if (ud->emitter) {
+                       ud_out = ud->emitter (obj->value.ud);
+                       if (ud_out == NULL) {
+                               ud_out = "null";
+                       }
+               }
+               ucl_emitter_print_string_msgpack (ctx, obj->value.sv, obj->len);
+               break;
+       }
+}
+
+static void
+ucl_emit_msgpack_start_obj (struct ucl_emitter_context *ctx,
+               const ucl_object_t *obj, bool print_key)
+{
+       ucl_emitter_print_object_msgpack (ctx, obj->len);
+}
+
+static void
+ucl_emit_msgpack_start_array (struct ucl_emitter_context *ctx,
+               const ucl_object_t *obj, bool print_key)
+{
+       ucl_emitter_print_array_msgpack (ctx, obj->len);
+}
+
+static void
+ucl_emit_msgpack_end_object (struct ucl_emitter_context *ctx,
+               const ucl_object_t *obj)
+{
+
+}
+
+static void
+ucl_emit_msgpack_end_array (struct ucl_emitter_context *ctx,
+               const ucl_object_t *obj)
+{
+
+}
+
 unsigned char *
 ucl_object_emit (const ucl_object_t *obj, enum ucl_emitter emit_type)
+{
+       return ucl_object_emit_len (obj, emit_type, NULL);
+}
+
+unsigned char *
+ucl_object_emit_len (const ucl_object_t *obj, enum ucl_emitter emit_type,
+               size_t *outlen)
 {
        unsigned char *res = NULL;
        struct ucl_emitter_functions *func;
+       UT_string *s;
+
        if (obj == NULL) {
                return NULL;
        }
 
        func = ucl_object_emit_memory_funcs ((void **)&res);
+       s = func->ud;
 
        if (func != NULL) {
                ucl_object_emit_full (obj, emit_type, func);
+
+               if (outlen != NULL) {
+                       *outlen = s->i;
+               }
+
                ucl_object_emit_funcs_free (func);
        }
 
index 91cad78bcbf6b7f09dd6e5e43d6a5f58e544d24f..95ac9a5d57764f053c67a48bab7c142e6ab69351 100644 (file)
@@ -62,6 +62,12 @@ static const struct ucl_emitter_context ucl_standard_emitters[] = {
                .id = UCL_EMIT_YAML,
                .func = NULL,
                .ops = &ucl_standartd_emitter_ops[UCL_EMIT_YAML]
+       },
+       [UCL_EMIT_MSGPACK] = {
+               .name = "msgpack",
+               .id = UCL_EMIT_MSGPACK,
+               .func = NULL,
+               .ops = &ucl_standartd_emitter_ops[UCL_EMIT_MSGPACK]
        }
 };
 
@@ -73,7 +79,7 @@ static const struct ucl_emitter_context ucl_standard_emitters[] = {
 const struct ucl_emitter_context *
 ucl_emit_get_standard_context (enum ucl_emitter emit_type)
 {
-       if (emit_type >= UCL_EMIT_JSON && emit_type <= UCL_EMIT_YAML) {
+       if (emit_type >= UCL_EMIT_JSON && emit_type < UCL_EMIT_MAX) {
                return &ucl_standard_emitters[emit_type];
        }
 
index 4730b82b8fae3560e949660a45883094ae4ddc06..183eac84f4a050d477d574a26a57cd638af422bb 100644 (file)
@@ -371,6 +371,11 @@ ucl_hash_iterate (ucl_hash_t *hashlin, ucl_hash_iter_t *iter)
 
        if (it == NULL) {
                it = UCL_ALLOC (sizeof (*it));
+
+               if (it == NULL) {
+                       return NULL;
+               }
+
                it->cur = &hashlin->ar.a[0];
                it->end = it->cur + hashlin->ar.n;
        }
index 836fcad80c333a2085d0327aa33c951e55da11d4..fe926dbaa4332b09fab13637c16d4415ec611708 100644 (file)
@@ -192,9 +192,11 @@ struct ucl_parser {
        unsigned int recursion;
        int flags;
        unsigned default_priority;
+       int err_code;
        ucl_object_t *top_obj;
        ucl_object_t *cur_obj;
        ucl_object_t *trash_objs;
+       ucl_object_t *includepaths;
        char *cur_file;
        struct ucl_macro *macroes;
        struct ucl_stack *stack;
@@ -222,13 +224,21 @@ size_t ucl_unescape_json_string (char *str, size_t len);
  * Handle include macro
  * @param data include data
  * @param len length of data
+ * @param args UCL object representing arguments to the macro
  * @param ud user data
- * @param err error ptr
  * @return
  */
 bool ucl_include_handler (const unsigned char *data, size_t len,
                const ucl_object_t *args, void* ud);
 
+/**
+ * Handle tryinclude macro
+ * @param data include data
+ * @param len length of data
+ * @param args UCL object representing arguments to the macro
+ * @param ud user data
+ * @return
+ */
 bool ucl_try_include_handler (const unsigned char *data, size_t len,
                const ucl_object_t *args, void* ud);
 
@@ -236,17 +246,41 @@ bool ucl_try_include_handler (const unsigned char *data, size_t len,
  * Handle includes macro
  * @param data include data
  * @param len length of data
+ * @param args UCL object representing arguments to the macro
  * @param ud user data
- * @param err error ptr
  * @return
  */
 bool ucl_includes_handler (const unsigned char *data, size_t len,
                const ucl_object_t *args, void* ud);
 
+/**
+ * Handle priority macro
+ * @param data include data
+ * @param len length of data
+ * @param args UCL object representing arguments to the macro
+ * @param ud user data
+ * @return
+ */
+bool ucl_priority_handler (const unsigned char *data, size_t len,
+               const ucl_object_t *args, void* ud);
+
+/**
+ * Handle load macro
+ * @param data include data
+ * @param len length of data
+ * @param args UCL object representing arguments to the macro
+ * @param ud user data
+ * @return
+ */
+bool ucl_load_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);
 size_t ucl_strlcpy_tolower (char *dst, const char *src, size_t siz);
 
+char *ucl_strnstr (const char *s, const char *find, int len);
+char *ucl_strncasestr (const char *s, const char *find, int len);
 
 #ifdef __GNUC__
 static inline void
@@ -398,4 +432,64 @@ unsigned char * ucl_object_emit_single_json (const ucl_object_t *obj);
  */
 bool ucl_maybe_long_string (const ucl_object_t *obj);
 
+/**
+ * Print integer to the msgpack output
+ * @param ctx
+ * @param val
+ */
+void ucl_emitter_print_int_msgpack (struct ucl_emitter_context *ctx,
+               int64_t val);
+/**
+ * Print integer to the msgpack output
+ * @param ctx
+ * @param val
+ */
+void ucl_emitter_print_double_msgpack (struct ucl_emitter_context *ctx,
+               double val);
+/**
+ * Print double to the msgpack output
+ * @param ctx
+ * @param val
+ */
+void ucl_emitter_print_bool_msgpack (struct ucl_emitter_context *ctx,
+               bool val);
+/**
+ * Print string to the msgpack output
+ * @param ctx
+ * @param s
+ * @param len
+ */
+void ucl_emitter_print_string_msgpack (struct ucl_emitter_context *ctx,
+               const char *s, size_t len);
+
+/**
+ * Print array preamble for msgpack
+ * @param ctx
+ * @param len
+ */
+void ucl_emitter_print_array_msgpack (struct ucl_emitter_context *ctx,
+               size_t len);
+
+/**
+ * Print object preamble for msgpack
+ * @param ctx
+ * @param len
+ */
+void ucl_emitter_print_object_msgpack (struct ucl_emitter_context *ctx,
+               size_t len);
+/**
+ * Print NULL to the msgpack output
+ * @param ctx
+ */
+void ucl_emitter_print_null_msgpack (struct ucl_emitter_context *ctx);
+/**
+ * Print object's key if needed to the msgpakc output
+ * @param print_key
+ * @param ctx
+ * @param obj
+ */
+void ucl_emitter_print_key_msgpack (bool print_key,
+               struct ucl_emitter_context *ctx,
+               const ucl_object_t *obj);
+
 #endif /* UCL_INTERNAL_H_ */
index 804744972fe2422871662d234573a548f72555eb..8f177938c7978340b319a9be9eb1c5306d7be9c6 100644 (file)
@@ -67,6 +67,7 @@ ucl_set_err (struct ucl_parser *parser, int code, const char *str, UT_string **e
        else {
                filename = "<unknown>";
        }
+
        if (chunk->pos < chunk->end) {
                if (isgraph (*chunk->pos)) {
                        fmt_string = "error while parsing %s: "
@@ -84,6 +85,8 @@ ucl_set_err (struct ucl_parser *parser, int code, const char *str, UT_string **e
                ucl_create_err (err, "error while parsing %s: at the end of chunk: %s",
                        filename, str);
        }
+
+       parser->err_code = code;
 }
 
 /**
@@ -513,7 +516,7 @@ ucl_copy_or_store_ptr (struct ucl_parser *parser,
                /* Copy string */
                *dst = UCL_ALLOC (in_len + 1);
                if (*dst == NULL) {
-                       ucl_set_err (parser, 0, "cannot allocate memory for a string",
+                       ucl_set_err (parser, UCL_EINTERNAL, "cannot allocate memory for a string",
                                        &parser->err);
                        return false;
                }
@@ -585,7 +588,7 @@ 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, 0, "cannot allocate memory for an object",
+               ucl_set_err (parser, UCL_EINTERNAL, "cannot allocate memory for an object",
                                &parser->err);
                ucl_object_unref (obj);
                return NULL;
@@ -858,6 +861,7 @@ set_obj:
  * Parse possible number
  * @param parser
  * @param chunk
+ * @param obj
  * @return true if a number has been parsed
  */
 static bool
@@ -877,7 +881,8 @@ ucl_lex_number (struct ucl_parser *parser,
                return true;
        }
        else if (ret == ERANGE) {
-               ucl_set_err (parser, ERANGE, "numeric value out of range", &parser->err);
+               ucl_set_err (parser, UCL_ESYNTAX, "numeric value out of range",
+                               &parser->err);
        }
 
        return false;
@@ -887,6 +892,9 @@ ucl_lex_number (struct ucl_parser *parser,
  * Parse quoted string with possible escapes
  * @param parser
  * @param chunk
+ * @param need_unescape
+ * @param ucl_escape
+ * @param var_expand
  * @return true if a string has been parsed
  */
 static bool
@@ -973,6 +981,7 @@ ucl_parser_append_elt (struct ucl_parser *parser, ucl_hash_t *cont,
                /* Implicit array */
                top->flags |= UCL_OBJECT_MULTIVALUE;
                DL_APPEND (top, elt);
+               parser->stack->obj->len ++;
        }
        else {
                if ((top->flags & UCL_OBJECT_MULTIVALUE) != 0) {
@@ -996,6 +1005,8 @@ ucl_parser_append_elt (struct ucl_parser *parser, ucl_hash_t *cont,
  * Parse a key in an object
  * @param parser
  * @param chunk
+ * @param next_key
+ * @param end_of_object
  * @return true if a key has been parsed
  */
 static bool
@@ -1246,6 +1257,8 @@ ucl_parse_key (struct ucl_parser *parser, struct ucl_chunk *chunk,
  * Parse a cl string
  * @param parser
  * @param chunk
+ * @param var_expand
+ * @param need_unescape
  * @return true if a key has been parsed
  */
 static bool
@@ -1315,6 +1328,8 @@ ucl_parse_string_value (struct ucl_parser *parser,
  * @param chunk
  * @param term
  * @param term_len
+ * @param beg
+ * @param var_expand
  * @return size of multiline string or 0 in case of error
  */
 static int
@@ -1505,6 +1520,7 @@ ucl_parse_value (struct ucl_parser *parser, struct ucl_chunk *chunk)
                                                }
 
                                                obj->type = UCL_STRING;
+                                               obj->flags |= UCL_OBJECT_MULTILINE;
                                                if ((str_len = ucl_copy_or_store_ptr (parser, c,
                                                                &obj->trash_stack[UCL_TRASH_VALUE],
                                                                &obj->value.sv, str_len - 1, false,
@@ -1552,7 +1568,7 @@ parse_string:
                        }
                        str_len = chunk->pos - c - stripped_spaces;
                        if (str_len <= 0) {
-                               ucl_set_err (parser, 0, "string value must not be empty",
+                               ucl_set_err (parser, UCL_ESYNTAX, "string value must not be empty",
                                                &parser->err);
                                return false;
                        }
@@ -1675,6 +1691,9 @@ ucl_parse_after_value (struct ucl_parser *parser, struct ucl_chunk *chunk)
  * Handle macro data
  * @param parser
  * @param chunk
+ * @param marco
+ * @param macro_start
+ * @param macro_len
  * @return
  */
 static bool
@@ -1813,6 +1832,7 @@ ucl_parse_macro_arguments (struct ucl_parser *parser,
                        if (chunk->remain == 0) {
                                goto restore_chunk;
                        }
+                       args_len ++;
                        ucl_chunk_skipc (chunk, p);
                        break;
                case 99:
@@ -1865,8 +1885,6 @@ restore_chunk:
 /**
  * Handle the main states of rcl parser
  * @param parser parser structure
- * @param data the pointer to the beginning of a chunk
- * @param len the length of a chunk
  * @return true if chunk has been parsed and false in case of error
  */
 static bool
@@ -2096,8 +2114,11 @@ ucl_parser_new (int flags)
        ucl_parser_register_macro (new, "include", ucl_include_handler, new);
        ucl_parser_register_macro (new, "try_include", ucl_try_include_handler, new);
        ucl_parser_register_macro (new, "includes", ucl_includes_handler, new);
+       ucl_parser_register_macro (new, "priority", ucl_priority_handler, new);
+       ucl_parser_register_macro (new, "load", ucl_load_handler, new);
 
        new->flags = flags;
+       new->includepaths = NULL;
 
        /* Initial assumption about filevars */
        ucl_parser_set_filevars (new, NULL, false);
@@ -2258,8 +2279,8 @@ ucl_parser_add_chunk (struct ucl_parser *parser, const unsigned char *data,
 }
 
 bool
-ucl_parser_add_string (struct ucl_parser *parser, const char *data,
-               size_t len)
+ucl_parser_add_string_priority (struct ucl_parser *parser, const char *data,
+               size_t len, unsigned priority)
 {
        if (data == NULL) {
                ucl_create_err (&parser->err, "invalid string added");
@@ -2269,5 +2290,40 @@ ucl_parser_add_string (struct ucl_parser *parser, const char *data,
                len = strlen (data);
        }
 
-       return ucl_parser_add_chunk (parser, (const unsigned char *)data, len);
+       return ucl_parser_add_chunk_priority (parser,
+                       (const unsigned char *)data, len, priority);
+}
+
+bool
+ucl_parser_add_string (struct ucl_parser *parser, const char *data,
+               size_t len)
+{
+       if (parser == NULL) {
+               return false;
+       }
+
+       return ucl_parser_add_string_priority (parser,
+                       (const unsigned char *)data, len, parser->default_priority);
+}
+
+bool
+ucl_set_include_path (struct ucl_parser *parser, ucl_object_t *paths)
+{
+       if (parser == NULL || paths == NULL) {
+               return false;
+       }
+
+       if (parser->includepaths == NULL) {
+               parser->includepaths = ucl_object_copy (paths);
+       }
+       else {
+               ucl_object_unref (parser->includepaths);
+               parser->includepaths = ucl_object_copy (paths);
+       }
+
+       if (parser->includepaths == NULL) {
+               return false;
+       }
+
+       return true;
 }
index c8ea06c2687f50c981db86b37df3612a520408c7..bdac1d214903e2c0c5b107614c0e49217497a03b 100644 (file)
@@ -1,4 +1,5 @@
 /* Copyright (c) 2013, Vsevolod Stakhov
+ * Copyright (c) 2015 Allan Jude <allanjude@freebsd.org>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -25,6 +26,7 @@
 #include "ucl_internal.h"
 #include "ucl_chartable.h"
 #include "kvec.h"
+#include <stdarg.h>
 
 #ifndef _WIN32
 #include <glob.h>
@@ -425,7 +427,7 @@ ucl_copy_value_trash (const ucl_object_t *obj)
        return obj->trash_stack[UCL_TRASH_VALUE];
 }
 
-UCL_EXTERN ucl_object_t*
+ucl_object_t*
 ucl_parser_get_object (struct ucl_parser *parser)
 {
        if (parser->state != UCL_STATE_ERROR && parser->top_obj != NULL) {
@@ -435,7 +437,7 @@ ucl_parser_get_object (struct ucl_parser *parser)
        return NULL;
 }
 
-UCL_EXTERN void
+void
 ucl_parser_free (struct ucl_parser *parser)
 {
        struct ucl_stack *stack, *stmp;
@@ -453,6 +455,10 @@ ucl_parser_free (struct ucl_parser *parser)
                ucl_object_unref (parser->top_obj);
        }
 
+       if (parser->includepaths != NULL) {
+               ucl_object_unref (parser->includepaths);
+       }
+
        LL_FOREACH_SAFE (parser->stack, stack, stmp) {
                free (stack);
        }
@@ -487,29 +493,61 @@ ucl_parser_free (struct ucl_parser *parser)
        UCL_FREE (sizeof (struct ucl_parser), parser);
 }
 
-UCL_EXTERN const char *
+const char *
 ucl_parser_get_error(struct ucl_parser *parser)
 {
        if (parser == NULL) {
                return NULL;
        }
 
-       if (parser->err == NULL)
+       if (parser->err == NULL) {
                return NULL;
+       }
 
        return utstring_body (parser->err);
 }
 
-UCL_EXTERN void
+int
+ucl_parser_get_error_code(struct ucl_parser *parser)
+{
+       if (parser == NULL) {
+               return 0;
+       }
+
+       return parser->err_code;
+}
+
+unsigned
+ucl_parser_get_column(struct ucl_parser *parser)
+{
+       if (parser == NULL || parser->chunks == NULL) {
+               return 0;
+       }
+
+       return parser->chunks->column;
+}
+
+unsigned
+ucl_parser_get_linenum(struct ucl_parser *parser)
+{
+       if (parser == NULL || parser->chunks == NULL) {
+               return 0;
+       }
+
+       return parser->chunks->line;
+}
+
+void
 ucl_parser_clear_error(struct ucl_parser *parser)
 {
        if (parser != NULL && parser->err != NULL) {
                utstring_free(parser->err);
                parser->err = NULL;
+               parser->err_code = 0;
        }
 }
 
-UCL_EXTERN bool
+bool
 ucl_pubkey_add (struct ucl_parser *parser, const unsigned char *key, size_t len)
 {
 #ifndef HAVE_OPENSSL
@@ -769,7 +807,7 @@ 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,
-               unsigned priority)
+               bool use_prefix, const char *prefix, const char *target, unsigned priority)
 {
 
        bool res;
@@ -842,20 +880,27 @@ ucl_include_url (const unsigned char *data, size_t len,
 static bool
 ucl_include_file_single (const unsigned char *data, size_t len,
                struct ucl_parser *parser, bool check_signature, bool must_exist,
-               unsigned priority)
+               bool use_prefix, const char *prefix, const char *target,
+               bool soft_fail, unsigned priority)
 {
        bool res;
        struct ucl_chunk *chunk;
        unsigned char *buf = NULL;
-       char *old_curfile;
-       size_t buflen;
+       char *old_curfile, *ext;
+       size_t buflen = 0;
        char filebuf[PATH_MAX], realbuf[PATH_MAX];
        int prev_state;
        struct ucl_variable *cur_var, *tmp_var, *old_curdir = NULL,
                        *old_filename = NULL;
+       ucl_object_t *nest_obj = NULL, *old_obj = NULL, *new_obj = NULL;
+       ucl_hash_t *container = NULL;
+       struct ucl_stack *st = NULL;
 
        snprintf (filebuf, sizeof (filebuf), "%.*s", (int)len, data);
        if (ucl_realpath (filebuf, realbuf) == NULL) {
+               if (soft_fail) {
+                       return false;
+               }
                if (!must_exist) {
                        return true;
                }
@@ -867,12 +912,18 @@ ucl_include_file_single (const unsigned char *data, size_t len,
 
        if (parser->cur_file && strcmp (realbuf, parser->cur_file) == 0) {
                /* We are likely including the file itself */
+               if (soft_fail) {
+                       return false;
+               }
                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)) {
+               if (soft_fail) {
+                       return false;
+               }
                return (!must_exist || false);
        }
 
@@ -920,6 +971,111 @@ ucl_include_file_single (const unsigned char *data, size_t len,
        prev_state = parser->state;
        parser->state = UCL_STATE_INIT;
 
+       if (use_prefix && prefix == NULL) {
+               /* Auto generate a key name based on the included filename */
+               prefix = basename (realbuf);
+               ext = strrchr(prefix, '.');
+               if (ext != NULL && (strcmp (ext, ".conf") == 0 || strcmp (ext, ".ucl") == 0)) {
+                       /* Strip off .conf or .ucl */
+                       *ext = '\0';
+               }
+       }
+       if (prefix != NULL) {
+               /* This is a prefixed include */
+               container = parser->stack->obj->value.ov;
+
+               old_obj = __DECONST (ucl_object_t *, ucl_hash_search (container, prefix, strlen (prefix)));
+
+               if (strcasecmp (target, "array") == 0 && old_obj == NULL) {
+                       /* Create an array with key: prefix */
+                       old_obj = ucl_object_new_full (UCL_ARRAY, priority);
+                       old_obj->key = prefix;
+                       old_obj->keylen = strlen (prefix);
+                       ucl_copy_key_trash(old_obj);
+                       old_obj->prev = old_obj;
+                       old_obj->next = NULL;
+
+                       container = ucl_hash_insert_object (container, old_obj,
+                                       parser->flags & UCL_PARSER_KEY_LOWERCASE);
+                       parser->stack->obj->len ++;
+
+                       nest_obj = ucl_object_new_full (UCL_OBJECT, priority);
+                       nest_obj->prev = nest_obj;
+                       nest_obj->next = NULL;
+
+                       ucl_array_append (old_obj, nest_obj);
+               }
+               else if (old_obj == NULL) {
+                       /* Create an object with key: prefix */
+                       nest_obj = ucl_object_new_full (UCL_OBJECT, priority);
+                       nest_obj->key = prefix;
+                       nest_obj->keylen = strlen (prefix);
+                       ucl_copy_key_trash(nest_obj);
+                       nest_obj->prev = nest_obj;
+                       nest_obj->next = NULL;
+
+                       container = ucl_hash_insert_object (container, nest_obj,
+                                       parser->flags & UCL_PARSER_KEY_LOWERCASE);
+                       parser->stack->obj->len ++;
+               }
+               else if (strcasecmp (target, "array") == 0 || ucl_object_type(old_obj) == UCL_ARRAY) {
+                       if (ucl_object_type(old_obj) == UCL_ARRAY) {
+                               /* Append to the existing array */
+                               nest_obj = ucl_object_new_full (UCL_OBJECT, priority);
+                               nest_obj->prev = nest_obj;
+                               nest_obj->next = NULL;
+
+                               ucl_array_append (old_obj, nest_obj);
+                       }
+                       else {
+                               /* Convert the object to an array */
+                               new_obj = ucl_object_typed_new (UCL_ARRAY);
+                               new_obj->key = old_obj->key;
+                               new_obj->keylen = old_obj->keylen;
+                               new_obj->flags |= UCL_OBJECT_MULTIVALUE;
+                               new_obj->prev = new_obj;
+                               new_obj->next = NULL;
+
+                               nest_obj = ucl_object_new_full (UCL_OBJECT, priority);
+                               nest_obj->prev = nest_obj;
+                               nest_obj->next = NULL;
+
+                               ucl_array_append (new_obj, old_obj);
+                               ucl_array_append (new_obj, nest_obj);
+                               ucl_hash_replace (container, old_obj, new_obj);
+                       }
+               }
+               else {
+                       if (ucl_object_type (old_obj) == UCL_OBJECT) {
+                               /* Append to existing Object*/
+                               nest_obj = old_obj;
+                       }
+                       else {
+                               /* The key is not an object */
+                               ucl_create_err (&parser->err,
+                                               "Conflicting type for key: %s",
+                                               prefix);
+                               return false;
+                       }
+               }
+
+                /* Put all of the content of the include inside that object */
+               parser->stack->obj->value.ov = container;
+
+               if (nest_obj != NULL) {
+                       st = UCL_ALLOC (sizeof (struct ucl_stack));
+                       if (st == NULL) {
+                               ucl_create_err (&parser->err, "cannot allocate memory for an object");
+                               ucl_object_unref (nest_obj);
+                               return NULL;
+                       }
+                       st->obj = nest_obj;
+                       st->level = parser->stack->level;
+                       LL_PREPEND (parser->stack, st);
+                       parser->cur_obj = nest_obj;
+               }
+       }
+
        res = ucl_parser_add_chunk_priority (parser, buf, buflen, priority);
        if (!res && !must_exist) {
                /* Free error */
@@ -928,6 +1084,12 @@ ucl_include_file_single (const unsigned char *data, size_t len,
                parser->state = UCL_STATE_AFTER_VALUE;
        }
 
+       /* Stop nesting the include, take 1 level off the stack */
+       if (prefix != NULL && nest_obj != NULL) {
+               parser->stack = st->next;
+               UCL_FREE (sizeof (struct ucl_stack), st);
+       }
+
        /* Remove chunk from the stack */
        chunk = parser->chunks;
        if (chunk != NULL) {
@@ -983,7 +1145,8 @@ ucl_include_file_single (const unsigned char *data, size_t len,
 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)
+               bool allow_glob, bool use_prefix, const char *prefix,
+               const char *target, bool soft_fail, unsigned priority)
 {
        const unsigned char *p = data, *end = data + len;
        bool need_glob = false;
@@ -994,7 +1157,7 @@ ucl_include_file (const unsigned char *data, size_t len,
 #ifndef _WIN32
        if (!allow_glob) {
                return ucl_include_file_single (data, len, parser, check_signature,
-                       must_exist, priority);
+                       must_exist, use_prefix, prefix, target, soft_fail, priority);
        }
        else {
                /* Check for special symbols in a filename */
@@ -1008,14 +1171,18 @@ ucl_include_file (const unsigned char *data, size_t len,
                if (need_glob) {
                        glob_t globbuf;
                        memset (&globbuf, 0, sizeof (globbuf));
-                       ucl_strlcpy (glob_pattern, (const char *)data, sizeof (glob_pattern));
+                       ucl_strlcpy (glob_pattern, (const char *)data,
+                               (len + 1 < sizeof (glob_pattern) ? len + 1 : 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)) {
+                                               must_exist, use_prefix, prefix, target, soft_fail, priority)) {
+                                       if (soft_fail) {
+                                               continue;
+                                       }
                                        globfree (&globbuf);
                                        return false;
                                }
@@ -1031,14 +1198,14 @@ ucl_include_file (const unsigned char *data, size_t len,
                }
                else {
                        return ucl_include_file_single (data, len, parser, check_signature,
-                               must_exist, priority);
+                               must_exist, use_prefix, prefix, target, soft_fail, priority);
                }
        }
 #else
        /* Win32 compilers do not support globbing. Therefore, for Win32,
           treat allow_glob/need_glob as a NOOP and just return */
        return ucl_include_file_single (data, len, parser, check_signature,
-               must_exist, priority);
+               must_exist, use_prefix, prefix, target, soft_fail, priority);
 #endif
        
        return true;
@@ -1060,52 +1227,107 @@ ucl_include_common (const unsigned char *data, size_t len,
                bool default_try,
                bool default_sign)
 {
-       bool try_load, allow_glob, allow_url, need_sign;
+       bool try_load, allow_glob, allow_url, need_sign, use_prefix, search;
+       const char *prefix, *target;
        unsigned priority;
        const ucl_object_t *param;
-       ucl_object_iter_t it = NULL;
+       ucl_object_iter_t it = NULL, ip = NULL;
+       char ipath[PATH_MAX];
 
        /* Default values */
        try_load = default_try;
        allow_glob = false;
        allow_url = true;
        need_sign = default_sign;
+       use_prefix = false;
+       prefix = NULL;
+       target = "object";
        priority = 0;
+       search = false;
 
        /* 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) {
+                               if (strncmp (param->key, "try", param->keylen) == 0) {
                                        try_load = ucl_object_toboolean (param);
                                }
-                               else if (strcmp (param->key, "sign") == 0) {
+                               else if (strncmp (param->key, "sign", param->keylen) == 0) {
                                        need_sign = ucl_object_toboolean (param);
                                }
-                               else if (strcmp (param->key, "glob") == 0) {
-                                       allow_glob =  ucl_object_toboolean (param);
+                               else if (strncmp (param->key, "glob", param->keylen) == 0) {
+                                       allow_glob = ucl_object_toboolean (param);
+                               }
+                               else if (strncmp (param->key, "url", param->keylen) == 0) {
+                                       allow_url = ucl_object_toboolean (param);
                                }
-                               else if (strcmp (param->key, "url") == 0) {
-                                       allow_url =  ucl_object_toboolean (param);
+                               else if (strncmp (param->key, "prefix", param->keylen) == 0) {
+                                       use_prefix = ucl_object_toboolean (param);
+                               }
+                       }
+                       else if (param->type == UCL_STRING) {
+                               if (strncmp (param->key, "key", param->keylen) == 0) {
+                                       prefix = ucl_object_tostring (param);
+                               }
+                               else if (strncmp (param->key, "target", param->keylen) == 0) {
+                                       target = ucl_object_tostring (param);
+                               }
+                       }
+                       else if (param->type == UCL_ARRAY) {
+                               if (strncmp (param->key, "path", param->keylen) == 0) {
+                                       ucl_set_include_path (parser, __DECONST(ucl_object_t *, param));
                                }
                        }
                        else if (param->type == UCL_INT) {
-                               if (strcmp (param->key, "priority") == 0) {
+                               if (strncmp (param->key, "priority", param->keylen) == 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);
+       if (parser->includepaths == NULL) {
+               if (allow_url && ucl_strnstr (data, "://", len) != NULL) {
+                       /* Globbing is not used for URL's */
+                       return ucl_include_url (data, len, parser, need_sign,
+                                       !try_load, use_prefix, prefix, target, priority);
+               }
+               else if (data != NULL) {
+                       /* Try to load a file */
+                       return ucl_include_file (data, len, parser, need_sign, !try_load,
+                                       allow_glob, use_prefix, prefix, target, false, priority);
+               }
        }
-       else if (allow_url) {
-               /* Globbing is not used for URL's */
-               return ucl_include_url (data, len, parser, need_sign, !try_load,
-                               priority);
+       else {
+               if (allow_url && ucl_strnstr (data, "://", len) != NULL) {
+                       /* Globbing is not used for URL's */
+                       return ucl_include_url (data, len, parser, need_sign,
+                                       !try_load, use_prefix, prefix, target, priority);
+               }
+
+               ip = ucl_object_iterate_new (parser->includepaths);
+               while ((param = ucl_object_iterate_safe (ip, true)) != NULL) {
+                       if (ucl_object_type(param) == UCL_STRING) {
+                               snprintf (ipath, sizeof (ipath), "%s/%.*s", ucl_object_tostring(param),
+                                               (int)len, data);
+                               if ((search = ucl_include_file (ipath, strlen (ipath), parser, need_sign,
+                                               !try_load, allow_glob, use_prefix, prefix, target, true, priority))) {
+                                       if (!allow_glob) {
+                                               break;
+                                       }
+                               }
+                       }
+               }
+               ucl_object_iterate_free (ip);
+               if (search == true) {
+                       return true;
+               }
+               else {
+                       ucl_create_err (&parser->err,
+                                       "cannot find file: %.*s in search path",
+                                       (int)len, data);
+                       return false;
+               }
        }
 
        return false;
@@ -1115,11 +1337,11 @@ ucl_include_common (const unsigned char *data, size_t len,
  * Handle include macro
  * @param data include data
  * @param len length of data
+ * @param args UCL object representing arguments to the macro
  * @param ud user data
- * @param err error ptr
  * @return
  */
-UCL_EXTERN bool
+bool
 ucl_include_handler (const unsigned char *data, size_t len,
                const ucl_object_t *args, void* ud)
 {
@@ -1132,11 +1354,11 @@ ucl_include_handler (const unsigned char *data, size_t len,
  * Handle includes macro
  * @param data include data
  * @param len length of data
+ * @param args UCL object representing arguments to the macro
  * @param ud user data
- * @param err error ptr
  * @return
  */
-UCL_EXTERN bool
+bool
 ucl_includes_handler (const unsigned char *data, size_t len,
                const ucl_object_t *args, void* ud)
 {
@@ -1145,8 +1367,15 @@ ucl_includes_handler (const unsigned char *data, size_t len,
        return ucl_include_common (data, len, args, parser, false, true);
 }
 
-
-UCL_EXTERN bool
+/**
+ * Handle tryinclude macro
+ * @param data include data
+ * @param len length of data
+ * @param args UCL object representing arguments to the macro
+ * @param ud user data
+ * @return
+ */
+bool
 ucl_try_include_handler (const unsigned char *data, size_t len,
                const ucl_object_t *args, void* ud)
 {
@@ -1155,7 +1384,201 @@ ucl_try_include_handler (const unsigned char *data, size_t len,
        return ucl_include_common (data, len, args, parser, true, false);
 }
 
-UCL_EXTERN bool
+/**
+ * Handle priority macro
+ * @param data include data
+ * @param len length of data
+ * @param args UCL object representing arguments to the macro
+ * @param ud user data
+ * @return
+ */
+bool
+ucl_priority_handler (const unsigned char *data, size_t len,
+               const ucl_object_t *args, void* ud)
+{
+       struct ucl_parser *parser = ud;
+       unsigned priority = 255;
+       const ucl_object_t *param;
+       bool found = false;
+       char *value = NULL, *leftover = NULL;
+       ucl_object_iter_t it = NULL;
+
+       if (parser == NULL) {
+               return false;
+       }
+
+       /* Process arguments */
+       if (args != NULL && args->type == UCL_OBJECT) {
+               while ((param = ucl_iterate_object (args, &it, true)) != NULL) {
+                       if (param->type == UCL_INT) {
+                               if (strncmp (param->key, "priority", param->keylen) == 0) {
+                                       priority = ucl_object_toint (param);
+                                       found = true;
+                               }
+                       }
+               }
+       }
+
+       if (len > 0) {
+               value = malloc(len + 1);
+               ucl_strlcpy(value, (const char *)data, len + 1);
+               priority = strtol(value, &leftover, 10);
+               if (*leftover != '\0') {
+                       ucl_create_err (&parser->err, "Invalid priority value in macro: %s",
+                               value);
+                       free(value);
+                       return false;
+               }
+               free(value);
+               found = true;
+       }
+
+       if (found == true) {
+               parser->chunks->priority = priority;
+               return true;
+       }
+
+       ucl_create_err (&parser->err, "Unable to parse priority macro");
+       return false;
+}
+
+/**
+ * Handle load macro
+ * @param data include data
+ * @param len length of data
+ * @param args UCL object representing arguments to the macro
+ * @param ud user data
+ * @return
+ */
+bool
+ucl_load_handler (const unsigned char *data, size_t len,
+               const ucl_object_t *args, void* ud)
+{
+       struct ucl_parser *parser = ud;
+       const ucl_object_t *param;
+       ucl_object_t *obj, *old_obj;
+       ucl_object_iter_t it = NULL;
+       bool try_load, multiline, test;
+       const char *target, *prefix;
+       char *load_file, *tmp;
+       unsigned char *buf;
+       size_t buflen;
+       unsigned priority;
+       int64_t iv;
+       ucl_hash_t *container = NULL;
+       enum ucl_string_flags flags;
+
+       /* Default values */
+       try_load = false;
+       multiline = false;
+       test = false;
+       target = "string";
+       prefix = NULL;
+       load_file = NULL;
+       buf = NULL;
+       buflen = 0;
+       priority = 0;
+       obj = NULL;
+       old_obj = NULL;
+       flags = 0;
+
+       if (parser == NULL) {
+               return false;
+       }
+
+       /* Process arguments */
+       if (args != NULL && args->type == UCL_OBJECT) {
+               while ((param = ucl_iterate_object (args, &it, true)) != NULL) {
+                       if (param->type == UCL_BOOLEAN) {
+                               if (strncmp (param->key, "try", param->keylen) == 0) {
+                                       try_load = ucl_object_toboolean (param);
+                               }
+                               else if (strncmp (param->key, "multiline", param->keylen) == 0) {
+                                       multiline = ucl_object_toboolean (param);
+                               }
+                               else if (strncmp (param->key, "escape", param->keylen) == 0) {
+                                       test = ucl_object_toboolean (param);
+                                       if (test) {
+                                               flags |= UCL_STRING_ESCAPE;
+                                       }
+                               }
+                               else if (strncmp (param->key, "trim", param->keylen) == 0) {
+                                       test = ucl_object_toboolean (param);
+                                       if (test) {
+                                               flags |= UCL_STRING_TRIM;
+                                       }
+                               }
+                       }
+                       else if (param->type == UCL_STRING) {
+                               if (strncmp (param->key, "key", param->keylen) == 0) {
+                                       prefix = ucl_object_tostring (param);
+                               }
+                               else if (strncmp (param->key, "target", param->keylen) == 0) {
+                                       target = ucl_object_tostring (param);
+                               }
+                       }
+                       else if (param->type == UCL_INT) {
+                               if (strncmp (param->key, "priority", param->keylen) == 0) {
+                                       priority = ucl_object_toint (param);
+                               }
+                       }
+               }
+       }
+
+       if (prefix == NULL || strlen(prefix) == 0) {
+               ucl_create_err (&parser->err, "No Key specified in load macro");
+               return false;
+       }
+
+       if (len > 0) {
+               asprintf (&load_file, "%.*s", (int)len, data);
+               if (!ucl_fetch_file (load_file, &buf, &buflen, &parser->err, !try_load)) {
+                       return (try_load || false);
+               }
+
+               container = parser->stack->obj->value.ov;
+               old_obj = __DECONST (ucl_object_t *, ucl_hash_search (container, prefix, strlen (prefix)));
+               if (old_obj != NULL) {
+                       ucl_create_err (&parser->err, "Key %s already exists", prefix);
+                       return false;
+               }
+
+               if (strcasecmp (target, "string") == 0) {
+                       obj = ucl_object_fromstring_common (buf, buflen, flags);
+                       ucl_copy_value_trash (obj);
+                       if (multiline) {
+                               obj->flags |= UCL_OBJECT_MULTILINE;
+                       }
+               }
+               else if (strcasecmp (target, "int") == 0) {
+                       asprintf(&tmp, "%.*s", (int)buflen, buf);
+                       iv = strtoll(tmp, NULL, 10);
+                       obj = ucl_object_fromint(iv);
+               }
+
+               if (buflen > 0) {
+                       ucl_munmap (buf, buflen);
+               }
+
+               if (obj != NULL) {
+                       obj->key = prefix;
+                       obj->keylen = strlen (prefix);
+                       ucl_copy_key_trash(obj);
+                       obj->prev = obj;
+                       obj->next = NULL;
+                       ucl_object_set_priority (obj, priority);
+                       container = ucl_hash_insert_object (container, obj,
+                                       parser->flags & UCL_PARSER_KEY_LOWERCASE);
+                       parser->stack->obj->value.ov = container;
+               }
+               return true;
+       }
+
+       ucl_create_err (&parser->err, "Unable to parse load macro");
+       return false;
+}
+
+bool
 ucl_parser_set_filevars (struct ucl_parser *parser, const char *filename, bool need_expand)
 {
        char realbuf[PATH_MAX], *curdir;
@@ -1185,8 +1608,9 @@ ucl_parser_set_filevars (struct ucl_parser *parser, const char *filename, bool n
        return true;
 }
 
-UCL_EXTERN bool
-ucl_parser_add_file (struct ucl_parser *parser, const char *filename)
+bool
+ucl_parser_add_file_priority (struct ucl_parser *parser, const char *filename,
+               unsigned priority)
 {
        unsigned char *buf;
        size_t len;
@@ -1209,7 +1633,7 @@ ucl_parser_add_file (struct ucl_parser *parser, const char *filename)
        }
        parser->cur_file = strdup (realbuf);
        ucl_parser_set_filevars (parser, realbuf, false);
-       ret = ucl_parser_add_chunk (parser, buf, len);
+       ret = ucl_parser_add_chunk_priority (parser, buf, len, priority);
 
        if (len > 0) {
                ucl_munmap (buf, len);
@@ -1218,8 +1642,20 @@ 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)
+bool
+ucl_parser_add_file (struct ucl_parser *parser, const char *filename)
+{
+       if (parser == NULL) {
+               return false;
+       }
+
+       return ucl_parser_add_file_priority(parser, filename,
+                       parser->default_priority);
+}
+
+bool
+ucl_parser_add_fd_priority (struct ucl_parser *parser, int fd,
+               unsigned priority)
 {
        unsigned char *buf;
        size_t len;
@@ -1242,7 +1678,7 @@ ucl_parser_add_fd (struct ucl_parser *parser, int fd)
        }
        parser->cur_file = NULL;
        len = st.st_size;
-       ret = ucl_parser_add_chunk (parser, buf, len);
+       ret = ucl_parser_add_chunk_priority (parser, buf, len, priority);
 
        if (len > 0) {
                ucl_munmap (buf, len);
@@ -1251,6 +1687,16 @@ ucl_parser_add_fd (struct ucl_parser *parser, int fd)
        return ret;
 }
 
+bool
+ucl_parser_add_fd (struct ucl_parser *parser, int fd)
+{
+       if (parser == NULL) {
+               return false;
+       }
+
+       return ucl_parser_add_fd_priority(parser, fd, parser->default_priority);
+}
+
 size_t
 ucl_strlcpy (char *dst, const char *src, size_t siz)
 {
@@ -1306,6 +1752,51 @@ ucl_strlcpy_tolower (char *dst, const char *src, size_t siz)
        return (s - src);    /* count does not include NUL */
 }
 
+/*
+ * Find the first occurrence of find in s
+ */
+char *
+ucl_strnstr (const char *s, const char *find, int len)
+{
+       char c, sc;
+       int mlen;
+
+       if ((c = *find++) != 0) {
+               mlen = strlen (find);
+               do {
+                       do {
+                               if ((sc = *s++) == 0 || len-- == 0)
+                                       return (NULL);
+                       } while (sc != c);
+               } while (strncmp (s, find, mlen) != 0);
+               s--;
+       }
+       return ((char *)s);
+}
+
+/*
+ * Find the first occurrence of find in s, ignore case.
+ */
+char *
+ucl_strncasestr (const char *s, const char *find, int len)
+{
+       char c, sc;
+       int mlen;
+
+       if ((c = *find++) != 0) {
+               c = tolower (c);
+               mlen = strlen (find);
+               do {
+                       do {
+                               if ((sc = *s++) == 0 || len-- == 0)
+                                       return (NULL);
+                       } while (tolower (sc) != c);
+               } while (strncasecmp (s, find, mlen) != 0);
+               s--;
+       }
+       return ((char *)s);
+}
+
 ucl_object_t *
 ucl_object_fromstring_common (const char *str, size_t len, enum ucl_string_flags flags)
 {
@@ -1560,7 +2051,7 @@ ucl_object_delete_keyl (ucl_object_t *top, const char *key, size_t keylen)
 bool
 ucl_object_delete_key (ucl_object_t *top, const char *key)
 {
-       return ucl_object_delete_keyl (top, key, strlen(key));
+       return ucl_object_delete_keyl (top, key, strlen (key));
 }
 
 ucl_object_t*
@@ -1585,7 +2076,7 @@ ucl_object_pop_keyl (ucl_object_t *top, const char *key, size_t keylen)
 ucl_object_t*
 ucl_object_pop_key (ucl_object_t *top, const char *key)
 {
-       return ucl_object_pop_keyl (top, key, strlen(key));
+       return ucl_object_pop_keyl (top, key, strlen (key));
 }
 
 bool
@@ -1663,10 +2154,45 @@ ucl_object_find_keyl (const ucl_object_t *obj, const char *key, size_t klen)
 const ucl_object_t *
 ucl_object_find_key (const ucl_object_t *obj, const char *key)
 {
-       if (key == NULL)
+       if (key == NULL) {
                return NULL;
+       }
 
-       return ucl_object_find_keyl (obj, key, strlen(key));
+       return ucl_object_find_keyl (obj, key, strlen (key));
+}
+
+const ucl_object_t*
+ucl_object_find_any_key (const ucl_object_t *obj,
+               const char *key, ...)
+{
+       va_list ap;
+       const ucl_object_t *ret = NULL;
+       const char *nk = NULL;
+
+       if (obj == NULL || key == NULL) {
+               return NULL;
+       }
+
+       ret = ucl_object_find_keyl (obj, key, strlen (key));
+
+       if (ret == NULL) {
+               va_start (ap, key);
+
+               while (ret == NULL) {
+                       nk = va_arg (ap, const char *);
+
+                       if (nk == NULL) {
+                               break;
+                       }
+                       else {
+                               ret = ucl_object_find_keyl (obj, nk, strlen (nk));
+                       }
+               }
+
+               va_end (ap);
+       }
+
+       return ret;
 }
 
 const ucl_object_t*
@@ -2012,6 +2538,11 @@ ucl_array_append (ucl_object_t *top, ucl_object_t *elt)
 
        if (vec == NULL) {
                vec = UCL_ALLOC (sizeof (*vec));
+
+               if (vec == NULL) {
+                       return false;
+               }
+
                kv_init (*vec);
                top->value.av = (void *)vec;
        }
@@ -2052,14 +2583,23 @@ bool
 ucl_array_merge (ucl_object_t *top, ucl_object_t *elt, bool copy)
 {
        unsigned i;
+       ucl_object_t *cp = NULL;
        ucl_object_t **obj;
-       UCL_ARRAY_GET (v1, top);
-       UCL_ARRAY_GET (v2, elt);
 
        if (elt == NULL || top == NULL || top->type != UCL_ARRAY || elt->type != UCL_ARRAY) {
                return false;
        }
 
+       if (copy) {
+               cp = ucl_object_copy (elt);
+       }
+       else {
+               cp = ucl_object_ref (elt);
+       }
+
+       UCL_ARRAY_GET (v1, top);
+       UCL_ARRAY_GET (v2, cp);
+
        kv_concat (ucl_object_t *, *v1, *v2);
 
        for (i = v2->n; i < v1->n; i ++) {
@@ -2067,14 +2607,7 @@ ucl_array_merge (ucl_object_t *top, ucl_object_t *elt, bool copy)
                if (*obj == NULL) {
                        continue;
                }
-
                top->len ++;
-               if (copy) {
-                       *obj = ucl_object_copy (*obj);
-               }
-               else {
-                       ucl_object_ref (*obj);
-               }
        }
 
        return true;
@@ -2087,6 +2620,10 @@ ucl_array_delete (ucl_object_t *top, ucl_object_t *elt)
        ucl_object_t *ret = NULL;
        unsigned i;
 
+       if (vec == NULL) {
+               return NULL;
+       }
+
        for (i = 0; i < vec->n; i ++) {
                if (kv_A (*vec, i) == elt) {
                        kv_del (ucl_object_t *, *vec, i);
@@ -2104,7 +2641,8 @@ ucl_array_head (const ucl_object_t *top)
 {
        UCL_ARRAY_GET (vec, top);
 
-       if (top == NULL || top->type != UCL_ARRAY || top->value.av == NULL) {
+       if (vec == NULL || top == NULL || top->type != UCL_ARRAY ||
+                       top->value.av == NULL) {
                return NULL;
        }
 
@@ -2173,6 +2711,10 @@ ucl_array_index_of (ucl_object_t *top, ucl_object_t *elt)
        UCL_ARRAY_GET (vec, top);
        unsigned i;
 
+       if (vec == NULL) {
+               return (unsigned int)(-1);
+       }
+
        for (i = 0; i < vec->n; i ++) {
                if (kv_A (*vec, i) == elt) {
                        return i;
@@ -2609,6 +3151,9 @@ ucl_object_set_priority (ucl_object_t *obj,
 {
        if (obj != NULL) {
                priority &= (0x1 << PRIOBITS) - 1;
-               obj->flags |= priority << ((sizeof (obj->flags) * NBBY) - PRIOBITS);
+               priority <<= ((sizeof (obj->flags) * NBBY) - PRIOBITS);
+               priority |= obj->flags & ((1 << ((sizeof (obj->flags) * NBBY) -
+                               PRIOBITS)) - 1);
+               obj->flags = priority;
        }
 }