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"
34 #include <libgen.h> /* For dirname */
37 typedef kvec_t(ucl_object_t *) ucl_array_t;
39 #define UCL_ARRAY_GET(ar, obj) ucl_array_t *ar = \
40 (ucl_array_t *)((obj) != NULL ? (obj)->value.av : NULL)
43 #include <openssl/err.h>
44 #include <openssl/sha.h>
45 #include <openssl/rsa.h>
46 #include <openssl/ssl.h>
47 #include <openssl/evp.h>
51 #include <curl/curl.h>
66 #ifndef PROT_READWRITE
67 #define PROT_READWRITE 3
76 #define MAP_FAILED ((void *) -1)
84 static void *ucl_mmap(char *addr, size_t length, int prot, int access, int fd, off_t offset)
87 HANDLE handle = INVALID_HANDLE_VALUE;
93 handle = CreateFileMapping((HANDLE) _get_osfhandle(fd), 0, PAGE_READONLY, 0, length, 0);
95 map = (void *) MapViewOfFile(handle, FILE_MAP_READ, 0, 0, length);
101 handle = CreateFileMapping((HANDLE) _get_osfhandle(fd), 0, PAGE_READWRITE, 0, length, 0);
103 map = (void *) MapViewOfFile(handle, FILE_MAP_WRITE, 0, 0, length);
109 handle = CreateFileMapping((HANDLE) _get_osfhandle(fd), 0, PAGE_READWRITE, 0, length, 0);
111 map = (void *) MapViewOfFile(handle, FILE_MAP_ALL_ACCESS, 0, 0, length);
116 if (map == (void *) NULL) {
117 return (void *) MAP_FAILED;
119 return (void *) ((char *) map + offset);
122 static int ucl_munmap(void *map,size_t length)
124 if (!UnmapViewOfFile(map)) {
130 static char* ucl_realpath(const char *path, char *resolved_path) {
132 char tmp[MAX_PATH + 1];
133 strncpy(tmp, path, sizeof(tmp)-1);
136 if (*p == '/') *p = '\\';
139 return _fullpath(resolved_path, tmp, MAX_PATH);
142 #define ucl_mmap mmap
143 #define ucl_munmap munmap
144 #define ucl_realpath realpath
147 typedef void (*ucl_object_dtor) (ucl_object_t *obj);
148 static void ucl_object_free_internal (ucl_object_t *obj, bool allow_rec,
149 ucl_object_dtor dtor);
150 static void ucl_object_dtor_unref (ucl_object_t *obj);
153 ucl_object_dtor_free (ucl_object_t *obj)
155 if (obj->trash_stack[UCL_TRASH_KEY] != NULL) {
156 UCL_FREE (obj->hh.keylen, obj->trash_stack[UCL_TRASH_KEY]);
158 if (obj->trash_stack[UCL_TRASH_VALUE] != NULL) {
159 UCL_FREE (obj->len, obj->trash_stack[UCL_TRASH_VALUE]);
161 /* Do not free ephemeral objects */
162 if ((obj->flags & UCL_OBJECT_EPHEMERAL) == 0) {
163 if (obj->type != UCL_USERDATA) {
164 UCL_FREE (sizeof (ucl_object_t), obj);
167 struct ucl_object_userdata *ud = (struct ucl_object_userdata *)obj;
169 ud->dtor (obj->value.ud);
171 UCL_FREE (sizeof (*ud), obj);
177 * This is a helper function that performs exactly the same as
178 * `ucl_object_unref` but it doesn't iterate over elements allowing
179 * to use it for individual elements of arrays and multiple values
182 ucl_object_dtor_unref_single (ucl_object_t *obj)
185 #ifdef HAVE_ATOMIC_BUILTINS
186 unsigned int rc = __sync_sub_and_fetch (&obj->ref, 1);
189 if (--obj->ref == 0) {
191 ucl_object_free_internal (obj, false, ucl_object_dtor_unref);
197 ucl_object_dtor_unref (ucl_object_t *obj)
200 ucl_object_dtor_free (obj);
203 /* This may cause dtor unref being called one more time */
204 ucl_object_dtor_unref_single (obj);
209 ucl_object_free_internal (ucl_object_t *obj, bool allow_rec, ucl_object_dtor dtor)
211 ucl_object_t *tmp, *sub;
213 while (obj != NULL) {
214 if (obj->type == UCL_ARRAY) {
215 UCL_ARRAY_GET (vec, obj);
219 for (i = 0; i < vec->n; i ++) {
220 sub = kv_A (*vec, i);
231 UCL_FREE (sizeof (*vec), vec);
234 else if (obj->type == UCL_OBJECT) {
235 if (obj->value.ov != NULL) {
236 ucl_hash_destroy (obj->value.ov, (ucl_hash_free_func *)dtor);
250 ucl_object_free (ucl_object_t *obj)
252 ucl_object_free_internal (obj, true, ucl_object_dtor_free);
256 ucl_unescape_json_string (char *str, size_t len)
258 char *t = str, *h = str;
264 /* t is target (tortoise), h is source (hare) */
295 for (i = 0; i < 4; i++) {
297 if (isdigit (h[i])) {
300 else if (h[i] >= 'a' && h[i] <= 'f') {
301 uval += h[i] - 'a' + 10;
303 else if (h[i] >= 'A' && h[i] <= 'F') {
304 uval += h[i] - 'A' + 10;
317 else if(uval < 0x800) {
318 t[0] = 0xC0 + ((uval & 0x7C0) >> 6);
319 t[1] = 0x80 + ((uval & 0x03F));
322 else if(uval < 0x10000) {
323 t[0] = 0xE0 + ((uval & 0xF000) >> 12);
324 t[1] = 0x80 + ((uval & 0x0FC0) >> 6);
325 t[2] = 0x80 + ((uval & 0x003F));
328 else if(uval <= 0x10FFFF) {
329 t[0] = 0xF0 + ((uval & 0x1C0000) >> 18);
330 t[1] = 0x80 + ((uval & 0x03F000) >> 12);
331 t[2] = 0x80 + ((uval & 0x000FC0) >> 6);
332 t[3] = 0x80 + ((uval & 0x00003F));
361 ucl_copy_key_trash (const ucl_object_t *obj)
363 ucl_object_t *deconst;
368 if (obj->trash_stack[UCL_TRASH_KEY] == NULL && obj->key != NULL) {
369 deconst = __DECONST (ucl_object_t *, obj);
370 deconst->trash_stack[UCL_TRASH_KEY] = malloc (obj->keylen + 1);
371 if (deconst->trash_stack[UCL_TRASH_KEY] != NULL) {
372 memcpy (deconst->trash_stack[UCL_TRASH_KEY], obj->key, obj->keylen);
373 deconst->trash_stack[UCL_TRASH_KEY][obj->keylen] = '\0';
375 deconst->key = obj->trash_stack[UCL_TRASH_KEY];
376 deconst->flags |= UCL_OBJECT_ALLOCATED_KEY;
379 return obj->trash_stack[UCL_TRASH_KEY];
383 ucl_copy_value_trash (const ucl_object_t *obj)
385 ucl_object_t *deconst;
390 if (obj->trash_stack[UCL_TRASH_VALUE] == NULL) {
391 deconst = __DECONST (ucl_object_t *, obj);
392 if (obj->type == UCL_STRING) {
394 /* Special case for strings */
395 deconst->trash_stack[UCL_TRASH_VALUE] = malloc (obj->len + 1);
396 if (deconst->trash_stack[UCL_TRASH_VALUE] != NULL) {
397 memcpy (deconst->trash_stack[UCL_TRASH_VALUE], obj->value.sv, obj->len);
398 deconst->trash_stack[UCL_TRASH_VALUE][obj->len] = '\0';
399 deconst->value.sv = obj->trash_stack[UCL_TRASH_VALUE];
403 /* Just emit value in json notation */
404 deconst->trash_stack[UCL_TRASH_VALUE] = ucl_object_emit_single_json (obj);
405 deconst->len = strlen (obj->trash_stack[UCL_TRASH_VALUE]);
407 deconst->flags |= UCL_OBJECT_ALLOCATED_VALUE;
409 return obj->trash_stack[UCL_TRASH_VALUE];
412 UCL_EXTERN ucl_object_t*
413 ucl_parser_get_object (struct ucl_parser *parser)
415 if (parser->state != UCL_STATE_ERROR && parser->top_obj != NULL) {
416 return ucl_object_ref (parser->top_obj);
423 ucl_parser_free (struct ucl_parser *parser)
425 struct ucl_stack *stack, *stmp;
426 struct ucl_macro *macro, *mtmp;
427 struct ucl_chunk *chunk, *ctmp;
428 struct ucl_pubkey *key, *ktmp;
429 struct ucl_variable *var, *vtmp;
431 if (parser == NULL) {
435 if (parser->top_obj != NULL) {
436 ucl_object_unref (parser->top_obj);
439 LL_FOREACH_SAFE (parser->stack, stack, stmp) {
442 HASH_ITER (hh, parser->macroes, macro, mtmp) {
444 HASH_DEL (parser->macroes, macro);
445 UCL_FREE (sizeof (struct ucl_macro), macro);
447 LL_FOREACH_SAFE (parser->chunks, chunk, ctmp) {
448 UCL_FREE (sizeof (struct ucl_chunk), chunk);
450 LL_FOREACH_SAFE (parser->keys, key, ktmp) {
451 UCL_FREE (sizeof (struct ucl_pubkey), key);
453 LL_FOREACH_SAFE (parser->variables, var, vtmp) {
456 UCL_FREE (sizeof (struct ucl_variable), var);
459 if (parser->err != NULL) {
460 utstring_free (parser->err);
463 if (parser->cur_file) {
464 free (parser->cur_file);
467 UCL_FREE (sizeof (struct ucl_parser), parser);
470 UCL_EXTERN const char *
471 ucl_parser_get_error(struct ucl_parser *parser)
473 if (parser == NULL) {
477 if (parser->err == NULL)
480 return utstring_body(parser->err);
484 ucl_parser_clear_error(struct ucl_parser *parser)
486 if (parser != NULL && parser->err != NULL) {
487 utstring_free(parser->err);
493 ucl_pubkey_add (struct ucl_parser *parser, const unsigned char *key, size_t len)
496 ucl_create_err (&parser->err, "cannot check signatures without openssl");
499 # if (OPENSSL_VERSION_NUMBER < 0x10000000L)
500 ucl_create_err (&parser->err, "cannot check signatures, openssl version is unsupported");
503 struct ucl_pubkey *nkey;
506 mem = BIO_new_mem_buf ((void *)key, len);
507 nkey = UCL_ALLOC (sizeof (struct ucl_pubkey));
509 ucl_create_err (&parser->err, "cannot allocate memory for key");
512 nkey->key = PEM_read_bio_PUBKEY (mem, &nkey->key, NULL, NULL);
514 if (nkey->key == NULL) {
515 UCL_FREE (sizeof (struct ucl_pubkey), nkey);
516 ucl_create_err (&parser->err, "%s",
517 ERR_error_string (ERR_get_error (), NULL));
520 LL_PREPEND (parser->keys, nkey);
527 struct ucl_curl_cbdata {
533 ucl_curl_write_callback (void* contents, size_t size, size_t nmemb, void* ud)
535 struct ucl_curl_cbdata *cbdata = ud;
536 size_t realsize = size * nmemb;
538 cbdata->buf = realloc (cbdata->buf, cbdata->buflen + realsize + 1);
539 if (cbdata->buf == NULL) {
543 memcpy (&(cbdata->buf[cbdata->buflen]), contents, realsize);
544 cbdata->buflen += realsize;
545 cbdata->buf[cbdata->buflen] = 0;
552 * Fetch a url and save results to the memory buffer
553 * @param url url to fetch
554 * @param len length of url
555 * @param buf target buffer
556 * @param buflen target length
560 ucl_fetch_url (const unsigned char *url, unsigned char **buf, size_t *buflen,
561 UT_string **err, bool must_exist)
565 struct url *fetch_url;
569 fetch_url = fetchParseURL (url);
570 if (fetch_url == NULL) {
571 ucl_create_err (err, "invalid URL %s: %s",
572 url, strerror (errno));
575 if ((in = fetchXGet (fetch_url, &us, "")) == NULL) {
577 ucl_create_err (err, "cannot fetch URL %s: %s",
578 url, strerror (errno));
580 fetchFreeURL (fetch_url);
585 *buf = malloc (*buflen);
587 ucl_create_err (err, "cannot allocate buffer for URL %s: %s",
588 url, strerror (errno));
590 fetchFreeURL (fetch_url);
594 if (fread (*buf, *buflen, 1, in) != 1) {
595 ucl_create_err (err, "cannot read URL %s: %s",
596 url, strerror (errno));
598 fetchFreeURL (fetch_url);
602 fetchFreeURL (fetch_url);
604 #elif defined(CURL_FOUND)
607 struct ucl_curl_cbdata cbdata;
609 curl = curl_easy_init ();
611 ucl_create_err (err, "CURL interface is broken");
614 if ((r = curl_easy_setopt (curl, CURLOPT_URL, url)) != CURLE_OK) {
615 ucl_create_err (err, "invalid URL %s: %s",
616 url, curl_easy_strerror (r));
617 curl_easy_cleanup (curl);
620 curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, ucl_curl_write_callback);
622 cbdata.buflen = *buflen;
623 curl_easy_setopt (curl, CURLOPT_WRITEDATA, &cbdata);
625 if ((r = curl_easy_perform (curl)) != CURLE_OK) {
627 ucl_create_err (err, "error fetching URL %s: %s",
628 url, curl_easy_strerror (r));
630 curl_easy_cleanup (curl);
637 *buflen = cbdata.buflen;
641 ucl_create_err (err, "URL support is disabled");
647 * Fetch a file and save results to the memory buffer
648 * @param filename filename to fetch
649 * @param len length of filename
650 * @param buf target buffer
651 * @param buflen target length
655 ucl_fetch_file (const unsigned char *filename, unsigned char **buf, size_t *buflen,
656 UT_string **err, bool must_exist)
661 if (stat (filename, &st) == -1 || !S_ISREG (st.st_mode)) {
663 ucl_create_err (err, "cannot stat file %s: %s",
664 filename, strerror (errno));
668 if (st.st_size == 0) {
669 /* Do not map empty files */
674 if ((fd = open (filename, O_RDONLY)) == -1) {
675 ucl_create_err (err, "cannot open file %s: %s",
676 filename, strerror (errno));
679 if ((*buf = ucl_mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
681 ucl_create_err (err, "cannot mmap file %s: %s",
682 filename, strerror (errno));
685 *buflen = st.st_size;
693 #if (defined(HAVE_OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x10000000L)
695 ucl_sig_check (const unsigned char *data, size_t datalen,
696 const unsigned char *sig, size_t siglen, struct ucl_parser *parser)
698 struct ucl_pubkey *key;
699 char dig[EVP_MAX_MD_SIZE];
701 EVP_PKEY_CTX *key_ctx;
702 EVP_MD_CTX *sign_ctx = NULL;
704 sign_ctx = EVP_MD_CTX_create ();
706 LL_FOREACH (parser->keys, key) {
707 key_ctx = EVP_PKEY_CTX_new (key->key, NULL);
708 if (key_ctx != NULL) {
709 if (EVP_PKEY_verify_init (key_ctx) <= 0) {
710 EVP_PKEY_CTX_free (key_ctx);
713 if (EVP_PKEY_CTX_set_rsa_padding (key_ctx, RSA_PKCS1_PADDING) <= 0) {
714 EVP_PKEY_CTX_free (key_ctx);
717 if (EVP_PKEY_CTX_set_signature_md (key_ctx, EVP_sha256 ()) <= 0) {
718 EVP_PKEY_CTX_free (key_ctx);
721 EVP_DigestInit (sign_ctx, EVP_sha256 ());
722 EVP_DigestUpdate (sign_ctx, data, datalen);
723 EVP_DigestFinal (sign_ctx, dig, &diglen);
725 if (EVP_PKEY_verify (key_ctx, sig, siglen, dig, diglen) == 1) {
726 EVP_MD_CTX_destroy (sign_ctx);
727 EVP_PKEY_CTX_free (key_ctx);
731 EVP_PKEY_CTX_free (key_ctx);
735 EVP_MD_CTX_destroy (sign_ctx);
742 * Include an url to configuration
750 ucl_include_url (const unsigned char *data, size_t len,
751 struct ucl_parser *parser, bool check_signature, bool must_exist,
756 unsigned char *buf = NULL;
758 struct ucl_chunk *chunk;
759 char urlbuf[PATH_MAX];
762 snprintf (urlbuf, sizeof (urlbuf), "%.*s", (int)len, data);
764 if (!ucl_fetch_url (urlbuf, &buf, &buflen, &parser->err, must_exist)) {
765 return (!must_exist || false);
768 if (check_signature) {
769 #if (defined(HAVE_OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x10000000L)
770 unsigned char *sigbuf = NULL;
772 /* We need to check signature first */
773 snprintf (urlbuf, sizeof (urlbuf), "%.*s.sig", (int)len, data);
774 if (!ucl_fetch_url (urlbuf, &sigbuf, &siglen, &parser->err, true)) {
777 if (!ucl_sig_check (buf, buflen, sigbuf, siglen, parser)) {
778 ucl_create_err (&parser->err, "cannot verify url %s: %s",
780 ERR_error_string (ERR_get_error (), NULL));
782 ucl_munmap (sigbuf, siglen);
787 ucl_munmap (sigbuf, siglen);
792 prev_state = parser->state;
793 parser->state = UCL_STATE_INIT;
795 res = ucl_parser_add_chunk_priority (parser, buf, buflen, priority);
797 /* Remove chunk from the stack */
798 chunk = parser->chunks;
800 parser->chunks = chunk->next;
801 UCL_FREE (sizeof (struct ucl_chunk), chunk);
805 parser->state = prev_state;
812 * Include a single file to the parser
816 * @param check_signature
823 ucl_include_file_single (const unsigned char *data, size_t len,
824 struct ucl_parser *parser, bool check_signature, bool must_exist,
828 struct ucl_chunk *chunk;
829 unsigned char *buf = NULL;
832 char filebuf[PATH_MAX], realbuf[PATH_MAX];
834 struct ucl_variable *cur_var, *tmp_var, *old_curdir = NULL,
835 *old_filename = NULL;
837 snprintf (filebuf, sizeof (filebuf), "%.*s", (int)len, data);
838 if (ucl_realpath (filebuf, realbuf) == NULL) {
842 ucl_create_err (&parser->err, "cannot open file %s: %s",
848 if (parser->cur_file && strcmp (realbuf, parser->cur_file) == 0) {
849 /* We are likely including the file itself */
850 ucl_create_err (&parser->err, "trying to include the file %s from itself",
855 if (!ucl_fetch_file (realbuf, &buf, &buflen, &parser->err, must_exist)) {
856 return (!must_exist || false);
859 if (check_signature) {
860 #if (defined(HAVE_OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x10000000L)
861 unsigned char *sigbuf = NULL;
863 /* We need to check signature first */
864 snprintf (filebuf, sizeof (filebuf), "%s.sig", realbuf);
865 if (!ucl_fetch_file (filebuf, &sigbuf, &siglen, &parser->err, true)) {
868 if (!ucl_sig_check (buf, buflen, sigbuf, siglen, parser)) {
869 ucl_create_err (&parser->err, "cannot verify file %s: %s",
871 ERR_error_string (ERR_get_error (), NULL));
873 ucl_munmap (sigbuf, siglen);
878 ucl_munmap (sigbuf, siglen);
883 old_curfile = parser->cur_file;
884 parser->cur_file = strdup (realbuf);
886 /* Store old file vars */
887 DL_FOREACH_SAFE (parser->variables, cur_var, tmp_var) {
888 if (strcmp (cur_var->var, "CURDIR") == 0) {
889 old_curdir = cur_var;
890 DL_DELETE (parser->variables, cur_var);
892 else if (strcmp (cur_var->var, "FILENAME") == 0) {
893 old_filename = cur_var;
894 DL_DELETE (parser->variables, cur_var);
898 ucl_parser_set_filevars (parser, realbuf, false);
900 prev_state = parser->state;
901 parser->state = UCL_STATE_INIT;
903 res = ucl_parser_add_chunk_priority (parser, buf, buflen, priority);
904 if (!res && !must_exist) {
906 utstring_free (parser->err);
908 parser->state = UCL_STATE_AFTER_VALUE;
911 /* Remove chunk from the stack */
912 chunk = parser->chunks;
914 parser->chunks = chunk->next;
915 UCL_FREE (sizeof (struct ucl_chunk), chunk);
916 parser->recursion --;
919 /* Restore old file vars */
920 parser->cur_file = old_curfile;
921 DL_FOREACH_SAFE (parser->variables, cur_var, tmp_var) {
922 if (strcmp (cur_var->var, "CURDIR") == 0 && old_curdir) {
923 DL_DELETE (parser->variables, cur_var);
925 free (cur_var->value);
926 UCL_FREE (sizeof (struct ucl_variable), cur_var);
928 else if (strcmp (cur_var->var, "FILENAME") == 0 && old_filename) {
929 DL_DELETE (parser->variables, cur_var);
931 free (cur_var->value);
932 UCL_FREE (sizeof (struct ucl_variable), cur_var);
936 DL_APPEND (parser->variables, old_filename);
939 DL_APPEND (parser->variables, old_curdir);
945 parser->state = prev_state;
948 ucl_munmap (buf, buflen);
955 * Include a file to configuration
963 ucl_include_file (const unsigned char *data, size_t len,
964 struct ucl_parser *parser, bool check_signature, bool must_exist,
965 bool allow_glob, unsigned priority)
967 const unsigned char *p = data, *end = data + len;
968 bool need_glob = false;
970 char glob_pattern[PATH_MAX];
975 return ucl_include_file_single (data, len, parser, check_signature,
976 must_exist, priority);
979 /* Check for special symbols in a filename */
981 if (*p == '*' || *p == '?') {
989 memset (&globbuf, 0, sizeof (globbuf));
990 ucl_strlcpy (glob_pattern, (const char *)data, sizeof (glob_pattern));
991 if (glob (glob_pattern, 0, NULL, &globbuf) != 0) {
992 return (!must_exist || false);
994 for (i = 0; i < globbuf.gl_pathc; i ++) {
995 if (!ucl_include_file_single ((unsigned char *)globbuf.gl_pathv[i],
996 strlen (globbuf.gl_pathv[i]), parser, check_signature,
997 must_exist, priority)) {
1003 globfree (&globbuf);
1005 if (cnt == 0 && must_exist) {
1006 ucl_create_err (&parser->err, "cannot match any files for pattern %s",
1012 return ucl_include_file_single (data, len, parser, check_signature,
1013 must_exist, priority);
1017 /* Win32 compilers do not support globbing. Therefore, for Win32,
1018 treat allow_glob/need_glob as a NOOP and just return */
1019 return ucl_include_file_single (data, len, parser, check_signature,
1020 must_exist, priority);
1027 * Common function to handle .*include* macros
1032 * @param default_try
1033 * @param default_sign
1037 ucl_include_common (const unsigned char *data, size_t len,
1038 const ucl_object_t *args, struct ucl_parser *parser,
1042 bool try_load, allow_glob, allow_url, need_sign;
1044 const ucl_object_t *param;
1045 ucl_object_iter_t it = NULL;
1047 /* Default values */
1048 try_load = default_try;
1051 need_sign = default_sign;
1054 /* Process arguments */
1055 if (args != NULL && args->type == UCL_OBJECT) {
1056 while ((param = ucl_iterate_object (args, &it, true)) != NULL) {
1057 if (param->type == UCL_BOOLEAN) {
1058 if (strcmp (param->key, "try") == 0) {
1059 try_load = ucl_object_toboolean (param);
1061 else if (strcmp (param->key, "sign") == 0) {
1062 need_sign = ucl_object_toboolean (param);
1064 else if (strcmp (param->key, "glob") == 0) {
1065 allow_glob = ucl_object_toboolean (param);
1067 else if (strcmp (param->key, "url") == 0) {
1068 allow_url = ucl_object_toboolean (param);
1071 else if (param->type == UCL_INT) {
1072 if (strcmp (param->key, "priority") == 0) {
1073 priority = ucl_object_toint (param);
1079 if (*data == '/' || *data == '.') {
1080 /* Try to load a file */
1081 return ucl_include_file (data, len, parser, need_sign, !try_load,
1082 allow_glob, priority);
1084 else if (allow_url) {
1085 /* Globbing is not used for URL's */
1086 return ucl_include_url (data, len, parser, need_sign, !try_load,
1094 * Handle include macro
1095 * @param data include data
1096 * @param len length of data
1097 * @param ud user data
1098 * @param err error ptr
1102 ucl_include_handler (const unsigned char *data, size_t len,
1103 const ucl_object_t *args, void* ud)
1105 struct ucl_parser *parser = ud;
1107 return ucl_include_common (data, len, args, parser, false, false);
1111 * Handle includes macro
1112 * @param data include data
1113 * @param len length of data
1114 * @param ud user data
1115 * @param err error ptr
1119 ucl_includes_handler (const unsigned char *data, size_t len,
1120 const ucl_object_t *args, void* ud)
1122 struct ucl_parser *parser = ud;
1124 return ucl_include_common (data, len, args, parser, false, true);
1129 ucl_try_include_handler (const unsigned char *data, size_t len,
1130 const ucl_object_t *args, void* ud)
1132 struct ucl_parser *parser = ud;
1134 return ucl_include_common (data, len, args, parser, true, false);
1138 ucl_parser_set_filevars (struct ucl_parser *parser, const char *filename, bool need_expand)
1140 char realbuf[PATH_MAX], *curdir;
1142 if (filename != NULL) {
1144 if (ucl_realpath (filename, realbuf) == NULL) {
1149 ucl_strlcpy (realbuf, filename, sizeof (realbuf));
1152 /* Define variables */
1153 ucl_parser_register_variable (parser, "FILENAME", realbuf);
1154 curdir = dirname (realbuf);
1155 ucl_parser_register_variable (parser, "CURDIR", curdir);
1158 /* Set everything from the current dir */
1159 curdir = getcwd (realbuf, sizeof (realbuf));
1160 ucl_parser_register_variable (parser, "FILENAME", "undef");
1161 ucl_parser_register_variable (parser, "CURDIR", curdir);
1168 ucl_parser_add_file (struct ucl_parser *parser, const char *filename)
1173 char realbuf[PATH_MAX];
1175 if (ucl_realpath (filename, realbuf) == NULL) {
1176 ucl_create_err (&parser->err, "cannot open file %s: %s",
1182 if (!ucl_fetch_file (realbuf, &buf, &len, &parser->err, true)) {
1186 if (parser->cur_file) {
1187 free (parser->cur_file);
1189 parser->cur_file = strdup (realbuf);
1190 ucl_parser_set_filevars (parser, realbuf, false);
1191 ret = ucl_parser_add_chunk (parser, buf, len);
1194 ucl_munmap (buf, len);
1201 ucl_parser_add_fd (struct ucl_parser *parser, int fd)
1208 if (fstat (fd, &st) == -1) {
1209 ucl_create_err (&parser->err, "cannot stat fd %d: %s",
1210 fd, strerror (errno));
1213 if ((buf = ucl_mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
1214 ucl_create_err (&parser->err, "cannot mmap fd %d: %s",
1215 fd, strerror (errno));
1219 if (parser->cur_file) {
1220 free (parser->cur_file);
1222 parser->cur_file = NULL;
1224 ret = ucl_parser_add_chunk (parser, buf, len);
1227 ucl_munmap (buf, len);
1234 ucl_strlcpy (char *dst, const char *src, size_t siz)
1237 const char *s = src;
1240 /* Copy as many bytes as will fit */
1243 if ((*d++ = *s++) == '\0') {
1249 if (n == 0 && siz != 0) {
1253 return (s - src - 1); /* count does not include NUL */
1257 ucl_strlcpy_unsafe (char *dst, const char *src, size_t siz)
1259 memcpy (dst, src, siz - 1);
1260 dst[siz - 1] = '\0';
1266 ucl_strlcpy_tolower (char *dst, const char *src, size_t siz)
1269 const char *s = src;
1272 /* Copy as many bytes as will fit */
1275 if ((*d++ = tolower (*s++)) == '\0') {
1281 if (n == 0 && siz != 0) {
1285 return (s - src); /* count does not include NUL */
1289 ucl_object_fromstring_common (const char *str, size_t len, enum ucl_string_flags flags)
1292 const char *start, *end, *p, *pos;
1300 obj = ucl_object_new ();
1305 if (flags & UCL_STRING_TRIM) {
1306 /* Skip leading spaces */
1307 for (start = str; (size_t)(start - str) < len; start ++) {
1308 if (!ucl_test_character (*start, UCL_CHARACTER_WHITESPACE_UNSAFE)) {
1312 /* Skip trailing spaces */
1313 for (end = str + len - 1; end > start; end --) {
1314 if (!ucl_test_character (*end, UCL_CHARACTER_WHITESPACE_UNSAFE)) {
1325 obj->type = UCL_STRING;
1326 if (flags & UCL_STRING_ESCAPE) {
1327 for (p = start, escaped_len = 0; p < end; p ++, escaped_len ++) {
1328 if (ucl_test_character (*p, UCL_CHARACTER_JSON_UNSAFE)) {
1332 dst = malloc (escaped_len + 1);
1334 for (p = start, d = dst; p < end; p ++, d ++) {
1335 if (ucl_test_character (*p, UCL_CHARACTER_JSON_UNSAFE)) {
1372 obj->value.sv = dst;
1373 obj->trash_stack[UCL_TRASH_VALUE] = dst;
1374 obj->len = escaped_len;
1378 dst = malloc (end - start + 1);
1380 ucl_strlcpy_unsafe (dst, start, end - start + 1);
1381 obj->value.sv = dst;
1382 obj->trash_stack[UCL_TRASH_VALUE] = dst;
1383 obj->len = end - start;
1386 if ((flags & UCL_STRING_PARSE) && dst != NULL) {
1387 /* Parse what we have */
1388 if (flags & UCL_STRING_PARSE_BOOLEAN) {
1389 if (!ucl_maybe_parse_boolean (obj, dst, obj->len) && (flags & UCL_STRING_PARSE_NUMBER)) {
1390 ucl_maybe_parse_number (obj, dst, dst + obj->len, &pos,
1391 flags & UCL_STRING_PARSE_DOUBLE,
1392 flags & UCL_STRING_PARSE_BYTES,
1393 flags & UCL_STRING_PARSE_TIME);
1397 ucl_maybe_parse_number (obj, dst, dst + obj->len, &pos,
1398 flags & UCL_STRING_PARSE_DOUBLE,
1399 flags & UCL_STRING_PARSE_BYTES,
1400 flags & UCL_STRING_PARSE_TIME);
1409 ucl_object_insert_key_common (ucl_object_t *top, ucl_object_t *elt,
1410 const char *key, size_t keylen, bool copy_key, bool merge, bool replace)
1412 ucl_object_t *found, *tmp;
1413 const ucl_object_t *cur;
1414 ucl_object_iter_t it = NULL;
1418 if (elt == NULL || key == NULL) {
1426 if (top->type != UCL_OBJECT) {
1427 /* It is possible to convert NULL type to an object */
1428 if (top->type == UCL_NULL) {
1429 top->type = UCL_OBJECT;
1432 /* Refuse converting of other object types */
1437 if (top->value.ov == NULL) {
1438 top->value.ov = ucl_hash_create (false);
1442 keylen = strlen (key);
1445 for (p = key; p < key + keylen; p ++) {
1446 if (ucl_test_character (*p, UCL_CHARACTER_UCL_UNSAFE)) {
1447 elt->flags |= UCL_OBJECT_NEED_KEY_ESCAPE;
1452 /* workaround for some use cases */
1453 if (elt->trash_stack[UCL_TRASH_KEY] != NULL &&
1454 key != (const char *)elt->trash_stack[UCL_TRASH_KEY]) {
1455 /* Remove copied key */
1456 free (elt->trash_stack[UCL_TRASH_KEY]);
1457 elt->trash_stack[UCL_TRASH_KEY] = NULL;
1458 elt->flags &= ~UCL_OBJECT_ALLOCATED_KEY;
1462 elt->keylen = keylen;
1465 ucl_copy_key_trash (elt);
1468 found = __DECONST (ucl_object_t *, ucl_hash_search_obj (top->value.ov, elt));
1470 if (found == NULL) {
1471 top->value.ov = ucl_hash_insert_object (top->value.ov, elt, false);
1479 ucl_hash_replace (top->value.ov, found, elt);
1480 ucl_object_unref (found);
1483 if (found->type != UCL_OBJECT && elt->type == UCL_OBJECT) {
1484 /* Insert old elt to new one */
1485 ucl_object_insert_key_common (elt, found, found->key,
1486 found->keylen, copy_key, false, false);
1487 ucl_hash_delete (top->value.ov, found);
1488 top->value.ov = ucl_hash_insert_object (top->value.ov, elt, false);
1490 else if (found->type == UCL_OBJECT && elt->type != UCL_OBJECT) {
1491 /* Insert new to old */
1492 ucl_object_insert_key_common (found, elt, elt->key,
1493 elt->keylen, copy_key, false, false);
1495 else if (found->type == UCL_OBJECT && elt->type == UCL_OBJECT) {
1496 /* Mix two hashes */
1497 while ((cur = ucl_iterate_object (elt, &it, true)) != NULL) {
1498 tmp = ucl_object_ref (cur);
1499 ucl_object_insert_key_common (found, tmp, cur->key,
1500 cur->keylen, copy_key, false, false);
1502 ucl_object_unref (elt);
1505 /* Just make a list of scalars */
1506 DL_APPEND (found, elt);
1510 DL_APPEND (found, elt);
1518 ucl_object_delete_keyl (ucl_object_t *top, const char *key, size_t keylen)
1520 ucl_object_t *found;
1522 if (top == NULL || key == NULL) {
1526 found = __DECONST (ucl_object_t *, ucl_object_find_keyl (top, key, keylen));
1528 if (found == NULL) {
1532 ucl_hash_delete (top->value.ov, found);
1533 ucl_object_unref (found);
1540 ucl_object_delete_key (ucl_object_t *top, const char *key)
1542 return ucl_object_delete_keyl (top, key, strlen(key));
1546 ucl_object_pop_keyl (ucl_object_t *top, const char *key, size_t keylen)
1548 const ucl_object_t *found;
1550 if (top == NULL || key == NULL) {
1553 found = ucl_object_find_keyl (top, key, keylen);
1555 if (found == NULL) {
1558 ucl_hash_delete (top->value.ov, found);
1561 return __DECONST (ucl_object_t *, found);
1565 ucl_object_pop_key (ucl_object_t *top, const char *key)
1567 return ucl_object_pop_keyl (top, key, strlen(key));
1571 ucl_object_insert_key (ucl_object_t *top, ucl_object_t *elt,
1572 const char *key, size_t keylen, bool copy_key)
1574 return ucl_object_insert_key_common (top, elt, key, keylen, copy_key, false, false);
1578 ucl_object_insert_key_merged (ucl_object_t *top, ucl_object_t *elt,
1579 const char *key, size_t keylen, bool copy_key)
1581 return ucl_object_insert_key_common (top, elt, key, keylen, copy_key, true, false);
1585 ucl_object_replace_key (ucl_object_t *top, ucl_object_t *elt,
1586 const char *key, size_t keylen, bool copy_key)
1588 return ucl_object_insert_key_common (top, elt, key, keylen, copy_key, false, true);
1592 ucl_object_merge (ucl_object_t *top, ucl_object_t *elt, bool copy)
1594 ucl_object_t *cur = NULL, *cp = NULL, *found = NULL;
1595 ucl_object_iter_t iter = NULL;
1597 if (top == NULL || top->type != UCL_OBJECT || elt == NULL || elt->type != UCL_OBJECT) {
1601 /* Mix two hashes */
1602 while ((cur = (ucl_object_t*)ucl_hash_iterate (elt->value.ov, &iter))) {
1604 cp = ucl_object_copy (cur);
1607 cp = ucl_object_ref (cur);
1609 found = __DECONST(ucl_object_t *, ucl_hash_search (top->value.ov, cp->key, cp->keylen));
1610 if (found == NULL) {
1611 /* The key does not exist */
1612 top->value.ov = ucl_hash_insert_object (top->value.ov, cp, false);
1616 /* The key already exists, replace it */
1617 ucl_hash_replace (top->value.ov, found, cp);
1618 ucl_object_unref (found);
1625 const ucl_object_t *
1626 ucl_object_find_keyl (const ucl_object_t *obj, const char *key, size_t klen)
1628 const ucl_object_t *ret;
1631 if (obj == NULL || obj->type != UCL_OBJECT || key == NULL) {
1637 ret = ucl_hash_search_obj (obj->value.ov, &srch);
1642 const ucl_object_t *
1643 ucl_object_find_key (const ucl_object_t *obj, const char *key)
1648 return ucl_object_find_keyl (obj, key, strlen(key));
1652 ucl_iterate_object (const ucl_object_t *obj, ucl_object_iter_t *iter, bool expand_values)
1654 const ucl_object_t *elt = NULL;
1656 if (obj == NULL || iter == NULL) {
1660 if (expand_values) {
1661 switch (obj->type) {
1663 return (const ucl_object_t*)ucl_hash_iterate (obj->value.ov, iter);
1667 UCL_ARRAY_GET (vec, obj);
1668 idx = (unsigned int)(uintptr_t)(*iter);
1671 while (idx < kv_size (*vec)) {
1672 if ((elt = kv_A (*vec, idx)) != NULL) {
1678 *iter = (void *)(uintptr_t)idx;
1685 /* Go to linear iteration */
1689 /* Treat everything as a linear list */
1694 else if (elt == obj) {
1697 *iter = __DECONST (void *, elt->next ? elt->next : obj);
1704 const char safe_iter_magic[4] = {'u', 'i', 't', 'e'};
1705 struct ucl_object_safe_iter {
1706 char magic[4]; /* safety check */
1707 const ucl_object_t *impl_it; /* implicit object iteration */
1708 ucl_object_iter_t expl_it; /* explicit iteration */
1711 #define UCL_SAFE_ITER(ptr) (struct ucl_object_safe_iter *)(ptr)
1712 #define UCL_SAFE_ITER_CHECK(it) do { \
1713 assert (it != NULL); \
1714 assert (memcmp (it->magic, safe_iter_magic, sizeof (it->magic)) == 0); \
1718 ucl_object_iterate_new (const ucl_object_t *obj)
1720 struct ucl_object_safe_iter *it;
1722 it = UCL_ALLOC (sizeof (*it));
1724 memcpy (it->magic, safe_iter_magic, sizeof (it->magic));
1729 return (ucl_object_iter_t)it;
1734 ucl_object_iterate_reset (ucl_object_iter_t it, const ucl_object_t *obj)
1736 struct ucl_object_safe_iter *rit = UCL_SAFE_ITER (it);
1738 UCL_SAFE_ITER_CHECK (rit);
1741 rit->expl_it = NULL;
1747 ucl_object_iterate_safe (ucl_object_iter_t it, bool expand_values)
1749 struct ucl_object_safe_iter *rit = UCL_SAFE_ITER (it);
1750 const ucl_object_t *ret = NULL;
1752 UCL_SAFE_ITER_CHECK (rit);
1754 if (rit->impl_it == NULL) {
1758 if (rit->impl_it->type == UCL_OBJECT || rit->impl_it->type == UCL_ARRAY) {
1759 ret = ucl_iterate_object (rit->impl_it, &rit->expl_it, true);
1762 /* Need to switch to another implicit object in chain */
1763 rit->impl_it = rit->impl_it->next;
1764 rit->expl_it = NULL;
1765 return ucl_object_iterate_safe (it, expand_values);
1769 /* Just iterate over the implicit array */
1771 rit->impl_it = rit->impl_it->next;
1772 if (expand_values) {
1773 /* We flatten objects if need to expand values */
1774 if (ret->type == UCL_OBJECT || ret->type == UCL_ARRAY) {
1775 return ucl_object_iterate_safe (it, expand_values);
1784 ucl_object_iterate_free (ucl_object_iter_t it)
1786 struct ucl_object_safe_iter *rit = UCL_SAFE_ITER (it);
1788 UCL_SAFE_ITER_CHECK (rit);
1790 UCL_FREE (sizeof (*rit), it);
1793 const ucl_object_t *
1794 ucl_lookup_path (const ucl_object_t *top, const char *path_in) {
1795 const ucl_object_t *o = NULL, *found;
1800 if (path_in == NULL || top == NULL) {
1807 /* Skip leading dots */
1813 while (*p != '\0') {
1815 if (*p == '.' || *p == '\0') {
1817 switch (top->type) {
1819 /* Key should be an int */
1820 index = strtoul (c, &err_str, 10);
1821 if (err_str != NULL && (*err_str != '.' && *err_str != '\0')) {
1824 o = ucl_array_find_index (top, index);
1827 o = ucl_object_find_keyl (top, c, p - c);
1847 ucl_object_new (void)
1849 return ucl_object_typed_new (UCL_NULL);
1853 ucl_object_typed_new (ucl_type_t type)
1855 return ucl_object_new_full (type, 0);
1859 ucl_object_new_full (ucl_type_t type, unsigned priority)
1863 if (type != UCL_USERDATA) {
1864 new = UCL_ALLOC (sizeof (ucl_object_t));
1866 memset (new, 0, sizeof (ucl_object_t));
1868 new->type = (type <= UCL_NULL ? type : UCL_NULL);
1871 ucl_object_set_priority (new, priority);
1873 if (type == UCL_ARRAY) {
1874 new->value.av = UCL_ALLOC (sizeof (ucl_array_t));
1875 if (new->value.av) {
1876 memset (new->value.av, 0, sizeof (ucl_array_t));
1877 UCL_ARRAY_GET (vec, new);
1879 /* Preallocate some space for arrays */
1880 kv_resize (ucl_object_t *, *vec, 8);
1886 new = ucl_object_new_userdata (NULL, NULL);
1887 ucl_object_set_priority (new, priority);
1894 ucl_object_new_userdata (ucl_userdata_dtor dtor, ucl_userdata_emitter emitter)
1896 struct ucl_object_userdata *new;
1897 size_t nsize = sizeof (*new);
1899 new = UCL_ALLOC (nsize);
1901 memset (new, 0, nsize);
1903 new->obj.type = UCL_USERDATA;
1904 new->obj.next = NULL;
1905 new->obj.prev = (ucl_object_t *)new;
1907 new->emitter = emitter;
1910 return (ucl_object_t *)new;
1914 ucl_object_type (const ucl_object_t *obj)
1920 ucl_object_fromstring (const char *str)
1922 return ucl_object_fromstring_common (str, 0, UCL_STRING_ESCAPE);
1926 ucl_object_fromlstring (const char *str, size_t len)
1928 return ucl_object_fromstring_common (str, len, UCL_STRING_ESCAPE);
1932 ucl_object_fromint (int64_t iv)
1936 obj = ucl_object_new ();
1938 obj->type = UCL_INT;
1946 ucl_object_fromdouble (double dv)
1950 obj = ucl_object_new ();
1952 obj->type = UCL_FLOAT;
1960 ucl_object_frombool (bool bv)
1964 obj = ucl_object_new ();
1966 obj->type = UCL_BOOLEAN;
1974 ucl_array_append (ucl_object_t *top, ucl_object_t *elt)
1976 UCL_ARRAY_GET (vec, top);
1978 if (elt == NULL || top == NULL) {
1983 vec = UCL_ALLOC (sizeof (*vec));
1985 top->value.av = (void *)vec;
1988 kv_push (ucl_object_t *, *vec, elt);
1996 ucl_array_prepend (ucl_object_t *top, ucl_object_t *elt)
1998 UCL_ARRAY_GET (vec, top);
2000 if (elt == NULL || top == NULL) {
2005 vec = UCL_ALLOC (sizeof (*vec));
2007 top->value.av = (void *)vec;
2008 kv_push (ucl_object_t *, *vec, elt);
2011 /* Slow O(n) algorithm */
2012 kv_prepend (ucl_object_t *, *vec, elt);
2021 ucl_array_merge (ucl_object_t *top, ucl_object_t *elt, bool copy)
2025 UCL_ARRAY_GET (v1, top);
2026 UCL_ARRAY_GET (v2, elt);
2028 if (elt == NULL || top == NULL || top->type != UCL_ARRAY || elt->type != UCL_ARRAY) {
2032 kv_concat (ucl_object_t *, *v1, *v2);
2034 for (i = v2->n; i < v1->n; i ++) {
2035 obj = &kv_A (*v1, i);
2042 *obj = ucl_object_copy (*obj);
2045 ucl_object_ref (*obj);
2053 ucl_array_delete (ucl_object_t *top, ucl_object_t *elt)
2055 UCL_ARRAY_GET (vec, top);
2056 ucl_object_t *ret = NULL;
2059 for (i = 0; i < vec->n; i ++) {
2060 if (kv_A (*vec, i) == elt) {
2061 kv_del (ucl_object_t *, *vec, i);
2071 const ucl_object_t *
2072 ucl_array_head (const ucl_object_t *top)
2074 UCL_ARRAY_GET (vec, top);
2076 if (top == NULL || top->type != UCL_ARRAY || top->value.av == NULL) {
2080 return (vec->n > 0 ? vec->a[0] : NULL);
2083 const ucl_object_t *
2084 ucl_array_tail (const ucl_object_t *top)
2086 UCL_ARRAY_GET (vec, top);
2088 if (top == NULL || top->type != UCL_ARRAY || top->value.av == NULL) {
2092 return (vec->n > 0 ? vec->a[vec->n - 1] : NULL);
2096 ucl_array_pop_last (ucl_object_t *top)
2098 UCL_ARRAY_GET (vec, top);
2099 ucl_object_t **obj, *ret = NULL;
2101 if (vec != NULL && vec->n > 0) {
2102 obj = &kv_A (*vec, vec->n - 1);
2104 kv_del (ucl_object_t *, *vec, vec->n - 1);
2112 ucl_array_pop_first (ucl_object_t *top)
2114 UCL_ARRAY_GET (vec, top);
2115 ucl_object_t **obj, *ret = NULL;
2117 if (vec != NULL && vec->n > 0) {
2118 obj = &kv_A (*vec, 0);
2120 kv_del (ucl_object_t *, *vec, 0);
2127 const ucl_object_t *
2128 ucl_array_find_index (const ucl_object_t *top, unsigned int index)
2130 UCL_ARRAY_GET (vec, top);
2132 if (vec != NULL && vec->n > 0 && index < vec->n) {
2133 return kv_A (*vec, index);
2140 ucl_array_replace_index (ucl_object_t *top, ucl_object_t *elt,
2143 UCL_ARRAY_GET (vec, top);
2144 ucl_object_t *ret = NULL;
2146 if (vec != NULL && vec->n > 0 && index < vec->n) {
2147 ret = kv_A (*vec, index);
2148 kv_A (*vec, index) = elt;
2155 ucl_elt_append (ucl_object_t *head, ucl_object_t *elt)
2164 elt->prev = head->prev;
2165 head->prev->next = elt;
2174 ucl_object_todouble_safe (const ucl_object_t *obj, double *target)
2176 if (obj == NULL || target == NULL) {
2179 switch (obj->type) {
2181 *target = obj->value.iv; /* Probaly could cause overflow */
2185 *target = obj->value.dv;
2195 ucl_object_todouble (const ucl_object_t *obj)
2199 ucl_object_todouble_safe (obj, &result);
2204 ucl_object_toint_safe (const ucl_object_t *obj, int64_t *target)
2206 if (obj == NULL || target == NULL) {
2209 switch (obj->type) {
2211 *target = obj->value.iv;
2215 *target = obj->value.dv; /* Loosing of decimal points */
2225 ucl_object_toint (const ucl_object_t *obj)
2229 ucl_object_toint_safe (obj, &result);
2234 ucl_object_toboolean_safe (const ucl_object_t *obj, bool *target)
2236 if (obj == NULL || target == NULL) {
2239 switch (obj->type) {
2241 *target = (obj->value.iv == true);
2251 ucl_object_toboolean (const ucl_object_t *obj)
2253 bool result = false;
2255 ucl_object_toboolean_safe (obj, &result);
2260 ucl_object_tostring_safe (const ucl_object_t *obj, const char **target)
2262 if (obj == NULL || target == NULL) {
2266 switch (obj->type) {
2268 *target = ucl_copy_value_trash (obj);
2278 ucl_object_tostring (const ucl_object_t *obj)
2280 const char *result = NULL;
2282 ucl_object_tostring_safe (obj, &result);
2287 ucl_object_tostring_forced (const ucl_object_t *obj)
2289 return ucl_copy_value_trash (obj);
2293 ucl_object_tolstring_safe (const ucl_object_t *obj, const char **target, size_t *tlen)
2295 if (obj == NULL || target == NULL) {
2298 switch (obj->type) {
2300 *target = obj->value.sv;
2313 ucl_object_tolstring (const ucl_object_t *obj, size_t *tlen)
2315 const char *result = NULL;
2317 ucl_object_tolstring_safe (obj, &result, tlen);
2322 ucl_object_key (const ucl_object_t *obj)
2324 return ucl_copy_key_trash (obj);
2328 ucl_object_keyl (const ucl_object_t *obj, size_t *len)
2330 if (len == NULL || obj == NULL) {
2338 ucl_object_ref (const ucl_object_t *obj)
2340 ucl_object_t *res = NULL;
2343 if (obj->flags & UCL_OBJECT_EPHEMERAL) {
2345 * Use deep copy for ephemeral objects, note that its refcount
2346 * is NOT increased, since ephemeral objects does not need refcount
2349 res = ucl_object_copy (obj);
2352 res = __DECONST (ucl_object_t *, obj);
2353 #ifdef HAVE_ATOMIC_BUILTINS
2354 (void)__sync_add_and_fetch (&res->ref, 1);
2363 static ucl_object_t *
2364 ucl_object_copy_internal (const ucl_object_t *other, bool allow_array)
2368 ucl_object_iter_t it = NULL;
2369 const ucl_object_t *cur;
2371 new = malloc (sizeof (*new));
2374 memcpy (new, other, sizeof (*new));
2375 if (other->flags & UCL_OBJECT_EPHEMERAL) {
2376 /* Copied object is always non ephemeral */
2377 new->flags &= ~UCL_OBJECT_EPHEMERAL;
2380 /* Unlink from others */
2384 /* deep copy of values stored */
2385 if (other->trash_stack[UCL_TRASH_KEY] != NULL) {
2386 new->trash_stack[UCL_TRASH_KEY] =
2387 strdup (other->trash_stack[UCL_TRASH_KEY]);
2388 if (other->key == (const char *)other->trash_stack[UCL_TRASH_KEY]) {
2389 new->key = new->trash_stack[UCL_TRASH_KEY];
2392 if (other->trash_stack[UCL_TRASH_VALUE] != NULL) {
2393 new->trash_stack[UCL_TRASH_VALUE] =
2394 strdup (other->trash_stack[UCL_TRASH_VALUE]);
2395 if (new->type == UCL_STRING) {
2396 new->value.sv = new->trash_stack[UCL_TRASH_VALUE];
2400 if (other->type == UCL_ARRAY || other->type == UCL_OBJECT) {
2401 /* reset old value */
2402 memset (&new->value, 0, sizeof (new->value));
2404 while ((cur = ucl_iterate_object (other, &it, true)) != NULL) {
2405 if (other->type == UCL_ARRAY) {
2406 ucl_array_append (new, ucl_object_copy_internal (cur, false));
2409 ucl_object_t *cp = ucl_object_copy_internal (cur, true);
2411 ucl_object_insert_key (new, cp, cp->key, cp->keylen,
2417 else if (allow_array && other->next != NULL) {
2418 LL_FOREACH (other->next, cur) {
2419 ucl_object_t *cp = ucl_object_copy_internal (cur, false);
2421 DL_APPEND (new, cp);
2431 ucl_object_copy (const ucl_object_t *other)
2433 return ucl_object_copy_internal (other, true);
2437 ucl_object_unref (ucl_object_t *obj)
2440 #ifdef HAVE_ATOMIC_BUILTINS
2441 unsigned int rc = __sync_sub_and_fetch (&obj->ref, 1);
2444 if (--obj->ref == 0) {
2446 ucl_object_free_internal (obj, true, ucl_object_dtor_unref);
2452 ucl_object_compare (const ucl_object_t *o1, const ucl_object_t *o2)
2454 const ucl_object_t *it1, *it2;
2455 ucl_object_iter_t iter = NULL;
2458 if (o1->type != o2->type) {
2459 return (o1->type) - (o2->type);
2464 if (o1->len == o2->len && o1->len > 0) {
2465 ret = strcmp (ucl_object_tostring(o1), ucl_object_tostring(o2));
2468 ret = o1->len - o2->len;
2474 ret = ucl_object_todouble (o1) - ucl_object_todouble (o2);
2477 ret = ucl_object_toboolean (o1) - ucl_object_toboolean (o2);
2480 if (o1->len == o2->len && o1->len > 0) {
2481 UCL_ARRAY_GET (vec1, o1);
2482 UCL_ARRAY_GET (vec2, o2);
2485 /* Compare all elements in both arrays */
2486 for (i = 0; i < vec1->n; i ++) {
2487 it1 = kv_A (*vec1, i);
2488 it2 = kv_A (*vec2, i);
2490 if (it1 == NULL && it2 != NULL) {
2493 else if (it2 == NULL && it1 != NULL) {
2496 else if (it1 != NULL && it2 != NULL) {
2497 ret = ucl_object_compare (it1, it2);
2505 ret = o1->len - o2->len;
2509 if (o1->len == o2->len && o1->len > 0) {
2510 while ((it1 = ucl_iterate_object (o1, &iter, true)) != NULL) {
2511 it2 = ucl_object_find_key (o2, ucl_object_key (it1));
2516 ret = ucl_object_compare (it1, it2);
2523 ret = o1->len - o2->len;
2535 ucl_object_array_sort (ucl_object_t *ar,
2536 int (*cmp)(const ucl_object_t *o1, const ucl_object_t *o2))
2538 UCL_ARRAY_GET (vec, ar);
2540 if (cmp == NULL || ar == NULL || ar->type != UCL_ARRAY) {
2544 qsort (vec->a, vec->n, sizeof (ucl_object_t *),
2545 (int (*)(const void *, const void *))cmp);
2551 ucl_object_get_priority (const ucl_object_t *obj)
2557 return (obj->flags >> ((sizeof (obj->flags) * NBBY) - PRIOBITS));
2561 ucl_object_set_priority (ucl_object_t *obj,
2562 unsigned int priority)
2565 priority &= (0x1 << PRIOBITS) - 1;
2566 obj->flags |= priority << ((sizeof (obj->flags) * NBBY) - PRIOBITS);