]> source.dussan.org Git - rspamd.git/commitdiff
Sync with libucl.
authorVsevolod Stakhov <vsevolod@highsecure.ru>
Tue, 13 Oct 2015 16:05:19 +0000 (17:05 +0100)
committerVsevolod Stakhov <vsevolod@highsecure.ru>
Tue, 13 Oct 2015 16:05:19 +0000 (17:05 +0100)
contrib/libucl/tree.h
contrib/libucl/ucl.h
contrib/libucl/ucl_emitter.c
contrib/libucl/ucl_internal.h
contrib/libucl/ucl_msgpack.c
contrib/libucl/ucl_parser.c
contrib/libucl/ucl_util.c

index cee9373698c2fa3423962aa7d0e938daee0438d9..404b4a867be9064e90220270b76d290c487d727c 100644 (file)
 
 
 #define TREE_DELTA_MAX 1
+#ifndef _HU_FUNCTION
+# if defined(__GNUC__) || defined(__clang__)
+#   define _HU_FUNCTION(x) __attribute__((__unused__)) x
+# else
+#   define _HU_FUNCTION(x) x
+# endif
+#endif
 
 #define TREE_ENTRY(type)                       \
   struct {                                     \
@@ -68,9 +75,9 @@
 
 #define TREE_DEFINE(node, field)                                                                       \
                                                                                                        \
-  struct node *TREE_BALANCE_##node##_##field(struct node *);                                           \
+  static struct node *_HU_FUNCTION(TREE_BALANCE_##node##_##field)(struct node *);                                              \
                                                                                                        \
-  struct node *TREE_ROTL_##node##_##field(struct node *self)                                           \
+  static struct node *_HU_FUNCTION(TREE_ROTL_##node##_##field)(struct node *self)                                              \
   {                                                                                                    \
     struct node *r= self->field.avl_right;                                                             \
     self->field.avl_right= r->field.avl_left;                                                          \
@@ -78,7 +85,7 @@
     return TREE_BALANCE_##node##_##field(r);                                                           \
   }                                                                                                    \
                                                                                                        \
-  struct node *TREE_ROTR_##node##_##field(struct node *self)                                           \
+  static struct node *_HU_FUNCTION(TREE_ROTR_##node##_##field)(struct node *self)                                              \
   {                                                                                                    \
     struct node *l= self->field.avl_left;                                                              \
     self->field.avl_left= l->field.avl_right;                                                          \
@@ -86,7 +93,7 @@
     return TREE_BALANCE_##node##_##field(l);                                                           \
   }                                                                                                    \
                                                                                                        \
-  struct node *TREE_BALANCE_##node##_##field(struct node *self)                                                \
+  static struct node *_HU_FUNCTION(TREE_BALANCE_##node##_##field)(struct node *self)                                           \
   {                                                                                                    \
     int delta= TREE_DELTA(self, field);                                                                        \
                                                                                                        \
     return self;                                                                                       \
   }                                                                                                    \
                                                                                                        \
-  struct node *TREE_INSERT_##node##_##field                                                            \
+  static struct node *_HU_FUNCTION(TREE_INSERT_##node##_##field)                                                               \
     (struct node *self, struct node *elm, int (*compare)(struct node *lhs, struct node *rhs))          \
   {                                                                                                    \
     if (!self)                                                                                         \
     return TREE_BALANCE_##node##_##field(self);                                                                \
   }                                                                                                    \
                                                                                                        \
-  struct node *TREE_FIND_##node##_##field                                                              \
+  static struct node *_HU_FUNCTION(TREE_FIND_##node##_##field)                                                         \
     (struct node *self, struct node *elm, int (*compare)(struct node *lhs, struct node *rhs))          \
   {                                                                                                    \
     if (!self)                                                                                         \
       return TREE_FIND_##node##_##field(self->field.avl_right, elm, compare);                          \
   }                                                                                                    \
                                                                                                        \
-  struct node *TREE_MOVE_RIGHT(struct node *self, struct node *rhs)                                    \
+  static struct node *_HU_FUNCTION(TREE_MOVE_RIGHT)(struct node *self, struct node *rhs)                                       \
   {                                                                                                    \
     if (!self)                                                                                         \
       return rhs;                                                                                      \
     return TREE_BALANCE_##node##_##field(self);                                                                \
   }                                                                                                    \
                                                                                                        \
-  struct node *TREE_REMOVE_##node##_##field                                                            \
+  static struct node *_HU_FUNCTION(TREE_REMOVE_##node##_##field)                                                               \
     (struct node *self, struct node *elm, int (*compare)(struct node *lhs, struct node *rhs))          \
   {                                                                                                    \
     if (!self) return 0;                                                                               \
     return TREE_BALANCE_##node##_##field(self);                                                                \
   }                                                                                                    \
                                                                                                        \
-  void TREE_FORWARD_APPLY_ALL_##node##_##field                                                         \
+  static void _HU_FUNCTION(TREE_FORWARD_APPLY_ALL_##node##_##field)                                                            \
     (struct node *self, void (*function)(struct node *node, void *data), void *data)                   \
   {                                                                                                    \
     if (self)                                                                                          \
       }                                                                                                        \
   }                                                                                                    \
                                                                                                        \
-  void TREE_REVERSE_APPLY_ALL_##node##_##field                                                         \
+  static void _HU_FUNCTION(TREE_REVERSE_APPLY_ALL_##node##_##field)                                                            \
     (struct node *self, void (*function)(struct node *node, void *data), void *data)                   \
   {                                                                                                    \
     if (self)                                                                                          \
index 2e0183671a03e59b550373a1c01c100078b5e070..bd209295bcb16e0182168e0158af520a07e399f2 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013, Vsevolod Stakhov
+/* Copyright (c) 2013-2015, Vsevolod Stakhov
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -182,9 +182,29 @@ typedef enum ucl_object_flags {
        UCL_OBJECT_EPHEMERAL = (1 << 3), /**< Temporary object that does not need to be freed really */
        UCL_OBJECT_MULTILINE = (1 << 4), /**< String should be displayed as multiline string */
        UCL_OBJECT_MULTIVALUE = (1 << 5), /**< Object is a key with multiple values */
-       UCL_OBJECT_INHERITED = (1 << 6) /**< Object has been inherited from another */
+       UCL_OBJECT_INHERITED = (1 << 6), /**< Object has been inherited from another */
+       UCL_OBJECT_BINARY = (1 << 7) /**< Object contains raw binary data */
 } ucl_object_flags_t;
 
+/**
+ * Duplicate policy types
+ */
+enum ucl_duplicate_strategy {
+       UCL_DUPLICATE_APPEND = 0, /**< Default policy to merge based on priorities */
+       UCL_DUPLICATE_MERGE,     /**< Merge new object with old one */
+       UCL_DUPLICATE_REWRITE,   /**< Rewrite old keys */
+       UCL_DUPLICATE_ERROR      /**< Stop parsing on duplicate found */
+};
+
+/**
+ * Input format type
+ */
+enum ucl_parse_type {
+       UCL_PARSE_UCL = 0, /**< Default ucl format */
+       UCL_PARSE_MSGPACK, /**< Message pack input format */
+       UCL_PARSE_CSEXP /**< Canonical S-expressions */
+};
+
 /**
  * UCL object structure. Please mention that the most of fields should not be touched by
  * UCL users. In future, this structure may be converted to private one.
@@ -195,7 +215,7 @@ typedef struct ucl_object_s {
         */
        union {
                int64_t iv;                                                     /**< Int value of an object */
-               const char *sv;                                 /**< String value of an object */
+               const char *sv;                                         /**< String value of an object */
                double dv;                                                      /**< Double value of an object */
                void *av;                                                       /**< Array                                      */
                void *ov;                                                       /**< Object                                     */
@@ -916,6 +936,21 @@ UCL_EXTERN bool ucl_parser_add_chunk (struct ucl_parser *parser,
 UCL_EXTERN bool ucl_parser_add_chunk_priority (struct ucl_parser *parser,
                const unsigned char *data, size_t len, unsigned priority);
 
+/**
+ * Full version of ucl_add_chunk with priority and duplicate strategy
+ * @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)
+ * @param strat duplicates merging strategy
+ * @param parse_type input format
+ * @return true if chunk has been added and false in case of error
+ */
+UCL_EXTERN bool ucl_parser_add_chunk_full (struct ucl_parser *parser,
+               const unsigned char *data, size_t len, unsigned priority,
+               enum ucl_duplicate_strategy strat, enum ucl_parse_type parse_type);
+
 /**
  * Load ucl object from a string
  * @param parser parser structure
index 12cd31c7d8279903967544844431d7d665c42cf1..8bfbf09b8bd783d1b717b6ef2d7c4d2af45935cc 100644 (file)
@@ -498,7 +498,14 @@ ucl_emit_msgpack_elt (struct ucl_emitter_context *ctx,
 
        case UCL_STRING:
                ucl_emitter_print_key_msgpack (print_key, ctx, obj);
-               ucl_emitter_print_string_msgpack (ctx, obj->value.sv, obj->len);
+
+               if (obj->flags & UCL_OBJECT_BINARY) {
+                       ucl_emitter_print_binary_string_msgpack (ctx, obj->value.sv,
+                                       obj->len);
+               }
+               else {
+                       ucl_emitter_print_string_msgpack (ctx, obj->value.sv, obj->len);
+               }
                break;
 
        case UCL_NULL:
@@ -509,27 +516,31 @@ ucl_emit_msgpack_elt (struct ucl_emitter_context *ctx,
        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);
+               it = NULL;
 
-               while ((cur = ucl_object_iterate_safe (it, true)) != NULL) {
+               while ((cur = ucl_iterate_object (obj, &it, true)) != NULL) {
                        LL_FOREACH (cur, celt) {
                                ucl_emit_msgpack_elt (ctx, celt, false, true);
+                               /* XXX:
+                                * in msgpack the length of objects is encoded within a single elt
+                                * so in case of multi-value keys we are using merely the first
+                                * element ignoring others
+                                */
+                               break;
                        }
                }
 
-               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);
+               it = NULL;
 
-               while ((cur = ucl_object_iterate_safe (it, true)) != NULL) {
+               while ((cur = ucl_iterate_object (obj, &it, true)) != NULL) {
                        ucl_emit_msgpack_elt (ctx, cur, false, false);
                }
 
-               ucl_object_iterate_free (it);
                break;
 
        case UCL_USERDATA:
index 885e8c57ff3e915e8609d0bbf17df88533a7e41b..da7df1270b677aa4bb88256941249c7d89567cf0 100644 (file)
@@ -157,7 +157,7 @@ struct ucl_macro {
 struct ucl_stack {
        ucl_object_t *obj;
        struct ucl_stack *next;
-       int level;
+       uint64_t level;
 };
 
 struct ucl_chunk {
@@ -168,6 +168,8 @@ struct ucl_chunk {
        unsigned int line;
        unsigned int column;
        unsigned priority;
+       enum ucl_duplicate_strategy strategy;
+       enum ucl_parse_type parse_type;
        struct ucl_chunk *next;
 };
 
@@ -303,6 +305,8 @@ ucl_create_err (UT_string **err, const char *fmt, ...)
 __attribute__ (( format( printf, 2, 3) ));
 #endif
 
+#undef UCL_FATAL_ERRORS
+
 static inline void
 ucl_create_err (UT_string **err, const char *fmt, ...)
 
@@ -314,6 +318,10 @@ ucl_create_err (UT_string **err, const char *fmt, ...)
                utstring_printf_va (*err, fmt, ap);
                va_end (ap);
        }
+
+#ifdef UCL_FATAL_ERRORS
+       assert (0);
+#endif
 }
 
 /**
@@ -477,6 +485,15 @@ void ucl_emitter_print_bool_msgpack (struct ucl_emitter_context *ctx,
 void ucl_emitter_print_string_msgpack (struct ucl_emitter_context *ctx,
                const char *s, size_t len);
 
+/**
+ * Print binary string to the msgpack output
+ * @param ctx
+ * @param s
+ * @param len
+ */
+void ucl_emitter_print_binary_string_msgpack (struct ucl_emitter_context *ctx,
+               const char *s, size_t len);
+
 /**
  * Print array preamble for msgpack
  * @param ctx
@@ -498,7 +515,7 @@ void ucl_emitter_print_object_msgpack (struct ucl_emitter_context *ctx,
  */
 void ucl_emitter_print_null_msgpack (struct ucl_emitter_context *ctx);
 /**
- * Print object's key if needed to the msgpakc output
+ * Print object's key if needed to the msgpack output
  * @param print_key
  * @param ctx
  * @param obj
@@ -507,4 +524,20 @@ void ucl_emitter_print_key_msgpack (bool print_key,
                struct ucl_emitter_context *ctx,
                const ucl_object_t *obj);
 
+/**
+ * Add new element to an object using the current merge strategy and priority
+ * @param parser
+ * @param nobj
+ * @return
+ */
+bool ucl_parser_process_object_element (struct ucl_parser *parser,
+               ucl_object_t *nobj);
+
+/**
+ * Parse msgpack chunk
+ * @param parser
+ * @return
+ */
+bool ucl_parse_msgpack (struct ucl_parser *parser);
+
 #endif /* UCL_INTERNAL_H_ */
index 5c315430c2115f9229ea5c8006719ada07d13685..e8ebfba8a35eb68a4ec46c664d579a3aa3197cad 100644 (file)
 #define TO_BE16 SWAP_LE_BE16
 #define TO_BE32 SWAP_LE_BE32
 #define TO_BE64 SWAP_LE_BE64
+#define FROM_BE16 SWAP_LE_BE16
+#define FROM_BE32 SWAP_LE_BE32
+#define FROM_BE64 SWAP_LE_BE64
 #else
 #define TO_BE16(val) (uint16_t)(val)
 #define TO_BE32(val) (uint32_t)(val)
 #define TO_BE64(val) (uint64_t)(val)
+#define FROM_BE16(val) (uint16_t)(val)
+#define FROM_BE32(val) (uint32_t)(val)
+#define FROM_BE64(val) (uint64_t)(val)
 #endif
 
 void
@@ -139,9 +145,9 @@ ucl_emitter_print_int_msgpack (struct ucl_emitter_context *ctx, int64_t val)
                /* Bithack abs */
                uval = ((val ^ (val >> 63)) - (val >> 63));
 
-               if (val >= -(1 << 5)) {
+               if (val > -(1 << 5)) {
                        len = 1;
-                       buf[0] = mask_negative | (uval & 0xff);
+                       buf[0] = (mask_negative | uval) & 0xff;
                }
                else if (uval <= 0xff) {
                        len = 2;
@@ -240,6 +246,39 @@ ucl_emitter_print_string_msgpack (struct ucl_emitter_context *ctx,
        func->ucl_emitter_append_len (s, len, func->ud);
 }
 
+void
+ucl_emitter_print_binary_string_msgpack (struct ucl_emitter_context *ctx,
+               const char *s, size_t len)
+{
+       const struct ucl_emitter_functions *func = ctx->func;
+       const unsigned char l8_ch = 0xc4, l16_ch = 0xc5, l32_ch = 0xc6;
+       unsigned char buf[5];
+       unsigned blen;
+
+       if (len <= 0xff) {
+               blen = 2;
+               buf[0] = l8_ch;
+               buf[1] = len & 0xff;
+       }
+       else if (len <= 0xffff) {
+               uint16_t bl = TO_BE16 (len);
+
+               blen = 3;
+               buf[0] = l16_ch;
+               memcpy (&buf[1], &bl, sizeof (bl));
+       }
+       else {
+               uint32_t bl = TO_BE32 (len);
+
+               blen = 5;
+               buf[0] = l32_ch;
+               memcpy (&buf[1], &bl, sizeof (bl));
+       }
+
+       func->ucl_emitter_append_len (buf, blen, func->ud);
+       func->ucl_emitter_append_len (s, len, func->ud);
+}
+
 void
 ucl_emitter_print_null_msgpack (struct ucl_emitter_context *ctx)
 {
@@ -317,3 +356,1245 @@ ucl_emitter_print_object_msgpack (struct ucl_emitter_context *ctx, size_t len)
 
        func->ucl_emitter_append_len (buf, blen, func->ud);
 }
+
+
+enum ucl_msgpack_format {
+       msgpack_positive_fixint = 0,
+       msgpack_fixmap,
+       msgpack_fixarray,
+       msgpack_fixstr,
+       msgpack_nil,
+       msgpack_false,
+       msgpack_true,
+       msgpack_bin8,
+       msgpack_bin16,
+       msgpack_bin32,
+       msgpack_ext8,
+       msgpack_ext16,
+       msgpack_ext32,
+       msgpack_float32,
+       msgpack_float64,
+       msgpack_uint8,
+       msgpack_uint16,
+       msgpack_uint32,
+       msgpack_uint64,
+       msgpack_int8,
+       msgpack_int16,
+       msgpack_int32,
+       msgpack_int64,
+       msgpack_fixext1,
+       msgpack_fixext2,
+       msgpack_fixext4,
+       msgpack_fixext8,
+       msgpack_fixext16,
+       msgpack_str8,
+       msgpack_str16,
+       msgpack_str32,
+       msgpack_array16,
+       msgpack_array32,
+       msgpack_map16,
+       msgpack_map32,
+       msgpack_negative_fixint,
+       msgpack_invalid
+};
+
+typedef ssize_t (*ucl_msgpack_parse_function)(struct ucl_parser *parser,
+               struct ucl_stack *container, size_t len, enum ucl_msgpack_format fmt,
+               const unsigned char *pos, size_t remain);
+
+static ssize_t ucl_msgpack_parse_map (struct ucl_parser *parser,
+               struct ucl_stack *container, size_t len, enum ucl_msgpack_format fmt,
+               const unsigned char *pos, size_t remain);
+static ssize_t ucl_msgpack_parse_array (struct ucl_parser *parser,
+               struct ucl_stack *container, size_t len, enum ucl_msgpack_format fmt,
+               const unsigned char *pos, size_t remain);
+static ssize_t ucl_msgpack_parse_string (struct ucl_parser *parser,
+               struct ucl_stack *container, size_t len, enum ucl_msgpack_format fmt,
+               const unsigned char *pos, size_t remain);
+static ssize_t ucl_msgpack_parse_int (struct ucl_parser *parser,
+               struct ucl_stack *container, size_t len, enum ucl_msgpack_format fmt,
+               const unsigned char *pos, size_t remain);
+static ssize_t ucl_msgpack_parse_float (struct ucl_parser *parser,
+               struct ucl_stack *container, size_t len, enum ucl_msgpack_format fmt,
+               const unsigned char *pos, size_t remain);
+static ssize_t ucl_msgpack_parse_bool (struct ucl_parser *parser,
+               struct ucl_stack *container, size_t len, enum ucl_msgpack_format fmt,
+               const unsigned char *pos, size_t remain);
+static ssize_t ucl_msgpack_parse_null (struct ucl_parser *parser,
+               struct ucl_stack *container, size_t len, enum ucl_msgpack_format fmt,
+               const unsigned char *pos, size_t remain);
+static ssize_t ucl_msgpack_parse_ignore (struct ucl_parser *parser,
+               struct ucl_stack *container, size_t len, enum ucl_msgpack_format fmt,
+               const unsigned char *pos, size_t remain);
+
+#define MSGPACK_FLAG_FIXED (1 << 0)
+#define MSGPACK_FLAG_CONTAINER (1 << 1)
+#define MSGPACK_FLAG_TYPEVALUE (1 << 2)
+#define MSGPACK_FLAG_EXT (1 << 3)
+#define MSGPACK_FLAG_ASSOC (1 << 4)
+#define MSGPACK_FLAG_KEY (1 << 5)
+#define MSGPACK_CONTAINER_BIT (1ULL << 62)
+
+/*
+ * Search tree packed in array
+ */
+struct ucl_msgpack_parser {
+       uint8_t prefix;                                         /* Prefix byte                                  */
+       uint8_t prefixlen;                                      /* Length of prefix in bits             */
+       uint8_t fmt;                                            /* The desired format                   */
+       uint8_t len;                                            /* Length of the object
+                                                                                 (either length bytes
+                                                                                 or length of value in case
+                                                                                 of fixed objects                              */
+       uint8_t flags;                                          /* Flags of the specified type  */
+       ucl_msgpack_parse_function func;        /* Parser function                              */
+} parsers[] = {
+       {
+                       0xa0,
+                       3,
+                       msgpack_fixstr,
+                       0,
+                       MSGPACK_FLAG_FIXED|MSGPACK_FLAG_KEY,
+                       ucl_msgpack_parse_string
+       },
+       {
+                       0x0,
+                       1,
+                       msgpack_positive_fixint,
+                       0,
+                       MSGPACK_FLAG_FIXED|MSGPACK_FLAG_TYPEVALUE,
+                       ucl_msgpack_parse_int
+       },
+       {
+                       0xe0,
+                       3,
+                       msgpack_negative_fixint,
+                       0,
+                       MSGPACK_FLAG_FIXED|MSGPACK_FLAG_TYPEVALUE,
+                       ucl_msgpack_parse_int
+       },
+       {
+                       0x80,
+                       4,
+                       msgpack_fixmap,
+                       0,
+                       MSGPACK_FLAG_FIXED|MSGPACK_FLAG_CONTAINER|MSGPACK_FLAG_ASSOC,
+                       ucl_msgpack_parse_map
+       },
+       {
+                       0x90,
+                       4,
+                       msgpack_fixarray,
+                       0,
+                       MSGPACK_FLAG_FIXED|MSGPACK_FLAG_CONTAINER,
+                       ucl_msgpack_parse_array
+       },
+       {
+                       0xd9,
+                       8,
+                       msgpack_str8,
+                       1,
+                       MSGPACK_FLAG_KEY,
+                       ucl_msgpack_parse_string
+       },
+       {
+                       0xc4,
+                       8,
+                       msgpack_bin8,
+                       1,
+                       MSGPACK_FLAG_KEY,
+                       ucl_msgpack_parse_string
+       },
+       {
+                       0xcf,
+                       8,
+                       msgpack_uint64,
+                       8,
+                       MSGPACK_FLAG_FIXED,
+                       ucl_msgpack_parse_int
+       },
+       {
+                       0xd3,
+                       8,
+                       msgpack_int64,
+                       8,
+                       MSGPACK_FLAG_FIXED,
+                       ucl_msgpack_parse_int
+       },
+       {
+                       0xce,
+                       8,
+                       msgpack_uint32,
+                       4,
+                       MSGPACK_FLAG_FIXED,
+                       ucl_msgpack_parse_int
+       },
+       {
+                       0xd2,
+                       8,
+                       msgpack_int32,
+                       4,
+                       MSGPACK_FLAG_FIXED,
+                       ucl_msgpack_parse_int
+       },
+       {
+                       0xcb,
+                       8,
+                       msgpack_float64,
+                       8,
+                       MSGPACK_FLAG_FIXED,
+                       ucl_msgpack_parse_float
+       },
+       {
+                       0xca,
+                       8,
+                       msgpack_float32,
+                       4,
+                       MSGPACK_FLAG_FIXED,
+                       ucl_msgpack_parse_float
+       },
+       {
+                       0xc2,
+                       8,
+                       msgpack_false,
+                       1,
+                       MSGPACK_FLAG_FIXED | MSGPACK_FLAG_TYPEVALUE,
+                       ucl_msgpack_parse_bool
+       },
+       {
+                       0xc3,
+                       8,
+                       msgpack_true,
+                       1,
+                       MSGPACK_FLAG_FIXED | MSGPACK_FLAG_TYPEVALUE,
+                       ucl_msgpack_parse_bool
+       },
+       {
+                       0xcc,
+                       8,
+                       msgpack_uint8,
+                       1,
+                       MSGPACK_FLAG_FIXED,
+                       ucl_msgpack_parse_int
+       },
+       {
+                       0xcd,
+                       8,
+                       msgpack_uint16,
+                       2,
+                       MSGPACK_FLAG_FIXED,
+                       ucl_msgpack_parse_int
+       },
+       {
+                       0xd0,
+                       8,
+                       msgpack_int8,
+                       1,
+                       MSGPACK_FLAG_FIXED,
+                       ucl_msgpack_parse_int
+       },
+       {
+                       0xd1,
+                       8,
+                       msgpack_int16,
+                       2,
+                       MSGPACK_FLAG_FIXED,
+                       ucl_msgpack_parse_int
+       },
+       {
+                       0xc0,
+                       8,
+                       msgpack_nil,
+                       0,
+                       MSGPACK_FLAG_FIXED | MSGPACK_FLAG_TYPEVALUE,
+                       ucl_msgpack_parse_null
+       },
+       {
+                       0xda,
+                       8,
+                       msgpack_str16,
+                       2,
+                       MSGPACK_FLAG_KEY,
+                       ucl_msgpack_parse_string
+       },
+       {
+                       0xdb,
+                       8,
+                       msgpack_str32,
+                       4,
+                       MSGPACK_FLAG_KEY,
+                       ucl_msgpack_parse_string
+       },
+       {
+                       0xc5,
+                       8,
+                       msgpack_bin16,
+                       2,
+                       MSGPACK_FLAG_KEY,
+                       ucl_msgpack_parse_string
+       },
+       {
+                       0xc6,
+                       8,
+                       msgpack_bin32,
+                       4,
+                       MSGPACK_FLAG_KEY,
+                       ucl_msgpack_parse_string
+       },
+       {
+                       0xdc,
+                       8,
+                       msgpack_array16,
+                       2,
+                       MSGPACK_FLAG_CONTAINER,
+                       ucl_msgpack_parse_array
+       },
+       {
+                       0xdd,
+                       8,
+                       msgpack_array32,
+                       4,
+                       MSGPACK_FLAG_CONTAINER,
+                       ucl_msgpack_parse_array
+       },
+       {
+                       0xde,
+                       8,
+                       msgpack_map16,
+                       2,
+                       MSGPACK_FLAG_CONTAINER|MSGPACK_FLAG_ASSOC,
+                       ucl_msgpack_parse_map
+       },
+       {
+                       0xdf,
+                       8,
+                       msgpack_map32,
+                       4,
+                       MSGPACK_FLAG_CONTAINER|MSGPACK_FLAG_ASSOC,
+                       ucl_msgpack_parse_map
+       },
+       {
+                       0xc7,
+                       8,
+                       msgpack_ext8,
+                       1,
+                       MSGPACK_FLAG_EXT,
+                       ucl_msgpack_parse_ignore
+       },
+       {
+                       0xc8,
+                       8,
+                       msgpack_ext16,
+                       2,
+                       MSGPACK_FLAG_EXT,
+                       ucl_msgpack_parse_ignore
+       },
+       {
+                       0xc9,
+                       8,
+                       msgpack_ext32,
+                       4,
+                       MSGPACK_FLAG_EXT,
+                       ucl_msgpack_parse_ignore
+       },
+       {
+                       0xd4,
+                       8,
+                       msgpack_fixext1,
+                       1,
+                       MSGPACK_FLAG_FIXED | MSGPACK_FLAG_EXT,
+                       ucl_msgpack_parse_ignore
+       },
+       {
+                       0xd5,
+                       8,
+                       msgpack_fixext2,
+                       2,
+                       MSGPACK_FLAG_FIXED | MSGPACK_FLAG_EXT,
+                       ucl_msgpack_parse_ignore
+       },
+       {
+                       0xd6,
+                       8,
+                       msgpack_fixext4,
+                       4,
+                       MSGPACK_FLAG_FIXED | MSGPACK_FLAG_EXT,
+                       ucl_msgpack_parse_ignore
+       },
+       {
+                       0xd7,
+                       8,
+                       msgpack_fixext8,
+                       8,
+                       MSGPACK_FLAG_FIXED | MSGPACK_FLAG_EXT,
+                       ucl_msgpack_parse_ignore
+       },
+       {
+                       0xd8,
+                       8,
+                       msgpack_fixext16,
+                       16,
+                       MSGPACK_FLAG_FIXED | MSGPACK_FLAG_EXT,
+                       ucl_msgpack_parse_ignore
+       }
+};
+
+#undef MSGPACK_DEBUG_PARSER
+
+static inline struct ucl_msgpack_parser *
+ucl_msgpack_get_parser_from_type (unsigned char t)
+{
+       unsigned int i, shift, mask;
+
+       for (i = 0; i < sizeof (parsers) / sizeof (parsers[0]); i ++) {
+               shift = CHAR_BIT - parsers[i].prefixlen;
+               mask = parsers[i].prefix >> shift;
+
+               if (mask == (t >> shift)) {
+                       return &parsers[i];
+               }
+       }
+
+       return NULL;
+}
+
+static inline struct ucl_stack *
+ucl_msgpack_get_container (struct ucl_parser *parser,
+               struct ucl_msgpack_parser *obj_parser, uint64_t len)
+{
+       struct ucl_stack *stack;
+
+       assert (obj_parser != NULL);
+
+       if (obj_parser->flags & MSGPACK_FLAG_CONTAINER) {
+               assert ((len & MSGPACK_CONTAINER_BIT) == 0);
+               /*
+                * Insert new container to the stack
+                */
+               if (parser->stack == NULL) {
+                       parser->stack = calloc (1, sizeof (struct ucl_stack));
+
+                       if (parser->stack == NULL) {
+                               ucl_create_err (&parser->err, "no memory");
+                               return NULL;
+                       }
+               }
+               else {
+                       stack = calloc (1, sizeof (struct ucl_stack));
+
+                       if (stack == NULL) {
+                               ucl_create_err (&parser->err, "no memory");
+                               return NULL;
+                       }
+
+                       stack->next = parser->stack;
+                       parser->stack = stack;
+               }
+
+               parser->stack->level = len | MSGPACK_CONTAINER_BIT;
+
+#ifdef MSGPACK_DEBUG_PARSER
+               stack = parser->stack;
+               while (stack) {
+                       fprintf(stderr, "+");
+                       stack = stack->next;
+               }
+
+               fprintf(stderr, "%s -> %d\n", obj_parser->flags & MSGPACK_FLAG_ASSOC ? "object" : "array", (int)len);
+#endif
+       }
+       else {
+               /*
+                * Get the current stack top
+                */
+               if (parser->stack) {
+                       return parser->stack;
+               }
+               else {
+                       ucl_create_err (&parser->err, "bad top level object for msgpack");
+                       return NULL;
+               }
+       }
+
+       return parser->stack;
+}
+
+static bool
+ucl_msgpack_is_container_finished (struct ucl_stack *container)
+{
+       uint64_t level;
+
+       assert (container != NULL);
+
+       if (container->level & MSGPACK_CONTAINER_BIT) {
+               level = container->level & ~MSGPACK_CONTAINER_BIT;
+
+               if (level == 0) {
+                       return true;
+               }
+       }
+
+       return false;
+}
+
+static bool
+ucl_msgpack_insert_object (struct ucl_parser *parser,
+               const unsigned char *key,
+               size_t keylen, ucl_object_t *obj)
+{
+       uint64_t level;
+       struct ucl_stack *container;
+
+       container = parser->stack;
+       assert (container != NULL);
+       assert (container->level > 0);
+       assert (obj != NULL);
+       assert (container->obj != NULL);
+
+       if (container->obj->type == UCL_ARRAY) {
+               ucl_array_append (container->obj, obj);
+       }
+       else if (container->obj->type == UCL_OBJECT) {
+               if (key == NULL || keylen == 0) {
+                       ucl_create_err (&parser->err, "cannot insert object with no key");
+                       return false;
+               }
+
+               obj->key = key;
+               obj->keylen = keylen;
+
+               if (!(parser->flags & UCL_PARSER_ZEROCOPY)) {
+                       ucl_copy_key_trash (obj);
+               }
+
+               ucl_parser_process_object_element (parser, obj);
+       }
+       else {
+               ucl_create_err (&parser->err, "bad container type");
+               return false;
+       }
+
+       if (container->level & MSGPACK_CONTAINER_BIT) {
+               level = container->level & ~MSGPACK_CONTAINER_BIT;
+               container->level = (level - 1) | MSGPACK_CONTAINER_BIT;
+       }
+
+       return true;
+}
+
+static struct ucl_stack *
+ucl_msgpack_get_next_container (struct ucl_parser *parser)
+{
+       struct ucl_stack *cur = NULL;
+       uint64_t level;
+
+       cur = parser->stack;
+
+       if (cur == NULL) {
+               return NULL;
+       }
+
+       if (cur->level & MSGPACK_CONTAINER_BIT) {
+               level = cur->level & ~MSGPACK_CONTAINER_BIT;
+
+               if (level == 0) {
+                       /* We need to switch to the previous container */
+                       parser->stack = cur->next;
+                       parser->cur_obj = cur->obj;
+                       free (cur);
+
+#ifdef MSGPACK_DEBUG_PARSER
+                       cur = parser->stack;
+                       while (cur) {
+                               fprintf(stderr, "-");
+                               cur = cur->next;
+                       }
+                       fprintf(stderr, "-%s -> %d\n", parser->cur_obj->type == UCL_OBJECT ? "object" : "array", (int)parser->cur_obj->len);
+#endif
+
+                       return ucl_msgpack_get_next_container (parser);
+               }
+       }
+
+       /*
+        * For UCL containers we don't know length, so we just insert the whole
+        * message pack blob into the top level container
+        */
+
+       assert (cur->obj != NULL);
+
+       return cur;
+}
+
+#define CONSUME_RET do {                                                                       \
+       if (ret != -1) {                                                                                \
+               p += ret;                                                                                       \
+               remain -= ret;                                                                          \
+               obj_parser = NULL;                                                                      \
+               assert (remain >= 0);                                                           \
+       }                                                                                                               \
+       else {                                                                                                  \
+               ucl_create_err (&parser->err,                                           \
+                       "cannot parse type %d of len %u",                               \
+                       (int)obj_parser->fmt,                                                   \
+                       (unsigned)len);                                                                 \
+               return false;                                                                           \
+       }                                                                                                               \
+} while(0)
+
+#define GET_NEXT_STATE do {                                                                    \
+       container = ucl_msgpack_get_next_container (parser);    \
+       if (container == NULL) {                                                                \
+               ucl_create_err (&parser->err,                                           \
+                                       "empty container");                                             \
+               return false;                                                                           \
+       }                                                                                                               \
+       next_state = container->obj->type == UCL_OBJECT ?               \
+                                       read_assoc_key : read_array_value;              \
+} while(0)
+
+static bool
+ucl_msgpack_consume (struct ucl_parser *parser)
+{
+       const unsigned char *p, *end, *key = NULL;
+       struct ucl_stack *container;
+       enum e_msgpack_parser_state {
+               read_type,
+               start_assoc,
+               start_array,
+               read_assoc_key,
+               read_assoc_value,
+               finish_assoc_value,
+               read_array_value,
+               finish_array_value,
+               error_state
+       } state = read_type, next_state = error_state;
+       struct ucl_msgpack_parser *obj_parser;
+       uint64_t len;
+       ssize_t ret, remain, keylen = 0;
+#ifdef MSGPACK_DEBUG_PARSER
+       uint64_t i;
+       enum e_msgpack_parser_state hist[256];
+#endif
+
+       p = parser->chunks->begin;
+       remain = parser->chunks->remain;
+       end = p + remain;
+
+
+       while (p < end) {
+#ifdef MSGPACK_DEBUG_PARSER
+               hist[i++ % 256] = state;
+#endif
+               switch (state) {
+               case read_type:
+                       obj_parser = ucl_msgpack_get_parser_from_type (*p);
+
+                       if (obj_parser == NULL) {
+                               ucl_create_err (&parser->err, "unknown msgpack format: %x",
+                                               (unsigned int)*p);
+
+                               return false;
+                       }
+                       /* Now check length sanity */
+                       if (obj_parser->flags & MSGPACK_FLAG_FIXED) {
+                               if (obj_parser->len == 0) {
+                                       /* We have an embedded size */
+                                       len = *p & ~obj_parser->prefix;
+                               }
+                               else {
+                                       if (remain < obj_parser->len) {
+                                               ucl_create_err (&parser->err, "not enough data remain to "
+                                                               "read object's length: %u remain, %u needed",
+                                                               (unsigned)remain, obj_parser->len);
+
+                                               return false;
+                                       }
+
+                                       len = obj_parser->len;
+                               }
+
+                               if (!(obj_parser->flags & MSGPACK_FLAG_TYPEVALUE)) {
+                                       /* We must pass value as the second byte */
+                                       if (remain > 0) {
+                                               p ++;
+                                               remain --;
+                                       }
+                               }
+                               else {
+                                       /* Len is irrelevant now */
+                                       len = 0;
+                               }
+                       }
+                       else {
+                               /* Length is not embedded */
+                               if (remain < obj_parser->len) {
+                                       ucl_create_err (&parser->err, "not enough data remain to "
+                                                       "read object's length: %u remain, %u needed",
+                                                       (unsigned)remain, obj_parser->len);
+
+                                       return false;
+                               }
+
+                               p ++;
+                               remain --;
+
+                               switch (obj_parser->len) {
+                               case 1:
+                                       len = *p;
+                                       break;
+                               case 2:
+                                       len = FROM_BE16 (*(uint16_t *)p);
+                                       break;
+                               case 4:
+                                       len = FROM_BE32 (*(uint32_t *)p);
+                                       break;
+                               case 8:
+                                       len = FROM_BE64 (*(uint64_t *)p);
+                                       break;
+                               default:
+                                       assert (0);
+                                       break;
+                               }
+
+                               p += obj_parser->len;
+                               remain -= obj_parser->len;
+                       }
+
+                       if (obj_parser->flags & MSGPACK_FLAG_ASSOC) {
+                               /* We have just read the new associative map */
+                               state = start_assoc;
+                       }
+                       else if (obj_parser->flags & MSGPACK_FLAG_CONTAINER){
+                               state = start_array;
+                       }
+                       else {
+                               state = next_state;
+                       }
+
+                       break;
+               case start_assoc:
+                       parser->cur_obj = ucl_object_new_full (UCL_OBJECT,
+                                       parser->chunks->priority);
+                       /* Insert to the previous level container */
+                       if (parser->stack && !ucl_msgpack_insert_object (parser,
+                                       key, keylen, parser->cur_obj)) {
+                               return false;
+                       }
+                       /* Get new container */
+                       container = ucl_msgpack_get_container (parser, obj_parser, len);
+
+                       if (container == NULL) {
+                               return false;
+                       }
+
+                       ret = obj_parser->func (parser, container, len, obj_parser->fmt,
+                                       p, remain);
+                       CONSUME_RET;
+                       key = NULL;
+                       keylen = 0;
+
+                       if (len > 0) {
+                               state = read_type;
+                               next_state = read_assoc_key;
+                       }
+                       else {
+                               /* Empty object */
+                               state = finish_assoc_value;
+                       }
+                       break;
+
+               case start_array:
+                       parser->cur_obj = ucl_object_new_full (UCL_ARRAY,
+                                       parser->chunks->priority);
+                       /* Insert to the previous level container */
+                       if (parser->stack && !ucl_msgpack_insert_object (parser,
+                                       key, keylen, parser->cur_obj)) {
+                               return false;
+                       }
+                       /* Get new container */
+                       container = ucl_msgpack_get_container (parser, obj_parser, len);
+
+                       if (container == NULL) {
+                               return false;
+                       }
+
+                       ret = obj_parser->func (parser, container, len, obj_parser->fmt,
+                                                               p, remain);
+                       CONSUME_RET;
+
+                       if (len > 0) {
+                               state = read_type;
+                               next_state = read_array_value;
+                       }
+                       else {
+                               /* Empty array */
+                               state = finish_array_value;
+                       }
+                       break;
+
+               case read_array_value:
+                       /*
+                        * p is now at the value start, len now contains length read and
+                        * obj_parser contains the corresponding specific parser
+                        */
+                       container = parser->stack;
+
+                       if (container == NULL) {
+                               return false;
+                       }
+
+                       ret = obj_parser->func (parser, container, len, obj_parser->fmt,
+                                       p, remain);
+                       CONSUME_RET;
+
+
+                       /* Insert value to the container and check if we have finished array */
+                       if (!ucl_msgpack_insert_object (parser, NULL, 0,
+                                       parser->cur_obj)) {
+                               return false;
+                       }
+
+                       if (ucl_msgpack_is_container_finished (container)) {
+                               state = finish_array_value;
+                       }
+                       else {
+                               /* Read more elements */
+                               state = read_type;
+                               next_state = read_array_value;
+                       }
+
+                       break;
+
+               case read_assoc_key:
+                       /*
+                        * Keys must have string type for ucl msgpack
+                        */
+                       if (!(obj_parser->flags & MSGPACK_FLAG_KEY)) {
+                               ucl_create_err (&parser->err, "bad type for key: %u, expected "
+                                               "string", (unsigned)obj_parser->fmt);
+
+                               return false;
+                       }
+
+                       key = p;
+                       keylen = len;
+
+                       if (keylen > remain || keylen == 0) {
+                               ucl_create_err (&parser->err, "too long or empty key");
+                               return false;
+                       }
+
+                       p += len;
+                       remain -= len;
+
+                       state = read_type;
+                       next_state = read_assoc_value;
+                       break;
+
+               case read_assoc_value:
+                       /*
+                        * p is now at the value start, len now contains length read and
+                        * obj_parser contains the corresponding specific parser
+                        */
+                       container = parser->stack;
+
+                       if (container == NULL) {
+                               return false;
+                       }
+
+                       ret = obj_parser->func (parser, container, len, obj_parser->fmt,
+                                       p, remain);
+                       CONSUME_RET;
+
+                       assert (key != NULL && keylen > 0);
+
+                       if (!ucl_msgpack_insert_object (parser, key, keylen,
+                                       parser->cur_obj)) {
+                               return false;
+                       }
+
+                       key = NULL;
+                       keylen = 0;
+
+                       if (ucl_msgpack_is_container_finished (container)) {
+                               state = finish_assoc_value;
+                       }
+                       else {
+                               /* Read more elements */
+                               state = read_type;
+                               next_state = read_assoc_key;
+                       }
+                       break;
+
+               case finish_array_value:
+               case finish_assoc_value:
+                       GET_NEXT_STATE;
+                       state = read_type;
+                       break;
+
+               case error_state:
+                       ucl_create_err (&parser->err, "invalid state machine state");
+
+                       return false;
+               }
+       }
+
+       /* Check the finishing state */
+       switch (state) {
+       case start_array:
+       case start_assoc:
+               /* Empty container at the end */
+               if (len != 0) {
+                       ucl_create_err (&parser->err, "invalid non-empty container at the end");
+
+                       return false;
+               }
+
+               parser->cur_obj = ucl_object_new_full (
+                               state == start_array ? UCL_ARRAY : UCL_OBJECT,
+                               parser->chunks->priority);
+               /* Insert to the previous level container */
+               if (!ucl_msgpack_insert_object (parser,
+                               key, keylen, parser->cur_obj)) {
+                       return false;
+               }
+               /* Get new container */
+               container = ucl_msgpack_get_container (parser, obj_parser, len);
+
+               if (container == NULL) {
+                       return false;
+               }
+
+               ret = obj_parser->func (parser, container, len, obj_parser->fmt,
+                               p, remain);
+               break;
+
+       case read_array_value:
+       case read_assoc_value:
+               if (len != 0) {
+                       ucl_create_err (&parser->err, "unfinished value at the end");
+
+                       return false;
+               }
+
+               container = parser->stack;
+
+               if (container == NULL) {
+                       return false;
+               }
+
+               ret = obj_parser->func (parser, container, len, obj_parser->fmt,
+                               p, remain);
+               CONSUME_RET;
+
+
+               /* Insert value to the container and check if we have finished array */
+               if (!ucl_msgpack_insert_object (parser, NULL, 0,
+                               parser->cur_obj)) {
+                       return false;
+               }
+               break;
+       case finish_array_value:
+       case finish_assoc_value:
+       case read_type:
+               /* Valid finishing state */
+               break;
+       default:
+               /* Invalid finishing state */
+               ucl_create_err (&parser->err, "invalid state machine finishing state: %d",
+                               state);
+
+               return false;
+       }
+
+       /* Rewind to the top level container */
+       ucl_msgpack_get_next_container (parser);
+       assert (parser->stack == NULL ||
+                       (parser->stack->level & MSGPACK_CONTAINER_BIT) == 0);
+
+       return true;
+}
+
+bool
+ucl_parse_msgpack (struct ucl_parser *parser)
+{
+       ucl_object_t *container = NULL;
+       const unsigned char *p;
+       bool ret;
+
+       assert (parser != NULL);
+       assert (parser->chunks != NULL);
+       assert (parser->chunks->begin != NULL);
+       assert (parser->chunks->remain != 0);
+
+       p = parser->chunks->begin;
+
+       if (parser->stack) {
+               container = parser->stack->obj;
+       }
+
+       /*
+        * When we start parsing message pack chunk, we must ensure that we
+        * have either a valid container or the top object inside message pack is
+        * of container type
+        */
+       if (container == NULL) {
+               if ((*p & 0x80) != 0x80 && !(*p >= 0xdc && *p <= 0xdf)) {
+                       ucl_create_err (&parser->err, "bad top level object for msgpack");
+                       return false;
+               }
+       }
+
+       ret = ucl_msgpack_consume (parser);
+
+       if (ret && parser->top_obj == NULL) {
+               parser->top_obj = parser->cur_obj;
+       }
+
+       return ret;
+}
+
+static ssize_t
+ucl_msgpack_parse_map (struct ucl_parser *parser,
+               struct ucl_stack *container, size_t len, enum ucl_msgpack_format fmt,
+               const unsigned char *pos, size_t remain)
+{
+       container->obj = parser->cur_obj;
+
+       return 0;
+}
+
+static ssize_t
+ucl_msgpack_parse_array (struct ucl_parser *parser,
+               struct ucl_stack *container, size_t len, enum ucl_msgpack_format fmt,
+               const unsigned char *pos, size_t remain)
+{
+       container->obj = parser->cur_obj;
+
+       return 0;
+}
+
+static ssize_t
+ucl_msgpack_parse_string (struct ucl_parser *parser,
+               struct ucl_stack *container, size_t len, enum ucl_msgpack_format fmt,
+               const unsigned char *pos, size_t remain)
+{
+       ucl_object_t *obj;
+
+       if (len > remain) {
+               return -1;
+       }
+
+       obj = ucl_object_new_full (UCL_STRING, parser->chunks->priority);
+       obj->value.sv = pos;
+       obj->len = len;
+
+       if (fmt >= msgpack_bin8 && fmt <= msgpack_bin32) {
+               obj->flags |= UCL_OBJECT_BINARY;
+       }
+
+       if (!(parser->flags & UCL_PARSER_ZEROCOPY)) {
+               if (obj->flags & UCL_OBJECT_BINARY) {
+                       obj->trash_stack[UCL_TRASH_VALUE] = malloc (len);
+
+                       if (obj->trash_stack[UCL_TRASH_VALUE] != NULL) {
+                               memcpy (obj->trash_stack[UCL_TRASH_VALUE], pos, len);
+                       }
+               }
+               else {
+                       ucl_copy_value_trash (obj);
+               }
+       }
+
+       parser->cur_obj = obj;
+
+       return len;
+}
+
+static ssize_t
+ucl_msgpack_parse_int (struct ucl_parser *parser,
+               struct ucl_stack *container, size_t len, enum ucl_msgpack_format fmt,
+               const unsigned char *pos, size_t remain)
+{
+       ucl_object_t *obj;
+
+       if (len > remain) {
+               return -1;
+       }
+
+       obj = ucl_object_new_full (UCL_INT, parser->chunks->priority);
+
+       switch (fmt) {
+       case msgpack_positive_fixint:
+               obj->value.iv = (*pos & 0x7f);
+               len = 1;
+               break;
+       case msgpack_negative_fixint:
+               obj->value.iv = - (*pos & 0x1f);
+               len = 1;
+               break;
+       case msgpack_uint8:
+               obj->value.iv = (unsigned char)*pos;
+               len = 1;
+               break;
+       case msgpack_int8:
+               obj->value.iv = (signed char)*pos;
+               len = 1;
+               break;
+       case msgpack_int16:
+               obj->value.iv = FROM_BE16 (*(int16_t *)pos);
+               len = 2;
+               break;
+       case msgpack_uint16:
+               obj->value.iv = FROM_BE16 (*(uint16_t *)pos);
+               len = 2;
+               break;
+       case msgpack_int32:
+               obj->value.iv = FROM_BE32 (*(int32_t *)pos);
+               len = 4;
+               break;
+       case msgpack_uint32:
+               obj->value.iv = FROM_BE32 (*(uint32_t *)pos);
+               len = 4;
+               break;
+       case msgpack_int64:
+               obj->value.iv = FROM_BE64 (*(int64_t *)pos);
+               len = 8;
+               break;
+       case msgpack_uint64:
+               obj->value.iv = FROM_BE64 (*(uint64_t *)pos);
+               len = 8;
+               break;
+       default:
+               assert (0);
+               break;
+       }
+
+       parser->cur_obj = obj;
+
+       return len;
+}
+
+static ssize_t
+ucl_msgpack_parse_float (struct ucl_parser *parser,
+               struct ucl_stack *container, size_t len, enum ucl_msgpack_format fmt,
+               const unsigned char *pos, size_t remain)
+{
+       ucl_object_t *obj;
+       union {
+               uint32_t i;
+               float f;
+       } d;
+
+       if (len > remain) {
+               return -1;
+       }
+
+       obj = ucl_object_new_full (UCL_FLOAT, parser->chunks->priority);
+
+       switch (fmt) {
+       case msgpack_float32:
+               d.i = FROM_BE32 (*(uint32_t *)pos);
+               /* XXX: can be slow */
+               obj->value.dv = d.f;
+               len = 4;
+               break;
+       case msgpack_float64:
+               obj->value.iv = FROM_BE64 (*(uint64_t *)pos);
+               len = 8;
+               break;
+       default:
+               assert (0);
+               break;
+       }
+
+       parser->cur_obj = obj;
+
+       return len;
+}
+
+static ssize_t
+ucl_msgpack_parse_bool (struct ucl_parser *parser,
+               struct ucl_stack *container, size_t len, enum ucl_msgpack_format fmt,
+               const unsigned char *pos, size_t remain)
+{
+       ucl_object_t *obj;
+
+       if (len > remain) {
+               return -1;
+       }
+
+       obj = ucl_object_new_full (UCL_BOOLEAN, parser->chunks->priority);
+
+       switch (fmt) {
+       case msgpack_true:
+               obj->value.iv = true;
+               break;
+       case msgpack_false:
+               obj->value.iv = false;
+               break;
+       default:
+               assert (0);
+               break;
+       }
+
+       parser->cur_obj = obj;
+
+       return 1;
+}
+
+static ssize_t
+ucl_msgpack_parse_null (struct ucl_parser *parser,
+               struct ucl_stack *container, size_t len, enum ucl_msgpack_format fmt,
+               const unsigned char *pos, size_t remain)
+{
+       ucl_object_t *obj;
+
+       if (len > remain) {
+               return -1;
+       }
+
+       obj = ucl_object_new_full (UCL_NULL, parser->chunks->priority);
+       parser->cur_obj = obj;
+
+       return 1;
+}
+
+static ssize_t
+ucl_msgpack_parse_ignore (struct ucl_parser *parser,
+               struct ucl_stack *container, size_t len, enum ucl_msgpack_format fmt,
+               const unsigned char *pos, size_t remain)
+{
+       if (len > remain) {
+               return -1;
+       }
+
+       switch (fmt) {
+       case msgpack_fixext1:
+               len = 2;
+               break;
+       case msgpack_fixext2:
+               len = 3;
+               break;
+       case msgpack_fixext4:
+               len = 5;
+               break;
+       case msgpack_fixext8:
+               len = 9;
+               break;
+       case msgpack_fixext16:
+               len = 17;
+               break;
+       case msgpack_ext8:
+       case msgpack_ext16:
+       case msgpack_ext32:
+               len = len + 1;
+               break;
+       default:
+               ucl_create_err (&parser->err, "bad type: %x", (unsigned)fmt);
+               return -1;
+       }
+
+       return len;
+}
index 03935d9d0ae6595e03fd0dbb584fdf0506715830..ae8b3c2591698847d0b0d3b9e7985892bb58f9dd 100644 (file)
@@ -562,7 +562,8 @@ ucl_copy_or_store_ptr (struct ucl_parser *parser,
  * @return
  */
 static inline ucl_object_t *
-ucl_add_parser_stack (ucl_object_t *obj, struct ucl_parser *parser, bool is_array, int level)
+ucl_parser_add_container (ucl_object_t *obj, struct ucl_parser *parser,
+               bool is_array, int level)
 {
        struct ucl_stack *st;
 
@@ -573,7 +574,9 @@ ucl_add_parser_stack (ucl_object_t *obj, struct ucl_parser *parser, bool is_arra
                else {
                        obj->type = UCL_OBJECT;
                }
-               obj->value.ov = ucl_hash_create (parser->flags & UCL_PARSER_KEY_LOWERCASE);
+               if (obj->value.ov == NULL) {
+                       obj->value.ov = ucl_hash_create (parser->flags & UCL_PARSER_KEY_LOWERCASE);
+               }
                parser->state = UCL_STATE_KEY;
        }
        else {
@@ -1001,6 +1004,98 @@ ucl_parser_append_elt (struct ucl_parser *parser, ucl_hash_t *cont,
        }
 }
 
+bool
+ucl_parser_process_object_element (struct ucl_parser *parser, ucl_object_t *nobj)
+{
+       ucl_hash_t *container;
+       ucl_object_t *tobj;
+
+       container = parser->stack->obj->value.ov;
+
+       tobj = __DECONST (ucl_object_t *, ucl_hash_search_obj (container, nobj));
+       if (tobj == NULL) {
+               container = ucl_hash_insert_object (container, nobj,
+                               parser->flags & UCL_PARSER_KEY_LOWERCASE);
+               nobj->prev = nobj;
+               nobj->next = NULL;
+               parser->stack->obj->len ++;
+       }
+       else {
+               unsigned priold = ucl_object_get_priority (tobj),
+                               prinew = ucl_object_get_priority (nobj);
+               switch (parser->chunks->strategy) {
+
+               case UCL_DUPLICATE_APPEND:
+                       /*
+                        * 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
+                        */
+
+
+                       /* Special case for inherited objects */
+                       if (tobj->flags & UCL_OBJECT_INHERITED) {
+                               prinew = priold + 1;
+                       }
+
+                       if (priold == prinew) {
+                               ucl_parser_append_elt (parser, container, tobj, nobj);
+                       }
+                       else if (priold > prinew) {
+                               /*
+                                * We add this new object to a list of trash objects just to ensure
+                                * that it won't come to any real object
+                                * XXX: rather inefficient approach
+                                */
+                               DL_APPEND (parser->trash_objs, nobj);
+                       }
+                       else {
+                               ucl_hash_replace (container, tobj, nobj);
+                               ucl_object_unref (tobj);
+                       }
+
+                       break;
+
+               case UCL_DUPLICATE_REWRITE:
+                       /* We just rewrite old values regardless of priority */
+                       ucl_hash_replace (container, tobj, nobj);
+                       ucl_object_unref (tobj);
+
+                       break;
+
+               case UCL_DUPLICATE_ERROR:
+                       ucl_create_err (&parser->err, "error while parsing %s: "
+                                       "line: %d, column: %d: duplicate element for key '%s' "
+                                       "has been found",
+                                       parser->cur_file ? parser->cur_file : "<unknown>",
+                                       parser->chunks->line, parser->chunks->column, nobj->key);
+                       return false;
+
+               case UCL_DUPLICATE_MERGE:
+                       /*
+                        * Here we do have some old object so we just push it on top of objects stack
+                        */
+                       if (tobj->type == UCL_OBJECT || tobj->type == UCL_ARRAY) {
+                               ucl_object_unref (nobj);
+                               nobj = tobj;
+                       }
+                       else {
+                               /* For other types we create implicit array as usual */
+                               ucl_parser_append_elt (parser, container, tobj, nobj);
+                       }
+                       break;
+               }
+       }
+
+       parser->stack->obj->value.ov = container;
+       parser->cur_obj = nobj;
+
+       return true;
+}
+
 /**
  * Parse a key in an object
  * @param parser
@@ -1018,8 +1113,7 @@ ucl_parse_key (struct ucl_parser *parser, struct ucl_chunk *chunk,
        bool got_quote = false, got_eq = false, got_semicolon = false,
                        need_unescape = false, ucl_escape = false, var_expand = false,
                        got_content = false, got_sep = false;
-       ucl_object_t *nobj, *tobj;
-       ucl_hash_t *container;
+       ucl_object_t *nobj;
        ssize_t keylen;
 
        p = chunk->pos;
@@ -1204,57 +1298,17 @@ ucl_parse_key (struct ucl_parser *parser, struct ucl_chunk *chunk,
                return false;
        }
 
-       container = parser->stack->obj->value.ov;
        nobj->key = key;
        nobj->keylen = keylen;
-       tobj = __DECONST (ucl_object_t *, ucl_hash_search_obj (container, nobj));
-       if (tobj == NULL) {
-               container = ucl_hash_insert_object (container, nobj,
-                               parser->flags & UCL_PARSER_KEY_LOWERCASE);
-               nobj->prev = nobj;
-               nobj->next = NULL;
-               parser->stack->obj->len ++;
-       }
-       else {
-               /*
-                * 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);
-
-               /* Special case for inherited objects */
-               if (tobj->flags & UCL_OBJECT_INHERITED) {
-                       prinew = priold + 1;
-               }
 
-               if (priold == prinew) {
-                       ucl_parser_append_elt (parser, container, tobj, nobj);
-               }
-               else if (priold > prinew) {
-                       /*
-                        * We add this new object to a list of trash objects just to ensure
-                        * that it won't come to any real object
-                        * XXX: rather inefficient approach
-                        */
-                       DL_APPEND (parser->trash_objs, nobj);
-               }
-               else {
-                       ucl_hash_replace (container, tobj, nobj);
-                       ucl_object_unref (tobj);
-               }
+       if (!ucl_parser_process_object_element (parser, nobj)) {
+               return false;
        }
 
        if (ucl_escape) {
                nobj->flags |= UCL_OBJECT_NEED_KEY_ESCAPE;
        }
 
-       parser->stack->obj->value.ov = container;
-       parser->cur_obj = nobj;
 
        return true;
 }
@@ -1387,8 +1441,8 @@ ucl_parse_multiline_string (struct ucl_parser *parser,
        return len;
 }
 
-static ucl_object_t*
-ucl_get_value_object (struct ucl_parser *parser)
+static inline ucl_object_t*
+ucl_parser_get_container (struct ucl_parser *parser)
 {
        ucl_object_t *t, *obj = NULL;
 
@@ -1400,7 +1454,12 @@ ucl_get_value_object (struct ucl_parser *parser)
                /* Object must be allocated */
                obj = ucl_object_new_full (UCL_NULL, parser->chunks->priority);
                t = parser->stack->obj;
-               ucl_array_append (t, obj);
+
+               if (!ucl_array_append (t, obj)) {
+                       ucl_object_unref (obj);
+                       return NULL;
+               }
+
                parser->cur_obj = obj;
        }
        else {
@@ -1451,7 +1510,7 @@ ucl_parse_value (struct ucl_parser *parser, struct ucl_chunk *chunk)
                                return false;
                        }
 
-                       obj = ucl_get_value_object (parser);
+                       obj = ucl_parser_get_container (parser);
                        str_len = chunk->pos - c - 2;
                        obj->type = UCL_STRING;
                        if ((str_len = ucl_copy_or_store_ptr (parser, c + 1,
@@ -1468,9 +1527,9 @@ ucl_parse_value (struct ucl_parser *parser, struct ucl_chunk *chunk)
                        return true;
                        break;
                case '{':
-                       obj = ucl_get_value_object (parser);
+                       obj = ucl_parser_get_container (parser);
                        /* We have a new object */
-                       obj = ucl_add_parser_stack (obj, parser, false, parser->stack->level);
+                       obj = ucl_parser_add_container (obj, parser, false, parser->stack->level);
                        if (obj == NULL) {
                                return false;
                        }
@@ -1480,9 +1539,9 @@ ucl_parse_value (struct ucl_parser *parser, struct ucl_chunk *chunk)
                        return true;
                        break;
                case '[':
-                       obj = ucl_get_value_object (parser);
+                       obj = ucl_parser_get_container (parser);
                        /* We have a new array */
-                       obj = ucl_add_parser_stack (obj, parser, true, parser->stack->level);
+                       obj = ucl_parser_add_container (obj, parser, true, parser->stack->level);
                        if (obj == NULL) {
                                return false;
                        }
@@ -1502,7 +1561,7 @@ ucl_parse_value (struct ucl_parser *parser, struct ucl_chunk *chunk)
                        }
                        break;
                case '<':
-                       obj = ucl_get_value_object (parser);
+                       obj = ucl_parser_get_container (parser);
                        /* We have something like multiline value, which must be <<[A-Z]+\n */
                        if (chunk->end - p > 3) {
                                if (memcmp (p, "<<", 2) == 0) {
@@ -1545,7 +1604,7 @@ ucl_parse_value (struct ucl_parser *parser, struct ucl_chunk *chunk)
                default:
 parse_string:
                        if (obj == NULL) {
-                               obj = ucl_get_value_object (parser);
+                               obj = ucl_parser_get_container (parser);
                        }
 
                        /* Parse atom */
@@ -1905,17 +1964,6 @@ ucl_state_machine (struct ucl_parser *parser)
        bool next_key = false, end_of_object = false, ret;
 
        if (parser->top_obj == NULL) {
-               if (*chunk->pos == '[') {
-                       obj = ucl_add_parser_stack (NULL, parser, true, 0);
-               }
-               else {
-                       obj = ucl_add_parser_stack (NULL, parser, false, 0);
-               }
-               if (obj == NULL) {
-                       return false;
-               }
-               parser->top_obj = obj;
-               parser->cur_obj = obj;
                parser->state = UCL_STATE_INIT;
        }
 
@@ -1939,7 +1987,9 @@ ucl_state_machine (struct ucl_parser *parser)
                                                UCL_CHARACTER_WHITESPACE_UNSAFE)) {
                                        ucl_chunk_skipc (chunk, p);
                                }
+
                                p = chunk->pos;
+
                                if (*p == '[') {
                                        parser->state = UCL_STATE_VALUE;
                                        ucl_chunk_skipc (chunk, p);
@@ -1950,6 +2000,23 @@ ucl_state_machine (struct ucl_parser *parser)
                                                ucl_chunk_skipc (chunk, p);
                                        }
                                }
+
+                               if (parser->top_obj == NULL) {
+                                       if (parser->state == UCL_STATE_VALUE) {
+                                               obj = ucl_parser_add_container (NULL, parser, true, 0);
+                                       }
+                                       else {
+                                               obj = ucl_parser_add_container (NULL, parser, false, 0);
+                                       }
+
+                                       if (obj == NULL) {
+                                               return false;
+                                       }
+
+                                       parser->top_obj = obj;
+                                       parser->cur_obj = obj;
+                               }
+
                        }
                        break;
                case UCL_STATE_KEY:
@@ -1983,7 +2050,7 @@ ucl_state_machine (struct ucl_parser *parser)
                        else if (parser->state != UCL_STATE_MACRO_NAME) {
                                if (next_key && parser->stack->obj->type == UCL_OBJECT) {
                                        /* Parse more keys and nest objects accordingly */
-                                       obj = ucl_add_parser_stack (parser->cur_obj, parser, false,
+                                       obj = ucl_parser_add_container (parser->cur_obj, parser, false,
                                                        parser->stack->level + 1);
                                        if (obj == NULL) {
                                                return false;
@@ -2270,8 +2337,9 @@ ucl_parser_set_variables_handler (struct ucl_parser *parser,
 }
 
 bool
-ucl_parser_add_chunk_priority (struct ucl_parser *parser, const unsigned char *data,
-               size_t len, unsigned priority)
+ucl_parser_add_chunk_full (struct ucl_parser *parser, const unsigned char *data,
+               size_t len, unsigned priority, enum ucl_duplicate_strategy strat,
+               enum ucl_parse_type parse_type)
 {
        struct ucl_chunk *chunk;
 
@@ -2300,14 +2368,24 @@ ucl_parser_add_chunk_priority (struct ucl_parser *parser, const unsigned char *d
                chunk->line = 1;
                chunk->column = 0;
                chunk->priority = priority;
+               chunk->strategy = strat;
+               chunk->parse_type = parse_type;
                LL_PREPEND (parser->chunks, chunk);
                parser->recursion ++;
+
                if (parser->recursion > UCL_MAX_RECURSION) {
                        ucl_create_err (&parser->err, "maximum include nesting limit is reached: %d",
                                        parser->recursion);
                        return false;
                }
-               return ucl_state_machine (parser);
+
+               switch (parse_type) {
+               default:
+               case UCL_PARSE_UCL:
+                       return ucl_state_machine (parser);
+               case UCL_PARSE_MSGPACK:
+                       return ucl_parse_msgpack (parser);
+               }
        }
 
        ucl_create_err (&parser->err, "a parser is in an invalid state");
@@ -2315,6 +2393,19 @@ ucl_parser_add_chunk_priority (struct ucl_parser *parser, const unsigned char *d
        return false;
 }
 
+bool
+ucl_parser_add_chunk_priority (struct ucl_parser *parser,
+               const unsigned char *data, size_t len, unsigned priority)
+{
+       /* We dereference parser, so this check is essential */
+       if (parser == NULL) {
+               return false;
+       }
+
+       return ucl_parser_add_chunk_full (parser, data, len,
+                               priority, UCL_DUPLICATE_APPEND, UCL_PARSE_UCL);
+}
+
 bool
 ucl_parser_add_chunk (struct ucl_parser *parser, const unsigned char *data,
                size_t len)
@@ -2323,8 +2414,8 @@ ucl_parser_add_chunk (struct ucl_parser *parser, const unsigned char *data,
                return false;
        }
 
-       return ucl_parser_add_chunk_priority (parser, data, len,
-                       parser->default_priority);
+       return ucl_parser_add_chunk_full (parser, data, len,
+                       parser->default_priority, UCL_DUPLICATE_APPEND, UCL_PARSE_UCL);
 }
 
 bool
index 7833e1f1cfe3e26c246b6f3c55dd19fc2d02408e..9ea151e37c4bc46c9b79d79f1cad2b235231e7c7 100644 (file)
@@ -410,11 +410,24 @@ ucl_copy_value_trash (const ucl_object_t *obj)
                if (obj->type == UCL_STRING) {
 
                        /* Special case for strings */
-                       deconst->trash_stack[UCL_TRASH_VALUE] = malloc (obj->len + 1);
-                       if (deconst->trash_stack[UCL_TRASH_VALUE] != NULL) {
-                               memcpy (deconst->trash_stack[UCL_TRASH_VALUE], obj->value.sv, obj->len);
-                               deconst->trash_stack[UCL_TRASH_VALUE][obj->len] = '\0';
-                               deconst->value.sv = obj->trash_stack[UCL_TRASH_VALUE];
+                       if (obj->flags & UCL_OBJECT_BINARY) {
+                               deconst->trash_stack[UCL_TRASH_VALUE] = malloc (obj->len);
+                               if (deconst->trash_stack[UCL_TRASH_VALUE] != NULL) {
+                                       memcpy (deconst->trash_stack[UCL_TRASH_VALUE],
+                                                       obj->value.sv,
+                                                       obj->len);
+                                       deconst->value.sv = obj->trash_stack[UCL_TRASH_VALUE];
+                               }
+                       }
+                       else {
+                               deconst->trash_stack[UCL_TRASH_VALUE] = malloc (obj->len + 1);
+                               if (deconst->trash_stack[UCL_TRASH_VALUE] != NULL) {
+                                       memcpy (deconst->trash_stack[UCL_TRASH_VALUE],
+                                                       obj->value.sv,
+                                                       obj->len);
+                                       deconst->trash_stack[UCL_TRASH_VALUE][obj->len] = '\0';
+                                       deconst->value.sv = obj->trash_stack[UCL_TRASH_VALUE];
+                               }
                        }
                }
                else {
@@ -424,6 +437,7 @@ ucl_copy_value_trash (const ucl_object_t *obj)
                }
                deconst->flags |= UCL_OBJECT_ALLOCATED_VALUE;
        }
+       
        return obj->trash_stack[UCL_TRASH_VALUE];
 }
 
@@ -796,6 +810,20 @@ ucl_sig_check (const unsigned char *data, size_t datalen,
 }
 #endif
 
+struct ucl_include_params {
+       bool check_signature;
+       bool must_exist;
+       bool use_glob;
+       bool use_prefix;
+       bool soft_fail;
+       bool allow_glob;
+       unsigned priority;
+       enum ucl_duplicate_strategy strat;
+       enum ucl_parse_type parse_type;
+       const char *prefix;
+       const char *target;
+};
+
 /**
  * Include an url to configuration
  * @param data
@@ -806,8 +834,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,
-               bool use_prefix, const char *prefix, const char *target, unsigned priority)
+               struct ucl_parser *parser,
+               struct ucl_include_params *params)
 {
 
        bool res;
@@ -819,11 +847,11 @@ ucl_include_url (const unsigned char *data, size_t len,
 
        snprintf (urlbuf, sizeof (urlbuf), "%.*s", (int)len, data);
 
-       if (!ucl_fetch_url (urlbuf, &buf, &buflen, &parser->err, must_exist)) {
-               return (!must_exist || false);
+       if (!ucl_fetch_url (urlbuf, &buf, &buflen, &parser->err, params->must_exist)) {
+               return (!params->must_exist || false);
        }
 
-       if (check_signature) {
+       if (params->check_signature) {
 #if (defined(HAVE_OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x10000000L)
                unsigned char *sigbuf = NULL;
                size_t siglen = 0;
@@ -850,7 +878,8 @@ ucl_include_url (const unsigned char *data, size_t len,
        prev_state = parser->state;
        parser->state = UCL_STATE_INIT;
 
-       res = ucl_parser_add_chunk_priority (parser, buf, buflen, priority);
+       res = ucl_parser_add_chunk_full (parser, buf, buflen, params->priority,
+                       params->strat, params->parse_type);
        if (res == true) {
                /* Remove chunk from the stack */
                chunk = parser->chunks;
@@ -879,9 +908,7 @@ 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,
-               bool use_prefix, const char *prefix, const char *target,
-               bool soft_fail, unsigned priority)
+               struct ucl_parser *parser, struct ucl_include_params *params)
 {
        bool res;
        struct ucl_chunk *chunk;
@@ -898,10 +925,10 @@ ucl_include_file_single (const unsigned char *data, size_t len,
 
        snprintf (filebuf, sizeof (filebuf), "%.*s", (int)len, data);
        if (ucl_realpath (filebuf, realbuf) == NULL) {
-               if (soft_fail) {
+               if (params->soft_fail) {
                        return false;
                }
-               if (!must_exist) {
+               if (!params->must_exist) {
                        return true;
                }
                ucl_create_err (&parser->err, "cannot open file %s: %s",
@@ -912,22 +939,23 @@ 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) {
+               if (params->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) {
+       if (!ucl_fetch_file (realbuf, &buf, &buflen, &parser->err, params->must_exist)) {
+               if (params->soft_fail) {
                        return false;
                }
-               return (!must_exist || false);
+               return (!params->must_exist || false);
        }
 
-       if (check_signature) {
+       if (params->check_signature) {
 #if (defined(HAVE_OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x10000000L)
                unsigned char *sigbuf = NULL;
                size_t siglen = 0;
@@ -971,26 +999,27 @@ 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) {
+       if (params->use_prefix && params->prefix == NULL) {
                /* Auto generate a key name based on the included filename */
-               prefix = basename (realbuf);
-               ext = strrchr(prefix, '.');
+               params->prefix = basename (realbuf);
+               ext = strrchr (params->prefix, '.');
                if (ext != NULL && (strcmp (ext, ".conf") == 0 || strcmp (ext, ".ucl") == 0)) {
                        /* Strip off .conf or .ucl */
                        *ext = '\0';
                }
        }
-       if (prefix != NULL) {
+       if (params->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)));
+               old_obj = __DECONST (ucl_object_t *, ucl_hash_search (container,
+                               params->prefix, strlen (params->prefix)));
 
-               if (strcasecmp (target, "array") == 0 && old_obj == NULL) {
+               if (strcasecmp (params->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);
+                       old_obj = ucl_object_new_full (UCL_ARRAY, params->priority);
+                       old_obj->key = params->prefix;
+                       old_obj->keylen = strlen (params->prefix);
                        ucl_copy_key_trash(old_obj);
                        old_obj->prev = old_obj;
                        old_obj->next = NULL;
@@ -999,7 +1028,7 @@ ucl_include_file_single (const unsigned char *data, size_t len,
                                        parser->flags & UCL_PARSER_KEY_LOWERCASE);
                        parser->stack->obj->len ++;
 
-                       nest_obj = ucl_object_new_full (UCL_OBJECT, priority);
+                       nest_obj = ucl_object_new_full (UCL_OBJECT, params->priority);
                        nest_obj->prev = nest_obj;
                        nest_obj->next = NULL;
 
@@ -1007,9 +1036,9 @@ ucl_include_file_single (const unsigned char *data, size_t len,
                }
                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);
+                       nest_obj = ucl_object_new_full (UCL_OBJECT, params->priority);
+                       nest_obj->key = params->prefix;
+                       nest_obj->keylen = strlen (params->prefix);
                        ucl_copy_key_trash(nest_obj);
                        nest_obj->prev = nest_obj;
                        nest_obj->next = NULL;
@@ -1018,10 +1047,11 @@ ucl_include_file_single (const unsigned char *data, size_t len,
                                        parser->flags & UCL_PARSER_KEY_LOWERCASE);
                        parser->stack->obj->len ++;
                }
-               else if (strcasecmp (target, "array") == 0 || ucl_object_type(old_obj) == UCL_ARRAY) {
+               else if (strcasecmp (params->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 = ucl_object_new_full (UCL_OBJECT, params->priority);
                                nest_obj->prev = nest_obj;
                                nest_obj->next = NULL;
 
@@ -1036,7 +1066,7 @@ ucl_include_file_single (const unsigned char *data, size_t len,
                                new_obj->prev = new_obj;
                                new_obj->next = NULL;
 
-                               nest_obj = ucl_object_new_full (UCL_OBJECT, priority);
+                               nest_obj = ucl_object_new_full (UCL_OBJECT, params->priority);
                                nest_obj->prev = nest_obj;
                                nest_obj->next = NULL;
 
@@ -1054,7 +1084,7 @@ ucl_include_file_single (const unsigned char *data, size_t len,
                                /* The key is not an object */
                                ucl_create_err (&parser->err,
                                                "Conflicting type for key: %s",
-                                               prefix);
+                                               params->prefix);
                                return false;
                        }
                }
@@ -1076,8 +1106,9 @@ ucl_include_file_single (const unsigned char *data, size_t len,
                }
        }
 
-       res = ucl_parser_add_chunk_priority (parser, buf, buflen, priority);
-       if (!res && !must_exist) {
+       res = ucl_parser_add_chunk_full (parser, buf, buflen, params->priority,
+                       params->strat, params->parse_type);
+       if (!res && !params->must_exist) {
                /* Free error */
                utstring_free (parser->err);
                parser->err = NULL;
@@ -1085,7 +1116,7 @@ ucl_include_file_single (const unsigned char *data, size_t len,
        }
 
        /* Stop nesting the include, take 1 level off the stack */
-       if (prefix != NULL && nest_obj != NULL) {
+       if (params->prefix != NULL && nest_obj != NULL) {
                parser->stack = st->next;
                UCL_FREE (sizeof (struct ucl_stack), st);
        }
@@ -1144,9 +1175,7 @@ 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, bool use_prefix, const char *prefix,
-               const char *target, bool soft_fail, unsigned priority)
+               struct ucl_parser *parser, struct ucl_include_params *params)
 {
        const unsigned char *p = data, *end = data + len;
        bool need_glob = false;
@@ -1155,9 +1184,8 @@ ucl_include_file (const unsigned char *data, size_t len,
        size_t i;
 
 #ifndef _WIN32
-       if (!allow_glob) {
-               return ucl_include_file_single (data, len, parser, check_signature,
-                       must_exist, use_prefix, prefix, target, soft_fail, priority);
+       if (!params->allow_glob) {
+               return ucl_include_file_single (data, len, parser, params);
        }
        else {
                /* Check for special symbols in a filename */
@@ -1174,13 +1202,12 @@ ucl_include_file (const unsigned char *data, size_t len,
                        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);
+                               return (!params->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, use_prefix, prefix, target, soft_fail, priority)) {
-                                       if (soft_fail) {
+                                               strlen (globbuf.gl_pathv[i]), parser, params)) {
+                                       if (params->soft_fail) {
                                                continue;
                                        }
                                        globfree (&globbuf);
@@ -1190,22 +1217,20 @@ ucl_include_file (const unsigned char *data, size_t len,
                        }
                        globfree (&globbuf);
 
-                       if (cnt == 0 && must_exist) {
+                       if (cnt == 0 && params->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, use_prefix, prefix, target, soft_fail, priority);
+                       return ucl_include_file_single (data, len, parser, params);
                }
        }
 #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, use_prefix, prefix, target, soft_fail, priority);
+       return ucl_include_file_single (data, len, parser, params);
 #endif
        
        return true;
@@ -1227,22 +1252,25 @@ 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, use_prefix, search;
-       const char *prefix, *target;
-       unsigned priority;
+       bool allow_url, search;
+       const char *duplicate;
        const ucl_object_t *param;
        ucl_object_iter_t it = NULL, ip = NULL;
        char ipath[PATH_MAX];
+       struct ucl_include_params params;
 
        /* 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;
+       params.soft_fail = default_try;
+       params.allow_glob = false;
+       params.check_signature = default_sign;
+       params.use_prefix = false;
+       params.target = "object";
+       params.prefix = NULL;
+       params.priority = 0;
+       params.parse_type = UCL_PARSE_UCL;
+       params.strat = UCL_DUPLICATE_APPEND;
+       params.must_exist = !default_try;
+
        search = false;
 
        /* Process arguments */
@@ -1250,27 +1278,43 @@ ucl_include_common (const unsigned char *data, size_t len,
                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);
+                                       params.must_exist = !ucl_object_toboolean (param);
                                }
                                else if (strncmp (param->key, "sign", param->keylen) == 0) {
-                                       need_sign = ucl_object_toboolean (param);
+                                       params.check_signature = ucl_object_toboolean (param);
                                }
                                else if (strncmp (param->key, "glob", param->keylen) == 0) {
-                                       allow_glob = ucl_object_toboolean (param);
+                                       params.allow_glob = ucl_object_toboolean (param);
                                }
                                else if (strncmp (param->key, "url", param->keylen) == 0) {
                                        allow_url = ucl_object_toboolean (param);
                                }
                                else if (strncmp (param->key, "prefix", param->keylen) == 0) {
-                                       use_prefix = ucl_object_toboolean (param);
+                                       params.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);
+                                       params.prefix = ucl_object_tostring (param);
                                }
                                else if (strncmp (param->key, "target", param->keylen) == 0) {
-                                       target = ucl_object_tostring (param);
+                                       params.target = ucl_object_tostring (param);
+                               }
+                               else if (strncmp (param->key, "duplicate", param->keylen) == 0) {
+                                       duplicate = ucl_object_tostring (param);
+
+                                       if (strcmp (duplicate, "append") == 0) {
+                                               params.strat = UCL_DUPLICATE_APPEND;
+                                       }
+                                       else if (strcmp (duplicate, "merge") == 0) {
+                                               params.strat = UCL_DUPLICATE_MERGE;
+                                       }
+                                       else if (strcmp (duplicate, "rewrite") == 0) {
+                                               params.strat = UCL_DUPLICATE_REWRITE;
+                                       }
+                                       else if (strcmp (duplicate, "error") == 0) {
+                                               params.strat = UCL_DUPLICATE_ERROR;
+                                       }
                                }
                        }
                        else if (param->type == UCL_ARRAY) {
@@ -1280,7 +1324,7 @@ ucl_include_common (const unsigned char *data, size_t len,
                        }
                        else if (param->type == UCL_INT) {
                                if (strncmp (param->key, "priority", param->keylen) == 0) {
-                                       priority = ucl_object_toint (param);
+                                       params.priority = ucl_object_toint (param);
                                }
                        }
                }
@@ -1289,20 +1333,17 @@ ucl_include_common (const unsigned char *data, size_t len,
        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);
+                       return ucl_include_url (data, len, parser, &params);
                }
                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);
+                       return ucl_include_file (data, len, parser, &params);
                }
        }
        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);
+                       return ucl_include_url (data, len, parser, &params);
                }
 
                ip = ucl_object_iterate_new (parser->includepaths);
@@ -1310,9 +1351,9 @@ ucl_include_common (const unsigned char *data, size_t len,
                        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) {
+                               if ((search = ucl_include_file (ipath, strlen (ipath),
+                                               parser, &params))) {
+                                       if (!params.allow_glob) {
                                                break;
                                        }
                                }
@@ -2903,7 +2944,9 @@ ucl_object_tostring_safe (const ucl_object_t *obj, const char **target)
 
        switch (obj->type) {
        case UCL_STRING:
-               *target = ucl_copy_value_trash (obj);
+               if (!(obj->flags & UCL_OBJECT_BINARY)) {
+                       *target = ucl_copy_value_trash (obj);
+               }
                break;
        default:
                return false;
@@ -2924,7 +2967,12 @@ ucl_object_tostring (const ucl_object_t *obj)
 const char *
 ucl_object_tostring_forced (const ucl_object_t *obj)
 {
-       return ucl_copy_value_trash (obj);
+       /* TODO: For binary strings we might encode string here */
+       if (!(obj->flags & UCL_OBJECT_BINARY)) {
+               return ucl_copy_value_trash (obj);
+       }
+
+       return NULL;
 }
 
 bool