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