1 /* $NetBSD: prop_dictionary.c,v 1.46 2023/06/14 00:35:18 rin Exp $ */ 2 3 /*- 4 * Copyright (c) 2006, 2007, 2020 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Jason R. Thorpe. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include "prop_object_impl.h" 33 #include <prop/prop_array.h> 34 #include <prop/prop_dictionary.h> 35 #include <prop/prop_string.h> 36 37 #include <sys/rbtree.h> 38 39 #if !defined(_KERNEL) && !defined(_STANDALONE) 40 #include <errno.h> 41 #endif 42 43 /* 44 * We implement these like arrays, but we keep them sorted by key. 45 * This allows us to binary-search as well as keep externalized output 46 * sane-looking for human eyes. 47 */ 48 49 #define EXPAND_STEP 16 50 51 /* 52 * prop_dictionary_keysym_t is allocated with space at the end to hold the 53 * key. This must be a regular object so that we can maintain sane iterator 54 * semantics -- we don't want to require that the caller release the result 55 * of prop_object_iterator_next(). 56 * 57 * We'd like to have some small'ish keysym objects for up-to-16 characters 58 * in a key, some for up-to-32 characters in a key, and then a final bucket 59 * for up-to-128 characters in a key (not including NUL). Keys longer than 60 * 128 characters are not allowed. 61 */ 62 struct _prop_dictionary_keysym { 63 struct _prop_object pdk_obj; 64 size_t pdk_size; 65 struct rb_node pdk_link; 66 char pdk_key[1]; 67 /* actually variable length */ 68 }; 69 70 /* pdk_key[1] takes care of the NUL */ 71 #define PDK_SIZE_16 (sizeof(struct _prop_dictionary_keysym) + 16) 72 #define PDK_SIZE_32 (sizeof(struct _prop_dictionary_keysym) + 32) 73 #define PDK_SIZE_128 (sizeof(struct _prop_dictionary_keysym) + 128) 74 75 #define PDK_MAXKEY 128 76 77 _PROP_POOL_INIT(_prop_dictionary_keysym16_pool, PDK_SIZE_16, "pdict16") 78 _PROP_POOL_INIT(_prop_dictionary_keysym32_pool, PDK_SIZE_32, "pdict32") 79 _PROP_POOL_INIT(_prop_dictionary_keysym128_pool, PDK_SIZE_128, "pdict128") 80 81 struct _prop_dict_entry { 82 prop_dictionary_keysym_t pde_key; 83 prop_object_t pde_objref; 84 }; 85 86 struct _prop_dictionary { 87 struct _prop_object pd_obj; 88 _PROP_RWLOCK_DECL(pd_rwlock) 89 struct _prop_dict_entry *pd_array; 90 unsigned int pd_capacity; 91 unsigned int pd_count; 92 int pd_flags; 93 94 uint32_t pd_version; 95 }; 96 97 #define PD_F_IMMUTABLE 0x01 /* dictionary is immutable */ 98 99 _PROP_POOL_INIT(_prop_dictionary_pool, sizeof(struct _prop_dictionary), 100 "propdict") 101 _PROP_MALLOC_DEFINE(M_PROP_DICT, "prop dictionary", 102 "property dictionary container object") 103 104 static _prop_object_free_rv_t 105 _prop_dictionary_free(prop_stack_t, prop_object_t *); 106 static void _prop_dictionary_emergency_free(prop_object_t); 107 static bool _prop_dictionary_externalize( 108 struct _prop_object_externalize_context *, 109 void *); 110 static _prop_object_equals_rv_t 111 _prop_dictionary_equals(prop_object_t, prop_object_t, 112 void **, void **, 113 prop_object_t *, prop_object_t *); 114 static void _prop_dictionary_equals_finish(prop_object_t, prop_object_t); 115 static prop_object_iterator_t 116 _prop_dictionary_iterator_locked(prop_dictionary_t); 117 static prop_object_t 118 _prop_dictionary_iterator_next_object_locked(void *); 119 static prop_object_t 120 _prop_dictionary_get_keysym(prop_dictionary_t, 121 prop_dictionary_keysym_t, bool); 122 static prop_object_t 123 _prop_dictionary_get(prop_dictionary_t, const char *, bool); 124 125 static void _prop_dictionary_lock(void); 126 static void _prop_dictionary_unlock(void); 127 128 static const struct _prop_object_type _prop_object_type_dictionary = { 129 .pot_type = PROP_TYPE_DICTIONARY, 130 .pot_free = _prop_dictionary_free, 131 .pot_emergency_free = _prop_dictionary_emergency_free, 132 .pot_extern = _prop_dictionary_externalize, 133 .pot_equals = _prop_dictionary_equals, 134 .pot_equals_finish = _prop_dictionary_equals_finish, 135 .pot_lock = _prop_dictionary_lock, 136 .pot_unlock = _prop_dictionary_unlock, 137 }; 138 139 static _prop_object_free_rv_t 140 _prop_dict_keysym_free(prop_stack_t, prop_object_t *); 141 static bool _prop_dict_keysym_externalize( 142 struct _prop_object_externalize_context *, 143 void *); 144 static _prop_object_equals_rv_t 145 _prop_dict_keysym_equals(prop_object_t, prop_object_t, 146 void **, void **, 147 prop_object_t *, prop_object_t *); 148 149 static const struct _prop_object_type _prop_object_type_dict_keysym = { 150 .pot_type = PROP_TYPE_DICT_KEYSYM, 151 .pot_free = _prop_dict_keysym_free, 152 .pot_extern = _prop_dict_keysym_externalize, 153 .pot_equals = _prop_dict_keysym_equals, 154 }; 155 156 #define prop_object_is_dictionary(x) \ 157 ((x) != NULL && (x)->pd_obj.po_type == &_prop_object_type_dictionary) 158 #define prop_object_is_dictionary_keysym(x) \ 159 ((x) != NULL && (x)->pdk_obj.po_type == &_prop_object_type_dict_keysym) 160 161 #define prop_dictionary_is_immutable(x) \ 162 (((x)->pd_flags & PD_F_IMMUTABLE) != 0) 163 164 struct _prop_dictionary_iterator { 165 struct _prop_object_iterator pdi_base; 166 unsigned int pdi_index; 167 }; 168 169 /* 170 * Dictionary key symbols are immutable, and we are likely to have many 171 * duplicated key symbols. So, to save memory, we unique'ify key symbols 172 * so we only have to have one copy of each string. 173 */ 174 175 static int 176 /*ARGSUSED*/ 177 _prop_dict_keysym_rb_compare_nodes(void *ctx _PROP_ARG_UNUSED, 178 const void *n1, const void *n2) 179 { 180 const struct _prop_dictionary_keysym *pdk1 = n1; 181 const struct _prop_dictionary_keysym *pdk2 = n2; 182 183 return strcmp(pdk1->pdk_key, pdk2->pdk_key); 184 } 185 186 static int 187 /*ARGSUSED*/ 188 _prop_dict_keysym_rb_compare_key(void *ctx _PROP_ARG_UNUSED, 189 const void *n, const void *v) 190 { 191 const struct _prop_dictionary_keysym *pdk = n; 192 const char *cp = v; 193 194 return strcmp(pdk->pdk_key, cp); 195 } 196 197 static const rb_tree_ops_t _prop_dict_keysym_rb_tree_ops = { 198 .rbto_compare_nodes = _prop_dict_keysym_rb_compare_nodes, 199 .rbto_compare_key = _prop_dict_keysym_rb_compare_key, 200 .rbto_node_offset = offsetof(struct _prop_dictionary_keysym, pdk_link), 201 .rbto_context = NULL 202 }; 203 204 static struct rb_tree _prop_dict_keysym_tree; 205 206 _PROP_ONCE_DECL(_prop_dict_init_once) 207 _PROP_MUTEX_DECL_STATIC(_prop_dict_keysym_tree_mutex) 208 209 static int 210 _prop_dict_init(void) 211 { 212 213 _PROP_MUTEX_INIT(_prop_dict_keysym_tree_mutex); 214 rb_tree_init(&_prop_dict_keysym_tree, 215 &_prop_dict_keysym_rb_tree_ops); 216 return 0; 217 } 218 219 static void 220 _prop_dict_keysym_put(prop_dictionary_keysym_t pdk) 221 { 222 223 if (pdk->pdk_size <= PDK_SIZE_16) 224 _PROP_POOL_PUT(_prop_dictionary_keysym16_pool, pdk); 225 else if (pdk->pdk_size <= PDK_SIZE_32) 226 _PROP_POOL_PUT(_prop_dictionary_keysym32_pool, pdk); 227 else { 228 _PROP_ASSERT(pdk->pdk_size <= PDK_SIZE_128); 229 _PROP_POOL_PUT(_prop_dictionary_keysym128_pool, pdk); 230 } 231 } 232 233 /* ARGSUSED */ 234 static _prop_object_free_rv_t 235 _prop_dict_keysym_free(prop_stack_t stack, prop_object_t *obj) 236 { 237 prop_dictionary_keysym_t pdk = *obj; 238 239 rb_tree_remove_node(&_prop_dict_keysym_tree, pdk); 240 _prop_dict_keysym_put(pdk); 241 242 return _PROP_OBJECT_FREE_DONE; 243 } 244 245 static bool 246 _prop_dict_keysym_externalize(struct _prop_object_externalize_context *ctx, 247 void *v) 248 { 249 prop_dictionary_keysym_t pdk = v; 250 251 /* We externalize these as strings, and they're never empty. */ 252 253 _PROP_ASSERT(pdk->pdk_key[0] != '\0'); 254 255 if (_prop_object_externalize_start_tag(ctx, "string") == false || 256 _prop_object_externalize_append_encoded_cstring(ctx, 257 pdk->pdk_key) == false || 258 _prop_object_externalize_end_tag(ctx, "string") == false) 259 return (false); 260 261 return (true); 262 } 263 264 /* ARGSUSED */ 265 static _prop_object_equals_rv_t 266 _prop_dict_keysym_equals(prop_object_t v1, prop_object_t v2, 267 void **stored_pointer1, void **stored_pointer2, 268 prop_object_t *next_obj1, prop_object_t *next_obj2) 269 { 270 prop_dictionary_keysym_t pdk1 = v1; 271 prop_dictionary_keysym_t pdk2 = v2; 272 273 /* 274 * There is only ever one copy of a keysym at any given time, 275 * so we can reduce this to a simple pointer equality check. 276 */ 277 if (pdk1 == pdk2) 278 return _PROP_OBJECT_EQUALS_TRUE; 279 else 280 return _PROP_OBJECT_EQUALS_FALSE; 281 } 282 283 static prop_dictionary_keysym_t 284 _prop_dict_keysym_alloc(const char *key) 285 { 286 prop_dictionary_keysym_t opdk, pdk, rpdk; 287 size_t size; 288 289 _PROP_ONCE_RUN(_prop_dict_init_once, _prop_dict_init); 290 291 /* 292 * Check to see if this already exists in the tree. If it does, 293 * we just retain it and return it. 294 */ 295 _PROP_MUTEX_LOCK(_prop_dict_keysym_tree_mutex); 296 opdk = rb_tree_find_node(&_prop_dict_keysym_tree, key); 297 if (opdk != NULL) { 298 prop_object_retain(opdk); 299 _PROP_MUTEX_UNLOCK(_prop_dict_keysym_tree_mutex); 300 return (opdk); 301 } 302 _PROP_MUTEX_UNLOCK(_prop_dict_keysym_tree_mutex); 303 304 /* 305 * Not in the tree. Create it now. 306 */ 307 308 size = sizeof(*pdk) + strlen(key) /* pdk_key[1] covers the NUL */; 309 310 if (size <= PDK_SIZE_16) 311 pdk = _PROP_POOL_GET(_prop_dictionary_keysym16_pool); 312 else if (size <= PDK_SIZE_32) 313 pdk = _PROP_POOL_GET(_prop_dictionary_keysym32_pool); 314 else if (size <= PDK_SIZE_128) 315 pdk = _PROP_POOL_GET(_prop_dictionary_keysym128_pool); 316 else 317 pdk = NULL; /* key too long */ 318 319 if (pdk == NULL) 320 return (NULL); 321 322 _prop_object_init(&pdk->pdk_obj, &_prop_object_type_dict_keysym); 323 324 strcpy(pdk->pdk_key, key); 325 pdk->pdk_size = size; 326 327 /* 328 * We dropped the mutex when we allocated the new object, so 329 * we have to check again if it is in the tree. 330 */ 331 _PROP_MUTEX_LOCK(_prop_dict_keysym_tree_mutex); 332 opdk = rb_tree_find_node(&_prop_dict_keysym_tree, key); 333 if (opdk != NULL) { 334 prop_object_retain(opdk); 335 _PROP_MUTEX_UNLOCK(_prop_dict_keysym_tree_mutex); 336 _prop_dict_keysym_put(pdk); 337 return (opdk); 338 } 339 rpdk = rb_tree_insert_node(&_prop_dict_keysym_tree, pdk); 340 _PROP_ASSERT(rpdk == pdk); 341 _PROP_MUTEX_UNLOCK(_prop_dict_keysym_tree_mutex); 342 return (rpdk); 343 } 344 345 static _prop_object_free_rv_t 346 _prop_dictionary_free(prop_stack_t stack, prop_object_t *obj) 347 { 348 prop_dictionary_t pd = *obj; 349 prop_dictionary_keysym_t pdk; 350 prop_object_t po; 351 352 _PROP_ASSERT(pd->pd_count <= pd->pd_capacity); 353 _PROP_ASSERT((pd->pd_capacity == 0 && pd->pd_array == NULL) || 354 (pd->pd_capacity != 0 && pd->pd_array != NULL)); 355 356 /* The empty dictorinary is easy, handle that first. */ 357 if (pd->pd_count == 0) { 358 if (pd->pd_array != NULL) 359 _PROP_FREE(pd->pd_array, M_PROP_DICT); 360 361 _PROP_RWLOCK_DESTROY(pd->pd_rwlock); 362 363 _PROP_POOL_PUT(_prop_dictionary_pool, pd); 364 365 return (_PROP_OBJECT_FREE_DONE); 366 } 367 368 po = pd->pd_array[pd->pd_count - 1].pde_objref; 369 _PROP_ASSERT(po != NULL); 370 371 if (stack == NULL) { 372 /* 373 * If we are in emergency release mode, 374 * just let caller recurse down. 375 */ 376 *obj = po; 377 return (_PROP_OBJECT_FREE_FAILED); 378 } 379 380 /* Otherwise, try to push the current object on the stack. */ 381 if (!_prop_stack_push(stack, pd, NULL, NULL, NULL)) { 382 /* Push failed, entering emergency release mode. */ 383 return (_PROP_OBJECT_FREE_FAILED); 384 } 385 /* Object pushed on stack, caller will release it. */ 386 --pd->pd_count; 387 pdk = pd->pd_array[pd->pd_count].pde_key; 388 _PROP_ASSERT(pdk != NULL); 389 390 prop_object_release(pdk); 391 392 *obj = po; 393 return (_PROP_OBJECT_FREE_RECURSE); 394 } 395 396 397 static void 398 _prop_dictionary_lock(void) 399 { 400 401 /* XXX: once necessary or paranoia? */ 402 _PROP_ONCE_RUN(_prop_dict_init_once, _prop_dict_init); 403 _PROP_MUTEX_LOCK(_prop_dict_keysym_tree_mutex); 404 } 405 406 static void 407 _prop_dictionary_unlock(void) 408 { 409 _PROP_MUTEX_UNLOCK(_prop_dict_keysym_tree_mutex); 410 } 411 412 static void 413 _prop_dictionary_emergency_free(prop_object_t obj) 414 { 415 prop_dictionary_t pd = obj; 416 prop_dictionary_keysym_t pdk; 417 418 _PROP_ASSERT(pd->pd_count != 0); 419 --pd->pd_count; 420 421 pdk = pd->pd_array[pd->pd_count].pde_key; 422 _PROP_ASSERT(pdk != NULL); 423 prop_object_release(pdk); 424 } 425 426 static bool 427 _prop_dictionary_externalize(struct _prop_object_externalize_context *ctx, 428 void *v) 429 { 430 prop_dictionary_t pd = v; 431 prop_dictionary_keysym_t pdk; 432 struct _prop_object *po; 433 prop_object_iterator_t pi; 434 unsigned int i; 435 bool rv = false; 436 437 _PROP_RWLOCK_RDLOCK(pd->pd_rwlock); 438 439 if (pd->pd_count == 0) { 440 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock); 441 return (_prop_object_externalize_empty_tag(ctx, "dict")); 442 } 443 444 if (_prop_object_externalize_start_tag(ctx, "dict") == false || 445 _prop_object_externalize_append_char(ctx, '\n') == false) 446 goto out; 447 448 pi = _prop_dictionary_iterator_locked(pd); 449 if (pi == NULL) 450 goto out; 451 452 ctx->poec_depth++; 453 _PROP_ASSERT(ctx->poec_depth != 0); 454 455 while ((pdk = _prop_dictionary_iterator_next_object_locked(pi)) 456 != NULL) { 457 po = _prop_dictionary_get_keysym(pd, pdk, true); 458 if (po == NULL || 459 _prop_object_externalize_start_tag(ctx, "key") == false || 460 _prop_object_externalize_append_encoded_cstring(ctx, 461 pdk->pdk_key) == false || 462 _prop_object_externalize_end_tag(ctx, "key") == false || 463 (*po->po_type->pot_extern)(ctx, po) == false) { 464 prop_object_iterator_release(pi); 465 goto out; 466 } 467 } 468 469 prop_object_iterator_release(pi); 470 471 ctx->poec_depth--; 472 for (i = 0; i < ctx->poec_depth; i++) { 473 if (_prop_object_externalize_append_char(ctx, '\t') == false) 474 goto out; 475 } 476 if (_prop_object_externalize_end_tag(ctx, "dict") == false) 477 goto out; 478 479 rv = true; 480 481 out: 482 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock); 483 return (rv); 484 } 485 486 /* ARGSUSED */ 487 static _prop_object_equals_rv_t 488 _prop_dictionary_equals(prop_object_t v1, prop_object_t v2, 489 void **stored_pointer1, void **stored_pointer2, 490 prop_object_t *next_obj1, prop_object_t *next_obj2) 491 { 492 prop_dictionary_t dict1 = v1; 493 prop_dictionary_t dict2 = v2; 494 uintptr_t idx; 495 _prop_object_equals_rv_t rv = _PROP_OBJECT_EQUALS_FALSE; 496 497 if (dict1 == dict2) 498 return (_PROP_OBJECT_EQUALS_TRUE); 499 500 _PROP_ASSERT(*stored_pointer1 == *stored_pointer2); 501 502 idx = (uintptr_t)*stored_pointer1; 503 504 if (idx == 0) { 505 if ((uintptr_t)dict1 < (uintptr_t)dict2) { 506 _PROP_RWLOCK_RDLOCK(dict1->pd_rwlock); 507 _PROP_RWLOCK_RDLOCK(dict2->pd_rwlock); 508 } else { 509 _PROP_RWLOCK_RDLOCK(dict2->pd_rwlock); 510 _PROP_RWLOCK_RDLOCK(dict1->pd_rwlock); 511 } 512 } 513 514 if (dict1->pd_count != dict2->pd_count) 515 goto out; 516 517 if (idx == dict1->pd_count) { 518 rv = _PROP_OBJECT_EQUALS_TRUE; 519 goto out; 520 } 521 522 _PROP_ASSERT(idx < dict1->pd_count); 523 524 *stored_pointer1 = (void *)(idx + 1); 525 *stored_pointer2 = (void *)(idx + 1); 526 527 *next_obj1 = dict1->pd_array[idx].pde_objref; 528 *next_obj2 = dict2->pd_array[idx].pde_objref; 529 530 if (!prop_dictionary_keysym_equals(dict1->pd_array[idx].pde_key, 531 dict2->pd_array[idx].pde_key)) 532 goto out; 533 534 return (_PROP_OBJECT_EQUALS_RECURSE); 535 536 out: 537 _PROP_RWLOCK_UNLOCK(dict1->pd_rwlock); 538 _PROP_RWLOCK_UNLOCK(dict2->pd_rwlock); 539 return (rv); 540 } 541 542 static void 543 _prop_dictionary_equals_finish(prop_object_t v1, prop_object_t v2) 544 { 545 _PROP_RWLOCK_UNLOCK(((prop_dictionary_t)v1)->pd_rwlock); 546 _PROP_RWLOCK_UNLOCK(((prop_dictionary_t)v2)->pd_rwlock); 547 } 548 549 static prop_dictionary_t 550 _prop_dictionary_alloc(unsigned int capacity) 551 { 552 prop_dictionary_t pd; 553 struct _prop_dict_entry *array; 554 555 if (capacity != 0) { 556 array = _PROP_CALLOC(capacity * sizeof(*array), M_PROP_DICT); 557 if (array == NULL) 558 return (NULL); 559 } else 560 array = NULL; 561 562 pd = _PROP_POOL_GET(_prop_dictionary_pool); 563 if (pd != NULL) { 564 _prop_object_init(&pd->pd_obj, &_prop_object_type_dictionary); 565 566 _PROP_RWLOCK_INIT(pd->pd_rwlock); 567 pd->pd_array = array; 568 pd->pd_capacity = capacity; 569 pd->pd_count = 0; 570 pd->pd_flags = 0; 571 572 pd->pd_version = 0; 573 } else if (array != NULL) 574 _PROP_FREE(array, M_PROP_DICT); 575 576 return (pd); 577 } 578 579 static bool 580 _prop_dictionary_expand(prop_dictionary_t pd, unsigned int capacity) 581 { 582 struct _prop_dict_entry *array, *oarray; 583 584 /* 585 * Dictionary must be WRITE-LOCKED. 586 */ 587 588 oarray = pd->pd_array; 589 590 array = _PROP_CALLOC(capacity * sizeof(*array), M_PROP_DICT); 591 if (array == NULL) 592 return (false); 593 if (oarray != NULL) 594 memcpy(array, oarray, pd->pd_capacity * sizeof(*array)); 595 pd->pd_array = array; 596 pd->pd_capacity = capacity; 597 598 if (oarray != NULL) 599 _PROP_FREE(oarray, M_PROP_DICT); 600 601 return (true); 602 } 603 604 static prop_object_t 605 _prop_dictionary_iterator_next_object_locked(void *v) 606 { 607 struct _prop_dictionary_iterator *pdi = v; 608 prop_dictionary_t pd = pdi->pdi_base.pi_obj; 609 prop_dictionary_keysym_t pdk = NULL; 610 611 _PROP_ASSERT(prop_object_is_dictionary(pd)); 612 613 if (pd->pd_version != pdi->pdi_base.pi_version) 614 goto out; /* dictionary changed during iteration */ 615 616 _PROP_ASSERT(pdi->pdi_index <= pd->pd_count); 617 618 if (pdi->pdi_index == pd->pd_count) 619 goto out; /* we've iterated all objects */ 620 621 pdk = pd->pd_array[pdi->pdi_index].pde_key; 622 pdi->pdi_index++; 623 624 out: 625 return (pdk); 626 } 627 628 static prop_object_t 629 _prop_dictionary_iterator_next_object(void *v) 630 { 631 struct _prop_dictionary_iterator *pdi = v; 632 prop_dictionary_t pd _PROP_ARG_UNUSED = pdi->pdi_base.pi_obj; 633 prop_dictionary_keysym_t pdk; 634 635 _PROP_ASSERT(prop_object_is_dictionary(pd)); 636 637 _PROP_RWLOCK_RDLOCK(pd->pd_rwlock); 638 pdk = _prop_dictionary_iterator_next_object_locked(pdi); 639 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock); 640 return (pdk); 641 } 642 643 static void 644 _prop_dictionary_iterator_reset_locked(void *v) 645 { 646 struct _prop_dictionary_iterator *pdi = v; 647 prop_dictionary_t pd = pdi->pdi_base.pi_obj; 648 649 _PROP_ASSERT(prop_object_is_dictionary(pd)); 650 651 pdi->pdi_index = 0; 652 pdi->pdi_base.pi_version = pd->pd_version; 653 } 654 655 static void 656 _prop_dictionary_iterator_reset(void *v) 657 { 658 struct _prop_dictionary_iterator *pdi = v; 659 prop_dictionary_t pd _PROP_ARG_UNUSED = pdi->pdi_base.pi_obj; 660 661 _PROP_RWLOCK_RDLOCK(pd->pd_rwlock); 662 _prop_dictionary_iterator_reset_locked(pdi); 663 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock); 664 } 665 666 /* 667 * prop_dictionary_create -- 668 * Create a dictionary. 669 */ 670 prop_dictionary_t 671 prop_dictionary_create(void) 672 { 673 674 return (_prop_dictionary_alloc(0)); 675 } 676 677 /* 678 * prop_dictionary_create_with_capacity -- 679 * Create a dictionary with the capacity to store N objects. 680 */ 681 prop_dictionary_t 682 prop_dictionary_create_with_capacity(unsigned int capacity) 683 { 684 685 return (_prop_dictionary_alloc(capacity)); 686 } 687 688 /* 689 * prop_dictionary_copy -- 690 * Copy a dictionary. The new dictionary has an initial capacity equal 691 * to the number of objects stored int the original dictionary. The new 692 * dictionary contains references to the original dictionary's objects, 693 * not copies of those objects (i.e. a shallow copy). 694 */ 695 prop_dictionary_t 696 prop_dictionary_copy(prop_dictionary_t opd) 697 { 698 prop_dictionary_t pd; 699 prop_dictionary_keysym_t pdk; 700 prop_object_t po; 701 unsigned int idx; 702 703 if (! prop_object_is_dictionary(opd)) 704 return (NULL); 705 706 _PROP_RWLOCK_RDLOCK(opd->pd_rwlock); 707 708 pd = _prop_dictionary_alloc(opd->pd_count); 709 if (pd != NULL) { 710 for (idx = 0; idx < opd->pd_count; idx++) { 711 pdk = opd->pd_array[idx].pde_key; 712 po = opd->pd_array[idx].pde_objref; 713 714 prop_object_retain(pdk); 715 prop_object_retain(po); 716 717 pd->pd_array[idx].pde_key = pdk; 718 pd->pd_array[idx].pde_objref = po; 719 } 720 pd->pd_count = opd->pd_count; 721 pd->pd_flags = opd->pd_flags; 722 } 723 _PROP_RWLOCK_UNLOCK(opd->pd_rwlock); 724 return (pd); 725 } 726 727 /* 728 * prop_dictionary_copy_mutable -- 729 * Like prop_dictionary_copy(), but the resulting dictionary is 730 * mutable. 731 */ 732 prop_dictionary_t 733 prop_dictionary_copy_mutable(prop_dictionary_t opd) 734 { 735 prop_dictionary_t pd; 736 737 if (! prop_object_is_dictionary(opd)) 738 return (NULL); 739 740 pd = prop_dictionary_copy(opd); 741 if (pd != NULL) 742 pd->pd_flags &= ~PD_F_IMMUTABLE; 743 744 return (pd); 745 } 746 747 /* 748 * prop_dictionary_make_immutable -- 749 * Set the immutable flag on that dictionary. 750 */ 751 void 752 prop_dictionary_make_immutable(prop_dictionary_t pd) 753 { 754 755 _PROP_RWLOCK_WRLOCK(pd->pd_rwlock); 756 if (prop_dictionary_is_immutable(pd) == false) 757 pd->pd_flags |= PD_F_IMMUTABLE; 758 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock); 759 } 760 761 /* 762 * prop_dictionary_count -- 763 * Return the number of objects stored in the dictionary. 764 */ 765 unsigned int 766 prop_dictionary_count(prop_dictionary_t pd) 767 { 768 unsigned int rv; 769 770 if (! prop_object_is_dictionary(pd)) 771 return (0); 772 773 _PROP_RWLOCK_RDLOCK(pd->pd_rwlock); 774 rv = pd->pd_count; 775 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock); 776 777 return (rv); 778 } 779 780 /* 781 * prop_dictionary_ensure_capacity -- 782 * Ensure that the dictionary has the capacity to store the specified 783 * total number of objects (including the objects already stored in 784 * the dictionary). 785 */ 786 bool 787 prop_dictionary_ensure_capacity(prop_dictionary_t pd, unsigned int capacity) 788 { 789 bool rv; 790 791 if (! prop_object_is_dictionary(pd)) 792 return (false); 793 794 _PROP_RWLOCK_WRLOCK(pd->pd_rwlock); 795 if (capacity > pd->pd_capacity) 796 rv = _prop_dictionary_expand(pd, capacity); 797 else 798 rv = true; 799 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock); 800 return (rv); 801 } 802 803 static prop_object_iterator_t 804 _prop_dictionary_iterator_locked(prop_dictionary_t pd) 805 { 806 struct _prop_dictionary_iterator *pdi; 807 808 if (! prop_object_is_dictionary(pd)) 809 return (NULL); 810 811 pdi = _PROP_CALLOC(sizeof(*pdi), M_TEMP); 812 if (pdi == NULL) 813 return (NULL); 814 pdi->pdi_base.pi_next_object = _prop_dictionary_iterator_next_object; 815 pdi->pdi_base.pi_reset = _prop_dictionary_iterator_reset; 816 prop_object_retain(pd); 817 pdi->pdi_base.pi_obj = pd; 818 _prop_dictionary_iterator_reset_locked(pdi); 819 820 return (&pdi->pdi_base); 821 } 822 823 /* 824 * prop_dictionary_iterator -- 825 * Return an iterator for the dictionary. The dictionary is retained by 826 * the iterator. 827 */ 828 prop_object_iterator_t 829 prop_dictionary_iterator(prop_dictionary_t pd) 830 { 831 prop_object_iterator_t pi; 832 833 _PROP_RWLOCK_RDLOCK(pd->pd_rwlock); 834 pi = _prop_dictionary_iterator_locked(pd); 835 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock); 836 return (pi); 837 } 838 839 /* 840 * prop_dictionary_all_keys -- 841 * Return an array containing a snapshot of all of the keys 842 * in the dictionary. 843 */ 844 prop_array_t 845 prop_dictionary_all_keys(prop_dictionary_t pd) 846 { 847 prop_array_t array; 848 unsigned int idx; 849 bool rv = true; 850 851 if (! prop_object_is_dictionary(pd)) 852 return (NULL); 853 854 /* There is no pressing need to lock the dictionary for this. */ 855 array = prop_array_create_with_capacity(pd->pd_count); 856 857 _PROP_RWLOCK_RDLOCK(pd->pd_rwlock); 858 859 for (idx = 0; idx < pd->pd_count; idx++) { 860 rv = prop_array_add(array, pd->pd_array[idx].pde_key); 861 if (rv == false) 862 break; 863 } 864 865 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock); 866 867 if (rv == false) { 868 prop_object_release(array); 869 array = NULL; 870 } 871 return (array); 872 } 873 874 static struct _prop_dict_entry * 875 _prop_dict_lookup(prop_dictionary_t pd, const char *key, 876 unsigned int *idxp) 877 { 878 struct _prop_dict_entry *pde; 879 unsigned int base, idx, distance; 880 int res; 881 882 /* 883 * Dictionary must be READ-LOCKED or WRITE-LOCKED. 884 */ 885 886 for (idx = 0, base = 0, distance = pd->pd_count; distance != 0; 887 distance >>= 1) { 888 idx = base + (distance >> 1); 889 pde = &pd->pd_array[idx]; 890 _PROP_ASSERT(pde->pde_key != NULL); 891 res = strcmp(key, pde->pde_key->pdk_key); 892 if (res == 0) { 893 if (idxp != NULL) 894 *idxp = idx; 895 return (pde); 896 } 897 if (res > 0) { /* key > pdk_key: move right */ 898 base = idx + 1; 899 distance--; 900 } /* else move left */ 901 } 902 903 /* idx points to the slot we looked at last. */ 904 if (idxp != NULL) 905 *idxp = idx; 906 return (NULL); 907 } 908 909 static prop_object_t 910 _prop_dictionary_get(prop_dictionary_t pd, const char *key, bool locked) 911 { 912 const struct _prop_dict_entry *pde; 913 prop_object_t po = NULL; 914 915 if (! prop_object_is_dictionary(pd)) 916 return (NULL); 917 918 if (!locked) { 919 _PROP_RWLOCK_RDLOCK(pd->pd_rwlock); 920 } 921 pde = _prop_dict_lookup(pd, key, NULL); 922 if (pde != NULL) { 923 _PROP_ASSERT(pde->pde_objref != NULL); 924 po = pde->pde_objref; 925 } 926 if (!locked) { 927 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock); 928 } 929 return (po); 930 } 931 /* 932 * prop_dictionary_get -- 933 * Return the object stored with specified key. 934 */ 935 prop_object_t 936 prop_dictionary_get(prop_dictionary_t pd, const char *key) 937 { 938 prop_object_t po = NULL; 939 940 if (! prop_object_is_dictionary(pd)) 941 return (NULL); 942 943 _PROP_RWLOCK_RDLOCK(pd->pd_rwlock); 944 po = _prop_dictionary_get(pd, key, true); 945 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock); 946 return (po); 947 } 948 949 static prop_object_t 950 _prop_dictionary_get_keysym(prop_dictionary_t pd, prop_dictionary_keysym_t pdk, 951 bool locked) 952 { 953 954 if (! (prop_object_is_dictionary(pd) && 955 prop_object_is_dictionary_keysym(pdk))) 956 return (NULL); 957 958 return (_prop_dictionary_get(pd, pdk->pdk_key, locked)); 959 } 960 961 /* 962 * prop_dictionary_get_keysym -- 963 * Return the object stored at the location encoded by the keysym. 964 */ 965 prop_object_t 966 prop_dictionary_get_keysym(prop_dictionary_t pd, prop_dictionary_keysym_t pdk) 967 { 968 969 return (_prop_dictionary_get_keysym(pd, pdk, false)); 970 } 971 972 /* 973 * prop_dictionary_set -- 974 * Store a reference to an object at with the specified key. 975 * If the key already exist, the original object is released. 976 */ 977 bool 978 prop_dictionary_set(prop_dictionary_t pd, const char *key, prop_object_t po) 979 { 980 struct _prop_dict_entry *pde; 981 prop_dictionary_keysym_t pdk; 982 unsigned int idx; 983 bool rv = false; 984 985 if (! prop_object_is_dictionary(pd)) 986 return (false); 987 988 _PROP_ASSERT(pd->pd_count <= pd->pd_capacity); 989 990 if (prop_dictionary_is_immutable(pd)) 991 return (false); 992 993 _PROP_RWLOCK_WRLOCK(pd->pd_rwlock); 994 995 pde = _prop_dict_lookup(pd, key, &idx); 996 if (pde != NULL) { 997 prop_object_t opo = pde->pde_objref; 998 prop_object_retain(po); 999 pde->pde_objref = po; 1000 prop_object_release(opo); 1001 rv = true; 1002 goto out; 1003 } 1004 1005 pdk = _prop_dict_keysym_alloc(key); 1006 if (pdk == NULL) 1007 goto out; 1008 1009 if (pd->pd_count == pd->pd_capacity && 1010 _prop_dictionary_expand(pd, 1011 pd->pd_capacity + EXPAND_STEP) == false) { 1012 prop_object_release(pdk); 1013 goto out; 1014 } 1015 1016 /* At this point, the store will succeed. */ 1017 prop_object_retain(po); 1018 1019 if (pd->pd_count == 0) { 1020 pd->pd_array[0].pde_key = pdk; 1021 pd->pd_array[0].pde_objref = po; 1022 pd->pd_count++; 1023 pd->pd_version++; 1024 rv = true; 1025 goto out; 1026 } 1027 1028 pde = &pd->pd_array[idx]; 1029 _PROP_ASSERT(pde->pde_key != NULL); 1030 1031 if (strcmp(key, pde->pde_key->pdk_key) < 0) { 1032 /* 1033 * key < pdk_key: insert to the left. This is the same as 1034 * inserting to the right, except we decrement the current 1035 * index first. 1036 * 1037 * Because we're unsigned, we have to special case 0 1038 * (grumble). 1039 */ 1040 if (idx == 0) { 1041 memmove(&pd->pd_array[1], &pd->pd_array[0], 1042 pd->pd_count * sizeof(*pde)); 1043 pd->pd_array[0].pde_key = pdk; 1044 pd->pd_array[0].pde_objref = po; 1045 pd->pd_count++; 1046 pd->pd_version++; 1047 rv = true; 1048 goto out; 1049 } 1050 idx--; 1051 } 1052 1053 memmove(&pd->pd_array[idx + 2], &pd->pd_array[idx + 1], 1054 (pd->pd_count - (idx + 1)) * sizeof(*pde)); 1055 pd->pd_array[idx + 1].pde_key = pdk; 1056 pd->pd_array[idx + 1].pde_objref = po; 1057 pd->pd_count++; 1058 1059 pd->pd_version++; 1060 1061 rv = true; 1062 1063 out: 1064 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock); 1065 return (rv); 1066 } 1067 1068 /* 1069 * prop_dictionary_set_keysym -- 1070 * Replace the object in the dictionary at the location encoded by 1071 * the keysym. 1072 */ 1073 bool 1074 prop_dictionary_set_keysym(prop_dictionary_t pd, prop_dictionary_keysym_t pdk, 1075 prop_object_t po) 1076 { 1077 1078 if (! (prop_object_is_dictionary(pd) && 1079 prop_object_is_dictionary_keysym(pdk))) 1080 return (false); 1081 1082 return (prop_dictionary_set(pd, pdk->pdk_key, po)); 1083 } 1084 1085 static void 1086 _prop_dictionary_remove(prop_dictionary_t pd, struct _prop_dict_entry *pde, 1087 unsigned int idx) 1088 { 1089 prop_dictionary_keysym_t pdk = pde->pde_key; 1090 prop_object_t po = pde->pde_objref; 1091 1092 /* 1093 * Dictionary must be WRITE-LOCKED. 1094 */ 1095 1096 _PROP_ASSERT(pd->pd_count != 0); 1097 _PROP_ASSERT(idx < pd->pd_count); 1098 _PROP_ASSERT(pde == &pd->pd_array[idx]); 1099 1100 idx++; 1101 memmove(&pd->pd_array[idx - 1], &pd->pd_array[idx], 1102 (pd->pd_count - idx) * sizeof(*pde)); 1103 pd->pd_count--; 1104 pd->pd_version++; 1105 1106 1107 prop_object_release(pdk); 1108 1109 prop_object_release(po); 1110 } 1111 1112 /* 1113 * prop_dictionary_remove -- 1114 * Remove the reference to an object with the specified key from 1115 * the dictionary. 1116 */ 1117 void 1118 prop_dictionary_remove(prop_dictionary_t pd, const char *key) 1119 { 1120 struct _prop_dict_entry *pde; 1121 unsigned int idx; 1122 1123 if (! prop_object_is_dictionary(pd)) 1124 return; 1125 1126 _PROP_RWLOCK_WRLOCK(pd->pd_rwlock); 1127 1128 /* XXX Should this be a _PROP_ASSERT()? */ 1129 if (prop_dictionary_is_immutable(pd)) 1130 goto out; 1131 1132 pde = _prop_dict_lookup(pd, key, &idx); 1133 /* XXX Should this be a _PROP_ASSERT()? */ 1134 if (pde == NULL) 1135 goto out; 1136 1137 _prop_dictionary_remove(pd, pde, idx); 1138 out: 1139 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock); 1140 } 1141 1142 /* 1143 * prop_dictionary_remove_keysym -- 1144 * Remove a reference to an object stored in the dictionary at the 1145 * location encoded by the keysym. 1146 */ 1147 void 1148 prop_dictionary_remove_keysym(prop_dictionary_t pd, 1149 prop_dictionary_keysym_t pdk) 1150 { 1151 1152 if (! (prop_object_is_dictionary(pd) && 1153 prop_object_is_dictionary_keysym(pdk))) 1154 return; 1155 1156 prop_dictionary_remove(pd, pdk->pdk_key); 1157 } 1158 1159 /* 1160 * prop_dictionary_equals -- 1161 * Return true if the two dictionaries are equivalent. Note we do a 1162 * by-value comparison of the objects in the dictionary. 1163 */ 1164 bool 1165 prop_dictionary_equals(prop_dictionary_t dict1, prop_dictionary_t dict2) 1166 { 1167 if (!prop_object_is_dictionary(dict1) || 1168 !prop_object_is_dictionary(dict2)) 1169 return (false); 1170 1171 return (prop_object_equals(dict1, dict2)); 1172 } 1173 1174 /* 1175 * prop_dictionary_keysym_value -- 1176 * Return a reference to the keysym's value. 1177 */ 1178 const char * 1179 prop_dictionary_keysym_value(prop_dictionary_keysym_t pdk) 1180 { 1181 1182 if (! prop_object_is_dictionary_keysym(pdk)) 1183 return (NULL); 1184 1185 return (pdk->pdk_key); 1186 } 1187 1188 _PROP_DEPRECATED(prop_dictionary_keysym_cstring_nocopy, 1189 "this program uses prop_dictionary_keysym_cstring_nocopy(), " 1190 "which is deprecated; use prop_dictionary_keysym_value() instead.") 1191 const char * 1192 prop_dictionary_keysym_cstring_nocopy(prop_dictionary_keysym_t pdk) 1193 { 1194 1195 if (! prop_object_is_dictionary_keysym(pdk)) 1196 return (NULL); 1197 1198 return (pdk->pdk_key); 1199 } 1200 1201 /* 1202 * prop_dictionary_keysym_equals -- 1203 * Return true if the two dictionary key symbols are equivalent. 1204 * Note: We do not compare the object references. 1205 */ 1206 bool 1207 prop_dictionary_keysym_equals(prop_dictionary_keysym_t pdk1, 1208 prop_dictionary_keysym_t pdk2) 1209 { 1210 if (!prop_object_is_dictionary_keysym(pdk1) || 1211 !prop_object_is_dictionary_keysym(pdk2)) 1212 return (false); 1213 1214 return (prop_object_equals(pdk1, pdk2)); 1215 } 1216 1217 /* 1218 * prop_dictionary_externalize -- 1219 * Externalize a dictionary, returning a NUL-terminated buffer 1220 * containing the XML-style representation. The buffer is allocated 1221 * with the M_TEMP memory type. 1222 */ 1223 char * 1224 prop_dictionary_externalize(prop_dictionary_t pd) 1225 { 1226 struct _prop_object_externalize_context *ctx; 1227 char *cp; 1228 1229 ctx = _prop_object_externalize_context_alloc(); 1230 if (ctx == NULL) 1231 return (NULL); 1232 1233 if (_prop_object_externalize_header(ctx) == false || 1234 (*pd->pd_obj.po_type->pot_extern)(ctx, pd) == false || 1235 _prop_object_externalize_footer(ctx) == false) { 1236 /* We are responsible for releasing the buffer. */ 1237 _PROP_FREE(ctx->poec_buf, M_TEMP); 1238 _prop_object_externalize_context_free(ctx); 1239 return (NULL); 1240 } 1241 1242 cp = ctx->poec_buf; 1243 _prop_object_externalize_context_free(ctx); 1244 1245 return (cp); 1246 } 1247 1248 /* 1249 * _prop_dictionary_internalize -- 1250 * Parse a <dict>...</dict> and return the object created from the 1251 * external representation. 1252 * 1253 * Internal state in via rec_data is the storage area for the last processed 1254 * key. 1255 * _prop_dictionary_internalize_body is the upper half of the parse loop. 1256 * It is responsible for parsing the key directly and storing it in the area 1257 * referenced by rec_data. 1258 * _prop_dictionary_internalize_cont is the lower half and called with the value 1259 * associated with the key. 1260 */ 1261 static bool _prop_dictionary_internalize_body(prop_stack_t, 1262 prop_object_t *, struct _prop_object_internalize_context *, char *); 1263 1264 bool 1265 _prop_dictionary_internalize(prop_stack_t stack, prop_object_t *obj, 1266 struct _prop_object_internalize_context *ctx) 1267 { 1268 prop_dictionary_t dict; 1269 char *tmpkey; 1270 1271 /* We don't currently understand any attributes. */ 1272 if (ctx->poic_tagattr != NULL) 1273 return (true); 1274 1275 dict = prop_dictionary_create(); 1276 if (dict == NULL) 1277 return (true); 1278 1279 if (ctx->poic_is_empty_element) { 1280 *obj = dict; 1281 return (true); 1282 } 1283 1284 tmpkey = _PROP_MALLOC(PDK_MAXKEY + 1, M_TEMP); 1285 if (tmpkey == NULL) { 1286 prop_object_release(dict); 1287 return (true); 1288 } 1289 1290 *obj = dict; 1291 /* 1292 * Opening tag is found, storage for key allocated and 1293 * now continue to the first element. 1294 */ 1295 return _prop_dictionary_internalize_body(stack, obj, ctx, tmpkey); 1296 } 1297 1298 static bool 1299 _prop_dictionary_internalize_continue(prop_stack_t stack, prop_object_t *obj, 1300 struct _prop_object_internalize_context *ctx, void *data, prop_object_t child) 1301 { 1302 prop_dictionary_t dict = *obj; 1303 char *tmpkey = data; 1304 1305 _PROP_ASSERT(tmpkey != NULL); 1306 1307 if (child == NULL || 1308 prop_dictionary_set(dict, tmpkey, child) == false) { 1309 _PROP_FREE(tmpkey, M_TEMP); 1310 if (child != NULL) 1311 prop_object_release(child); 1312 prop_object_release(dict); 1313 *obj = NULL; 1314 return (true); 1315 } 1316 1317 prop_object_release(child); 1318 1319 /* 1320 * key, value was added, now continue looking for the next key 1321 * or the closing tag. 1322 */ 1323 return _prop_dictionary_internalize_body(stack, obj, ctx, tmpkey); 1324 } 1325 1326 static bool 1327 _prop_dictionary_internalize_body(prop_stack_t stack, prop_object_t *obj, 1328 struct _prop_object_internalize_context *ctx, char *tmpkey) 1329 { 1330 prop_dictionary_t dict = *obj; 1331 size_t keylen; 1332 1333 /* Fetch the next tag. */ 1334 if (_prop_object_internalize_find_tag(ctx, NULL, _PROP_TAG_TYPE_EITHER) == false) 1335 goto bad; 1336 1337 /* Check to see if this is the end of the dictionary. */ 1338 if (_PROP_TAG_MATCH(ctx, "dict") && 1339 ctx->poic_tag_type == _PROP_TAG_TYPE_END) { 1340 _PROP_FREE(tmpkey, M_TEMP); 1341 return (true); 1342 } 1343 1344 /* Ok, it must be a non-empty key start tag. */ 1345 if (!_PROP_TAG_MATCH(ctx, "key") || 1346 ctx->poic_tag_type != _PROP_TAG_TYPE_START || 1347 ctx->poic_is_empty_element) 1348 goto bad; 1349 1350 if (_prop_object_internalize_decode_string(ctx, 1351 tmpkey, PDK_MAXKEY, &keylen, 1352 &ctx->poic_cp) == false) 1353 goto bad; 1354 1355 _PROP_ASSERT(keylen <= PDK_MAXKEY); 1356 tmpkey[keylen] = '\0'; 1357 1358 if (_prop_object_internalize_find_tag(ctx, "key", 1359 _PROP_TAG_TYPE_END) == false) 1360 goto bad; 1361 1362 /* ..and now the beginning of the value. */ 1363 if (_prop_object_internalize_find_tag(ctx, NULL, 1364 _PROP_TAG_TYPE_START) == false) 1365 goto bad; 1366 1367 /* 1368 * Key is found, now wait for value to be parsed. 1369 */ 1370 if (_prop_stack_push(stack, *obj, 1371 _prop_dictionary_internalize_continue, 1372 tmpkey, NULL)) 1373 return (false); 1374 1375 bad: 1376 _PROP_FREE(tmpkey, M_TEMP); 1377 prop_object_release(dict); 1378 *obj = NULL; 1379 return (true); 1380 } 1381 1382 /* 1383 * prop_dictionary_internalize -- 1384 * Create a dictionary by parsing the NUL-terminated XML-style 1385 * representation. 1386 */ 1387 prop_dictionary_t 1388 prop_dictionary_internalize(const char *xml) 1389 { 1390 return _prop_generic_internalize(xml, "dict"); 1391 } 1392 1393 #if !defined(_KERNEL) && !defined(_STANDALONE) 1394 /* 1395 * prop_dictionary_externalize_to_file -- 1396 * Externalize a dictionary to the specified file. 1397 */ 1398 bool 1399 prop_dictionary_externalize_to_file(prop_dictionary_t dict, const char *fname) 1400 { 1401 char *xml; 1402 bool rv; 1403 int save_errno = 0; /* XXXGCC -Wuninitialized [mips, ...] */ 1404 1405 xml = prop_dictionary_externalize(dict); 1406 if (xml == NULL) 1407 return (false); 1408 rv = _prop_object_externalize_write_file(fname, xml, strlen(xml)); 1409 if (rv == false) 1410 save_errno = errno; 1411 _PROP_FREE(xml, M_TEMP); 1412 if (rv == false) 1413 errno = save_errno; 1414 1415 return (rv); 1416 } 1417 1418 /* 1419 * prop_dictionary_internalize_from_file -- 1420 * Internalize a dictionary from a file. 1421 */ 1422 prop_dictionary_t 1423 prop_dictionary_internalize_from_file(const char *fname) 1424 { 1425 struct _prop_object_internalize_mapped_file *mf; 1426 prop_dictionary_t dict; 1427 1428 mf = _prop_object_internalize_map_file(fname); 1429 if (mf == NULL) 1430 return (NULL); 1431 dict = prop_dictionary_internalize(mf->poimf_xml); 1432 _prop_object_internalize_unmap_file(mf); 1433 1434 return (dict); 1435 } 1436 #endif /* !_KERNEL && !_STANDALONE */ 1437