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