prop_array.c revision 1.17
1/* $NetBSD: prop_array.c,v 1.17 2008/05/24 14:24:04 yamt 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 39struct _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 56static int _prop_array_free(prop_stack_t, prop_object_t *); 57static void _prop_array_emergency_free(prop_object_t); 58static bool _prop_array_externalize( 59 struct _prop_object_externalize_context *, 60 void *); 61static bool _prop_array_equals(prop_object_t, prop_object_t, 62 void **, void **, 63 prop_object_t *, prop_object_t *); 64static void _prop_array_equals_finish(prop_object_t, prop_object_t); 65 66static 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 80struct _prop_array_iterator { 81 struct _prop_object_iterator pai_base; 82 unsigned int pai_index; 83}; 84 85#define EXPAND_STEP 16 86 87static 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 132static 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 141static 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 */ 195static 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 244static 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 251static 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 284static 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 309static 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 316 _PROP_ASSERT(prop_object_is_array(pa)); 317 318 _PROP_RWLOCK_RDLOCK(pa->pa_rwlock); 319 320 if (pa->pa_version != pai->pai_base.pi_version) 321 goto out; /* array changed during iteration */ 322 323 _PROP_ASSERT(pai->pai_index <= pa->pa_count); 324 325 if (pai->pai_index == pa->pa_count) 326 goto out; /* we've iterated all objects */ 327 328 po = pa->pa_array[pai->pai_index]; 329 pai->pai_index++; 330 331 out: 332 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); 333 return (po); 334} 335 336static void 337_prop_array_iterator_reset(void *v) 338{ 339 struct _prop_array_iterator *pai = v; 340 prop_array_t pa = pai->pai_base.pi_obj; 341 342 _PROP_ASSERT(prop_object_is_array(pa)); 343 344 _PROP_RWLOCK_RDLOCK(pa->pa_rwlock); 345 346 pai->pai_index = 0; 347 pai->pai_base.pi_version = pa->pa_version; 348 349 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); 350} 351 352/* 353 * prop_array_create -- 354 * Create an empty array. 355 */ 356prop_array_t 357prop_array_create(void) 358{ 359 360 return (_prop_array_alloc(0)); 361} 362 363/* 364 * prop_array_create_with_capacity -- 365 * Create an array with the capacity to store N objects. 366 */ 367prop_array_t 368prop_array_create_with_capacity(unsigned int capacity) 369{ 370 371 return (_prop_array_alloc(capacity)); 372} 373 374/* 375 * prop_array_copy -- 376 * Copy an array. The new array has an initial capacity equal to 377 * the number of objects stored in the original array. The new 378 * array contains references to the original array's objects, not 379 * copies of those objects (i.e. a shallow copy). 380 */ 381prop_array_t 382prop_array_copy(prop_array_t opa) 383{ 384 prop_array_t pa; 385 prop_object_t po; 386 unsigned int idx; 387 388 if (! prop_object_is_array(opa)) 389 return (NULL); 390 391 _PROP_RWLOCK_RDLOCK(opa->pa_rwlock); 392 393 pa = _prop_array_alloc(opa->pa_count); 394 if (pa != NULL) { 395 for (idx = 0; idx < opa->pa_count; idx++) { 396 po = opa->pa_array[idx]; 397 prop_object_retain(po); 398 pa->pa_array[idx] = po; 399 } 400 pa->pa_count = opa->pa_count; 401 pa->pa_flags = opa->pa_flags; 402 } 403 _PROP_RWLOCK_UNLOCK(opa->pa_rwlock); 404 return (pa); 405} 406 407/* 408 * prop_array_copy_mutable -- 409 * Like prop_array_copy(), but the resulting array is mutable. 410 */ 411prop_array_t 412prop_array_copy_mutable(prop_array_t opa) 413{ 414 prop_array_t pa; 415 416 pa = prop_array_copy(opa); 417 if (pa != NULL) 418 pa->pa_flags &= ~PA_F_IMMUTABLE; 419 420 return (pa); 421} 422 423/* 424 * prop_array_capacity -- 425 * Return the capacity of the array. 426 */ 427unsigned int 428prop_array_capacity(prop_array_t pa) 429{ 430 unsigned int rv; 431 432 if (! prop_object_is_array(pa)) 433 return (0); 434 435 _PROP_RWLOCK_RDLOCK(pa->pa_rwlock); 436 rv = pa->pa_capacity; 437 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); 438 439 return (rv); 440} 441 442/* 443 * prop_array_count -- 444 * Return the number of objects stored in the array. 445 */ 446unsigned int 447prop_array_count(prop_array_t pa) 448{ 449 unsigned int rv; 450 451 if (! prop_object_is_array(pa)) 452 return (0); 453 454 _PROP_RWLOCK_RDLOCK(pa->pa_rwlock); 455 rv = pa->pa_count; 456 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); 457 458 return (rv); 459} 460 461/* 462 * prop_array_ensure_capacity -- 463 * Ensure that the array has the capacity to store the specified 464 * total number of objects (inluding the objects already stored 465 * in the array). 466 */ 467bool 468prop_array_ensure_capacity(prop_array_t pa, unsigned int capacity) 469{ 470 bool rv; 471 472 if (! prop_object_is_array(pa)) 473 return (false); 474 475 _PROP_RWLOCK_WRLOCK(pa->pa_rwlock); 476 if (capacity > pa->pa_capacity) 477 rv = _prop_array_expand(pa, capacity); 478 else 479 rv = true; 480 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); 481 482 return (rv); 483} 484 485/* 486 * prop_array_iterator -- 487 * Return an iterator for the array. The array is retained by 488 * the iterator. 489 */ 490prop_object_iterator_t 491prop_array_iterator(prop_array_t pa) 492{ 493 struct _prop_array_iterator *pai; 494 495 if (! prop_object_is_array(pa)) 496 return (NULL); 497 498 pai = _PROP_CALLOC(sizeof(*pai), M_TEMP); 499 if (pai == NULL) 500 return (NULL); 501 pai->pai_base.pi_next_object = _prop_array_iterator_next_object; 502 pai->pai_base.pi_reset = _prop_array_iterator_reset; 503 prop_object_retain(pa); 504 pai->pai_base.pi_obj = pa; 505 _prop_array_iterator_reset(pai); 506 507 return (&pai->pai_base); 508} 509 510/* 511 * prop_array_make_immutable -- 512 * Make the array immutable. 513 */ 514void 515prop_array_make_immutable(prop_array_t pa) 516{ 517 518 _PROP_RWLOCK_WRLOCK(pa->pa_rwlock); 519 if (prop_array_is_immutable(pa) == false) 520 pa->pa_flags |= PA_F_IMMUTABLE; 521 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); 522} 523 524/* 525 * prop_array_mutable -- 526 * Returns true if the array is mutable. 527 */ 528bool 529prop_array_mutable(prop_array_t pa) 530{ 531 bool rv; 532 533 _PROP_RWLOCK_RDLOCK(pa->pa_rwlock); 534 rv = prop_array_is_immutable(pa) == false; 535 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); 536 537 return (rv); 538} 539 540/* 541 * prop_array_get -- 542 * Return the object stored at the specified array index. 543 */ 544prop_object_t 545prop_array_get(prop_array_t pa, unsigned int idx) 546{ 547 prop_object_t po = NULL; 548 549 if (! prop_object_is_array(pa)) 550 return (NULL); 551 552 _PROP_RWLOCK_RDLOCK(pa->pa_rwlock); 553 if (idx >= pa->pa_count) 554 goto out; 555 po = pa->pa_array[idx]; 556 _PROP_ASSERT(po != NULL); 557 out: 558 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); 559 return (po); 560} 561 562static bool 563_prop_array_add(prop_array_t pa, prop_object_t po) 564{ 565 566 /* 567 * Array must be WRITE-LOCKED. 568 */ 569 570 _PROP_ASSERT(pa->pa_count <= pa->pa_capacity); 571 572 if (prop_array_is_immutable(pa) || 573 (pa->pa_count == pa->pa_capacity && 574 _prop_array_expand(pa, pa->pa_capacity + EXPAND_STEP) == false)) 575 return (false); 576 577 prop_object_retain(po); 578 pa->pa_array[pa->pa_count++] = po; 579 pa->pa_version++; 580 581 return (true); 582} 583 584/* 585 * prop_array_set -- 586 * Store a reference to an object at the specified array index. 587 * This method is not allowed to create holes in the array; the 588 * caller must either be setting the object just beyond the existing 589 * count or replacing an already existing object reference. 590 */ 591bool 592prop_array_set(prop_array_t pa, unsigned int idx, prop_object_t po) 593{ 594 prop_object_t opo; 595 bool rv = false; 596 597 if (! prop_object_is_array(pa)) 598 return (false); 599 600 _PROP_RWLOCK_WRLOCK(pa->pa_rwlock); 601 602 if (prop_array_is_immutable(pa)) 603 goto out; 604 605 if (idx == pa->pa_count) { 606 rv = _prop_array_add(pa, po); 607 goto out; 608 } 609 610 _PROP_ASSERT(idx < pa->pa_count); 611 612 opo = pa->pa_array[idx]; 613 _PROP_ASSERT(opo != NULL); 614 615 prop_object_retain(po); 616 pa->pa_array[idx] = po; 617 pa->pa_version++; 618 619 prop_object_release(opo); 620 621 rv = true; 622 623 out: 624 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); 625 return (rv); 626} 627 628/* 629 * prop_array_add -- 630 * Add a refrerence to an object to the specified array, appending 631 * to the end and growing the array's capacity, if necessary. 632 */ 633bool 634prop_array_add(prop_array_t pa, prop_object_t po) 635{ 636 bool rv; 637 638 if (! prop_object_is_array(pa)) 639 return (false); 640 641 _PROP_RWLOCK_WRLOCK(pa->pa_rwlock); 642 rv = _prop_array_add(pa, po); 643 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); 644 645 return (rv); 646} 647 648/* 649 * prop_array_remove -- 650 * Remove the reference to an object from an array at the specified 651 * index. The array will be compacted following the removal. 652 */ 653void 654prop_array_remove(prop_array_t pa, unsigned int idx) 655{ 656 prop_object_t po; 657 658 if (! prop_object_is_array(pa)) 659 return; 660 661 _PROP_RWLOCK_WRLOCK(pa->pa_rwlock); 662 663 _PROP_ASSERT(idx < pa->pa_count); 664 665 /* XXX Should this be a _PROP_ASSERT()? */ 666 if (prop_array_is_immutable(pa)) { 667 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); 668 return; 669 } 670 671 po = pa->pa_array[idx]; 672 _PROP_ASSERT(po != NULL); 673 674 for (++idx; idx < pa->pa_count; idx++) 675 pa->pa_array[idx - 1] = pa->pa_array[idx]; 676 pa->pa_count--; 677 pa->pa_version++; 678 679 _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); 680 681 prop_object_release(po); 682} 683 684/* 685 * prop_array_equals -- 686 * Return true if the two arrays are equivalent. Note we do a 687 * by-value comparison of the objects in the array. 688 */ 689bool 690prop_array_equals(prop_array_t array1, prop_array_t array2) 691{ 692 if (!prop_object_is_array(array1) || !prop_object_is_array(array2)) 693 return (false); 694 695 return (prop_object_equals(array1, array2)); 696} 697 698/* 699 * prop_array_externalize -- 700 * Externalize an array, return a NUL-terminated buffer 701 * containing the XML-style representation. The buffer is allocated 702 * with the M_TEMP memory type. 703 */ 704char * 705prop_array_externalize(prop_array_t pa) 706{ 707 struct _prop_object_externalize_context *ctx; 708 char *cp; 709 710 ctx = _prop_object_externalize_context_alloc(); 711 if (ctx == NULL) 712 return (NULL); 713 714 if (_prop_object_externalize_header(ctx) == false || 715 (*pa->pa_obj.po_type->pot_extern)(ctx, pa) == false || 716 _prop_object_externalize_footer(ctx) == false) { 717 /* We are responsible for releasing the buffer. */ 718 _PROP_FREE(ctx->poec_buf, M_TEMP); 719 _prop_object_externalize_context_free(ctx); 720 return (NULL); 721 } 722 723 cp = ctx->poec_buf; 724 _prop_object_externalize_context_free(ctx); 725 726 return (cp); 727} 728 729/* 730 * _prop_array_internalize -- 731 * Parse an <array>...</array> and return the object created from the 732 * external representation. 733 */ 734static bool _prop_array_internalize_body(prop_stack_t, prop_object_t *, 735 struct _prop_object_internalize_context *); 736 737bool 738_prop_array_internalize(prop_stack_t stack, prop_object_t *obj, 739 struct _prop_object_internalize_context *ctx) 740{ 741 /* We don't currently understand any attributes. */ 742 if (ctx->poic_tagattr != NULL) 743 return (true); 744 745 *obj = prop_array_create(); 746 /* 747 * We are done if the create failed or no child elements exist. 748 */ 749 if (*obj == NULL || ctx->poic_is_empty_element) 750 return (true); 751 752 /* 753 * Opening tag is found, now continue to the first element. 754 */ 755 return (_prop_array_internalize_body(stack, obj, ctx)); 756} 757 758static bool 759_prop_array_internalize_continue(prop_stack_t stack, 760 prop_object_t *obj, 761 struct _prop_object_internalize_context *ctx, 762 void *data, prop_object_t child) 763{ 764 prop_array_t array; 765 766 _PROP_ASSERT(data == NULL); 767 768 if (child == NULL) 769 goto bad; /* Element could not be parsed. */ 770 771 array = *obj; 772 773 if (prop_array_add(array, child) == false) { 774 prop_object_release(child); 775 goto bad; 776 } 777 prop_object_release(child); 778 779 /* 780 * Current element is processed and added, look for next. 781 */ 782 return (_prop_array_internalize_body(stack, obj, ctx)); 783 784 bad: 785 prop_object_release(*obj); 786 *obj = NULL; 787 return (true); 788} 789 790static bool 791_prop_array_internalize_body(prop_stack_t stack, prop_object_t *obj, 792 struct _prop_object_internalize_context *ctx) 793{ 794 prop_array_t array = *obj; 795 796 _PROP_ASSERT(array != NULL); 797 798 /* Fetch the next tag. */ 799 if (_prop_object_internalize_find_tag(ctx, NULL, 800 _PROP_TAG_TYPE_EITHER) == false) 801 goto bad; 802 803 /* Check to see if this is the end of the array. */ 804 if (_PROP_TAG_MATCH(ctx, "array") && 805 ctx->poic_tag_type == _PROP_TAG_TYPE_END) { 806 /* It is, so don't iterate any further. */ 807 return (true); 808 } 809 810 if (_prop_stack_push(stack, array, 811 _prop_array_internalize_continue, NULL, NULL)) 812 return (false); 813 814 bad: 815 prop_object_release(array); 816 *obj = NULL; 817 return (true); 818} 819 820/* 821 * prop_array_internalize -- 822 * Create an array by parsing the XML-style representation. 823 */ 824prop_array_t 825prop_array_internalize(const char *xml) 826{ 827 return _prop_generic_internalize(xml, "array"); 828} 829 830#if !defined(_KERNEL) && !defined(_STANDALONE) 831/* 832 * prop_array_externalize_to_file -- 833 * Externalize an array to the specified file. 834 */ 835bool 836prop_array_externalize_to_file(prop_array_t array, const char *fname) 837{ 838 char *xml; 839 bool rv; 840 int save_errno = 0; /* XXXGCC -Wuninitialized [mips, ...] */ 841 842 xml = prop_array_externalize(array); 843 if (xml == NULL) 844 return (false); 845 rv = _prop_object_externalize_write_file(fname, xml, strlen(xml)); 846 if (rv == false) 847 save_errno = errno; 848 _PROP_FREE(xml, M_TEMP); 849 if (rv == false) 850 errno = save_errno; 851 852 return (rv); 853} 854 855/* 856 * prop_array_internalize_from_file -- 857 * Internalize an array from a file. 858 */ 859prop_array_t 860prop_array_internalize_from_file(const char *fname) 861{ 862 struct _prop_object_internalize_mapped_file *mf; 863 prop_array_t array; 864 865 mf = _prop_object_internalize_map_file(fname); 866 if (mf == NULL) 867 return (NULL); 868 array = prop_array_internalize(mf->poimf_xml); 869 _prop_object_internalize_unmap_file(mf); 870 871 return (array); 872} 873#endif /* _KERNEL && !_STANDALONE */ 874