1 /* Copyright (c) 2013, Vsevolod Stakhov
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 * * Redistributions of source code must retain the above copyright
7 * notice, this list of conditions and the following disclaimer.
8 * * Redistributions in binary form must reproduce the above copyright
9 * notice, this list of conditions and the following disclaimer in the
10 * documentation and/or other materials provided with the distribution.
12 * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
13 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
14 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
15 * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
16 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
17 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
18 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
19 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
20 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
21 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 #include "ucl_internal.h"
26 #include "ucl_chartable.h"
28 #include <libgen.h> /* For dirname */
31 #include <openssl/err.h>
32 #include <openssl/sha.h>
33 #include <openssl/rsa.h>
34 #include <openssl/ssl.h>
35 #include <openssl/evp.h>
43 #define PROT_READWRITE 3
46 #define MAP_FAILED ((void *) -1)
48 static void *mmap(char *addr, size_t length, int prot, int access, int fd, off_t offset)
51 HANDLE handle = INVALID_HANDLE_VALUE;
57 handle = CreateFileMapping((HANDLE) _get_osfhandle(fd), 0, PAGE_READONLY, 0, length, 0);
59 map = (void *) MapViewOfFile(handle, FILE_MAP_READ, 0, 0, length);
65 handle = CreateFileMapping((HANDLE) _get_osfhandle(fd), 0, PAGE_READWRITE, 0, length, 0);
67 map = (void *) MapViewOfFile(handle, FILE_MAP_WRITE, 0, 0, length);
73 handle = CreateFileMapping((HANDLE) _get_osfhandle(fd), 0, PAGE_READWRITE, 0, length, 0);
75 map = (void *) MapViewOfFile(handle, FILE_MAP_ALL_ACCESS, 0, 0, length);
80 if (map == (void *) NULL) {
81 return (void *) MAP_FAILED;
83 return (void *) ((char *) map + offset);
86 static int munmap(void *map,size_t length)
88 if (!UnmapViewOfFile(map)) {
94 static char* realpath(const char *path, char *resolved_path) {
96 char tmp[MAX_PATH + 1];
97 strncpy(tmp, path, sizeof(tmp)-1);
100 if (*p == '/') *p = '\\';
103 return _fullpath(resolved_path, tmp, MAX_PATH);
109 * Utilities for rcl parsing
114 ucl_object_free_internal (ucl_object_t *obj, bool allow_rec)
116 ucl_object_t *sub, *tmp;
118 while (obj != NULL) {
119 if (obj->trash_stack[UCL_TRASH_KEY] != NULL) {
120 UCL_FREE (obj->hh.keylen, obj->trash_stack[UCL_TRASH_KEY]);
122 if (obj->trash_stack[UCL_TRASH_VALUE] != NULL) {
123 UCL_FREE (obj->len, obj->trash_stack[UCL_TRASH_VALUE]);
126 if (obj->type == UCL_ARRAY) {
128 while (sub != NULL) {
130 ucl_object_free_internal (sub, false);
134 else if (obj->type == UCL_OBJECT) {
135 if (obj->value.ov != NULL) {
136 ucl_hash_destroy (obj->value.ov, (ucl_hash_free_func *)ucl_object_unref);
140 UCL_FREE (sizeof (ucl_object_t), obj);
150 ucl_object_free (ucl_object_t *obj)
152 ucl_object_free_internal (obj, true);
156 ucl_unescape_json_string (char *str, size_t len)
158 char *t = str, *h = str;
161 /* t is target (tortoise), h is source (hare) */
191 for (i = 0; i < 4; i++) {
193 if (isdigit (h[i])) {
196 else if (h[i] >= 'a' && h[i] <= 'f') {
197 uval += h[i] - 'a' + 10;
199 else if (h[i] >= 'A' && h[i] <= 'F') {
200 uval += h[i] - 'A' + 10;
210 else if(uval < 0x800) {
211 t[0] = 0xC0 + ((uval & 0x7C0) >> 6);
212 t[1] = 0x80 + ((uval & 0x03F));
215 else if(uval < 0x10000) {
216 t[0] = 0xE0 + ((uval & 0xF000) >> 12);
217 t[1] = 0x80 + ((uval & 0x0FC0) >> 6);
218 t[2] = 0x80 + ((uval & 0x003F));
221 else if(uval <= 0x10FFFF) {
222 t[0] = 0xF0 + ((uval & 0x1C0000) >> 18);
223 t[1] = 0x80 + ((uval & 0x03F000) >> 12);
224 t[2] = 0x80 + ((uval & 0x000FC0) >> 6);
225 t[3] = 0x80 + ((uval & 0x00003F));
250 ucl_copy_key_trash (ucl_object_t *obj)
252 if (obj->trash_stack[UCL_TRASH_KEY] == NULL && obj->key != NULL) {
253 obj->trash_stack[UCL_TRASH_KEY] = malloc (obj->keylen + 1);
254 if (obj->trash_stack[UCL_TRASH_KEY] != NULL) {
255 memcpy (obj->trash_stack[UCL_TRASH_KEY], obj->key, obj->keylen);
256 obj->trash_stack[UCL_TRASH_KEY][obj->keylen] = '\0';
258 obj->key = obj->trash_stack[UCL_TRASH_KEY];
259 obj->flags |= UCL_OBJECT_ALLOCATED_KEY;
262 return obj->trash_stack[UCL_TRASH_KEY];
266 ucl_copy_value_trash (ucl_object_t *obj)
268 if (obj->trash_stack[UCL_TRASH_VALUE] == NULL) {
269 if (obj->type == UCL_STRING) {
270 /* Special case for strings */
271 obj->trash_stack[UCL_TRASH_VALUE] = malloc (obj->len + 1);
272 if (obj->trash_stack[UCL_TRASH_VALUE] != NULL) {
273 memcpy (obj->trash_stack[UCL_TRASH_VALUE], obj->value.sv, obj->len);
274 obj->trash_stack[UCL_TRASH_VALUE][obj->len] = '\0';
275 obj->value.sv = obj->trash_stack[UCL_TRASH_VALUE];
279 /* Just emit value in json notation */
280 obj->trash_stack[UCL_TRASH_VALUE] = ucl_object_emit_single_json (obj);
281 obj->len = strlen (obj->trash_stack[UCL_TRASH_VALUE]);
283 obj->flags |= UCL_OBJECT_ALLOCATED_VALUE;
285 return obj->trash_stack[UCL_TRASH_VALUE];
288 UCL_EXTERN ucl_object_t*
289 ucl_parser_get_object (struct ucl_parser *parser)
291 if (parser->state != UCL_STATE_ERROR && parser->top_obj != NULL) {
292 return ucl_object_ref (parser->top_obj);
299 ucl_parser_free (struct ucl_parser *parser)
301 struct ucl_stack *stack, *stmp;
302 struct ucl_macro *macro, *mtmp;
303 struct ucl_chunk *chunk, *ctmp;
304 struct ucl_pubkey *key, *ktmp;
305 struct ucl_variable *var, *vtmp;
307 if (parser->top_obj != NULL) {
308 ucl_object_unref (parser->top_obj);
311 LL_FOREACH_SAFE (parser->stack, stack, stmp) {
314 HASH_ITER (hh, parser->macroes, macro, mtmp) {
316 HASH_DEL (parser->macroes, macro);
317 UCL_FREE (sizeof (struct ucl_macro), macro);
319 LL_FOREACH_SAFE (parser->chunks, chunk, ctmp) {
320 UCL_FREE (sizeof (struct ucl_chunk), chunk);
322 LL_FOREACH_SAFE (parser->keys, key, ktmp) {
323 UCL_FREE (sizeof (struct ucl_pubkey), key);
325 LL_FOREACH_SAFE (parser->variables, var, vtmp) {
328 UCL_FREE (sizeof (struct ucl_variable), var);
331 if (parser->err != NULL) {
332 utstring_free(parser->err);
335 UCL_FREE (sizeof (struct ucl_parser), parser);
338 UCL_EXTERN const char *
339 ucl_parser_get_error(struct ucl_parser *parser)
341 if (parser->err == NULL)
344 return utstring_body(parser->err);
348 ucl_pubkey_add (struct ucl_parser *parser, const unsigned char *key, size_t len)
351 ucl_create_err (&parser->err, "cannot check signatures without openssl");
354 # if (OPENSSL_VERSION_NUMBER < 0x10000000L)
355 ucl_create_err (&parser->err, "cannot check signatures, openssl version is unsupported");
358 struct ucl_pubkey *nkey;
361 mem = BIO_new_mem_buf ((void *)key, len);
362 nkey = UCL_ALLOC (sizeof (struct ucl_pubkey));
363 nkey->key = PEM_read_bio_PUBKEY (mem, &nkey->key, NULL, NULL);
365 if (nkey->key == NULL) {
366 UCL_FREE (sizeof (struct ucl_pubkey), nkey);
367 ucl_create_err (&parser->err, "%s",
368 ERR_error_string (ERR_get_error (), NULL));
371 LL_PREPEND (parser->keys, nkey);
378 struct ucl_curl_cbdata {
384 ucl_curl_write_callback (void* contents, size_t size, size_t nmemb, void* ud)
386 struct ucl_curl_cbdata *cbdata = ud;
387 size_t realsize = size * nmemb;
389 cbdata->buf = realloc (cbdata->buf, cbdata->buflen + realsize + 1);
390 if (cbdata->buf == NULL) {
394 memcpy (&(cbdata->buf[cbdata->buflen]), contents, realsize);
395 cbdata->buflen += realsize;
396 cbdata->buf[cbdata->buflen] = 0;
403 * Fetch a url and save results to the memory buffer
404 * @param url url to fetch
405 * @param len length of url
406 * @param buf target buffer
407 * @param buflen target length
411 ucl_fetch_url (const unsigned char *url, unsigned char **buf, size_t *buflen,
412 UT_string **err, bool must_exist)
416 struct url *fetch_url;
420 fetch_url = fetchParseURL (url);
421 if (fetch_url == NULL) {
422 ucl_create_err (err, "invalid URL %s: %s",
423 url, strerror (errno));
426 if ((in = fetchXGet (fetch_url, &us, "")) == NULL) {
428 ucl_create_err (err, "cannot fetch URL %s: %s",
429 url, strerror (errno));
431 fetchFreeURL (fetch_url);
436 *buf = malloc (*buflen);
438 ucl_create_err (err, "cannot allocate buffer for URL %s: %s",
439 url, strerror (errno));
441 fetchFreeURL (fetch_url);
445 if (fread (*buf, *buflen, 1, in) != 1) {
446 ucl_create_err (err, "cannot read URL %s: %s",
447 url, strerror (errno));
449 fetchFreeURL (fetch_url);
453 fetchFreeURL (fetch_url);
455 #elif defined(CURL_FOUND)
458 struct ucl_curl_cbdata cbdata;
460 curl = curl_easy_init ();
462 ucl_create_err (err, "CURL interface is broken");
465 if ((r = curl_easy_setopt (curl, CURLOPT_URL, url)) != CURLE_OK) {
466 ucl_create_err (err, "invalid URL %s: %s",
467 url, curl_easy_strerror (r));
468 curl_easy_cleanup (curl);
471 curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, ucl_curl_write_callback);
473 cbdata.buflen = *buflen;
474 curl_easy_setopt (curl, CURLOPT_WRITEDATA, &cbdata);
476 if ((r = curl_easy_perform (curl)) != CURLE_OK) {
478 ucl_create_err (err, "error fetching URL %s: %s",
479 url, curl_easy_strerror (r));
481 curl_easy_cleanup (curl);
488 *buflen = cbdata.buflen;
492 ucl_create_err (err, "URL support is disabled");
498 * Fetch a file and save results to the memory buffer
499 * @param filename filename to fetch
500 * @param len length of filename
501 * @param buf target buffer
502 * @param buflen target length
506 ucl_fetch_file (const unsigned char *filename, unsigned char **buf, size_t *buflen,
507 UT_string **err, bool must_exist)
512 if (stat (filename, &st) == -1 || !S_ISREG (st.st_mode)) {
514 ucl_create_err (err, "cannot stat file %s: %s",
515 filename, strerror (errno));
519 if (st.st_size == 0) {
520 /* Do not map empty files */
525 if ((fd = open (filename, O_RDONLY)) == -1) {
526 ucl_create_err (err, "cannot open file %s: %s",
527 filename, strerror (errno));
530 if ((*buf = mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
532 ucl_create_err (err, "cannot mmap file %s: %s",
533 filename, strerror (errno));
536 *buflen = st.st_size;
544 #if (defined(HAVE_OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x10000000L)
546 ucl_sig_check (const unsigned char *data, size_t datalen,
547 const unsigned char *sig, size_t siglen, struct ucl_parser *parser)
549 struct ucl_pubkey *key;
550 char dig[EVP_MAX_MD_SIZE];
552 EVP_PKEY_CTX *key_ctx;
553 EVP_MD_CTX *sign_ctx = NULL;
555 sign_ctx = EVP_MD_CTX_create ();
557 LL_FOREACH (parser->keys, key) {
558 key_ctx = EVP_PKEY_CTX_new (key->key, NULL);
559 if (key_ctx != NULL) {
560 if (EVP_PKEY_verify_init (key_ctx) <= 0) {
561 EVP_PKEY_CTX_free (key_ctx);
564 if (EVP_PKEY_CTX_set_rsa_padding (key_ctx, RSA_PKCS1_PADDING) <= 0) {
565 EVP_PKEY_CTX_free (key_ctx);
568 if (EVP_PKEY_CTX_set_signature_md (key_ctx, EVP_sha256 ()) <= 0) {
569 EVP_PKEY_CTX_free (key_ctx);
572 EVP_DigestInit (sign_ctx, EVP_sha256 ());
573 EVP_DigestUpdate (sign_ctx, data, datalen);
574 EVP_DigestFinal (sign_ctx, dig, &diglen);
576 if (EVP_PKEY_verify (key_ctx, sig, siglen, dig, diglen) == 1) {
577 EVP_MD_CTX_destroy (sign_ctx);
578 EVP_PKEY_CTX_free (key_ctx);
582 EVP_PKEY_CTX_free (key_ctx);
586 EVP_MD_CTX_destroy (sign_ctx);
593 * Include an url to configuration
601 ucl_include_url (const unsigned char *data, size_t len,
602 struct ucl_parser *parser, bool check_signature, bool must_exist)
606 unsigned char *buf = NULL;
608 struct ucl_chunk *chunk;
609 char urlbuf[PATH_MAX];
612 snprintf (urlbuf, sizeof (urlbuf), "%.*s", (int)len, data);
614 if (!ucl_fetch_url (urlbuf, &buf, &buflen, &parser->err, must_exist)) {
615 return (!must_exist || false);
618 if (check_signature) {
619 #if (defined(HAVE_OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x10000000L)
620 unsigned char *sigbuf = NULL;
622 /* We need to check signature first */
623 snprintf (urlbuf, sizeof (urlbuf), "%.*s.sig", (int)len, data);
624 if (!ucl_fetch_url (urlbuf, &sigbuf, &siglen, &parser->err, true)) {
627 if (!ucl_sig_check (buf, buflen, sigbuf, siglen, parser)) {
628 ucl_create_err (&parser->err, "cannot verify url %s: %s",
630 ERR_error_string (ERR_get_error (), NULL));
632 munmap (sigbuf, siglen);
637 munmap (sigbuf, siglen);
642 prev_state = parser->state;
643 parser->state = UCL_STATE_INIT;
645 res = ucl_parser_add_chunk (parser, buf, buflen);
647 /* Remove chunk from the stack */
648 chunk = parser->chunks;
650 parser->chunks = chunk->next;
651 UCL_FREE (sizeof (struct ucl_chunk), chunk);
655 parser->state = prev_state;
662 * Include a file to configuration
670 ucl_include_file (const unsigned char *data, size_t len,
671 struct ucl_parser *parser, bool check_signature, bool must_exist)
674 struct ucl_chunk *chunk;
675 unsigned char *buf = NULL;
677 char filebuf[PATH_MAX], realbuf[PATH_MAX];
680 snprintf (filebuf, sizeof (filebuf), "%.*s", (int)len, data);
681 if (realpath (filebuf, realbuf) == NULL) {
685 ucl_create_err (&parser->err, "cannot open file %s: %s",
691 if (!ucl_fetch_file (realbuf, &buf, &buflen, &parser->err, must_exist)) {
692 return (!must_exist || false);
695 if (check_signature) {
696 #if (defined(HAVE_OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x10000000L)
697 unsigned char *sigbuf = NULL;
699 /* We need to check signature first */
700 snprintf (filebuf, sizeof (filebuf), "%s.sig", realbuf);
701 if (!ucl_fetch_file (filebuf, &sigbuf, &siglen, &parser->err, true)) {
704 if (!ucl_sig_check (buf, buflen, sigbuf, siglen, parser)) {
705 ucl_create_err (&parser->err, "cannot verify file %s: %s",
707 ERR_error_string (ERR_get_error (), NULL));
709 munmap (sigbuf, siglen);
714 munmap (sigbuf, siglen);
719 ucl_parser_set_filevars (parser, realbuf, false);
721 prev_state = parser->state;
722 parser->state = UCL_STATE_INIT;
724 res = ucl_parser_add_chunk (parser, buf, buflen);
726 /* Remove chunk from the stack */
727 chunk = parser->chunks;
729 parser->chunks = chunk->next;
730 UCL_FREE (sizeof (struct ucl_chunk), chunk);
734 parser->state = prev_state;
737 munmap (buf, buflen);
744 * Handle include macro
745 * @param data include data
746 * @param len length of data
747 * @param ud user data
748 * @param err error ptr
752 ucl_include_handler (const unsigned char *data, size_t len, void* ud)
754 struct ucl_parser *parser = ud;
756 if (*data == '/' || *data == '.') {
757 /* Try to load a file */
758 return ucl_include_file (data, len, parser, false, true);
761 return ucl_include_url (data, len, parser, false, true);
765 * Handle includes macro
766 * @param data include data
767 * @param len length of data
768 * @param ud user data
769 * @param err error ptr
773 ucl_includes_handler (const unsigned char *data, size_t len, void* ud)
775 struct ucl_parser *parser = ud;
777 if (*data == '/' || *data == '.') {
778 /* Try to load a file */
779 return ucl_include_file (data, len, parser, true, true);
782 return ucl_include_url (data, len, parser, true, true);
787 ucl_try_include_handler (const unsigned char *data, size_t len, void* ud)
789 struct ucl_parser *parser = ud;
791 if (*data == '/' || *data == '.') {
792 /* Try to load a file */
793 return ucl_include_file (data, len, parser, false, false);
796 return ucl_include_url (data, len, parser, false, false);
800 ucl_parser_set_filevars (struct ucl_parser *parser, const char *filename, bool need_expand)
802 char realbuf[PATH_MAX], *curdir;
804 if (filename != NULL) {
806 if (realpath (filename, realbuf) == NULL) {
811 ucl_strlcpy (realbuf, filename, sizeof (realbuf));
814 /* Define variables */
815 ucl_parser_register_variable (parser, "FILENAME", realbuf);
816 curdir = dirname (realbuf);
817 ucl_parser_register_variable (parser, "CURDIR", curdir);
820 /* Set everything from the current dir */
821 curdir = getcwd (realbuf, sizeof (realbuf));
822 ucl_parser_register_variable (parser, "FILENAME", "undef");
823 ucl_parser_register_variable (parser, "CURDIR", curdir);
830 ucl_parser_add_file (struct ucl_parser *parser, const char *filename)
835 char realbuf[PATH_MAX];
837 if (realpath (filename, realbuf) == NULL) {
838 ucl_create_err (&parser->err, "cannot open file %s: %s",
844 if (!ucl_fetch_file (realbuf, &buf, &len, &parser->err, true)) {
848 ucl_parser_set_filevars (parser, realbuf, false);
849 ret = ucl_parser_add_chunk (parser, buf, len);
859 ucl_strlcpy (char *dst, const char *src, size_t siz)
865 /* Copy as many bytes as will fit */
868 if ((*d++ = *s++) == '\0') {
874 if (n == 0 && siz != 0) {
878 return (s - src - 1); /* count does not include NUL */
882 ucl_strlcpy_unsafe (char *dst, const char *src, size_t siz)
884 memcpy (dst, src, siz - 1);
891 ucl_strlcpy_tolower (char *dst, const char *src, size_t siz)
897 /* Copy as many bytes as will fit */
900 if ((*d++ = tolower (*s++)) == '\0') {
906 if (n == 0 && siz != 0) {
910 return (s - src); /* count does not include NUL */
914 ucl_object_fromstring_common (const char *str, size_t len, enum ucl_string_flags flags)
917 const char *start, *end, *p, *pos;
925 obj = ucl_object_new ();
930 if (flags & UCL_STRING_TRIM) {
931 /* Skip leading spaces */
932 for (start = str; (size_t)(start - str) < len; start ++) {
933 if (!ucl_test_character (*start, UCL_CHARACTER_WHITESPACE_UNSAFE)) {
937 /* Skip trailing spaces */
938 for (end = str + len - 1; end > start; end --) {
939 if (!ucl_test_character (*end, UCL_CHARACTER_WHITESPACE_UNSAFE)) {
950 obj->type = UCL_STRING;
951 if (flags & UCL_STRING_ESCAPE) {
952 for (p = start, escaped_len = 0; p < end; p ++, escaped_len ++) {
953 if (ucl_test_character (*p, UCL_CHARACTER_JSON_UNSAFE)) {
957 dst = malloc (escaped_len + 1);
959 for (p = start, d = dst; p < end; p ++, d ++) {
960 if (ucl_test_character (*p, UCL_CHARACTER_JSON_UNSAFE)) {
998 obj->trash_stack[UCL_TRASH_VALUE] = dst;
999 obj->len = escaped_len;
1003 dst = malloc (end - start + 1);
1005 ucl_strlcpy_unsafe (dst, start, end - start + 1);
1006 obj->value.sv = dst;
1007 obj->trash_stack[UCL_TRASH_VALUE] = dst;
1008 obj->len = end - start;
1011 if ((flags & UCL_STRING_PARSE) && dst != NULL) {
1012 /* Parse what we have */
1013 if (flags & UCL_STRING_PARSE_BOOLEAN) {
1014 if (!ucl_maybe_parse_boolean (obj, dst, obj->len) && (flags & UCL_STRING_PARSE_NUMBER)) {
1015 ucl_maybe_parse_number (obj, dst, dst + obj->len, &pos,
1016 flags & UCL_STRING_PARSE_DOUBLE,
1017 flags & UCL_STRING_PARSE_BYTES);
1021 ucl_maybe_parse_number (obj, dst, dst + obj->len, &pos,
1022 flags & UCL_STRING_PARSE_DOUBLE,
1023 flags & UCL_STRING_PARSE_BYTES);
1031 static ucl_object_t *
1032 ucl_object_insert_key_common (ucl_object_t *top, ucl_object_t *elt,
1033 const char *key, size_t keylen, bool copy_key, bool merge, bool replace)
1035 ucl_object_t *found, *cur;
1036 ucl_object_iter_t it = NULL;
1039 if (elt == NULL || key == NULL) {
1044 top = ucl_object_new ();
1045 top->type = UCL_OBJECT;
1048 if (top->type != UCL_OBJECT) {
1049 /* It is possible to convert NULL type to an object */
1050 if (top->type == UCL_NULL) {
1051 top->type = UCL_OBJECT;
1054 /* Refuse converting of other object types */
1059 if (top->value.ov == NULL) {
1060 top->value.ov = ucl_hash_create ();
1064 keylen = strlen (key);
1067 for (p = key; p < key + keylen; p ++) {
1068 if (ucl_test_character (*p, UCL_CHARACTER_UCL_UNSAFE)) {
1069 elt->flags |= UCL_OBJECT_NEED_KEY_ESCAPE;
1075 elt->keylen = keylen;
1078 ucl_copy_key_trash (elt);
1081 found = ucl_hash_search_obj (top->value.ov, elt);
1084 top->value.ov = ucl_hash_insert_object (top->value.ov, elt);
1085 DL_APPEND (found, elt);
1089 ucl_hash_delete (top->value.ov, found);
1090 ucl_object_unref (found);
1091 top->value.ov = ucl_hash_insert_object (top->value.ov, elt);
1093 DL_APPEND (found, elt);
1096 if (found->type != UCL_OBJECT && elt->type == UCL_OBJECT) {
1097 /* Insert old elt to new one */
1098 elt = ucl_object_insert_key_common (elt, found, found->key, found->keylen, copy_key, false, false);
1099 ucl_hash_delete (top->value.ov, found);
1100 top->value.ov = ucl_hash_insert_object (top->value.ov, elt);
1102 else if (found->type == UCL_OBJECT && elt->type != UCL_OBJECT) {
1103 /* Insert new to old */
1104 found = ucl_object_insert_key_common (found, elt, elt->key, elt->keylen, copy_key, false, false);
1106 else if (found->type == UCL_OBJECT && elt->type == UCL_OBJECT) {
1107 /* Mix two hashes */
1108 while ((cur = ucl_iterate_object (elt, &it, true)) != NULL) {
1109 ucl_object_ref (cur);
1110 found = ucl_object_insert_key_common (found, cur, cur->key, cur->keylen, copy_key, false, false);
1112 ucl_object_unref (elt);
1115 /* Just make a list of scalars */
1116 DL_APPEND (found, elt);
1120 DL_APPEND (found, elt);
1128 ucl_object_delete_keyl(ucl_object_t *top, const char *key, size_t keylen)
1130 ucl_object_t *found;
1132 found = ucl_object_find_keyl(top, key, keylen);
1137 ucl_hash_delete(top->value.ov, found);
1138 ucl_object_unref (found);
1145 ucl_object_delete_key(ucl_object_t *top, const char *key)
1147 return ucl_object_delete_keyl(top, key, 0);
1151 ucl_object_insert_key (ucl_object_t *top, ucl_object_t *elt,
1152 const char *key, size_t keylen, bool copy_key)
1154 return ucl_object_insert_key_common (top, elt, key, keylen, copy_key, false, false);
1158 ucl_object_insert_key_merged (ucl_object_t *top, ucl_object_t *elt,
1159 const char *key, size_t keylen, bool copy_key)
1161 return ucl_object_insert_key_common (top, elt, key, keylen, copy_key, true, false);
1165 ucl_object_replace_key (ucl_object_t *top, ucl_object_t *elt,
1166 const char *key, size_t keylen, bool copy_key)
1168 return ucl_object_insert_key_common (top, elt, key, keylen, copy_key, false, true);
1172 ucl_object_find_keyl (ucl_object_t *obj, const char *key, size_t klen)
1174 ucl_object_t *ret, srch;
1176 if (obj == NULL || obj->type != UCL_OBJECT || key == NULL) {
1182 ret = ucl_hash_search_obj (obj->value.ov, &srch);
1188 ucl_object_find_key (ucl_object_t *obj, const char *key)
1191 ucl_object_t *ret, srch;
1193 if (obj == NULL || obj->type != UCL_OBJECT || key == NULL) {
1197 klen = strlen (key);
1200 ret = ucl_hash_search_obj (obj->value.ov, &srch);
1206 ucl_iterate_object (ucl_object_t *obj, ucl_object_iter_t *iter, bool expand_values)
1210 if (expand_values) {
1211 switch (obj->type) {
1213 return (ucl_object_t*)ucl_hash_iterate (obj->value.ov, iter);
1218 elt = obj->value.av;
1223 else if (elt == obj->value.av) {
1226 *iter = elt->next ? elt->next : obj->value.av;
1229 /* Go to linear iteration */
1233 /* Treat everything as a linear list */
1241 else if (elt == obj) {
1244 *iter = elt->next ? elt->next : obj;