]> source.dussan.org Git - rspamd.git/commitdiff
Backport fixes from libucl
authorVsevolod Stakhov <vsevolod@highsecure.ru>
Sun, 14 Feb 2016 13:34:42 +0000 (13:34 +0000)
committerVsevolod Stakhov <vsevolod@highsecure.ru>
Sun, 14 Feb 2016 13:34:42 +0000 (13:34 +0000)
contrib/libucl/lua_ucl.c
contrib/libucl/ucl.h
contrib/libucl/ucl_msgpack.c
contrib/libucl/ucl_parser.c
contrib/libucl/ucl_util.c

index 1cb716b2479427d449cef59c80be73d966a81ed5..3c42adbfec76e437d2f6fbe75b9170d4bfe1e8f2 100644 (file)
@@ -29,6 +29,7 @@
 #include "ucl_internal.h"
 #include "lua_ucl.h"
 #include <strings.h>
+#include <zconf.h>
 
 /***
  * @module ucl
@@ -69,6 +70,7 @@ func = "huh";
 #define PARSER_META "ucl.parser.meta"
 #define EMITTER_META "ucl.emitter.meta"
 #define NULL_META "null.emitter.meta"
+#define OBJECT_META "ucl.object.meta"
 
 static int ucl_object_lua_push_array (lua_State *L, const ucl_object_t *obj);
 static int ucl_object_lua_push_scalar (lua_State *L, const ucl_object_t *obj, bool allow_array);
@@ -420,9 +422,7 @@ ucl_object_lua_fromelt (lua_State *L, int idx)
                                        fd->idx = luaL_ref (L, LUA_REGISTRYINDEX);
 
                                        obj = ucl_object_new_userdata (lua_ucl_userdata_dtor,
-                                                       lua_ucl_userdata_emitter);
-                                       obj->type = UCL_USERDATA;
-                                       obj->value.ud = (void *)fd;
+                                                       lua_ucl_userdata_emitter, (void *)fd);
                                }
                        }
                }
@@ -460,6 +460,24 @@ ucl_object_lua_import (lua_State *L, int idx)
        return obj;
 }
 
+static int
+lua_ucl_to_string (lua_State *L, const ucl_object_t *obj, enum ucl_emitter type)
+{
+       unsigned char *result;
+
+       result = ucl_object_emit (obj, type);
+
+       if (result != NULL) {
+               lua_pushstring (L, (const char *)result);
+               free (result);
+       }
+       else {
+               lua_pushnil (L);
+       }
+
+       return 1;
+}
+
 static int
 lua_ucl_parser_init (lua_State *L)
 {
@@ -489,6 +507,23 @@ lua_ucl_parser_get (lua_State *L, int index)
        return *((struct ucl_parser **) luaL_checkudata(L, index, PARSER_META));
 }
 
+static ucl_object_t *
+lua_ucl_object_get (lua_State *L, int index)
+{
+       return *((ucl_object_t **) luaL_checkudata(L, index, OBJECT_META));
+}
+
+static void
+lua_ucl_push_opaque (lua_State *L, ucl_object_t *obj)
+{
+       ucl_object_t **pobj;
+
+       pobj = lua_newuserdata (L, sizeof (*pobj));
+       *pobj = obj;
+       luaL_getmetatable (L, OBJECT_META);
+       lua_setmetatable (L, -2);
+}
+
 /***
  * @method parser:parse_file(name)
  * Parse UCL object from file.
@@ -594,6 +629,105 @@ lua_ucl_parser_get_object (lua_State *L)
        return ret;
 }
 
+/***
+ * @method parser:get_object_wrapped()
+ * Get top object from parser and export it to userdata object without
+ * unwrapping to lua.
+ * @return {ucl.object or nil} ucl object wrapped variable
+ */
+static int
+lua_ucl_parser_get_object_wrapped (lua_State *L)
+{
+       struct ucl_parser *parser;
+       ucl_object_t *obj;
+       int ret = 1;
+
+       parser = lua_ucl_parser_get (L, 1);
+       obj = ucl_parser_get_object (parser);
+
+       if (obj != NULL) {
+               lua_ucl_push_opaque (L, obj);
+       }
+       else {
+               lua_pushnil (L);
+       }
+
+       return ret;
+}
+
+/***
+ * @method parser:validate(schema)
+ * Validates the top object in the parser against schema. Schema might be
+ * another object or a string that represents file to load schema from.
+ *
+ * @param {string/table} schema input schema
+ * @return {result,err} two values: boolean result and the corresponding error
+ *
+ */
+static int
+lua_ucl_parser_validate (lua_State *L)
+{
+       struct ucl_parser *parser, *schema_parser;
+       ucl_object_t *schema;
+       const char *schema_file;
+       struct ucl_schema_error err;
+
+       parser = lua_ucl_parser_get (L, 1);
+
+       if (parser && parser->top_obj) {
+               if (lua_type (L, 2) == LUA_TTABLE) {
+                       schema = ucl_object_lua_import (L, 2);
+
+                       if (schema == NULL) {
+                               lua_pushboolean (L, false);
+                               lua_pushstring (L, "cannot load schema from lua table");
+
+                               return 2;
+                       }
+               }
+               else if (lua_type (L, 2) == LUA_TSTRING) {
+                       schema_parser = ucl_parser_new (0);
+                       schema_file = luaL_checkstring (L, 2);
+
+                       if (!ucl_parser_add_file (schema_parser, schema_file)) {
+                               lua_pushboolean (L, false);
+                               lua_pushfstring (L, "cannot parse schema file \"%s\": "
+                                               "%s", schema_file, ucl_parser_get_error (parser));
+                               ucl_parser_free (schema_parser);
+
+                               return 2;
+                       }
+
+                       schema = ucl_parser_get_object (schema_parser);
+                       ucl_parser_free (schema_parser);
+               }
+               else {
+                       lua_pushboolean (L, false);
+                       lua_pushstring (L, "invalid schema argument");
+
+                       return 2;
+               }
+
+               if (!ucl_object_validate (schema, parser->top_obj, &err)) {
+                       lua_pushboolean (L, false);
+                       lua_pushfstring (L, "validation error: "
+                                       "%s", err.msg);
+               }
+               else {
+                       lua_pushboolean (L, true);
+                       lua_pushnil (L);
+               }
+
+               ucl_object_unref (schema);
+       }
+       else {
+               lua_pushboolean (L, false);
+               lua_pushstring (L, "invalid parser or empty top object");
+       }
+
+       return 2;
+}
+
 static int
 lua_ucl_parser_gc (lua_State *L)
 {
@@ -605,6 +739,189 @@ lua_ucl_parser_gc (lua_State *L)
        return 0;
 }
 
+/***
+ * @method object:unwrap()
+ * Unwraps opaque ucl object to the native lua object (performing copying)
+ * @return {variant} any lua object
+ */
+static int
+lua_ucl_object_unwrap (lua_State *L)
+{
+       ucl_object_t *obj;
+
+       obj = lua_ucl_object_get (L, 1);
+
+       if (obj) {
+               ucl_object_push_lua (L, obj, true);
+       }
+       else {
+               lua_pushnil (L);
+       }
+
+       return 1;
+}
+
+/***
+ * @method object:tostring(type)
+ * Unwraps opaque ucl object to string (json by default). Optionally you can
+ * specify output format:
+ *
+ * - `json` - fine printed json
+ * - `json-compact` - compacted json
+ * - `config` - fine printed configuration
+ * - `ucl` - same as `config`
+ * - `yaml` - embedded yaml
+ * @param {string} type optional
+ * @return {string} string representation of the opaque ucl object
+ */
+static int
+lua_ucl_object_tostring (lua_State *L)
+{
+       ucl_object_t *obj;
+       enum ucl_emitter format = UCL_EMIT_JSON_COMPACT;
+
+       obj = lua_ucl_object_get (L, 1);
+
+       if (obj) {
+               if (lua_gettop (L) > 1) {
+                       if (lua_type (L, 2) == LUA_TSTRING) {
+                               const char *strtype = lua_tostring (L, 2);
+
+                               if (strcasecmp (strtype, "json") == 0) {
+                                       format = UCL_EMIT_JSON;
+                               }
+                               else if (strcasecmp (strtype, "json-compact") == 0) {
+                                       format = UCL_EMIT_JSON_COMPACT;
+                               }
+                               else if (strcasecmp (strtype, "yaml") == 0) {
+                                       format = UCL_EMIT_YAML;
+                               }
+                               else if (strcasecmp (strtype, "config") == 0 ||
+                                               strcasecmp (strtype, "ucl") == 0) {
+                                       format = UCL_EMIT_CONFIG;
+                               }
+                       }
+               }
+
+               return lua_ucl_to_string (L, obj, format);
+       }
+       else {
+               lua_pushnil (L);
+       }
+
+       return 1;
+}
+
+/***
+ * @method object:validate(schema[, path[, ext_refs]])
+ * Validates the given ucl object using schema object represented as another
+ * opaque ucl object. You can also specify path in the form `#/path/def` to
+ * specify the specific schema element to perform validation.
+ *
+ * @param {ucl.object} schema schema object
+ * @param {string} path optional path for validation procedure
+ * @return {result,err} two values: boolean result and the corresponding
+ * error, if `ext_refs` are also specified, then they are returned as opaque
+ * ucl object as {result,err,ext_refs}
+ */
+static int
+lua_ucl_object_validate (lua_State *L)
+{
+       ucl_object_t *obj, *schema, *ext_refs = NULL;
+       const ucl_object_t *schema_elt;
+       bool res = false;
+       struct ucl_schema_error err;
+       const char *path = NULL;
+
+       obj = lua_ucl_object_get (L, 1);
+       schema = lua_ucl_object_get (L, 2);
+
+       if (schema && obj && ucl_object_type (schema) == UCL_OBJECT) {
+               if (lua_gettop (L) > 2) {
+                       if (lua_type (L, 3) == LUA_TSTRING) {
+                               path = lua_tostring (L, 3);
+                               if (path[0] == '#') {
+                                       path++;
+                               }
+                       }
+                       else if (lua_type (L, 3) == LUA_TUSERDATA || lua_type (L, 3) ==
+                                               LUA_TTABLE) {
+                               /* External refs */
+                               ext_refs = lua_ucl_object_get (L, 3);
+                       }
+
+                       if (lua_gettop (L) > 3) {
+                               if (lua_type (L, 4) == LUA_TUSERDATA || lua_type (L, 4) ==
+                                               LUA_TTABLE) {
+                                       /* External refs */
+                                       ext_refs = lua_ucl_object_get (L, 4);
+                               }
+                       }
+               }
+
+               if (path) {
+                       schema_elt = ucl_lookup_path_char (schema, path, '/');
+               }
+               else {
+                       /* Use the top object */
+                       schema_elt = schema;
+               }
+
+               if (schema_elt) {
+                       res = ucl_object_validate_root_ext (schema_elt, obj, schema,
+                                       ext_refs, &err);
+
+                       if (res) {
+                               lua_pushboolean (L, res);
+                               lua_pushnil (L);
+
+                               if (ext_refs) {
+                                       lua_ucl_push_opaque (L, ext_refs);
+                               }
+                       }
+                       else {
+                               lua_pushboolean (L, res);
+                               lua_pushfstring (L, "validation error: %s", err.msg);
+
+                               if (ext_refs) {
+                                       lua_ucl_push_opaque (L, ext_refs);
+                               }
+                       }
+               }
+               else {
+                       lua_pushboolean (L, res);
+
+                       lua_pushfstring (L, "cannot find the requested path: %s", path);
+
+                       if (ext_refs) {
+                               lua_ucl_push_opaque (L, ext_refs);
+                       }
+               }
+       }
+       else {
+               lua_pushboolean (L, res);
+               lua_pushstring (L, "invalid object or schema");
+       }
+
+       if (ext_refs) {
+               return 3;
+       }
+
+       return 2;
+}
+
+static int
+lua_ucl_object_gc (lua_State *L)
+{
+       ucl_object_t *obj;
+
+       obj = lua_ucl_object_get (L, 1);
+
+       ucl_object_unref (obj);
+
+       return 0;
+}
+
 static void
 lua_ucl_parser_mt (lua_State *L)
 {
@@ -625,25 +942,45 @@ lua_ucl_parser_mt (lua_State *L)
        lua_pushcfunction (L, lua_ucl_parser_get_object);
        lua_setfield (L, -2, "get_object");
 
+       lua_pushcfunction (L, lua_ucl_parser_get_object_wrapped);
+       lua_setfield (L, -2, "get_object_wrapped");
+
+       lua_pushcfunction (L, lua_ucl_parser_validate);
+       lua_setfield (L, -2, "validate");
+
        lua_pop (L, 1);
 }
 
-static int
-lua_ucl_to_string (lua_State *L, const ucl_object_t *obj, enum ucl_emitter type)
+static void
+lua_ucl_object_mt (lua_State *L)
 {
-       unsigned char *result;
+       luaL_newmetatable (L, OBJECT_META);
 
-       result = ucl_object_emit (obj, type);
+       lua_pushvalue(L, -1);
+       lua_setfield(L, -2, "__index");
 
-       if (result != NULL) {
-               lua_pushstring (L, (const char *)result);
-               free (result);
-       }
-       else {
-               lua_pushnil (L);
-       }
+       lua_pushcfunction (L, lua_ucl_object_gc);
+       lua_setfield (L, -2, "__gc");
 
-       return 1;
+       lua_pushcfunction (L, lua_ucl_object_tostring);
+       lua_setfield (L, -2, "__tostring");
+
+       lua_pushcfunction (L, lua_ucl_object_tostring);
+       lua_setfield (L, -2, "tostring");
+
+       lua_pushcfunction (L, lua_ucl_object_unwrap);
+       lua_setfield (L, -2, "unwrap");
+
+       lua_pushcfunction (L, lua_ucl_object_unwrap);
+       lua_setfield (L, -2, "tolua");
+
+       lua_pushcfunction (L, lua_ucl_object_validate);
+       lua_setfield (L, -2, "validate");
+
+       lua_pushstring (L, OBJECT_META);
+       lua_setfield (L, -2, "class");
+
+       lua_pop (L, 1);
 }
 
 static int
@@ -789,6 +1126,7 @@ luaopen_ucl (lua_State *L)
 {
        lua_ucl_parser_mt (L);
        lua_ucl_null_mt (L);
+       lua_ucl_object_mt (L);
 
        /* Create the refs weak table: */
        lua_createtable (L, 0, 2);
index 14e72d1193b1d9ab043bde14ecfe9870bb07e45d..ba18380a9ff07041e2ced6d1fb844e9ee0baca1f 100644 (file)
@@ -288,10 +288,12 @@ UCL_EXTERN ucl_object_t* ucl_object_new_full (ucl_type_t type, unsigned priority
 /**
  * Create new object with userdata dtor
  * @param dtor destructor function
+ * @param emitter emitter for userdata
+ * @param ptr opaque pointer
  * @return new object
  */
 UCL_EXTERN ucl_object_t* ucl_object_new_userdata (ucl_userdata_dtor dtor,
-               ucl_userdata_emitter emitter) UCL_WARN_UNUSED_RESULT;
+               ucl_userdata_emitter emitter, void *ptr) UCL_WARN_UNUSED_RESULT;
 
 /**
  * Perform deep copy of an object copying everything
@@ -751,6 +753,19 @@ UCL_EXTERN void ucl_object_unref (ucl_object_t *obj);
 UCL_EXTERN int ucl_object_compare (const ucl_object_t *o1,
                const ucl_object_t *o2);
 
+/**
+ * Compare objects `o1` and `o2` useful for sorting
+ * @param o1 the first object
+ * @param o2 the second object
+ * @return values >0, 0 and <0 if `o1` is more than, equal and less than `o2`.
+ * The order of comparison:
+ * 1) Type of objects
+ * 2) Size of objects
+ * 3) Content of objects
+ */
+UCL_EXTERN int ucl_object_compare_qsort (const ucl_object_t **o1,
+               const ucl_object_t **o2);
+
 /**
  * Sort UCL array using `cmp` compare function
  * @param ar
index 5620a7b114e7e97c44a47039cc8c865a32ffb147..96f4809f892ff421a2a6e2a68fa8a631b14bb5d1 100644 (file)
@@ -113,19 +113,19 @@ ucl_emitter_print_int_msgpack (struct ucl_emitter_context *ctx, int64_t val)
                        len = 1;
                        buf[0] = mask_positive & val;
                }
-               else if (val <= 0xff) {
+               else if (val <= UINT8_MAX) {
                        len = 2;
                        buf[0] = uint8_ch;
                        buf[1] = val & 0xff;
                }
-               else if (val <= 0xffff) {
+               else if (val <= UINT16_MAX) {
                        uint16_t v = TO_BE16 (val);
 
                        len = 3;
                        buf[0] = uint16_ch;
                        memcpy (&buf[1], &v, sizeof (v));
                }
-               else if (val <= 0xffffffff) {
+               else if (val <= UINT32_MAX) {
                        uint32_t v = TO_BE32 (val);
 
                        len = 5;
@@ -149,19 +149,20 @@ ucl_emitter_print_int_msgpack (struct ucl_emitter_context *ctx, int64_t val)
                        len = 1;
                        buf[0] = (mask_negative | uval) & 0xff;
                }
-               else if (uval <= 0xff) {
+               else if (uval <= INT8_MAX) {
+                       uint8_t v = (uint8_t)val;
                        len = 2;
                        buf[0] = int8_ch;
-                       buf[1] = (unsigned char)val;
+                       buf[1] = v;
                }
-               else if (uval <= 0xffff) {
+               else if (uval <= INT16_MAX) {
                        uint16_t v = TO_BE16 (val);
 
                        len = 3;
                        buf[0] = int16_ch;
                        memcpy (&buf[1], &v, sizeof (v));
                }
-               else if (uval <= 0xffffffff) {
+               else if (uval <= INT32_MAX) {
                        uint32_t v = TO_BE32 (val);
 
                        len = 5;
@@ -1418,6 +1419,10 @@ ucl_msgpack_parse_int (struct ucl_parser *parser,
                const unsigned char *pos, size_t remain)
 {
        ucl_object_t *obj;
+       int8_t iv8;
+       int16_t iv16;
+       int32_t iv32;
+       int64_t iv64;
 
        if (len > remain) {
                return -1;
@@ -1439,11 +1444,14 @@ ucl_msgpack_parse_int (struct ucl_parser *parser,
                len = 1;
                break;
        case msgpack_int8:
-               obj->value.iv = (signed char)*pos;
+               memcpy (&iv8, pos, sizeof (iv8));
+               obj->value.iv = iv8;
                len = 1;
                break;
        case msgpack_int16:
-               obj->value.iv = FROM_BE16 (*(int16_t *)pos);
+               memcpy (&iv16, pos, sizeof (iv16));
+               iv16 = FROM_BE16 (iv16);
+               obj->value.iv = iv16;
                len = 2;
                break;
        case msgpack_uint16:
@@ -1451,7 +1459,9 @@ ucl_msgpack_parse_int (struct ucl_parser *parser,
                len = 2;
                break;
        case msgpack_int32:
-               obj->value.iv = FROM_BE32 (*(int32_t *)pos);
+               memcpy (&iv32, pos, sizeof (iv32));
+               iv32 = FROM_BE32 (iv32);
+               obj->value.iv = iv32;
                len = 4;
                break;
        case msgpack_uint32:
@@ -1459,7 +1469,9 @@ ucl_msgpack_parse_int (struct ucl_parser *parser,
                len = 4;
                break;
        case msgpack_int64:
-               obj->value.iv = FROM_BE64 (*(int64_t *)pos);
+               memcpy (&iv64, pos, sizeof (iv64));
+               iv64 = FROM_BE64 (iv64);
+               obj->value.iv = iv64;
                len = 8;
                break;
        case msgpack_uint64:
index fa906b7fe88eb379d667ced6748286b870f5a5c0..9ac535cbe4b872ed3cf4771fcfe919d9090ad54f 100644 (file)
@@ -2198,7 +2198,7 @@ ucl_state_machine (struct ucl_parser *parser)
                        while (p < chunk->end && ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE)) {
                                ucl_chunk_skipc (chunk, p);
                        }
-                       if (*p == '}') {
+                       if (p == chunk->end || *p == '}') {
                                /* We have the end of an object */
                                parser->state = UCL_STATE_AFTER_VALUE;
                                continue;
index 15439feea3feea919804a0b165d2bc3eed61ee61..7626e09961d8206b2de5060629993dcf5b0c98df 100644 (file)
@@ -27,6 +27,7 @@
 #include "ucl_chartable.h"
 #include "kvec.h"
 #include <stdarg.h>
+#include <stdio.h> /* for asprintf */
 
 #ifndef _WIN32
 #include <glob.h>
@@ -306,6 +307,9 @@ ucl_unescape_json_string (char *str, size_t len)
                        case 'u':
                                /* Unicode escape */
                                uval = 0;
+                               h ++; /* u character */
+                               len --;
+
                                if (len > 3) {
                                        for (i = 0; i < 4; i++) {
                                                uval <<= 4;
@@ -322,8 +326,7 @@ ucl_unescape_json_string (char *str, size_t len)
                                                        break;
                                                }
                                        }
-                                       h += 3;
-                                       len -= 3;
+
                                        /* Encode */
                                        if(uval < 0x80) {
                                                t[0] = (char)uval;
@@ -340,6 +343,8 @@ ucl_unescape_json_string (char *str, size_t len)
                                                t[2] = 0x80 + ((uval & 0x003F));
                                                t += 3;
                                        }
+#if 0
+                                       /* It's not actually supported now */
                                        else if(uval <= 0x10FFFF) {
                                                t[0] = 0xF0 + ((uval & 0x1C0000) >> 18);
                                                t[1] = 0x80 + ((uval & 0x03F000) >> 12);
@@ -347,9 +352,19 @@ ucl_unescape_json_string (char *str, size_t len)
                                                t[3] = 0x80 + ((uval & 0x00003F));
                                                t += 4;
                                        }
+#endif
                                        else {
                                                *t++ = '?';
                                        }
+
+                                       /* Consume 4 characters of source */
+                                       h += 4;
+                                       len -= 4;
+
+                                       if (len > 0) {
+                                               len --; /* for '\' character */
+                                       }
+                                       continue;
                                }
                                else {
                                        *t++ = 'u';
@@ -1550,7 +1565,7 @@ ucl_load_handler (const unsigned char *data, size_t len,
        size_t buflen;
        unsigned priority;
        int64_t iv;
-       ucl_hash_t *container = NULL;
+       ucl_object_t *container = NULL;
        enum ucl_string_flags flags;
 
        /* Default values */
@@ -1610,19 +1625,32 @@ ucl_load_handler (const unsigned char *data, size_t len,
                }
        }
 
-       if (prefix == NULL || strlen(prefix) == 0) {
+       if (prefix == NULL || strlen (prefix) == 0) {
                ucl_create_err (&parser->err, "No Key specified in load macro");
                return false;
        }
 
        if (len > 0) {
                asprintf (&load_file, "%.*s", (int)len, data);
-               if (!ucl_fetch_file (load_file, &buf, &buflen, &parser->err, !try_load)) {
+
+               if (!load_file) {
+                       ucl_create_err (&parser->err, "cannot allocate memory for suffix");
+
+                       return false;
+               }
+
+               if (!ucl_fetch_file (load_file, &buf, &buflen, &parser->err,
+                               !try_load)) {
+                       free (load_file);
+
                        return (try_load || false);
                }
 
-               container = parser->stack->obj->value.ov;
-               old_obj = __DECONST (ucl_object_t *, ucl_hash_search (container, prefix, strlen (prefix)));
+               free (load_file);
+               container = parser->stack->obj;
+               old_obj = __DECONST (ucl_object_t *, ucl_object_find_key (container,
+                               prefix));
+
                if (old_obj != NULL) {
                        ucl_create_err (&parser->err, "Key %s already exists", prefix);
                        if (buflen > 0) {
@@ -1642,7 +1670,7 @@ ucl_load_handler (const unsigned char *data, size_t len,
                else if (strcasecmp (target, "int") == 0) {
                        asprintf(&tmp, "%.*s", (int)buflen, buf);
                        iv = strtoll(tmp, NULL, 10);
-                       obj = ucl_object_fromint(iv);
+                       obj = ucl_object_fromint (iv);
                }
 
                if (buflen > 0) {
@@ -1652,13 +1680,11 @@ ucl_load_handler (const unsigned char *data, size_t len,
                if (obj != NULL) {
                        obj->key = prefix;
                        obj->keylen = strlen (prefix);
-                       ucl_copy_key_trash(obj);
+                       ucl_copy_key_trash (obj);
                        obj->prev = obj;
                        obj->next = NULL;
                        ucl_object_set_priority (obj, priority);
-                       container = ucl_hash_insert_object (container, obj,
-                                       parser->flags & UCL_PARSER_KEY_LOWERCASE);
-                       parser->stack->obj->value.ov = container;
+                       ucl_object_insert_key (container, obj, obj->key, obj->keylen, false);
                }
 
                return true;
@@ -2576,7 +2602,7 @@ ucl_object_new_full (ucl_type_t type, unsigned priority)
                }
        }
        else {
-               new = ucl_object_new_userdata (NULL, NULL);
+               new = ucl_object_new_userdata (NULL, NULL, NULL);
                ucl_object_set_priority (new, priority);
        }
 
@@ -2584,7 +2610,9 @@ ucl_object_new_full (ucl_type_t type, unsigned priority)
 }
 
 ucl_object_t*
-ucl_object_new_userdata (ucl_userdata_dtor dtor, ucl_userdata_emitter emitter)
+ucl_object_new_userdata (ucl_userdata_dtor dtor,
+               ucl_userdata_emitter emitter,
+               void *ptr)
 {
        struct ucl_object_userdata *new;
        size_t nsize = sizeof (*new);
@@ -2598,6 +2626,7 @@ ucl_object_new_userdata (ucl_userdata_dtor dtor, ucl_userdata_emitter emitter)
                new->obj.prev = (ucl_object_t *)new;
                new->dtor = dtor;
                new->emitter = emitter;
+               new->obj.value.ud = ptr;
        }
 
        return (ucl_object_t *)new;
@@ -3266,6 +3295,13 @@ ucl_object_compare (const ucl_object_t *o1, const ucl_object_t *o2)
        return ret;
 }
 
+int
+ucl_object_compare_qsort (const ucl_object_t **o1,
+               const ucl_object_t **o2)
+{
+       return ucl_object_compare (*o1, *o2);
+}
+
 void
 ucl_object_array_sort (ucl_object_t *ar,
                int (*cmp)(const ucl_object_t **o1, const ucl_object_t **o2))