ucl_util.c revision 275223
1/* Copyright (c) 2013, Vsevolod Stakhov 2 * All rights reserved. 3 * 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. 11 * 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. 22 */ 23 24#include "ucl.h" 25#include "ucl_internal.h" 26#include "ucl_chartable.h" 27 28#include <glob.h> 29 30#ifdef HAVE_LIBGEN_H 31#include <libgen.h> /* For dirname */ 32#endif 33 34#ifdef HAVE_OPENSSL 35#include <openssl/err.h> 36#include <openssl/sha.h> 37#include <openssl/rsa.h> 38#include <openssl/ssl.h> 39#include <openssl/evp.h> 40#endif 41 42#ifdef CURL_FOUND 43#include <curl/curl.h> 44#endif 45#ifdef HAVE_FETCH_H 46#include <fetch.h> 47#endif 48 49#ifdef _WIN32 50#include <windows.h> 51 52#ifndef PROT_READ 53#define PROT_READ 1 54#endif 55#ifndef PROT_WRITE 56#define PROT_WRITE 2 57#endif 58#ifndef PROT_READWRITE 59#define PROT_READWRITE 3 60#endif 61#ifndef MAP_SHARED 62#define MAP_SHARED 1 63#endif 64#ifndef MAP_PRIVATE 65#define MAP_PRIVATE 2 66#endif 67#ifndef MAP_FAILED 68#define MAP_FAILED ((void *) -1) 69#endif 70 71static void *ucl_mmap(char *addr, size_t length, int prot, int access, int fd, off_t offset) 72{ 73 void *map = NULL; 74 HANDLE handle = INVALID_HANDLE_VALUE; 75 76 switch (prot) { 77 default: 78 case PROT_READ: 79 { 80 handle = CreateFileMapping((HANDLE) _get_osfhandle(fd), 0, PAGE_READONLY, 0, length, 0); 81 if (!handle) break; 82 map = (void *) MapViewOfFile(handle, FILE_MAP_READ, 0, 0, length); 83 CloseHandle(handle); 84 break; 85 } 86 case PROT_WRITE: 87 { 88 handle = CreateFileMapping((HANDLE) _get_osfhandle(fd), 0, PAGE_READWRITE, 0, length, 0); 89 if (!handle) break; 90 map = (void *) MapViewOfFile(handle, FILE_MAP_WRITE, 0, 0, length); 91 CloseHandle(handle); 92 break; 93 } 94 case PROT_READWRITE: 95 { 96 handle = CreateFileMapping((HANDLE) _get_osfhandle(fd), 0, PAGE_READWRITE, 0, length, 0); 97 if (!handle) break; 98 map = (void *) MapViewOfFile(handle, FILE_MAP_ALL_ACCESS, 0, 0, length); 99 CloseHandle(handle); 100 break; 101 } 102 } 103 if (map == (void *) NULL) { 104 return (void *) MAP_FAILED; 105 } 106 return (void *) ((char *) map + offset); 107} 108 109static int ucl_munmap(void *map,size_t length) 110{ 111 if (!UnmapViewOfFile(map)) { 112 return(-1); 113 } 114 return(0); 115} 116 117static char* ucl_realpath(const char *path, char *resolved_path) { 118 char *p; 119 char tmp[MAX_PATH + 1]; 120 strncpy(tmp, path, sizeof(tmp)-1); 121 p = tmp; 122 while(*p) { 123 if (*p == '/') *p = '\\'; 124 p++; 125 } 126 return _fullpath(resolved_path, tmp, MAX_PATH); 127} 128#else 129#define ucl_mmap mmap 130#define ucl_munmap munmap 131#define ucl_realpath realpath 132#endif 133 134typedef void (*ucl_object_dtor) (ucl_object_t *obj); 135static void ucl_object_free_internal (ucl_object_t *obj, bool allow_rec, 136 ucl_object_dtor dtor); 137static void ucl_object_dtor_unref (ucl_object_t *obj); 138 139static void 140ucl_object_dtor_free (ucl_object_t *obj) 141{ 142 if (obj->trash_stack[UCL_TRASH_KEY] != NULL) { 143 UCL_FREE (obj->hh.keylen, obj->trash_stack[UCL_TRASH_KEY]); 144 } 145 if (obj->trash_stack[UCL_TRASH_VALUE] != NULL) { 146 UCL_FREE (obj->len, obj->trash_stack[UCL_TRASH_VALUE]); 147 } 148 /* Do not free ephemeral objects */ 149 if ((obj->flags & UCL_OBJECT_EPHEMERAL) == 0) { 150 if (obj->type != UCL_USERDATA) { 151 UCL_FREE (sizeof (ucl_object_t), obj); 152 } 153 else { 154 struct ucl_object_userdata *ud = (struct ucl_object_userdata *)obj; 155 if (ud->dtor) { 156 ud->dtor (obj->value.ud); 157 } 158 UCL_FREE (sizeof (*ud), obj); 159 } 160 } 161} 162 163/* 164 * This is a helper function that performs exactly the same as 165 * `ucl_object_unref` but it doesn't iterate over elements allowing 166 * to use it for individual elements of arrays and multiple values 167 */ 168static void 169ucl_object_dtor_unref_single (ucl_object_t *obj) 170{ 171 if (obj != NULL) { 172#ifdef HAVE_ATOMIC_BUILTINS 173 unsigned int rc = __sync_sub_and_fetch (&obj->ref, 1); 174 if (rc == 0) { 175#else 176 if (--obj->ref == 0) { 177#endif 178 ucl_object_free_internal (obj, false, ucl_object_dtor_unref); 179 } 180 } 181} 182 183static void 184ucl_object_dtor_unref (ucl_object_t *obj) 185{ 186 if (obj->ref == 0) { 187 ucl_object_dtor_free (obj); 188 } 189 else { 190 /* This may cause dtor unref being called one more time */ 191 ucl_object_dtor_unref_single (obj); 192 } 193} 194 195static void 196ucl_object_free_internal (ucl_object_t *obj, bool allow_rec, ucl_object_dtor dtor) 197{ 198 ucl_object_t *sub, *tmp; 199 200 while (obj != NULL) { 201 if (obj->type == UCL_ARRAY) { 202 sub = obj->value.av; 203 while (sub != NULL) { 204 tmp = sub->next; 205 dtor (sub); 206 sub = tmp; 207 } 208 } 209 else if (obj->type == UCL_OBJECT) { 210 if (obj->value.ov != NULL) { 211 ucl_hash_destroy (obj->value.ov, (ucl_hash_free_func *)dtor); 212 } 213 } 214 tmp = obj->next; 215 dtor (obj); 216 obj = tmp; 217 218 if (!allow_rec) { 219 break; 220 } 221 } 222} 223 224void 225ucl_object_free (ucl_object_t *obj) 226{ 227 ucl_object_free_internal (obj, true, ucl_object_dtor_free); 228} 229 230size_t 231ucl_unescape_json_string (char *str, size_t len) 232{ 233 char *t = str, *h = str; 234 int i, uval; 235 236 if (len <= 1) { 237 return len; 238 } 239 /* t is target (tortoise), h is source (hare) */ 240 241 while (len) { 242 if (*h == '\\') { 243 h ++; 244 switch (*h) { 245 case 'n': 246 *t++ = '\n'; 247 break; 248 case 'r': 249 *t++ = '\r'; 250 break; 251 case 'b': 252 *t++ = '\b'; 253 break; 254 case 't': 255 *t++ = '\t'; 256 break; 257 case 'f': 258 *t++ = '\f'; 259 break; 260 case '\\': 261 *t++ = '\\'; 262 break; 263 case '"': 264 *t++ = '"'; 265 break; 266 case 'u': 267 /* Unicode escape */ 268 uval = 0; 269 if (len > 3) { 270 for (i = 0; i < 4; i++) { 271 uval <<= 4; 272 if (isdigit (h[i])) { 273 uval += h[i] - '0'; 274 } 275 else if (h[i] >= 'a' && h[i] <= 'f') { 276 uval += h[i] - 'a' + 10; 277 } 278 else if (h[i] >= 'A' && h[i] <= 'F') { 279 uval += h[i] - 'A' + 10; 280 } 281 else { 282 break; 283 } 284 } 285 h += 3; 286 len -= 3; 287 /* Encode */ 288 if(uval < 0x80) { 289 t[0] = (char)uval; 290 t ++; 291 } 292 else if(uval < 0x800) { 293 t[0] = 0xC0 + ((uval & 0x7C0) >> 6); 294 t[1] = 0x80 + ((uval & 0x03F)); 295 t += 2; 296 } 297 else if(uval < 0x10000) { 298 t[0] = 0xE0 + ((uval & 0xF000) >> 12); 299 t[1] = 0x80 + ((uval & 0x0FC0) >> 6); 300 t[2] = 0x80 + ((uval & 0x003F)); 301 t += 3; 302 } 303 else if(uval <= 0x10FFFF) { 304 t[0] = 0xF0 + ((uval & 0x1C0000) >> 18); 305 t[1] = 0x80 + ((uval & 0x03F000) >> 12); 306 t[2] = 0x80 + ((uval & 0x000FC0) >> 6); 307 t[3] = 0x80 + ((uval & 0x00003F)); 308 t += 4; 309 } 310 else { 311 *t++ = '?'; 312 } 313 } 314 else { 315 *t++ = 'u'; 316 } 317 break; 318 default: 319 *t++ = *h; 320 break; 321 } 322 h ++; 323 len --; 324 } 325 else { 326 *t++ = *h++; 327 } 328 len --; 329 } 330 *t = '\0'; 331 332 return (t - str); 333} 334 335char * 336ucl_copy_key_trash (const ucl_object_t *obj) 337{ 338 ucl_object_t *deconst; 339 340 if (obj == NULL) { 341 return NULL; 342 } 343 if (obj->trash_stack[UCL_TRASH_KEY] == NULL && obj->key != NULL) { 344 deconst = __DECONST (ucl_object_t *, obj); 345 deconst->trash_stack[UCL_TRASH_KEY] = malloc (obj->keylen + 1); 346 if (deconst->trash_stack[UCL_TRASH_KEY] != NULL) { 347 memcpy (deconst->trash_stack[UCL_TRASH_KEY], obj->key, obj->keylen); 348 deconst->trash_stack[UCL_TRASH_KEY][obj->keylen] = '\0'; 349 } 350 deconst->key = obj->trash_stack[UCL_TRASH_KEY]; 351 deconst->flags |= UCL_OBJECT_ALLOCATED_KEY; 352 } 353 354 return obj->trash_stack[UCL_TRASH_KEY]; 355} 356 357char * 358ucl_copy_value_trash (const ucl_object_t *obj) 359{ 360 ucl_object_t *deconst; 361 362 if (obj == NULL) { 363 return NULL; 364 } 365 if (obj->trash_stack[UCL_TRASH_VALUE] == NULL) { 366 deconst = __DECONST (ucl_object_t *, obj); 367 if (obj->type == UCL_STRING) { 368 369 /* Special case for strings */ 370 deconst->trash_stack[UCL_TRASH_VALUE] = malloc (obj->len + 1); 371 if (deconst->trash_stack[UCL_TRASH_VALUE] != NULL) { 372 memcpy (deconst->trash_stack[UCL_TRASH_VALUE], obj->value.sv, obj->len); 373 deconst->trash_stack[UCL_TRASH_VALUE][obj->len] = '\0'; 374 deconst->value.sv = obj->trash_stack[UCL_TRASH_VALUE]; 375 } 376 } 377 else { 378 /* Just emit value in json notation */ 379 deconst->trash_stack[UCL_TRASH_VALUE] = ucl_object_emit_single_json (obj); 380 deconst->len = strlen (obj->trash_stack[UCL_TRASH_VALUE]); 381 } 382 deconst->flags |= UCL_OBJECT_ALLOCATED_VALUE; 383 } 384 return obj->trash_stack[UCL_TRASH_VALUE]; 385} 386 387UCL_EXTERN ucl_object_t* 388ucl_parser_get_object (struct ucl_parser *parser) 389{ 390 if (parser->state != UCL_STATE_ERROR && parser->top_obj != NULL) { 391 return ucl_object_ref (parser->top_obj); 392 } 393 394 return NULL; 395} 396 397UCL_EXTERN void 398ucl_parser_free (struct ucl_parser *parser) 399{ 400 struct ucl_stack *stack, *stmp; 401 struct ucl_macro *macro, *mtmp; 402 struct ucl_chunk *chunk, *ctmp; 403 struct ucl_pubkey *key, *ktmp; 404 struct ucl_variable *var, *vtmp; 405 406 if (parser == NULL) { 407 return; 408 } 409 410 if (parser->top_obj != NULL) { 411 ucl_object_unref (parser->top_obj); 412 } 413 414 LL_FOREACH_SAFE (parser->stack, stack, stmp) { 415 free (stack); 416 } 417 HASH_ITER (hh, parser->macroes, macro, mtmp) { 418 free (macro->name); 419 HASH_DEL (parser->macroes, macro); 420 UCL_FREE (sizeof (struct ucl_macro), macro); 421 } 422 LL_FOREACH_SAFE (parser->chunks, chunk, ctmp) { 423 UCL_FREE (sizeof (struct ucl_chunk), chunk); 424 } 425 LL_FOREACH_SAFE (parser->keys, key, ktmp) { 426 UCL_FREE (sizeof (struct ucl_pubkey), key); 427 } 428 LL_FOREACH_SAFE (parser->variables, var, vtmp) { 429 free (var->value); 430 free (var->var); 431 UCL_FREE (sizeof (struct ucl_variable), var); 432 } 433 434 if (parser->err != NULL) { 435 utstring_free (parser->err); 436 } 437 438 if (parser->cur_file) { 439 free (parser->cur_file); 440 } 441 442 UCL_FREE (sizeof (struct ucl_parser), parser); 443} 444 445UCL_EXTERN const char * 446ucl_parser_get_error(struct ucl_parser *parser) 447{ 448 if (parser == NULL) { 449 return NULL; 450 } 451 452 if (parser->err == NULL) 453 return NULL; 454 455 return utstring_body(parser->err); 456} 457 458UCL_EXTERN bool 459ucl_pubkey_add (struct ucl_parser *parser, const unsigned char *key, size_t len) 460{ 461#ifndef HAVE_OPENSSL 462 ucl_create_err (&parser->err, "cannot check signatures without openssl"); 463 return false; 464#else 465# if (OPENSSL_VERSION_NUMBER < 0x10000000L) 466 ucl_create_err (&parser->err, "cannot check signatures, openssl version is unsupported"); 467 return EXIT_FAILURE; 468# else 469 struct ucl_pubkey *nkey; 470 BIO *mem; 471 472 mem = BIO_new_mem_buf ((void *)key, len); 473 nkey = UCL_ALLOC (sizeof (struct ucl_pubkey)); 474 if (nkey == NULL) { 475 ucl_create_err (&parser->err, "cannot allocate memory for key"); 476 return false; 477 } 478 nkey->key = PEM_read_bio_PUBKEY (mem, &nkey->key, NULL, NULL); 479 BIO_free (mem); 480 if (nkey->key == NULL) { 481 UCL_FREE (sizeof (struct ucl_pubkey), nkey); 482 ucl_create_err (&parser->err, "%s", 483 ERR_error_string (ERR_get_error (), NULL)); 484 return false; 485 } 486 LL_PREPEND (parser->keys, nkey); 487# endif 488#endif 489 return true; 490} 491 492#ifdef CURL_FOUND 493struct ucl_curl_cbdata { 494 unsigned char *buf; 495 size_t buflen; 496}; 497 498static size_t 499ucl_curl_write_callback (void* contents, size_t size, size_t nmemb, void* ud) 500{ 501 struct ucl_curl_cbdata *cbdata = ud; 502 size_t realsize = size * nmemb; 503 504 cbdata->buf = realloc (cbdata->buf, cbdata->buflen + realsize + 1); 505 if (cbdata->buf == NULL) { 506 return 0; 507 } 508 509 memcpy (&(cbdata->buf[cbdata->buflen]), contents, realsize); 510 cbdata->buflen += realsize; 511 cbdata->buf[cbdata->buflen] = 0; 512 513 return realsize; 514} 515#endif 516 517/** 518 * Fetch a url and save results to the memory buffer 519 * @param url url to fetch 520 * @param len length of url 521 * @param buf target buffer 522 * @param buflen target length 523 * @return 524 */ 525static bool 526ucl_fetch_url (const unsigned char *url, unsigned char **buf, size_t *buflen, 527 UT_string **err, bool must_exist) 528{ 529 530#ifdef HAVE_FETCH_H 531 struct url *fetch_url; 532 struct url_stat us; 533 FILE *in; 534 535 fetch_url = fetchParseURL (url); 536 if (fetch_url == NULL) { 537 ucl_create_err (err, "invalid URL %s: %s", 538 url, strerror (errno)); 539 return false; 540 } 541 if ((in = fetchXGet (fetch_url, &us, "")) == NULL) { 542 if (!must_exist) { 543 ucl_create_err (err, "cannot fetch URL %s: %s", 544 url, strerror (errno)); 545 } 546 fetchFreeURL (fetch_url); 547 return false; 548 } 549 550 *buflen = us.size; 551 *buf = malloc (*buflen); 552 if (*buf == NULL) { 553 ucl_create_err (err, "cannot allocate buffer for URL %s: %s", 554 url, strerror (errno)); 555 fclose (in); 556 fetchFreeURL (fetch_url); 557 return false; 558 } 559 560 if (fread (*buf, *buflen, 1, in) != 1) { 561 ucl_create_err (err, "cannot read URL %s: %s", 562 url, strerror (errno)); 563 fclose (in); 564 fetchFreeURL (fetch_url); 565 return false; 566 } 567 568 fetchFreeURL (fetch_url); 569 return true; 570#elif defined(CURL_FOUND) 571 CURL *curl; 572 int r; 573 struct ucl_curl_cbdata cbdata; 574 575 curl = curl_easy_init (); 576 if (curl == NULL) { 577 ucl_create_err (err, "CURL interface is broken"); 578 return false; 579 } 580 if ((r = curl_easy_setopt (curl, CURLOPT_URL, url)) != CURLE_OK) { 581 ucl_create_err (err, "invalid URL %s: %s", 582 url, curl_easy_strerror (r)); 583 curl_easy_cleanup (curl); 584 return false; 585 } 586 curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, ucl_curl_write_callback); 587 cbdata.buf = *buf; 588 cbdata.buflen = *buflen; 589 curl_easy_setopt (curl, CURLOPT_WRITEDATA, &cbdata); 590 591 if ((r = curl_easy_perform (curl)) != CURLE_OK) { 592 if (!must_exist) { 593 ucl_create_err (err, "error fetching URL %s: %s", 594 url, curl_easy_strerror (r)); 595 } 596 curl_easy_cleanup (curl); 597 if (cbdata.buf) { 598 free (cbdata.buf); 599 } 600 return false; 601 } 602 *buf = cbdata.buf; 603 *buflen = cbdata.buflen; 604 605 return true; 606#else 607 ucl_create_err (err, "URL support is disabled"); 608 return false; 609#endif 610} 611 612/** 613 * Fetch a file and save results to the memory buffer 614 * @param filename filename to fetch 615 * @param len length of filename 616 * @param buf target buffer 617 * @param buflen target length 618 * @return 619 */ 620static bool 621ucl_fetch_file (const unsigned char *filename, unsigned char **buf, size_t *buflen, 622 UT_string **err, bool must_exist) 623{ 624 int fd; 625 struct stat st; 626 627 if (stat (filename, &st) == -1 || !S_ISREG (st.st_mode)) { 628 if (must_exist) { 629 ucl_create_err (err, "cannot stat file %s: %s", 630 filename, strerror (errno)); 631 } 632 return false; 633 } 634 if (st.st_size == 0) { 635 /* Do not map empty files */ 636 *buf = ""; 637 *buflen = 0; 638 } 639 else { 640 if ((fd = open (filename, O_RDONLY)) == -1) { 641 ucl_create_err (err, "cannot open file %s: %s", 642 filename, strerror (errno)); 643 return false; 644 } 645 if ((*buf = ucl_mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) { 646 close (fd); 647 ucl_create_err (err, "cannot mmap file %s: %s", 648 filename, strerror (errno)); 649 return false; 650 } 651 *buflen = st.st_size; 652 close (fd); 653 } 654 655 return true; 656} 657 658 659#if (defined(HAVE_OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x10000000L) 660static inline bool 661ucl_sig_check (const unsigned char *data, size_t datalen, 662 const unsigned char *sig, size_t siglen, struct ucl_parser *parser) 663{ 664 struct ucl_pubkey *key; 665 char dig[EVP_MAX_MD_SIZE]; 666 unsigned int diglen; 667 EVP_PKEY_CTX *key_ctx; 668 EVP_MD_CTX *sign_ctx = NULL; 669 670 sign_ctx = EVP_MD_CTX_create (); 671 672 LL_FOREACH (parser->keys, key) { 673 key_ctx = EVP_PKEY_CTX_new (key->key, NULL); 674 if (key_ctx != NULL) { 675 if (EVP_PKEY_verify_init (key_ctx) <= 0) { 676 EVP_PKEY_CTX_free (key_ctx); 677 continue; 678 } 679 if (EVP_PKEY_CTX_set_rsa_padding (key_ctx, RSA_PKCS1_PADDING) <= 0) { 680 EVP_PKEY_CTX_free (key_ctx); 681 continue; 682 } 683 if (EVP_PKEY_CTX_set_signature_md (key_ctx, EVP_sha256 ()) <= 0) { 684 EVP_PKEY_CTX_free (key_ctx); 685 continue; 686 } 687 EVP_DigestInit (sign_ctx, EVP_sha256 ()); 688 EVP_DigestUpdate (sign_ctx, data, datalen); 689 EVP_DigestFinal (sign_ctx, dig, &diglen); 690 691 if (EVP_PKEY_verify (key_ctx, sig, siglen, dig, diglen) == 1) { 692 EVP_MD_CTX_destroy (sign_ctx); 693 EVP_PKEY_CTX_free (key_ctx); 694 return true; 695 } 696 697 EVP_PKEY_CTX_free (key_ctx); 698 } 699 } 700 701 EVP_MD_CTX_destroy (sign_ctx); 702 703 return false; 704} 705#endif 706 707/** 708 * Include an url to configuration 709 * @param data 710 * @param len 711 * @param parser 712 * @param err 713 * @return 714 */ 715static bool 716ucl_include_url (const unsigned char *data, size_t len, 717 struct ucl_parser *parser, bool check_signature, bool must_exist, 718 unsigned priority) 719{ 720 721 bool res; 722 unsigned char *buf = NULL; 723 size_t buflen = 0; 724 struct ucl_chunk *chunk; 725 char urlbuf[PATH_MAX]; 726 int prev_state; 727 728 snprintf (urlbuf, sizeof (urlbuf), "%.*s", (int)len, data); 729 730 if (!ucl_fetch_url (urlbuf, &buf, &buflen, &parser->err, must_exist)) { 731 return (!must_exist || false); 732 } 733 734 if (check_signature) { 735#if (defined(HAVE_OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x10000000L) 736 unsigned char *sigbuf = NULL; 737 size_t siglen = 0; 738 /* We need to check signature first */ 739 snprintf (urlbuf, sizeof (urlbuf), "%.*s.sig", (int)len, data); 740 if (!ucl_fetch_url (urlbuf, &sigbuf, &siglen, &parser->err, true)) { 741 return false; 742 } 743 if (!ucl_sig_check (buf, buflen, sigbuf, siglen, parser)) { 744 ucl_create_err (&parser->err, "cannot verify url %s: %s", 745 urlbuf, 746 ERR_error_string (ERR_get_error (), NULL)); 747 if (siglen > 0) { 748 ucl_munmap (sigbuf, siglen); 749 } 750 return false; 751 } 752 if (siglen > 0) { 753 ucl_munmap (sigbuf, siglen); 754 } 755#endif 756 } 757 758 prev_state = parser->state; 759 parser->state = UCL_STATE_INIT; 760 761 res = ucl_parser_add_chunk_priority (parser, buf, buflen, priority); 762 if (res == true) { 763 /* Remove chunk from the stack */ 764 chunk = parser->chunks; 765 if (chunk != NULL) { 766 parser->chunks = chunk->next; 767 UCL_FREE (sizeof (struct ucl_chunk), chunk); 768 } 769 } 770 771 parser->state = prev_state; 772 free (buf); 773 774 return res; 775} 776 777/** 778 * Include a single file to the parser 779 * @param data 780 * @param len 781 * @param parser 782 * @param check_signature 783 * @param must_exist 784 * @param allow_glob 785 * @param priority 786 * @return 787 */ 788static bool 789ucl_include_file_single (const unsigned char *data, size_t len, 790 struct ucl_parser *parser, bool check_signature, bool must_exist, 791 unsigned priority) 792{ 793 bool res; 794 struct ucl_chunk *chunk; 795 unsigned char *buf = NULL; 796 char *old_curfile; 797 size_t buflen; 798 char filebuf[PATH_MAX], realbuf[PATH_MAX]; 799 int prev_state; 800 struct ucl_variable *cur_var, *tmp_var, *old_curdir = NULL, 801 *old_filename = NULL; 802 803 snprintf (filebuf, sizeof (filebuf), "%.*s", (int)len, data); 804 if (ucl_realpath (filebuf, realbuf) == NULL) { 805 if (!must_exist) { 806 return true; 807 } 808 ucl_create_err (&parser->err, "cannot open file %s: %s", 809 filebuf, 810 strerror (errno)); 811 return false; 812 } 813 814 if (parser->cur_file && strcmp (realbuf, parser->cur_file) == 0) { 815 /* We are likely including the file itself */ 816 ucl_create_err (&parser->err, "trying to include the file %s from itself", 817 realbuf); 818 return false; 819 } 820 821 if (!ucl_fetch_file (realbuf, &buf, &buflen, &parser->err, must_exist)) { 822 return (!must_exist || false); 823 } 824 825 if (check_signature) { 826#if (defined(HAVE_OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x10000000L) 827 unsigned char *sigbuf = NULL; 828 size_t siglen = 0; 829 /* We need to check signature first */ 830 snprintf (filebuf, sizeof (filebuf), "%s.sig", realbuf); 831 if (!ucl_fetch_file (filebuf, &sigbuf, &siglen, &parser->err, true)) { 832 return false; 833 } 834 if (!ucl_sig_check (buf, buflen, sigbuf, siglen, parser)) { 835 ucl_create_err (&parser->err, "cannot verify file %s: %s", 836 filebuf, 837 ERR_error_string (ERR_get_error (), NULL)); 838 if (siglen > 0) { 839 ucl_munmap (sigbuf, siglen); 840 } 841 return false; 842 } 843 if (siglen > 0) { 844 ucl_munmap (sigbuf, siglen); 845 } 846#endif 847 } 848 849 old_curfile = parser->cur_file; 850 parser->cur_file = strdup (realbuf); 851 852 /* Store old file vars */ 853 DL_FOREACH_SAFE (parser->variables, cur_var, tmp_var) { 854 if (strcmp (cur_var->var, "CURDIR") == 0) { 855 old_curdir = cur_var; 856 DL_DELETE (parser->variables, cur_var); 857 } 858 else if (strcmp (cur_var->var, "FILENAME") == 0) { 859 old_filename = cur_var; 860 DL_DELETE (parser->variables, cur_var); 861 } 862 } 863 864 ucl_parser_set_filevars (parser, realbuf, false); 865 866 prev_state = parser->state; 867 parser->state = UCL_STATE_INIT; 868 869 res = ucl_parser_add_chunk_priority (parser, buf, buflen, priority); 870 if (!res && !must_exist) { 871 /* Free error */ 872 utstring_free (parser->err); 873 parser->err = NULL; 874 parser->state = UCL_STATE_AFTER_VALUE; 875 } 876 877 /* Remove chunk from the stack */ 878 chunk = parser->chunks; 879 if (chunk != NULL) { 880 parser->chunks = chunk->next; 881 UCL_FREE (sizeof (struct ucl_chunk), chunk); 882 parser->recursion --; 883 } 884 885 /* Restore old file vars */ 886 parser->cur_file = old_curfile; 887 DL_FOREACH_SAFE (parser->variables, cur_var, tmp_var) { 888 if (strcmp (cur_var->var, "CURDIR") == 0 && old_curdir) { 889 DL_DELETE (parser->variables, cur_var); 890 free (cur_var->var); 891 free (cur_var->value); 892 UCL_FREE (sizeof (struct ucl_variable), cur_var); 893 } 894 else if (strcmp (cur_var->var, "FILENAME") == 0 && old_filename) { 895 DL_DELETE (parser->variables, cur_var); 896 free (cur_var->var); 897 free (cur_var->value); 898 UCL_FREE (sizeof (struct ucl_variable), cur_var); 899 } 900 } 901 if (old_filename) { 902 DL_APPEND (parser->variables, old_filename); 903 } 904 if (old_curdir) { 905 DL_APPEND (parser->variables, old_curdir); 906 } 907 if (old_curfile) { 908 free (old_curfile); 909 } 910 911 parser->state = prev_state; 912 913 if (buflen > 0) { 914 ucl_munmap (buf, buflen); 915 } 916 917 return res; 918} 919 920/** 921 * Include a file to configuration 922 * @param data 923 * @param len 924 * @param parser 925 * @param err 926 * @return 927 */ 928static bool 929ucl_include_file (const unsigned char *data, size_t len, 930 struct ucl_parser *parser, bool check_signature, bool must_exist, 931 bool allow_glob, unsigned priority) 932{ 933 const unsigned char *p = data, *end = data + len; 934 bool need_glob = false; 935 int cnt = 0; 936 glob_t globbuf; 937 char glob_pattern[PATH_MAX]; 938 size_t i; 939 940 if (!allow_glob) { 941 return ucl_include_file_single (data, len, parser, check_signature, 942 must_exist, priority); 943 } 944 else { 945 /* Check for special symbols in a filename */ 946 while (p != end) { 947 if (*p == '*' || *p == '?') { 948 need_glob = true; 949 break; 950 } 951 p ++; 952 } 953 if (need_glob) { 954 memset (&globbuf, 0, sizeof (globbuf)); 955 ucl_strlcpy (glob_pattern, (const char *)data, sizeof (glob_pattern)); 956 if (glob (glob_pattern, 0, NULL, &globbuf) != 0) { 957 return (!must_exist || false); 958 } 959 for (i = 0; i < globbuf.gl_pathc; i ++) { 960 if (!ucl_include_file_single ((unsigned char *)globbuf.gl_pathv[i], 961 strlen (globbuf.gl_pathv[i]), parser, check_signature, 962 must_exist, priority)) { 963 globfree (&globbuf); 964 return false; 965 } 966 cnt ++; 967 } 968 globfree (&globbuf); 969 970 if (cnt == 0 && must_exist) { 971 ucl_create_err (&parser->err, "cannot match any files for pattern %s", 972 glob_pattern); 973 return false; 974 } 975 } 976 else { 977 return ucl_include_file_single (data, len, parser, check_signature, 978 must_exist, priority); 979 } 980 } 981 982 return true; 983} 984 985/** 986 * Common function to handle .*include* macros 987 * @param data 988 * @param len 989 * @param args 990 * @param parser 991 * @param default_try 992 * @param default_sign 993 * @return 994 */ 995static bool 996ucl_include_common (const unsigned char *data, size_t len, 997 const ucl_object_t *args, struct ucl_parser *parser, 998 bool default_try, 999 bool default_sign) 1000{ 1001 bool try_load, allow_glob, allow_url, need_sign; 1002 unsigned priority; 1003 const ucl_object_t *param; 1004 ucl_object_iter_t it = NULL; 1005 1006 /* Default values */ 1007 try_load = default_try; 1008 allow_glob = false; 1009 allow_url = true; 1010 need_sign = default_sign; 1011 priority = 0; 1012 1013 /* Process arguments */ 1014 if (args != NULL && args->type == UCL_OBJECT) { 1015 while ((param = ucl_iterate_object (args, &it, true)) != NULL) { 1016 if (param->type == UCL_BOOLEAN) { 1017 if (strcmp (param->key, "try") == 0) { 1018 try_load = ucl_object_toboolean (param); 1019 } 1020 else if (strcmp (param->key, "sign") == 0) { 1021 need_sign = ucl_object_toboolean (param); 1022 } 1023 else if (strcmp (param->key, "glob") == 0) { 1024 allow_glob = ucl_object_toboolean (param); 1025 } 1026 else if (strcmp (param->key, "url") == 0) { 1027 allow_url = ucl_object_toboolean (param); 1028 } 1029 } 1030 else if (param->type == UCL_INT) { 1031 if (strcmp (param->key, "priority") == 0) { 1032 priority = ucl_object_toint (param); 1033 } 1034 } 1035 } 1036 } 1037 1038 if (*data == '/' || *data == '.') { 1039 /* Try to load a file */ 1040 return ucl_include_file (data, len, parser, need_sign, !try_load, 1041 allow_glob, priority); 1042 } 1043 else if (allow_url) { 1044 /* Globbing is not used for URL's */ 1045 return ucl_include_url (data, len, parser, need_sign, !try_load, 1046 priority); 1047 } 1048 1049 return false; 1050} 1051 1052/** 1053 * Handle include macro 1054 * @param data include data 1055 * @param len length of data 1056 * @param ud user data 1057 * @param err error ptr 1058 * @return 1059 */ 1060UCL_EXTERN bool 1061ucl_include_handler (const unsigned char *data, size_t len, 1062 const ucl_object_t *args, void* ud) 1063{ 1064 struct ucl_parser *parser = ud; 1065 1066 return ucl_include_common (data, len, args, parser, false, false); 1067} 1068 1069/** 1070 * Handle includes macro 1071 * @param data include data 1072 * @param len length of data 1073 * @param ud user data 1074 * @param err error ptr 1075 * @return 1076 */ 1077UCL_EXTERN bool 1078ucl_includes_handler (const unsigned char *data, size_t len, 1079 const ucl_object_t *args, void* ud) 1080{ 1081 struct ucl_parser *parser = ud; 1082 1083 return ucl_include_common (data, len, args, parser, false, true); 1084} 1085 1086 1087UCL_EXTERN bool 1088ucl_try_include_handler (const unsigned char *data, size_t len, 1089 const ucl_object_t *args, void* ud) 1090{ 1091 struct ucl_parser *parser = ud; 1092 1093 return ucl_include_common (data, len, args, parser, true, false); 1094} 1095 1096UCL_EXTERN bool 1097ucl_parser_set_filevars (struct ucl_parser *parser, const char *filename, bool need_expand) 1098{ 1099 char realbuf[PATH_MAX], *curdir; 1100 1101 if (filename != NULL) { 1102 if (need_expand) { 1103 if (ucl_realpath (filename, realbuf) == NULL) { 1104 return false; 1105 } 1106 } 1107 else { 1108 ucl_strlcpy (realbuf, filename, sizeof (realbuf)); 1109 } 1110 1111 /* Define variables */ 1112 ucl_parser_register_variable (parser, "FILENAME", realbuf); 1113 curdir = dirname (realbuf); 1114 ucl_parser_register_variable (parser, "CURDIR", curdir); 1115 } 1116 else { 1117 /* Set everything from the current dir */ 1118 curdir = getcwd (realbuf, sizeof (realbuf)); 1119 ucl_parser_register_variable (parser, "FILENAME", "undef"); 1120 ucl_parser_register_variable (parser, "CURDIR", curdir); 1121 } 1122 1123 return true; 1124} 1125 1126UCL_EXTERN bool 1127ucl_parser_add_file (struct ucl_parser *parser, const char *filename) 1128{ 1129 unsigned char *buf; 1130 size_t len; 1131 bool ret; 1132 char realbuf[PATH_MAX]; 1133 1134 if (ucl_realpath (filename, realbuf) == NULL) { 1135 ucl_create_err (&parser->err, "cannot open file %s: %s", 1136 filename, 1137 strerror (errno)); 1138 return false; 1139 } 1140 1141 if (!ucl_fetch_file (realbuf, &buf, &len, &parser->err, true)) { 1142 return false; 1143 } 1144 1145 if (parser->cur_file) { 1146 free (parser->cur_file); 1147 } 1148 parser->cur_file = strdup (realbuf); 1149 ucl_parser_set_filevars (parser, realbuf, false); 1150 ret = ucl_parser_add_chunk (parser, buf, len); 1151 1152 if (len > 0) { 1153 ucl_munmap (buf, len); 1154 } 1155 1156 return ret; 1157} 1158 1159UCL_EXTERN bool 1160ucl_parser_add_fd (struct ucl_parser *parser, int fd) 1161{ 1162 unsigned char *buf; 1163 size_t len; 1164 bool ret; 1165 struct stat st; 1166 1167 if (fstat (fd, &st) == -1) { 1168 ucl_create_err (&parser->err, "cannot stat fd %d: %s", 1169 fd, strerror (errno)); 1170 return false; 1171 } 1172 if ((buf = ucl_mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) { 1173 ucl_create_err (&parser->err, "cannot mmap fd %d: %s", 1174 fd, strerror (errno)); 1175 return false; 1176 } 1177 1178 if (parser->cur_file) { 1179 free (parser->cur_file); 1180 } 1181 parser->cur_file = NULL; 1182 len = st.st_size; 1183 ret = ucl_parser_add_chunk (parser, buf, len); 1184 1185 if (len > 0) { 1186 ucl_munmap (buf, len); 1187 } 1188 1189 return ret; 1190} 1191 1192size_t 1193ucl_strlcpy (char *dst, const char *src, size_t siz) 1194{ 1195 char *d = dst; 1196 const char *s = src; 1197 size_t n = siz; 1198 1199 /* Copy as many bytes as will fit */ 1200 if (n != 0) { 1201 while (--n != 0) { 1202 if ((*d++ = *s++) == '\0') { 1203 break; 1204 } 1205 } 1206 } 1207 1208 if (n == 0 && siz != 0) { 1209 *d = '\0'; 1210 } 1211 1212 return (s - src - 1); /* count does not include NUL */ 1213} 1214 1215size_t 1216ucl_strlcpy_unsafe (char *dst, const char *src, size_t siz) 1217{ 1218 memcpy (dst, src, siz - 1); 1219 dst[siz - 1] = '\0'; 1220 1221 return siz - 1; 1222} 1223 1224size_t 1225ucl_strlcpy_tolower (char *dst, const char *src, size_t siz) 1226{ 1227 char *d = dst; 1228 const char *s = src; 1229 size_t n = siz; 1230 1231 /* Copy as many bytes as will fit */ 1232 if (n != 0) { 1233 while (--n != 0) { 1234 if ((*d++ = tolower (*s++)) == '\0') { 1235 break; 1236 } 1237 } 1238 } 1239 1240 if (n == 0 && siz != 0) { 1241 *d = '\0'; 1242 } 1243 1244 return (s - src); /* count does not include NUL */ 1245} 1246 1247ucl_object_t * 1248ucl_object_fromstring_common (const char *str, size_t len, enum ucl_string_flags flags) 1249{ 1250 ucl_object_t *obj; 1251 const char *start, *end, *p, *pos; 1252 char *dst, *d; 1253 size_t escaped_len; 1254 1255 if (str == NULL) { 1256 return NULL; 1257 } 1258 1259 obj = ucl_object_new (); 1260 if (obj) { 1261 if (len == 0) { 1262 len = strlen (str); 1263 } 1264 if (flags & UCL_STRING_TRIM) { 1265 /* Skip leading spaces */ 1266 for (start = str; (size_t)(start - str) < len; start ++) { 1267 if (!ucl_test_character (*start, UCL_CHARACTER_WHITESPACE_UNSAFE)) { 1268 break; 1269 } 1270 } 1271 /* Skip trailing spaces */ 1272 for (end = str + len - 1; end > start; end --) { 1273 if (!ucl_test_character (*end, UCL_CHARACTER_WHITESPACE_UNSAFE)) { 1274 break; 1275 } 1276 } 1277 end ++; 1278 } 1279 else { 1280 start = str; 1281 end = str + len; 1282 } 1283 1284 obj->type = UCL_STRING; 1285 if (flags & UCL_STRING_ESCAPE) { 1286 for (p = start, escaped_len = 0; p < end; p ++, escaped_len ++) { 1287 if (ucl_test_character (*p, UCL_CHARACTER_JSON_UNSAFE)) { 1288 escaped_len ++; 1289 } 1290 } 1291 dst = malloc (escaped_len + 1); 1292 if (dst != NULL) { 1293 for (p = start, d = dst; p < end; p ++, d ++) { 1294 if (ucl_test_character (*p, UCL_CHARACTER_JSON_UNSAFE)) { 1295 switch (*p) { 1296 case '\n': 1297 *d++ = '\\'; 1298 *d = 'n'; 1299 break; 1300 case '\r': 1301 *d++ = '\\'; 1302 *d = 'r'; 1303 break; 1304 case '\b': 1305 *d++ = '\\'; 1306 *d = 'b'; 1307 break; 1308 case '\t': 1309 *d++ = '\\'; 1310 *d = 't'; 1311 break; 1312 case '\f': 1313 *d++ = '\\'; 1314 *d = 'f'; 1315 break; 1316 case '\\': 1317 *d++ = '\\'; 1318 *d = '\\'; 1319 break; 1320 case '"': 1321 *d++ = '\\'; 1322 *d = '"'; 1323 break; 1324 } 1325 } 1326 else { 1327 *d = *p; 1328 } 1329 } 1330 *d = '\0'; 1331 obj->value.sv = dst; 1332 obj->trash_stack[UCL_TRASH_VALUE] = dst; 1333 obj->len = escaped_len; 1334 } 1335 } 1336 else { 1337 dst = malloc (end - start + 1); 1338 if (dst != NULL) { 1339 ucl_strlcpy_unsafe (dst, start, end - start + 1); 1340 obj->value.sv = dst; 1341 obj->trash_stack[UCL_TRASH_VALUE] = dst; 1342 obj->len = end - start; 1343 } 1344 } 1345 if ((flags & UCL_STRING_PARSE) && dst != NULL) { 1346 /* Parse what we have */ 1347 if (flags & UCL_STRING_PARSE_BOOLEAN) { 1348 if (!ucl_maybe_parse_boolean (obj, dst, obj->len) && (flags & UCL_STRING_PARSE_NUMBER)) { 1349 ucl_maybe_parse_number (obj, dst, dst + obj->len, &pos, 1350 flags & UCL_STRING_PARSE_DOUBLE, 1351 flags & UCL_STRING_PARSE_BYTES, 1352 flags & UCL_STRING_PARSE_TIME); 1353 } 1354 } 1355 else { 1356 ucl_maybe_parse_number (obj, dst, dst + obj->len, &pos, 1357 flags & UCL_STRING_PARSE_DOUBLE, 1358 flags & UCL_STRING_PARSE_BYTES, 1359 flags & UCL_STRING_PARSE_TIME); 1360 } 1361 } 1362 } 1363 1364 return obj; 1365} 1366 1367static bool 1368ucl_object_insert_key_common (ucl_object_t *top, ucl_object_t *elt, 1369 const char *key, size_t keylen, bool copy_key, bool merge, bool replace) 1370{ 1371 ucl_object_t *found, *tmp; 1372 const ucl_object_t *cur; 1373 ucl_object_iter_t it = NULL; 1374 const char *p; 1375 int ret = true; 1376 1377 if (elt == NULL || key == NULL) { 1378 return false; 1379 } 1380 1381 if (top == NULL) { 1382 return false; 1383 } 1384 1385 if (top->type != UCL_OBJECT) { 1386 /* It is possible to convert NULL type to an object */ 1387 if (top->type == UCL_NULL) { 1388 top->type = UCL_OBJECT; 1389 } 1390 else { 1391 /* Refuse converting of other object types */ 1392 return false; 1393 } 1394 } 1395 1396 if (top->value.ov == NULL) { 1397 top->value.ov = ucl_hash_create (); 1398 } 1399 1400 if (keylen == 0) { 1401 keylen = strlen (key); 1402 } 1403 1404 for (p = key; p < key + keylen; p ++) { 1405 if (ucl_test_character (*p, UCL_CHARACTER_UCL_UNSAFE)) { 1406 elt->flags |= UCL_OBJECT_NEED_KEY_ESCAPE; 1407 break; 1408 } 1409 } 1410 1411 /* workaround for some use cases */ 1412 if (elt->trash_stack[UCL_TRASH_KEY] != NULL && 1413 key != (const char *)elt->trash_stack[UCL_TRASH_KEY]) { 1414 /* Remove copied key */ 1415 free (elt->trash_stack[UCL_TRASH_KEY]); 1416 elt->trash_stack[UCL_TRASH_KEY] = NULL; 1417 elt->flags &= ~UCL_OBJECT_ALLOCATED_KEY; 1418 } 1419 1420 elt->key = key; 1421 elt->keylen = keylen; 1422 1423 if (copy_key) { 1424 ucl_copy_key_trash (elt); 1425 } 1426 1427 found = __DECONST (ucl_object_t *, ucl_hash_search_obj (top->value.ov, elt)); 1428 1429 if (found == NULL) { 1430 top->value.ov = ucl_hash_insert_object (top->value.ov, elt); 1431 top->len ++; 1432 if (replace) { 1433 ret = false; 1434 } 1435 } 1436 else { 1437 if (replace) { 1438 ucl_hash_replace (top->value.ov, found, elt); 1439 ucl_object_unref (found); 1440 } 1441 else if (merge) { 1442 if (found->type != UCL_OBJECT && elt->type == UCL_OBJECT) { 1443 /* Insert old elt to new one */ 1444 ucl_object_insert_key_common (elt, found, found->key, 1445 found->keylen, copy_key, false, false); 1446 ucl_hash_delete (top->value.ov, found); 1447 top->value.ov = ucl_hash_insert_object (top->value.ov, elt); 1448 } 1449 else if (found->type == UCL_OBJECT && elt->type != UCL_OBJECT) { 1450 /* Insert new to old */ 1451 ucl_object_insert_key_common (found, elt, elt->key, 1452 elt->keylen, copy_key, false, false); 1453 } 1454 else if (found->type == UCL_OBJECT && elt->type == UCL_OBJECT) { 1455 /* Mix two hashes */ 1456 while ((cur = ucl_iterate_object (elt, &it, true)) != NULL) { 1457 tmp = ucl_object_ref (cur); 1458 ucl_object_insert_key_common (found, tmp, cur->key, 1459 cur->keylen, copy_key, false, false); 1460 } 1461 ucl_object_unref (elt); 1462 } 1463 else { 1464 /* Just make a list of scalars */ 1465 DL_APPEND (found, elt); 1466 } 1467 } 1468 else { 1469 DL_APPEND (found, elt); 1470 } 1471 } 1472 1473 return ret; 1474} 1475 1476bool 1477ucl_object_delete_keyl (ucl_object_t *top, const char *key, size_t keylen) 1478{ 1479 ucl_object_t *found; 1480 1481 if (top == NULL || key == NULL) { 1482 return false; 1483 } 1484 1485 found = __DECONST (ucl_object_t *, ucl_object_find_keyl (top, key, keylen)); 1486 1487 if (found == NULL) { 1488 return false; 1489 } 1490 1491 ucl_hash_delete (top->value.ov, found); 1492 ucl_object_unref (found); 1493 top->len --; 1494 1495 return true; 1496} 1497 1498bool 1499ucl_object_delete_key (ucl_object_t *top, const char *key) 1500{ 1501 return ucl_object_delete_keyl (top, key, strlen(key)); 1502} 1503 1504ucl_object_t* 1505ucl_object_pop_keyl (ucl_object_t *top, const char *key, size_t keylen) 1506{ 1507 const ucl_object_t *found; 1508 1509 if (top == NULL || key == NULL) { 1510 return false; 1511 } 1512 found = ucl_object_find_keyl (top, key, keylen); 1513 1514 if (found == NULL) { 1515 return NULL; 1516 } 1517 ucl_hash_delete (top->value.ov, found); 1518 top->len --; 1519 1520 return __DECONST (ucl_object_t *, found); 1521} 1522 1523ucl_object_t* 1524ucl_object_pop_key (ucl_object_t *top, const char *key) 1525{ 1526 return ucl_object_pop_keyl (top, key, strlen(key)); 1527} 1528 1529bool 1530ucl_object_insert_key (ucl_object_t *top, ucl_object_t *elt, 1531 const char *key, size_t keylen, bool copy_key) 1532{ 1533 return ucl_object_insert_key_common (top, elt, key, keylen, copy_key, false, false); 1534} 1535 1536bool 1537ucl_object_insert_key_merged (ucl_object_t *top, ucl_object_t *elt, 1538 const char *key, size_t keylen, bool copy_key) 1539{ 1540 return ucl_object_insert_key_common (top, elt, key, keylen, copy_key, true, false); 1541} 1542 1543bool 1544ucl_object_replace_key (ucl_object_t *top, ucl_object_t *elt, 1545 const char *key, size_t keylen, bool copy_key) 1546{ 1547 return ucl_object_insert_key_common (top, elt, key, keylen, copy_key, false, true); 1548} 1549 1550bool 1551ucl_object_merge (ucl_object_t *top, ucl_object_t *elt, bool copy) 1552{ 1553 ucl_object_t *cur = NULL, *cp = NULL, *found = NULL; 1554 ucl_object_iter_t iter = NULL; 1555 1556 if (top == NULL || top->type != UCL_OBJECT || elt == NULL || elt->type != UCL_OBJECT) { 1557 return false; 1558 } 1559 1560 /* Mix two hashes */ 1561 while ((cur = (ucl_object_t*)ucl_hash_iterate (elt->value.ov, &iter))) { 1562 if (copy) { 1563 cp = ucl_object_copy (cur); 1564 } 1565 else { 1566 cp = ucl_object_ref (cur); 1567 } 1568 found = __DECONST(ucl_object_t *, ucl_hash_search (top->value.ov, cp->key, cp->keylen)); 1569 if (found == NULL) { 1570 /* The key does not exist */ 1571 top->value.ov = ucl_hash_insert_object (top->value.ov, cp); 1572 top->len ++; 1573 } 1574 else { 1575 /* The key already exists, replace it */ 1576 ucl_hash_replace (top->value.ov, found, cp); 1577 ucl_object_unref (found); 1578 } 1579 } 1580 1581 return true; 1582} 1583 1584const ucl_object_t * 1585ucl_object_find_keyl (const ucl_object_t *obj, const char *key, size_t klen) 1586{ 1587 const ucl_object_t *ret; 1588 ucl_object_t srch; 1589 1590 if (obj == NULL || obj->type != UCL_OBJECT || key == NULL) { 1591 return NULL; 1592 } 1593 1594 srch.key = key; 1595 srch.keylen = klen; 1596 ret = ucl_hash_search_obj (obj->value.ov, &srch); 1597 1598 return ret; 1599} 1600 1601const ucl_object_t * 1602ucl_object_find_key (const ucl_object_t *obj, const char *key) 1603{ 1604 if (key == NULL) 1605 return NULL; 1606 1607 return ucl_object_find_keyl (obj, key, strlen(key)); 1608} 1609 1610const ucl_object_t* 1611ucl_iterate_object (const ucl_object_t *obj, ucl_object_iter_t *iter, bool expand_values) 1612{ 1613 const ucl_object_t *elt; 1614 1615 if (obj == NULL || iter == NULL) { 1616 return NULL; 1617 } 1618 1619 if (expand_values) { 1620 switch (obj->type) { 1621 case UCL_OBJECT: 1622 return (const ucl_object_t*)ucl_hash_iterate (obj->value.ov, iter); 1623 break; 1624 case UCL_ARRAY: 1625 elt = *iter; 1626 if (elt == NULL) { 1627 elt = obj->value.av; 1628 if (elt == NULL) { 1629 return NULL; 1630 } 1631 } 1632 else if (elt == obj->value.av) { 1633 return NULL; 1634 } 1635 *iter = elt->next ? elt->next : obj->value.av; 1636 return elt; 1637 default: 1638 /* Go to linear iteration */ 1639 break; 1640 } 1641 } 1642 /* Treat everything as a linear list */ 1643 elt = *iter; 1644 if (elt == NULL) { 1645 elt = obj; 1646 } 1647 else if (elt == obj) { 1648 return NULL; 1649 } 1650 *iter = __DECONST (void *, elt->next ? elt->next : obj); 1651 return elt; 1652 1653 /* Not reached */ 1654 return NULL; 1655} 1656 1657const ucl_object_t * 1658ucl_lookup_path (const ucl_object_t *top, const char *path_in) { 1659 const ucl_object_t *o = NULL, *found; 1660 const char *p, *c; 1661 char *err_str; 1662 unsigned index; 1663 1664 if (path_in == NULL || top == NULL) { 1665 return NULL; 1666 } 1667 1668 found = NULL; 1669 p = path_in; 1670 1671 /* Skip leading dots */ 1672 while (*p == '.') { 1673 p ++; 1674 } 1675 1676 c = p; 1677 while (*p != '\0') { 1678 p ++; 1679 if (*p == '.' || *p == '\0') { 1680 if (p > c) { 1681 switch (top->type) { 1682 case UCL_ARRAY: 1683 /* Key should be an int */ 1684 index = strtoul (c, &err_str, 10); 1685 if (err_str != NULL && (*err_str != '.' && *err_str != '\0')) { 1686 return NULL; 1687 } 1688 o = ucl_array_find_index (top, index); 1689 break; 1690 default: 1691 o = ucl_object_find_keyl (top, c, p - c); 1692 break; 1693 } 1694 if (o == NULL) { 1695 return NULL; 1696 } 1697 top = o; 1698 } 1699 if (*p != '\0') { 1700 c = p + 1; 1701 } 1702 } 1703 } 1704 found = o; 1705 1706 return found; 1707} 1708 1709 1710ucl_object_t * 1711ucl_object_new (void) 1712{ 1713 return ucl_object_typed_new (UCL_NULL); 1714} 1715 1716ucl_object_t * 1717ucl_object_typed_new (ucl_type_t type) 1718{ 1719 return ucl_object_new_full (type, 0); 1720} 1721 1722ucl_object_t * 1723ucl_object_new_full (ucl_type_t type, unsigned priority) 1724{ 1725 ucl_object_t *new; 1726 1727 if (type != UCL_USERDATA) { 1728 new = UCL_ALLOC (sizeof (ucl_object_t)); 1729 if (new != NULL) { 1730 memset (new, 0, sizeof (ucl_object_t)); 1731 new->ref = 1; 1732 new->type = (type <= UCL_NULL ? type : UCL_NULL); 1733 new->next = NULL; 1734 new->prev = new; 1735 ucl_object_set_priority (new, priority); 1736 } 1737 } 1738 else { 1739 new = ucl_object_new_userdata (NULL, NULL); 1740 ucl_object_set_priority (new, priority); 1741 } 1742 1743 return new; 1744} 1745 1746ucl_object_t* 1747ucl_object_new_userdata (ucl_userdata_dtor dtor, ucl_userdata_emitter emitter) 1748{ 1749 struct ucl_object_userdata *new; 1750 size_t nsize = sizeof (*new); 1751 1752 new = UCL_ALLOC (nsize); 1753 if (new != NULL) { 1754 memset (new, 0, nsize); 1755 new->obj.ref = 1; 1756 new->obj.type = UCL_USERDATA; 1757 new->obj.next = NULL; 1758 new->obj.prev = (ucl_object_t *)new; 1759 new->dtor = dtor; 1760 new->emitter = emitter; 1761 } 1762 1763 return (ucl_object_t *)new; 1764} 1765 1766ucl_type_t 1767ucl_object_type (const ucl_object_t *obj) 1768{ 1769 return obj->type; 1770} 1771 1772ucl_object_t* 1773ucl_object_fromstring (const char *str) 1774{ 1775 return ucl_object_fromstring_common (str, 0, UCL_STRING_ESCAPE); 1776} 1777 1778ucl_object_t * 1779ucl_object_fromlstring (const char *str, size_t len) 1780{ 1781 return ucl_object_fromstring_common (str, len, UCL_STRING_ESCAPE); 1782} 1783 1784ucl_object_t * 1785ucl_object_fromint (int64_t iv) 1786{ 1787 ucl_object_t *obj; 1788 1789 obj = ucl_object_new (); 1790 if (obj != NULL) { 1791 obj->type = UCL_INT; 1792 obj->value.iv = iv; 1793 } 1794 1795 return obj; 1796} 1797 1798ucl_object_t * 1799ucl_object_fromdouble (double dv) 1800{ 1801 ucl_object_t *obj; 1802 1803 obj = ucl_object_new (); 1804 if (obj != NULL) { 1805 obj->type = UCL_FLOAT; 1806 obj->value.dv = dv; 1807 } 1808 1809 return obj; 1810} 1811 1812ucl_object_t* 1813ucl_object_frombool (bool bv) 1814{ 1815 ucl_object_t *obj; 1816 1817 obj = ucl_object_new (); 1818 if (obj != NULL) { 1819 obj->type = UCL_BOOLEAN; 1820 obj->value.iv = bv; 1821 } 1822 1823 return obj; 1824} 1825 1826bool 1827ucl_array_append (ucl_object_t *top, ucl_object_t *elt) 1828{ 1829 ucl_object_t *head; 1830 1831 if (elt == NULL || top == NULL) { 1832 return false; 1833 } 1834 1835 head = top->value.av; 1836 if (head == NULL) { 1837 top->value.av = elt; 1838 elt->prev = elt; 1839 } 1840 else { 1841 elt->prev = head->prev; 1842 head->prev->next = elt; 1843 head->prev = elt; 1844 } 1845 elt->next = NULL; 1846 top->len ++; 1847 1848 return true; 1849} 1850 1851bool 1852ucl_array_prepend (ucl_object_t *top, ucl_object_t *elt) 1853{ 1854 ucl_object_t *head; 1855 1856 if (elt == NULL || top == NULL) { 1857 return false; 1858 } 1859 1860 1861 head = top->value.av; 1862 if (head == NULL) { 1863 top->value.av = elt; 1864 elt->prev = elt; 1865 } 1866 else { 1867 elt->prev = head->prev; 1868 head->prev = elt; 1869 } 1870 elt->next = head; 1871 top->value.av = elt; 1872 top->len ++; 1873 1874 return true; 1875} 1876 1877bool 1878ucl_array_merge (ucl_object_t *top, ucl_object_t *elt, bool copy) 1879{ 1880 ucl_object_t *cur, *tmp, *cp; 1881 1882 if (elt == NULL || top == NULL || top->type != UCL_ARRAY || elt->type != UCL_ARRAY) { 1883 return false; 1884 } 1885 1886 DL_FOREACH_SAFE (elt->value.av, cur, tmp) { 1887 if (copy) { 1888 cp = ucl_object_copy (cur); 1889 } 1890 else { 1891 cp = ucl_object_ref (cur); 1892 } 1893 if (cp != NULL) { 1894 ucl_array_append (top, cp); 1895 } 1896 } 1897 1898 return true; 1899} 1900 1901ucl_object_t * 1902ucl_array_delete (ucl_object_t *top, ucl_object_t *elt) 1903{ 1904 ucl_object_t *head; 1905 1906 if (top == NULL || top->type != UCL_ARRAY || top->value.av == NULL) { 1907 return NULL; 1908 } 1909 head = top->value.av; 1910 1911 if (elt->prev == elt) { 1912 top->value.av = NULL; 1913 } 1914 else if (elt == head) { 1915 elt->next->prev = elt->prev; 1916 top->value.av = elt->next; 1917 } 1918 else { 1919 elt->prev->next = elt->next; 1920 if (elt->next) { 1921 elt->next->prev = elt->prev; 1922 } 1923 else { 1924 head->prev = elt->prev; 1925 } 1926 } 1927 elt->next = NULL; 1928 elt->prev = elt; 1929 top->len --; 1930 1931 return elt; 1932} 1933 1934const ucl_object_t * 1935ucl_array_head (const ucl_object_t *top) 1936{ 1937 if (top == NULL || top->type != UCL_ARRAY || top->value.av == NULL) { 1938 return NULL; 1939 } 1940 return top->value.av; 1941} 1942 1943const ucl_object_t * 1944ucl_array_tail (const ucl_object_t *top) 1945{ 1946 if (top == NULL || top->type != UCL_ARRAY || top->value.av == NULL) { 1947 return NULL; 1948 } 1949 return top->value.av->prev; 1950} 1951 1952ucl_object_t * 1953ucl_array_pop_last (ucl_object_t *top) 1954{ 1955 return ucl_array_delete (top, __DECONST(ucl_object_t *, ucl_array_tail (top))); 1956} 1957 1958ucl_object_t * 1959ucl_array_pop_first (ucl_object_t *top) 1960{ 1961 return ucl_array_delete (top, __DECONST(ucl_object_t *, ucl_array_head (top))); 1962} 1963 1964const ucl_object_t * 1965ucl_array_find_index (const ucl_object_t *top, unsigned int index) 1966{ 1967 ucl_object_iter_t it = NULL; 1968 const ucl_object_t *ret; 1969 1970 if (top == NULL || top->type != UCL_ARRAY || top->len == 0 || 1971 (index + 1) > top->len) { 1972 return NULL; 1973 } 1974 1975 while ((ret = ucl_iterate_object (top, &it, true)) != NULL) { 1976 if (index == 0) { 1977 return ret; 1978 } 1979 --index; 1980 } 1981 1982 return NULL; 1983} 1984 1985ucl_object_t * 1986ucl_array_replace_index (ucl_object_t *top, ucl_object_t *elt, 1987 unsigned int index) 1988{ 1989 ucl_object_t *cur, *tmp; 1990 1991 if (top == NULL || top->type != UCL_ARRAY || elt == NULL || 1992 top->len == 0 || (index + 1) > top->len) { 1993 return NULL; 1994 } 1995 1996 DL_FOREACH_SAFE (top->value.av, cur, tmp) { 1997 if (index == 0) { 1998 DL_REPLACE_ELEM (top->value.av, cur, elt); 1999 return cur; 2000 } 2001 --index; 2002 } 2003 2004 return NULL; 2005} 2006 2007ucl_object_t * 2008ucl_elt_append (ucl_object_t *head, ucl_object_t *elt) 2009{ 2010 2011 if (head == NULL) { 2012 elt->next = NULL; 2013 elt->prev = elt; 2014 head = elt; 2015 } 2016 else { 2017 elt->prev = head->prev; 2018 head->prev->next = elt; 2019 head->prev = elt; 2020 elt->next = NULL; 2021 } 2022 2023 return head; 2024} 2025 2026bool 2027ucl_object_todouble_safe (const ucl_object_t *obj, double *target) 2028{ 2029 if (obj == NULL || target == NULL) { 2030 return false; 2031 } 2032 switch (obj->type) { 2033 case UCL_INT: 2034 *target = obj->value.iv; /* Probaly could cause overflow */ 2035 break; 2036 case UCL_FLOAT: 2037 case UCL_TIME: 2038 *target = obj->value.dv; 2039 break; 2040 default: 2041 return false; 2042 } 2043 2044 return true; 2045} 2046 2047double 2048ucl_object_todouble (const ucl_object_t *obj) 2049{ 2050 double result = 0.; 2051 2052 ucl_object_todouble_safe (obj, &result); 2053 return result; 2054} 2055 2056bool 2057ucl_object_toint_safe (const ucl_object_t *obj, int64_t *target) 2058{ 2059 if (obj == NULL || target == NULL) { 2060 return false; 2061 } 2062 switch (obj->type) { 2063 case UCL_INT: 2064 *target = obj->value.iv; 2065 break; 2066 case UCL_FLOAT: 2067 case UCL_TIME: 2068 *target = obj->value.dv; /* Loosing of decimal points */ 2069 break; 2070 default: 2071 return false; 2072 } 2073 2074 return true; 2075} 2076 2077int64_t 2078ucl_object_toint (const ucl_object_t *obj) 2079{ 2080 int64_t result = 0; 2081 2082 ucl_object_toint_safe (obj, &result); 2083 return result; 2084} 2085 2086bool 2087ucl_object_toboolean_safe (const ucl_object_t *obj, bool *target) 2088{ 2089 if (obj == NULL || target == NULL) { 2090 return false; 2091 } 2092 switch (obj->type) { 2093 case UCL_BOOLEAN: 2094 *target = (obj->value.iv == true); 2095 break; 2096 default: 2097 return false; 2098 } 2099 2100 return true; 2101} 2102 2103bool 2104ucl_object_toboolean (const ucl_object_t *obj) 2105{ 2106 bool result = false; 2107 2108 ucl_object_toboolean_safe (obj, &result); 2109 return result; 2110} 2111 2112bool 2113ucl_object_tostring_safe (const ucl_object_t *obj, const char **target) 2114{ 2115 if (obj == NULL || target == NULL) { 2116 return false; 2117 } 2118 2119 switch (obj->type) { 2120 case UCL_STRING: 2121 *target = ucl_copy_value_trash (obj); 2122 break; 2123 default: 2124 return false; 2125 } 2126 2127 return true; 2128} 2129 2130const char * 2131ucl_object_tostring (const ucl_object_t *obj) 2132{ 2133 const char *result = NULL; 2134 2135 ucl_object_tostring_safe (obj, &result); 2136 return result; 2137} 2138 2139const char * 2140ucl_object_tostring_forced (const ucl_object_t *obj) 2141{ 2142 return ucl_copy_value_trash (obj); 2143} 2144 2145bool 2146ucl_object_tolstring_safe (const ucl_object_t *obj, const char **target, size_t *tlen) 2147{ 2148 if (obj == NULL || target == NULL) { 2149 return false; 2150 } 2151 switch (obj->type) { 2152 case UCL_STRING: 2153 *target = obj->value.sv; 2154 if (tlen != NULL) { 2155 *tlen = obj->len; 2156 } 2157 break; 2158 default: 2159 return false; 2160 } 2161 2162 return true; 2163} 2164 2165const char * 2166ucl_object_tolstring (const ucl_object_t *obj, size_t *tlen) 2167{ 2168 const char *result = NULL; 2169 2170 ucl_object_tolstring_safe (obj, &result, tlen); 2171 return result; 2172} 2173 2174const char * 2175ucl_object_key (const ucl_object_t *obj) 2176{ 2177 return ucl_copy_key_trash (obj); 2178} 2179 2180const char * 2181ucl_object_keyl (const ucl_object_t *obj, size_t *len) 2182{ 2183 if (len == NULL || obj == NULL) { 2184 return NULL; 2185 } 2186 *len = obj->keylen; 2187 return obj->key; 2188} 2189 2190ucl_object_t * 2191ucl_object_ref (const ucl_object_t *obj) 2192{ 2193 ucl_object_t *res = NULL; 2194 2195 if (obj != NULL) { 2196 if (obj->flags & UCL_OBJECT_EPHEMERAL) { 2197 /* 2198 * Use deep copy for ephemeral objects, note that its refcount 2199 * is NOT increased, since ephemeral objects does not need refcount 2200 * at all 2201 */ 2202 res = ucl_object_copy (obj); 2203 } 2204 else { 2205 res = __DECONST (ucl_object_t *, obj); 2206#ifdef HAVE_ATOMIC_BUILTINS 2207 (void)__sync_add_and_fetch (&res->ref, 1); 2208#else 2209 res->ref ++; 2210#endif 2211 } 2212 } 2213 return res; 2214} 2215 2216static ucl_object_t * 2217ucl_object_copy_internal (const ucl_object_t *other, bool allow_array) 2218{ 2219 2220 ucl_object_t *new; 2221 ucl_object_iter_t it = NULL; 2222 const ucl_object_t *cur; 2223 2224 new = malloc (sizeof (*new)); 2225 2226 if (new != NULL) { 2227 memcpy (new, other, sizeof (*new)); 2228 if (other->flags & UCL_OBJECT_EPHEMERAL) { 2229 /* Copied object is always non ephemeral */ 2230 new->flags &= ~UCL_OBJECT_EPHEMERAL; 2231 } 2232 new->ref = 1; 2233 /* Unlink from others */ 2234 new->next = NULL; 2235 new->prev = new; 2236 2237 /* deep copy of values stored */ 2238 if (other->trash_stack[UCL_TRASH_KEY] != NULL) { 2239 new->trash_stack[UCL_TRASH_KEY] = 2240 strdup (other->trash_stack[UCL_TRASH_KEY]); 2241 if (other->key == (const char *)other->trash_stack[UCL_TRASH_KEY]) { 2242 new->key = new->trash_stack[UCL_TRASH_KEY]; 2243 } 2244 } 2245 if (other->trash_stack[UCL_TRASH_VALUE] != NULL) { 2246 new->trash_stack[UCL_TRASH_VALUE] = 2247 strdup (other->trash_stack[UCL_TRASH_VALUE]); 2248 if (new->type == UCL_STRING) { 2249 new->value.sv = new->trash_stack[UCL_TRASH_VALUE]; 2250 } 2251 } 2252 2253 if (other->type == UCL_ARRAY || other->type == UCL_OBJECT) { 2254 /* reset old value */ 2255 memset (&new->value, 0, sizeof (new->value)); 2256 2257 while ((cur = ucl_iterate_object (other, &it, true)) != NULL) { 2258 if (other->type == UCL_ARRAY) { 2259 ucl_array_append (new, ucl_object_copy_internal (cur, false)); 2260 } 2261 else { 2262 ucl_object_t *cp = ucl_object_copy_internal (cur, true); 2263 if (cp != NULL) { 2264 ucl_object_insert_key (new, cp, cp->key, cp->keylen, 2265 false); 2266 } 2267 } 2268 } 2269 } 2270 else if (allow_array && other->next != NULL) { 2271 LL_FOREACH (other->next, cur) { 2272 ucl_object_t *cp = ucl_object_copy_internal (cur, false); 2273 if (cp != NULL) { 2274 DL_APPEND (new, cp); 2275 } 2276 } 2277 } 2278 } 2279 2280 return new; 2281} 2282 2283ucl_object_t * 2284ucl_object_copy (const ucl_object_t *other) 2285{ 2286 return ucl_object_copy_internal (other, true); 2287} 2288 2289void 2290ucl_object_unref (ucl_object_t *obj) 2291{ 2292 if (obj != NULL) { 2293#ifdef HAVE_ATOMIC_BUILTINS 2294 unsigned int rc = __sync_sub_and_fetch (&obj->ref, 1); 2295 if (rc == 0) { 2296#else 2297 if (--obj->ref == 0) { 2298#endif 2299 ucl_object_free_internal (obj, true, ucl_object_dtor_unref); 2300 } 2301 } 2302} 2303 2304int 2305ucl_object_compare (const ucl_object_t *o1, const ucl_object_t *o2) 2306{ 2307 const ucl_object_t *it1, *it2; 2308 ucl_object_iter_t iter = NULL; 2309 int ret = 0; 2310 2311 if (o1->type != o2->type) { 2312 return (o1->type) - (o2->type); 2313 } 2314 2315 switch (o1->type) { 2316 case UCL_STRING: 2317 if (o1->len == o2->len) { 2318 ret = strcmp (ucl_object_tostring(o1), ucl_object_tostring(o2)); 2319 } 2320 else { 2321 ret = o1->len - o2->len; 2322 } 2323 break; 2324 case UCL_FLOAT: 2325 case UCL_INT: 2326 case UCL_TIME: 2327 ret = ucl_object_todouble (o1) - ucl_object_todouble (o2); 2328 break; 2329 case UCL_BOOLEAN: 2330 ret = ucl_object_toboolean (o1) - ucl_object_toboolean (o2); 2331 break; 2332 case UCL_ARRAY: 2333 if (o1->len == o2->len) { 2334 it1 = o1->value.av; 2335 it2 = o2->value.av; 2336 /* Compare all elements in both arrays */ 2337 while (it1 != NULL && it2 != NULL) { 2338 ret = ucl_object_compare (it1, it2); 2339 if (ret != 0) { 2340 break; 2341 } 2342 it1 = it1->next; 2343 it2 = it2->next; 2344 } 2345 } 2346 else { 2347 ret = o1->len - o2->len; 2348 } 2349 break; 2350 case UCL_OBJECT: 2351 if (o1->len == o2->len) { 2352 while ((it1 = ucl_iterate_object (o1, &iter, true)) != NULL) { 2353 it2 = ucl_object_find_key (o2, ucl_object_key (it1)); 2354 if (it2 == NULL) { 2355 ret = 1; 2356 break; 2357 } 2358 ret = ucl_object_compare (it1, it2); 2359 if (ret != 0) { 2360 break; 2361 } 2362 } 2363 } 2364 else { 2365 ret = o1->len - o2->len; 2366 } 2367 break; 2368 default: 2369 ret = 0; 2370 break; 2371 } 2372 2373 return ret; 2374} 2375 2376void 2377ucl_object_array_sort (ucl_object_t *ar, 2378 int (*cmp)(const ucl_object_t *o1, const ucl_object_t *o2)) 2379{ 2380 if (cmp == NULL || ar == NULL || ar->type != UCL_ARRAY) { 2381 return; 2382 } 2383 2384 DL_SORT (ar->value.av, cmp); 2385} 2386 2387#define PRIOBITS 4 2388 2389unsigned int 2390ucl_object_get_priority (const ucl_object_t *obj) 2391{ 2392 if (obj == NULL) { 2393 return 0; 2394 } 2395 2396 return (obj->flags >> ((sizeof (obj->flags) * NBBY) - PRIOBITS)); 2397} 2398 2399void 2400ucl_object_set_priority (ucl_object_t *obj, 2401 unsigned int priority) 2402{ 2403 if (obj != NULL) { 2404 priority &= (0x1 << PRIOBITS) - 1; 2405 obj->flags |= priority << ((sizeof (obj->flags) * NBBY) - PRIOBITS); 2406 } 2407} 2408