1 /* $NetBSD: prop_array.c,v 1.16 2008/05/07 10:16:41 tron 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 <prop/prop_array.h> 33 #include "prop_object_impl.h" 34 35 #if !defined(_KERNEL) && !defined(_STANDALONE) 36 #include <errno.h> 37 #endif 38 39 struct _prop_array { 40 struct _prop_object pa_obj; 41 _PROP_RWLOCK_DECL(pa_rwlock) 42 prop_object_t * pa_array; 43 unsigned int pa_capacity; 44 unsigned int pa_count; 45 int pa_flags; 46 47 uint32_t pa_version; 48 }; 49 50 #define PA_F_IMMUTABLE 0x01 /* array is immutable */ 51 52 _PROP_POOL_INIT(_prop_array_pool, sizeof(struct _prop_array), "proparay") 53 _PROP_MALLOC_DEFINE(M_PROP_ARRAY, "prop array", 54 "property array container object") 55 56 static int _prop_array_free(prop_stack_t, prop_object_t *); 57 static void _prop_array_emergency_free(prop_object_t); 58 static bool _prop_array_externalize( 59 struct _prop_object_externalize_context *, 60 void *); 61 static bool _prop_array_equals(prop_object_t, prop_object_t, 62 void **, void **, 63 prop_object_t *, prop_object_t *); 64 static void _prop_array_equals_finish(prop_object_t, prop_object_t); 65 66 static const struct _prop_object_type _prop_object_type_array = { 67 .pot_type = PROP_TYPE_ARRAY, 68 .pot_free = _prop_array_free, 69 .pot_emergency_free = _prop_array_emergency_free, 70 .pot_extern = _prop_array_externalize, 71 .pot_equals = _prop_array_equals, 72 .pot_equals_finish = _prop_array_equals_finish, 73 }; 74 75 #define prop_object_is_array(x) \ 76 ((x) != NULL && (x)->pa_obj.po_type == &_prop_object_type_array) 77 78 #define prop_array_is_immutable(x) (((x)->pa_flags & PA_F_IMMUTABLE) != 0) 79 80 struct _prop_array_iterator { 81 struct _prop_object_iterator pai_base; 82 unsigned int pai_index; 83 }; 84 85 #define EXPAND_STEP 16 86 87 static int 88 _prop_array_free(prop_stack_t stack, prop_object_t *obj) 89 { 90 prop_array_t pa = *obj; 91 prop_object_t po; 92 93 _PROP_ASSERT(pa->pa_count <= pa->pa_capacity); 94 _PROP_ASSERT((pa->pa_capacity == 0 && pa->pa_array == NULL) || 95 (pa->pa_capacity != 0 && pa->pa_array != NULL)); 96 97 /* The easy case is an empty array, just free and return. */ 98 if (pa->pa_count == 0) { 99 if (pa->pa_array != NULL) 100 _PROP_FREE(pa->pa_array, M_PROP_ARRAY); 101 102 _PROP_RWLOCK_DESTROY(pa->pa_rwlock); 103 104 _PROP_POOL_PUT(_prop_array_pool, pa); 105 106 return (_PROP_OBJECT_FREE_DONE); 107 } 108 109 po = pa->pa_array[pa->pa_count - 1]; 110 _PROP_ASSERT(po != NULL); 111 112 if (stack == NULL) { 113 /* 114 * If we are in emergency release mode, 115 * just let caller recurse down. 116 */ 117 *obj = po; 118 return (_PROP_OBJECT_FREE_FAILED); 119 } 120 121 /* Otherwise, try to push the current object on the stack. */ 122 if (!_prop_stack_push(stack, pa, NULL, NULL, NULL)) { 123 /* Push failed, entering emergency release mode. */ 124 return (_PROP_OBJECT_FREE_FAILED); 125 } 126 /* Object pushed on stack, caller will release it. */ 127 --pa->pa_count; 128 *obj = po; 129 return (_PROP_OBJECT_FREE_RECURSE); 130 } 131 132 static void 133 _prop_array_emergency_free(prop_object_t obj) 134 { 135 prop_array_t pa = obj; 136 137 _PROP_ASSERT(pa->pa_count != 0); 138 --pa->pa_count; 139 } 140 141 static bool 142 _prop_array_externalize(struct _prop_object_externalize_context *ctx, 143 void *v) 144 { 145 prop_array_t pa = v; 146 struct _prop_object *po; 147 prop_object_iterator_t pi; 148 unsigned int i; 149 bool rv = false; 150 151 _PROP_RWLOCK_RDLOCK(pa->pa_rwlock); 152 153 if (pa->pa_count == 0) { 154 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); 155 return (_prop_object_externalize_empty_tag(ctx, "array")); 156 } 157 158 /* XXXJRT Hint "count" for the internalize step? */ 159 if (_prop_object_externalize_start_tag(ctx, "array") == false || 160 _prop_object_externalize_append_char(ctx, '\n') == false) 161 goto out; 162 163 pi = prop_array_iterator(pa); 164 if (pi == NULL) 165 goto out; 166 167 ctx->poec_depth++; 168 _PROP_ASSERT(ctx->poec_depth != 0); 169 170 while ((po = prop_object_iterator_next(pi)) != NULL) { 171 if ((*po->po_type->pot_extern)(ctx, po) == false) { 172 prop_object_iterator_release(pi); 173 goto out; 174 } 175 } 176 177 prop_object_iterator_release(pi); 178 179 ctx->poec_depth--; 180 for (i = 0; i < ctx->poec_depth; i++) { 181 if (_prop_object_externalize_append_char(ctx, '\t') == false) 182 goto out; 183 } 184 if (_prop_object_externalize_end_tag(ctx, "array") == false) 185 goto out; 186 187 rv = true; 188 189 out: 190 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); 191 return (rv); 192 } 193 194 /* ARGSUSED */ 195 static bool 196 _prop_array_equals(prop_object_t v1, prop_object_t v2, 197 void **stored_pointer1, void **stored_pointer2, 198 prop_object_t *next_obj1, prop_object_t *next_obj2) 199 { 200 prop_array_t array1 = v1; 201 prop_array_t array2 = v2; 202 uintptr_t idx; 203 bool rv = _PROP_OBJECT_EQUALS_FALSE; 204 205 if (array1 == array2) 206 return (_PROP_OBJECT_EQUALS_TRUE); 207 208 _PROP_ASSERT(*stored_pointer1 == *stored_pointer2); 209 idx = (uintptr_t)*stored_pointer1; 210 211 /* For the first iteration, lock the objects. */ 212 if (idx == 0) { 213 if ((uintptr_t)array1 < (uintptr_t)array2) { 214 _PROP_RWLOCK_RDLOCK(array1->pa_rwlock); 215 _PROP_RWLOCK_RDLOCK(array2->pa_rwlock); 216 } else { 217 _PROP_RWLOCK_RDLOCK(array2->pa_rwlock); 218 _PROP_RWLOCK_RDLOCK(array1->pa_rwlock); 219 } 220 } 221 222 if (array1->pa_count != array2->pa_count) 223 goto out; 224 if (idx == array1->pa_count) { 225 rv = true; 226 goto out; 227 } 228 _PROP_ASSERT(idx < array1->pa_count); 229 230 *stored_pointer1 = (void *)(idx + 1); 231 *stored_pointer2 = (void *)(idx + 1); 232 233 *next_obj1 = array1->pa_array[idx]; 234 *next_obj2 = array2->pa_array[idx]; 235 236 return (_PROP_OBJECT_EQUALS_RECURSE); 237 238 out: 239 _PROP_RWLOCK_UNLOCK(array1->pa_rwlock); 240 _PROP_RWLOCK_UNLOCK(array2->pa_rwlock); 241 return (rv); 242 } 243 244 static void 245 _prop_array_equals_finish(prop_object_t v1, prop_object_t v2) 246 { 247 _PROP_RWLOCK_UNLOCK(((prop_array_t)v1)->pa_rwlock); 248 _PROP_RWLOCK_UNLOCK(((prop_array_t)v2)->pa_rwlock); 249 } 250 251 static prop_array_t 252 _prop_array_alloc(unsigned int capacity) 253 { 254 prop_array_t pa; 255 prop_object_t *array; 256 257 if (capacity != 0) { 258 array = _PROP_CALLOC(capacity * sizeof(prop_object_t), 259 M_PROP_ARRAY); 260 if (array == NULL) 261 return (NULL); 262 } else 263 array = NULL; 264 265 266 pa = _PROP_POOL_GET(_prop_array_pool); 267 if (pa != NULL) { 268 _prop_object_init(&pa->pa_obj, &_prop_object_type_array); 269 pa->pa_obj.po_type = &_prop_object_type_array; 270 271 _PROP_RWLOCK_INIT(pa->pa_rwlock); 272 pa->pa_array = array; 273 pa->pa_capacity = capacity; 274 pa->pa_count = 0; 275 pa->pa_flags = 0; 276 277 pa->pa_version = 0; 278 } else if (array != NULL) 279 _PROP_FREE(array, M_PROP_ARRAY); 280 281 return (pa); 282 } 283 284 static bool 285 _prop_array_expand(prop_array_t pa, unsigned int capacity) 286 { 287 prop_object_t *array, *oarray; 288 289 /* 290 * Array must be WRITE-LOCKED. 291 */ 292 293 oarray = pa->pa_array; 294 295 array = _PROP_CALLOC(capacity * sizeof(*array), M_PROP_ARRAY); 296 if (array == NULL) 297 return (false); 298 if (oarray != NULL) 299 memcpy(array, oarray, pa->pa_capacity * sizeof(*array)); 300 pa->pa_array = array; 301 pa->pa_capacity = capacity; 302 303 if (oarray != NULL) 304 _PROP_FREE(oarray, M_PROP_ARRAY); 305 306 return (true); 307 } 308 309 static prop_object_t 310 _prop_array_iterator_next_object(void *v) 311 { 312 struct _prop_array_iterator *pai = v; 313 prop_array_t pa = pai->pai_base.pi_obj; 314 prop_object_t po = NULL; 315 bool acquired; 316 317 _PROP_ASSERT(prop_object_is_array(pa)); 318 acquired = _PROP_RWLOCK_TRYRDLOCK(pa->pa_rwlock); 319 _PROP_RWLOCK_OWNED(pa->pa_rwlock); 320 321 if (pa->pa_version != pai->pai_base.pi_version) 322 goto out; /* array changed during iteration */ 323 324 _PROP_ASSERT(pai->pai_index <= pa->pa_count); 325 326 if (pai->pai_index == pa->pa_count) 327 goto out; /* we've iterated all objects */ 328 329 po = pa->pa_array[pai->pai_index]; 330 pai->pai_index++; 331 332 out: 333 if (acquired) { 334 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); 335 } 336 return (po); 337 } 338 339 static void 340 _prop_array_iterator_reset(void *v) 341 { 342 struct _prop_array_iterator *pai = v; 343 prop_array_t pa = pai->pai_base.pi_obj; 344 bool acquired; 345 346 _PROP_ASSERT(prop_object_is_array(pa)); 347 acquired = _PROP_RWLOCK_TRYRDLOCK(pa->pa_rwlock); 348 _PROP_RWLOCK_OWNED(pa->pa_rwlock); 349 350 pai->pai_index = 0; 351 pai->pai_base.pi_version = pa->pa_version; 352 353 if (acquired) { 354 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); 355 } 356 } 357 358 /* 359 * prop_array_create -- 360 * Create an empty array. 361 */ 362 prop_array_t 363 prop_array_create(void) 364 { 365 366 return (_prop_array_alloc(0)); 367 } 368 369 /* 370 * prop_array_create_with_capacity -- 371 * Create an array with the capacity to store N objects. 372 */ 373 prop_array_t 374 prop_array_create_with_capacity(unsigned int capacity) 375 { 376 377 return (_prop_array_alloc(capacity)); 378 } 379 380 /* 381 * prop_array_copy -- 382 * Copy an array. The new array has an initial capacity equal to 383 * the number of objects stored in the original array. The new 384 * array contains references to the original array's objects, not 385 * copies of those objects (i.e. a shallow copy). 386 */ 387 prop_array_t 388 prop_array_copy(prop_array_t opa) 389 { 390 prop_array_t pa; 391 prop_object_t po; 392 unsigned int idx; 393 394 if (! prop_object_is_array(opa)) 395 return (NULL); 396 397 _PROP_RWLOCK_RDLOCK(opa->pa_rwlock); 398 399 pa = _prop_array_alloc(opa->pa_count); 400 if (pa != NULL) { 401 for (idx = 0; idx < opa->pa_count; idx++) { 402 po = opa->pa_array[idx]; 403 prop_object_retain(po); 404 pa->pa_array[idx] = po; 405 } 406 pa->pa_count = opa->pa_count; 407 pa->pa_flags = opa->pa_flags; 408 } 409 _PROP_RWLOCK_UNLOCK(opa->pa_rwlock); 410 return (pa); 411 } 412 413 /* 414 * prop_array_copy_mutable -- 415 * Like prop_array_copy(), but the resulting array is mutable. 416 */ 417 prop_array_t 418 prop_array_copy_mutable(prop_array_t opa) 419 { 420 prop_array_t pa; 421 422 pa = prop_array_copy(opa); 423 if (pa != NULL) 424 pa->pa_flags &= ~PA_F_IMMUTABLE; 425 426 return (pa); 427 } 428 429 /* 430 * prop_array_capacity -- 431 * Return the capacity of the array. 432 */ 433 unsigned int 434 prop_array_capacity(prop_array_t pa) 435 { 436 unsigned int rv; 437 438 if (! prop_object_is_array(pa)) 439 return (0); 440 441 _PROP_RWLOCK_RDLOCK(pa->pa_rwlock); 442 rv = pa->pa_capacity; 443 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); 444 445 return (rv); 446 } 447 448 /* 449 * prop_array_count -- 450 * Return the number of objects stored in the array. 451 */ 452 unsigned int 453 prop_array_count(prop_array_t pa) 454 { 455 unsigned int rv; 456 457 if (! prop_object_is_array(pa)) 458 return (0); 459 460 _PROP_RWLOCK_RDLOCK(pa->pa_rwlock); 461 rv = pa->pa_count; 462 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); 463 464 return (rv); 465 } 466 467 /* 468 * prop_array_ensure_capacity -- 469 * Ensure that the array has the capacity to store the specified 470 * total number of objects (inluding the objects already stored 471 * in the array). 472 */ 473 bool 474 prop_array_ensure_capacity(prop_array_t pa, unsigned int capacity) 475 { 476 bool rv; 477 478 if (! prop_object_is_array(pa)) 479 return (false); 480 481 _PROP_RWLOCK_WRLOCK(pa->pa_rwlock); 482 if (capacity > pa->pa_capacity) 483 rv = _prop_array_expand(pa, capacity); 484 else 485 rv = true; 486 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); 487 488 return (rv); 489 } 490 491 /* 492 * prop_array_iterator -- 493 * Return an iterator for the array. The array is retained by 494 * the iterator. 495 */ 496 prop_object_iterator_t 497 prop_array_iterator(prop_array_t pa) 498 { 499 struct _prop_array_iterator *pai; 500 501 if (! prop_object_is_array(pa)) 502 return (NULL); 503 504 pai = _PROP_CALLOC(sizeof(*pai), M_TEMP); 505 if (pai == NULL) 506 return (NULL); 507 pai->pai_base.pi_next_object = _prop_array_iterator_next_object; 508 pai->pai_base.pi_reset = _prop_array_iterator_reset; 509 prop_object_retain(pa); 510 pai->pai_base.pi_obj = pa; 511 _PROP_RWLOCK_RDLOCK(pa->pa_rwlock); 512 _prop_array_iterator_reset(pai); 513 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); 514 515 return (&pai->pai_base); 516 } 517 518 /* 519 * prop_array_make_immutable -- 520 * Make the array immutable. 521 */ 522 void 523 prop_array_make_immutable(prop_array_t pa) 524 { 525 526 _PROP_RWLOCK_WRLOCK(pa->pa_rwlock); 527 if (prop_array_is_immutable(pa) == false) 528 pa->pa_flags |= PA_F_IMMUTABLE; 529 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); 530 } 531 532 /* 533 * prop_array_mutable -- 534 * Returns true if the array is mutable. 535 */ 536 bool 537 prop_array_mutable(prop_array_t pa) 538 { 539 bool rv; 540 541 _PROP_RWLOCK_RDLOCK(pa->pa_rwlock); 542 rv = prop_array_is_immutable(pa) == false; 543 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); 544 545 return (rv); 546 } 547 548 /* 549 * prop_array_get -- 550 * Return the object stored at the specified array index. 551 */ 552 prop_object_t 553 prop_array_get(prop_array_t pa, unsigned int idx) 554 { 555 prop_object_t po = NULL; 556 557 if (! prop_object_is_array(pa)) 558 return (NULL); 559 560 _PROP_RWLOCK_RDLOCK(pa->pa_rwlock); 561 if (idx >= pa->pa_count) 562 goto out; 563 po = pa->pa_array[idx]; 564 _PROP_ASSERT(po != NULL); 565 out: 566 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); 567 return (po); 568 } 569 570 static bool 571 _prop_array_add(prop_array_t pa, prop_object_t po) 572 { 573 574 /* 575 * Array must be WRITE-LOCKED. 576 */ 577 578 _PROP_ASSERT(pa->pa_count <= pa->pa_capacity); 579 580 if (prop_array_is_immutable(pa) || 581 (pa->pa_count == pa->pa_capacity && 582 _prop_array_expand(pa, pa->pa_capacity + EXPAND_STEP) == false)) 583 return (false); 584 585 prop_object_retain(po); 586 pa->pa_array[pa->pa_count++] = po; 587 pa->pa_version++; 588 589 return (true); 590 } 591 592 /* 593 * prop_array_set -- 594 * Store a reference to an object at the specified array index. 595 * This method is not allowed to create holes in the array; the 596 * caller must either be setting the object just beyond the existing 597 * count or replacing an already existing object reference. 598 */ 599 bool 600 prop_array_set(prop_array_t pa, unsigned int idx, prop_object_t po) 601 { 602 prop_object_t opo; 603 bool rv = false; 604 605 if (! prop_object_is_array(pa)) 606 return (false); 607 608 _PROP_RWLOCK_WRLOCK(pa->pa_rwlock); 609 610 if (prop_array_is_immutable(pa)) 611 goto out; 612 613 if (idx == pa->pa_count) { 614 rv = _prop_array_add(pa, po); 615 goto out; 616 } 617 618 _PROP_ASSERT(idx < pa->pa_count); 619 620 opo = pa->pa_array[idx]; 621 _PROP_ASSERT(opo != NULL); 622 623 prop_object_retain(po); 624 pa->pa_array[idx] = po; 625 pa->pa_version++; 626 627 prop_object_release(opo); 628 629 rv = true; 630 631 out: 632 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); 633 return (rv); 634 } 635 636 /* 637 * prop_array_add -- 638 * Add a refrerence to an object to the specified array, appending 639 * to the end and growing the array's capacity, if necessary. 640 */ 641 bool 642 prop_array_add(prop_array_t pa, prop_object_t po) 643 { 644 bool rv; 645 646 if (! prop_object_is_array(pa)) 647 return (false); 648 649 _PROP_RWLOCK_WRLOCK(pa->pa_rwlock); 650 rv = _prop_array_add(pa, po); 651 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); 652 653 return (rv); 654 } 655 656 /* 657 * prop_array_remove -- 658 * Remove the reference to an object from an array at the specified 659 * index. The array will be compacted following the removal. 660 */ 661 void 662 prop_array_remove(prop_array_t pa, unsigned int idx) 663 { 664 prop_object_t po; 665 666 if (! prop_object_is_array(pa)) 667 return; 668 669 _PROP_RWLOCK_WRLOCK(pa->pa_rwlock); 670 671 _PROP_ASSERT(idx < pa->pa_count); 672 673 /* XXX Should this be a _PROP_ASSERT()? */ 674 if (prop_array_is_immutable(pa)) { 675 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); 676 return; 677 } 678 679 po = pa->pa_array[idx]; 680 _PROP_ASSERT(po != NULL); 681 682 for (++idx; idx < pa->pa_count; idx++) 683 pa->pa_array[idx - 1] = pa->pa_array[idx]; 684 pa->pa_count--; 685 pa->pa_version++; 686 687 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); 688 689 prop_object_release(po); 690 } 691 692 /* 693 * prop_array_equals -- 694 * Return true if the two arrays are equivalent. Note we do a 695 * by-value comparison of the objects in the array. 696 */ 697 bool 698 prop_array_equals(prop_array_t array1, prop_array_t array2) 699 { 700 if (!prop_object_is_array(array1) || !prop_object_is_array(array2)) 701 return (false); 702 703 return (prop_object_equals(array1, array2)); 704 } 705 706 /* 707 * prop_array_externalize -- 708 * Externalize an array, return a NUL-terminated buffer 709 * containing the XML-style representation. The buffer is allocated 710 * with the M_TEMP memory type. 711 */ 712 char * 713 prop_array_externalize(prop_array_t pa) 714 { 715 struct _prop_object_externalize_context *ctx; 716 char *cp; 717 718 ctx = _prop_object_externalize_context_alloc(); 719 if (ctx == NULL) 720 return (NULL); 721 722 if (_prop_object_externalize_header(ctx) == false || 723 (*pa->pa_obj.po_type->pot_extern)(ctx, pa) == false || 724 _prop_object_externalize_footer(ctx) == false) { 725 /* We are responsible for releasing the buffer. */ 726 _PROP_FREE(ctx->poec_buf, M_TEMP); 727 _prop_object_externalize_context_free(ctx); 728 return (NULL); 729 } 730 731 cp = ctx->poec_buf; 732 _prop_object_externalize_context_free(ctx); 733 734 return (cp); 735 } 736 737 /* 738 * _prop_array_internalize -- 739 * Parse an <array>...</array> and return the object created from the 740 * external representation. 741 */ 742 static bool _prop_array_internalize_body(prop_stack_t, prop_object_t *, 743 struct _prop_object_internalize_context *); 744 745 bool 746 _prop_array_internalize(prop_stack_t stack, prop_object_t *obj, 747 struct _prop_object_internalize_context *ctx) 748 { 749 /* We don't currently understand any attributes. */ 750 if (ctx->poic_tagattr != NULL) 751 return (true); 752 753 *obj = prop_array_create(); 754 /* 755 * We are done if the create failed or no child elements exist. 756 */ 757 if (*obj == NULL || ctx->poic_is_empty_element) 758 return (true); 759 760 /* 761 * Opening tag is found, now continue to the first element. 762 */ 763 return (_prop_array_internalize_body(stack, obj, ctx)); 764 } 765 766 static bool 767 _prop_array_internalize_continue(prop_stack_t stack, 768 prop_object_t *obj, 769 struct _prop_object_internalize_context *ctx, 770 void *data, prop_object_t child) 771 { 772 prop_array_t array; 773 774 _PROP_ASSERT(data == NULL); 775 776 if (child == NULL) 777 goto bad; /* Element could not be parsed. */ 778 779 array = *obj; 780 781 if (prop_array_add(array, child) == false) { 782 prop_object_release(child); 783 goto bad; 784 } 785 prop_object_release(child); 786 787 /* 788 * Current element is processed and added, look for next. 789 */ 790 return (_prop_array_internalize_body(stack, obj, ctx)); 791 792 bad: 793 prop_object_release(*obj); 794 *obj = NULL; 795 return (true); 796 } 797 798 static bool 799 _prop_array_internalize_body(prop_stack_t stack, prop_object_t *obj, 800 struct _prop_object_internalize_context *ctx) 801 { 802 prop_array_t array = *obj; 803 804 _PROP_ASSERT(array != NULL); 805 806 /* Fetch the next tag. */ 807 if (_prop_object_internalize_find_tag(ctx, NULL, 808 _PROP_TAG_TYPE_EITHER) == false) 809 goto bad; 810 811 /* Check to see if this is the end of the array. */ 812 if (_PROP_TAG_MATCH(ctx, "array") && 813 ctx->poic_tag_type == _PROP_TAG_TYPE_END) { 814 /* It is, so don't iterate any further. */ 815 return (true); 816 } 817 818 if (_prop_stack_push(stack, array, 819 _prop_array_internalize_continue, NULL, NULL)) 820 return (false); 821 822 bad: 823 prop_object_release(array); 824 *obj = NULL; 825 return (true); 826 } 827 828 /* 829 * prop_array_internalize -- 830 * Create an array by parsing the XML-style representation. 831 */ 832 prop_array_t 833 prop_array_internalize(const char *xml) 834 { 835 return _prop_generic_internalize(xml, "array"); 836 } 837 838 #if !defined(_KERNEL) && !defined(_STANDALONE) 839 /* 840 * prop_array_externalize_to_file -- 841 * Externalize an array to the specified file. 842 */ 843 bool 844 prop_array_externalize_to_file(prop_array_t array, const char *fname) 845 { 846 char *xml; 847 bool rv; 848 int save_errno = 0; /* XXXGCC -Wuninitialized [mips, ...] */ 849 850 xml = prop_array_externalize(array); 851 if (xml == NULL) 852 return (false); 853 rv = _prop_object_externalize_write_file(fname, xml, strlen(xml)); 854 if (rv == false) 855 save_errno = errno; 856 _PROP_FREE(xml, M_TEMP); 857 if (rv == false) 858 errno = save_errno; 859 860 return (rv); 861 } 862 863 /* 864 * prop_array_internalize_from_file -- 865 * Internalize an array from a file. 866 */ 867 prop_array_t 868 prop_array_internalize_from_file(const char *fname) 869 { 870 struct _prop_object_internalize_mapped_file *mf; 871 prop_array_t array; 872 873 mf = _prop_object_internalize_map_file(fname); 874 if (mf == NULL) 875 return (NULL); 876 array = prop_array_internalize(mf->poimf_xml); 877 _prop_object_internalize_unmap_file(mf); 878 879 return (array); 880 } 881 #endif /* _KERNEL && !_STANDALONE */ 882