1 /* $NetBSD: prop_dictionary.c,v 1.42 2020/06/06 21:25:59 thorpej 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 refrences 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 pde = _prop_dict_lookup(pd, key, NULL); 921 if (pde != NULL) { 922 _PROP_ASSERT(pde->pde_objref != NULL); 923 po = pde->pde_objref; 924 } 925 if (!locked) 926 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock); 927 return (po); 928 } 929 /* 930 * prop_dictionary_get -- 931 * Return the object stored with specified key. 932 */ 933 prop_object_t 934 prop_dictionary_get(prop_dictionary_t pd, const char *key) 935 { 936 prop_object_t po = NULL; 937 938 if (! prop_object_is_dictionary(pd)) 939 return (NULL); 940 941 _PROP_RWLOCK_RDLOCK(pd->pd_rwlock); 942 po = _prop_dictionary_get(pd, key, true); 943 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock); 944 return (po); 945 } 946 947 static prop_object_t 948 _prop_dictionary_get_keysym(prop_dictionary_t pd, prop_dictionary_keysym_t pdk, 949 bool locked) 950 { 951 952 if (! (prop_object_is_dictionary(pd) && 953 prop_object_is_dictionary_keysym(pdk))) 954 return (NULL); 955 956 return (_prop_dictionary_get(pd, pdk->pdk_key, locked)); 957 } 958 959 /* 960 * prop_dictionary_get_keysym -- 961 * Return the object stored at the location encoded by the keysym. 962 */ 963 prop_object_t 964 prop_dictionary_get_keysym(prop_dictionary_t pd, prop_dictionary_keysym_t pdk) 965 { 966 967 return (_prop_dictionary_get_keysym(pd, pdk, false)); 968 } 969 970 /* 971 * prop_dictionary_set -- 972 * Store a reference to an object at with the specified key. 973 * If the key already exisit, the original object is released. 974 */ 975 bool 976 prop_dictionary_set(prop_dictionary_t pd, const char *key, prop_object_t po) 977 { 978 struct _prop_dict_entry *pde; 979 prop_dictionary_keysym_t pdk; 980 unsigned int idx; 981 bool rv = false; 982 983 if (! prop_object_is_dictionary(pd)) 984 return (false); 985 986 _PROP_ASSERT(pd->pd_count <= pd->pd_capacity); 987 988 if (prop_dictionary_is_immutable(pd)) 989 return (false); 990 991 _PROP_RWLOCK_WRLOCK(pd->pd_rwlock); 992 993 pde = _prop_dict_lookup(pd, key, &idx); 994 if (pde != NULL) { 995 prop_object_t opo = pde->pde_objref; 996 prop_object_retain(po); 997 pde->pde_objref = po; 998 prop_object_release(opo); 999 rv = true; 1000 goto out; 1001 } 1002 1003 pdk = _prop_dict_keysym_alloc(key); 1004 if (pdk == NULL) 1005 goto out; 1006 1007 if (pd->pd_count == pd->pd_capacity && 1008 _prop_dictionary_expand(pd, 1009 pd->pd_capacity + EXPAND_STEP) == false) { 1010 prop_object_release(pdk); 1011 goto out; 1012 } 1013 1014 /* At this point, the store will succeed. */ 1015 prop_object_retain(po); 1016 1017 if (pd->pd_count == 0) { 1018 pd->pd_array[0].pde_key = pdk; 1019 pd->pd_array[0].pde_objref = po; 1020 pd->pd_count++; 1021 pd->pd_version++; 1022 rv = true; 1023 goto out; 1024 } 1025 1026 pde = &pd->pd_array[idx]; 1027 _PROP_ASSERT(pde->pde_key != NULL); 1028 1029 if (strcmp(key, pde->pde_key->pdk_key) < 0) { 1030 /* 1031 * key < pdk_key: insert to the left. This is the same as 1032 * inserting to the right, except we decrement the current 1033 * index first. 1034 * 1035 * Because we're unsigned, we have to special case 0 1036 * (grumble). 1037 */ 1038 if (idx == 0) { 1039 memmove(&pd->pd_array[1], &pd->pd_array[0], 1040 pd->pd_count * sizeof(*pde)); 1041 pd->pd_array[0].pde_key = pdk; 1042 pd->pd_array[0].pde_objref = po; 1043 pd->pd_count++; 1044 pd->pd_version++; 1045 rv = true; 1046 goto out; 1047 } 1048 idx--; 1049 } 1050 1051 memmove(&pd->pd_array[idx + 2], &pd->pd_array[idx + 1], 1052 (pd->pd_count - (idx + 1)) * sizeof(*pde)); 1053 pd->pd_array[idx + 1].pde_key = pdk; 1054 pd->pd_array[idx + 1].pde_objref = po; 1055 pd->pd_count++; 1056 1057 pd->pd_version++; 1058 1059 rv = true; 1060 1061 out: 1062 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock); 1063 return (rv); 1064 } 1065 1066 /* 1067 * prop_dictionary_set_keysym -- 1068 * Replace the object in the dictionary at the location encoded by 1069 * the keysym. 1070 */ 1071 bool 1072 prop_dictionary_set_keysym(prop_dictionary_t pd, prop_dictionary_keysym_t pdk, 1073 prop_object_t po) 1074 { 1075 1076 if (! (prop_object_is_dictionary(pd) && 1077 prop_object_is_dictionary_keysym(pdk))) 1078 return (false); 1079 1080 return (prop_dictionary_set(pd, pdk->pdk_key, po)); 1081 } 1082 1083 static void 1084 _prop_dictionary_remove(prop_dictionary_t pd, struct _prop_dict_entry *pde, 1085 unsigned int idx) 1086 { 1087 prop_dictionary_keysym_t pdk = pde->pde_key; 1088 prop_object_t po = pde->pde_objref; 1089 1090 /* 1091 * Dictionary must be WRITE-LOCKED. 1092 */ 1093 1094 _PROP_ASSERT(pd->pd_count != 0); 1095 _PROP_ASSERT(idx < pd->pd_count); 1096 _PROP_ASSERT(pde == &pd->pd_array[idx]); 1097 1098 idx++; 1099 memmove(&pd->pd_array[idx - 1], &pd->pd_array[idx], 1100 (pd->pd_count - idx) * sizeof(*pde)); 1101 pd->pd_count--; 1102 pd->pd_version++; 1103 1104 1105 prop_object_release(pdk); 1106 1107 prop_object_release(po); 1108 } 1109 1110 /* 1111 * prop_dictionary_remove -- 1112 * Remove the reference to an object with the specified key from 1113 * the dictionary. 1114 */ 1115 void 1116 prop_dictionary_remove(prop_dictionary_t pd, const char *key) 1117 { 1118 struct _prop_dict_entry *pde; 1119 unsigned int idx; 1120 1121 if (! prop_object_is_dictionary(pd)) 1122 return; 1123 1124 _PROP_RWLOCK_WRLOCK(pd->pd_rwlock); 1125 1126 /* XXX Should this be a _PROP_ASSERT()? */ 1127 if (prop_dictionary_is_immutable(pd)) 1128 goto out; 1129 1130 pde = _prop_dict_lookup(pd, key, &idx); 1131 /* XXX Should this be a _PROP_ASSERT()? */ 1132 if (pde == NULL) 1133 goto out; 1134 1135 _prop_dictionary_remove(pd, pde, idx); 1136 out: 1137 _PROP_RWLOCK_UNLOCK(pd->pd_rwlock); 1138 } 1139 1140 /* 1141 * prop_dictionary_remove_keysym -- 1142 * Remove a reference to an object stored in the dictionary at the 1143 * location encoded by the keysym. 1144 */ 1145 void 1146 prop_dictionary_remove_keysym(prop_dictionary_t pd, 1147 prop_dictionary_keysym_t pdk) 1148 { 1149 1150 if (! (prop_object_is_dictionary(pd) && 1151 prop_object_is_dictionary_keysym(pdk))) 1152 return; 1153 1154 prop_dictionary_remove(pd, pdk->pdk_key); 1155 } 1156 1157 /* 1158 * prop_dictionary_equals -- 1159 * Return true if the two dictionaries are equivalent. Note we do a 1160 * by-value comparison of the objects in the dictionary. 1161 */ 1162 bool 1163 prop_dictionary_equals(prop_dictionary_t dict1, prop_dictionary_t dict2) 1164 { 1165 if (!prop_object_is_dictionary(dict1) || 1166 !prop_object_is_dictionary(dict2)) 1167 return (false); 1168 1169 return (prop_object_equals(dict1, dict2)); 1170 } 1171 1172 /* 1173 * prop_dictionary_keysym_value -- 1174 * Return a reference to the keysym's value. 1175 */ 1176 const char * 1177 prop_dictionary_keysym_value(prop_dictionary_keysym_t pdk) 1178 { 1179 1180 if (! prop_object_is_dictionary_keysym(pdk)) 1181 return (NULL); 1182 1183 return (pdk->pdk_key); 1184 } 1185 1186 _PROP_DEPRECATED(prop_dictionary_keysym_cstring_nocopy, 1187 "this program uses prop_dictionary_keysym_cstring_nocopy(), " 1188 "which is deprecated; use prop_dictionary_keysym_value() instead.") 1189 const char * 1190 prop_dictionary_keysym_cstring_nocopy(prop_dictionary_keysym_t pdk) 1191 { 1192 1193 if (! prop_object_is_dictionary_keysym(pdk)) 1194 return (NULL); 1195 1196 return (pdk->pdk_key); 1197 } 1198 1199 /* 1200 * prop_dictionary_keysym_equals -- 1201 * Return true if the two dictionary key symbols are equivalent. 1202 * Note: We do not compare the object references. 1203 */ 1204 bool 1205 prop_dictionary_keysym_equals(prop_dictionary_keysym_t pdk1, 1206 prop_dictionary_keysym_t pdk2) 1207 { 1208 if (!prop_object_is_dictionary_keysym(pdk1) || 1209 !prop_object_is_dictionary_keysym(pdk2)) 1210 return (false); 1211 1212 return (prop_object_equals(pdk1, pdk2)); 1213 } 1214 1215 /* 1216 * prop_dictionary_externalize -- 1217 * Externalize a dictionary, returning a NUL-terminated buffer 1218 * containing the XML-style representation. The buffer is allocated 1219 * with the M_TEMP memory type. 1220 */ 1221 char * 1222 prop_dictionary_externalize(prop_dictionary_t pd) 1223 { 1224 struct _prop_object_externalize_context *ctx; 1225 char *cp; 1226 1227 ctx = _prop_object_externalize_context_alloc(); 1228 if (ctx == NULL) 1229 return (NULL); 1230 1231 if (_prop_object_externalize_header(ctx) == false || 1232 (*pd->pd_obj.po_type->pot_extern)(ctx, pd) == false || 1233 _prop_object_externalize_footer(ctx) == false) { 1234 /* We are responsible for releasing the buffer. */ 1235 _PROP_FREE(ctx->poec_buf, M_TEMP); 1236 _prop_object_externalize_context_free(ctx); 1237 return (NULL); 1238 } 1239 1240 cp = ctx->poec_buf; 1241 _prop_object_externalize_context_free(ctx); 1242 1243 return (cp); 1244 } 1245 1246 /* 1247 * _prop_dictionary_internalize -- 1248 * Parse a <dict>...</dict> and return the object created from the 1249 * external representation. 1250 * 1251 * Internal state in via rec_data is the storage area for the last processed 1252 * key. 1253 * _prop_dictionary_internalize_body is the upper half of the parse loop. 1254 * It is responsible for parsing the key directly and storing it in the area 1255 * referenced by rec_data. 1256 * _prop_dictionary_internalize_cont is the lower half and called with the value 1257 * associated with the key. 1258 */ 1259 static bool _prop_dictionary_internalize_body(prop_stack_t, 1260 prop_object_t *, struct _prop_object_internalize_context *, char *); 1261 1262 bool 1263 _prop_dictionary_internalize(prop_stack_t stack, prop_object_t *obj, 1264 struct _prop_object_internalize_context *ctx) 1265 { 1266 prop_dictionary_t dict; 1267 char *tmpkey; 1268 1269 /* We don't currently understand any attributes. */ 1270 if (ctx->poic_tagattr != NULL) 1271 return (true); 1272 1273 dict = prop_dictionary_create(); 1274 if (dict == NULL) 1275 return (true); 1276 1277 if (ctx->poic_is_empty_element) { 1278 *obj = dict; 1279 return (true); 1280 } 1281 1282 tmpkey = _PROP_MALLOC(PDK_MAXKEY + 1, M_TEMP); 1283 if (tmpkey == NULL) { 1284 prop_object_release(dict); 1285 return (true); 1286 } 1287 1288 *obj = dict; 1289 /* 1290 * Opening tag is found, storage for key allocated and 1291 * now continue to the first element. 1292 */ 1293 return _prop_dictionary_internalize_body(stack, obj, ctx, tmpkey); 1294 } 1295 1296 static bool 1297 _prop_dictionary_internalize_continue(prop_stack_t stack, prop_object_t *obj, 1298 struct _prop_object_internalize_context *ctx, void *data, prop_object_t child) 1299 { 1300 prop_dictionary_t dict = *obj; 1301 char *tmpkey = data; 1302 1303 _PROP_ASSERT(tmpkey != NULL); 1304 1305 if (child == NULL || 1306 prop_dictionary_set(dict, tmpkey, child) == false) { 1307 _PROP_FREE(tmpkey, M_TEMP); 1308 if (child != NULL) 1309 prop_object_release(child); 1310 prop_object_release(dict); 1311 *obj = NULL; 1312 return (true); 1313 } 1314 1315 prop_object_release(child); 1316 1317 /* 1318 * key, value was added, now continue looking for the next key 1319 * or the closing tag. 1320 */ 1321 return _prop_dictionary_internalize_body(stack, obj, ctx, tmpkey); 1322 } 1323 1324 static bool 1325 _prop_dictionary_internalize_body(prop_stack_t stack, prop_object_t *obj, 1326 struct _prop_object_internalize_context *ctx, char *tmpkey) 1327 { 1328 prop_dictionary_t dict = *obj; 1329 size_t keylen; 1330 1331 /* Fetch the next tag. */ 1332 if (_prop_object_internalize_find_tag(ctx, NULL, _PROP_TAG_TYPE_EITHER) == false) 1333 goto bad; 1334 1335 /* Check to see if this is the end of the dictionary. */ 1336 if (_PROP_TAG_MATCH(ctx, "dict") && 1337 ctx->poic_tag_type == _PROP_TAG_TYPE_END) { 1338 _PROP_FREE(tmpkey, M_TEMP); 1339 return (true); 1340 } 1341 1342 /* Ok, it must be a non-empty key start tag. */ 1343 if (!_PROP_TAG_MATCH(ctx, "key") || 1344 ctx->poic_tag_type != _PROP_TAG_TYPE_START || 1345 ctx->poic_is_empty_element) 1346 goto bad; 1347 1348 if (_prop_object_internalize_decode_string(ctx, 1349 tmpkey, PDK_MAXKEY, &keylen, 1350 &ctx->poic_cp) == false) 1351 goto bad; 1352 1353 _PROP_ASSERT(keylen <= PDK_MAXKEY); 1354 tmpkey[keylen] = '\0'; 1355 1356 if (_prop_object_internalize_find_tag(ctx, "key", 1357 _PROP_TAG_TYPE_END) == false) 1358 goto bad; 1359 1360 /* ..and now the beginning of the value. */ 1361 if (_prop_object_internalize_find_tag(ctx, NULL, 1362 _PROP_TAG_TYPE_START) == false) 1363 goto bad; 1364 1365 /* 1366 * Key is found, now wait for value to be parsed. 1367 */ 1368 if (_prop_stack_push(stack, *obj, 1369 _prop_dictionary_internalize_continue, 1370 tmpkey, NULL)) 1371 return (false); 1372 1373 bad: 1374 _PROP_FREE(tmpkey, M_TEMP); 1375 prop_object_release(dict); 1376 *obj = NULL; 1377 return (true); 1378 } 1379 1380 /* 1381 * prop_dictionary_internalize -- 1382 * Create a dictionary by parsing the NUL-terminated XML-style 1383 * representation. 1384 */ 1385 prop_dictionary_t 1386 prop_dictionary_internalize(const char *xml) 1387 { 1388 return _prop_generic_internalize(xml, "dict"); 1389 } 1390 1391 #if !defined(_KERNEL) && !defined(_STANDALONE) 1392 /* 1393 * prop_dictionary_externalize_to_file -- 1394 * Externalize a dictionary to the specified file. 1395 */ 1396 bool 1397 prop_dictionary_externalize_to_file(prop_dictionary_t dict, const char *fname) 1398 { 1399 char *xml; 1400 bool rv; 1401 int save_errno = 0; /* XXXGCC -Wuninitialized [mips, ...] */ 1402 1403 xml = prop_dictionary_externalize(dict); 1404 if (xml == NULL) 1405 return (false); 1406 rv = _prop_object_externalize_write_file(fname, xml, strlen(xml)); 1407 if (rv == false) 1408 save_errno = errno; 1409 _PROP_FREE(xml, M_TEMP); 1410 if (rv == false) 1411 errno = save_errno; 1412 1413 return (rv); 1414 } 1415 1416 /* 1417 * prop_dictionary_internalize_from_file -- 1418 * Internalize a dictionary from a file. 1419 */ 1420 prop_dictionary_t 1421 prop_dictionary_internalize_from_file(const char *fname) 1422 { 1423 struct _prop_object_internalize_mapped_file *mf; 1424 prop_dictionary_t dict; 1425 1426 mf = _prop_object_internalize_map_file(fname); 1427 if (mf == NULL) 1428 return (NULL); 1429 dict = prop_dictionary_internalize(mf->poimf_xml); 1430 _prop_object_internalize_unmap_file(mf); 1431 1432 return (dict); 1433 } 1434 #endif /* !_KERNEL && !_STANDALONE */ 1435