1 /* $NetBSD: prop_array.c,v 1.6 2006/08/22 21:21:23 thorpej Exp $ */ 2 3 /*- 4 * Copyright (c) 2006 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 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39 #include <prop/prop_array.h> 40 #include "prop_object_impl.h" 41 42 #if !defined(_KERNEL) && !defined(_STANDALONE) 43 #include <errno.h> 44 #endif 45 46 struct _prop_array { 47 struct _prop_object pa_obj; 48 prop_object_t * pa_array; 49 unsigned int pa_capacity; 50 unsigned int pa_count; 51 int pa_flags; 52 53 uint32_t pa_version; 54 }; 55 56 #define PA_F_IMMUTABLE 0x01 /* array is immutable */ 57 58 _PROP_POOL_INIT(_prop_array_pool, sizeof(struct _prop_array), "proparay") 59 _PROP_MALLOC_DEFINE(M_PROP_ARRAY, "prop array", 60 "property array container object") 61 62 static void _prop_array_free(void *); 63 static boolean_t _prop_array_externalize( 64 struct _prop_object_externalize_context *, 65 void *); 66 static boolean_t _prop_array_equals(void *, void *); 67 68 static const struct _prop_object_type _prop_object_type_array = { 69 .pot_type = PROP_TYPE_ARRAY, 70 .pot_free = _prop_array_free, 71 .pot_extern = _prop_array_externalize, 72 .pot_equals = _prop_array_equals, 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 void 88 _prop_array_free(void *v) 89 { 90 prop_array_t pa = v; 91 prop_object_t po; 92 unsigned int idx; 93 94 _PROP_ASSERT(pa->pa_count <= pa->pa_capacity); 95 _PROP_ASSERT((pa->pa_capacity == 0 && pa->pa_array == NULL) || 96 (pa->pa_capacity != 0 && pa->pa_array != NULL)); 97 98 for (idx = 0; idx < pa->pa_count; idx++) { 99 po = pa->pa_array[idx]; 100 _PROP_ASSERT(po != NULL); 101 prop_object_release(po); 102 } 103 104 if (pa->pa_array != NULL) 105 _PROP_FREE(pa->pa_array, M_PROP_ARRAY); 106 107 _PROP_POOL_PUT(_prop_array_pool, pa); 108 } 109 110 static boolean_t 111 _prop_array_externalize(struct _prop_object_externalize_context *ctx, 112 void *v) 113 { 114 prop_array_t pa = v; 115 struct _prop_object *po; 116 prop_object_iterator_t pi; 117 unsigned int i; 118 119 if (pa->pa_count == 0) 120 return (_prop_object_externalize_empty_tag(ctx, "array")); 121 122 /* XXXJRT Hint "count" for the internalize step? */ 123 if (_prop_object_externalize_start_tag(ctx, "array") == FALSE || 124 _prop_object_externalize_append_char(ctx, '\n') == FALSE) 125 return (FALSE); 126 127 pi = prop_array_iterator(pa); 128 if (pi == NULL) 129 return (FALSE); 130 131 ctx->poec_depth++; 132 _PROP_ASSERT(ctx->poec_depth != 0); 133 134 while ((po = prop_object_iterator_next(pi)) != NULL) { 135 if ((*po->po_type->pot_extern)(ctx, po) == FALSE) { 136 prop_object_iterator_release(pi); 137 return (FALSE); 138 } 139 } 140 141 prop_object_iterator_release(pi); 142 143 ctx->poec_depth--; 144 for (i = 0; i < ctx->poec_depth; i++) { 145 if (_prop_object_externalize_append_char(ctx, '\t') == FALSE) 146 return (FALSE); 147 } 148 if (_prop_object_externalize_end_tag(ctx, "array") == FALSE) 149 return (FALSE); 150 151 return (TRUE); 152 } 153 154 static boolean_t 155 _prop_array_equals(void *v1, void *v2) 156 { 157 prop_array_t array1 = v1; 158 prop_array_t array2 = v2; 159 unsigned int idx; 160 161 if (! (prop_object_is_array(array1) && 162 prop_object_is_array(array2))) 163 return (FALSE); 164 165 if (array1 == array2) 166 return (TRUE); 167 if (array1->pa_count != array2->pa_count) 168 return (FALSE); 169 170 for (idx = 0; idx < array1->pa_count; idx++) { 171 if (prop_object_equals(array1->pa_array[idx], 172 array2->pa_array[idx]) == FALSE) 173 return (FALSE); 174 } 175 176 return (TRUE); 177 } 178 179 static prop_array_t 180 _prop_array_alloc(unsigned int capacity) 181 { 182 prop_array_t pa; 183 prop_object_t *array; 184 185 if (capacity != 0) { 186 array = _PROP_CALLOC(capacity * sizeof(prop_object_t), 187 M_PROP_ARRAY); 188 if (array == NULL) 189 return (NULL); 190 } else 191 array = NULL; 192 193 194 pa = _PROP_POOL_GET(_prop_array_pool); 195 if (pa != NULL) { 196 _prop_object_init(&pa->pa_obj, &_prop_object_type_array); 197 pa->pa_obj.po_type = &_prop_object_type_array; 198 199 pa->pa_array = array; 200 pa->pa_capacity = capacity; 201 pa->pa_count = 0; 202 pa->pa_flags = 0; 203 204 pa->pa_version = 0; 205 } else if (array != NULL) 206 _PROP_FREE(array, M_PROP_ARRAY); 207 208 return (pa); 209 } 210 211 static boolean_t 212 _prop_array_expand(prop_array_t pa, unsigned int capacity) 213 { 214 prop_object_t *array, *oarray; 215 216 oarray = pa->pa_array; 217 218 array = _PROP_CALLOC(capacity * sizeof(*array), M_PROP_ARRAY); 219 if (array == NULL) 220 return (FALSE); 221 if (oarray != NULL) 222 memcpy(array, oarray, pa->pa_capacity * sizeof(*array)); 223 pa->pa_array = array; 224 pa->pa_capacity = capacity; 225 226 if (oarray != NULL) 227 _PROP_FREE(oarray, M_PROP_ARRAY); 228 229 return (TRUE); 230 } 231 232 static prop_object_t 233 _prop_array_iterator_next_object(void *v) 234 { 235 struct _prop_array_iterator *pai = v; 236 prop_array_t pa = pai->pai_base.pi_obj; 237 prop_object_t po; 238 239 _PROP_ASSERT(prop_object_is_array(pa)); 240 241 if (pa->pa_version != pai->pai_base.pi_version) 242 return (NULL); /* array changed during iteration */ 243 244 _PROP_ASSERT(pai->pai_index <= pa->pa_count); 245 246 if (pai->pai_index == pa->pa_count) 247 return (NULL); /* we've iterated all objects */ 248 249 po = pa->pa_array[pai->pai_index]; 250 pai->pai_index++; 251 252 return (po); 253 } 254 255 static void 256 _prop_array_iterator_reset(void *v) 257 { 258 struct _prop_array_iterator *pai = v; 259 prop_array_t pa = pai->pai_base.pi_obj; 260 261 _PROP_ASSERT(prop_object_is_array(pa)); 262 263 pai->pai_index = 0; 264 pai->pai_base.pi_version = pa->pa_version; 265 } 266 267 /* 268 * prop_array_create -- 269 * Create an empty array. 270 */ 271 prop_array_t 272 prop_array_create(void) 273 { 274 275 return (_prop_array_alloc(0)); 276 } 277 278 /* 279 * prop_array_create_with_capacity -- 280 * Create an array with the capacity to store N objects. 281 */ 282 prop_array_t 283 prop_array_create_with_capacity(unsigned int capacity) 284 { 285 286 return (_prop_array_alloc(capacity)); 287 } 288 289 /* 290 * prop_array_copy -- 291 * Copy an array. The new array has an initial capacity equal to 292 * the number of objects stored in the original array. The new 293 * array contains references to the original array's objects, not 294 * copies of those objects (i.e. a shallow copy). 295 */ 296 prop_array_t 297 prop_array_copy(prop_array_t opa) 298 { 299 prop_array_t pa; 300 prop_object_t po; 301 unsigned int idx; 302 303 if (! prop_object_is_array(opa)) 304 return (NULL); 305 306 pa = _prop_array_alloc(opa->pa_count); 307 if (pa != NULL) { 308 for (idx = 0; idx < opa->pa_count; idx++) { 309 po = opa->pa_array[idx]; 310 prop_object_retain(po); 311 pa->pa_array[idx] = po; 312 } 313 pa->pa_count = opa->pa_count; 314 pa->pa_flags = opa->pa_flags; 315 } 316 return (pa); 317 } 318 319 /* 320 * prop_array_copy_mutable -- 321 * Like prop_array_copy(), but the resulting array is mutable. 322 */ 323 prop_array_t 324 prop_array_copy_mutable(prop_array_t opa) 325 { 326 prop_array_t pa; 327 328 pa = prop_array_copy(opa); 329 if (pa != NULL) 330 pa->pa_flags &= ~PA_F_IMMUTABLE; 331 332 return (pa); 333 } 334 335 /* 336 * prop_array_capacity -- 337 * Return the capacity of the array. 338 */ 339 unsigned int 340 prop_array_capacity(prop_array_t pa) 341 { 342 343 if (! prop_object_is_array(pa)) 344 return (0); 345 346 return (pa->pa_capacity); 347 } 348 349 /* 350 * prop_array_count -- 351 * Return the number of objects stored in the array. 352 */ 353 unsigned int 354 prop_array_count(prop_array_t pa) 355 { 356 357 if (! prop_object_is_array(pa)) 358 return (0); 359 360 return (pa->pa_count); 361 } 362 363 /* 364 * prop_array_ensure_capacity -- 365 * Ensure that the array has the capacity to store the specified 366 * total number of objects (inluding the objects already stored 367 * in the array). 368 */ 369 boolean_t 370 prop_array_ensure_capacity(prop_array_t pa, unsigned int capacity) 371 { 372 373 if (! prop_object_is_array(pa)) 374 return (FALSE); 375 376 if (capacity > pa->pa_capacity) 377 return (_prop_array_expand(pa, capacity)); 378 return (TRUE); 379 } 380 381 /* 382 * prop_array_iterator -- 383 * Return an iterator for the array. The array is retained by 384 * the iterator. 385 */ 386 prop_object_iterator_t 387 prop_array_iterator(prop_array_t pa) 388 { 389 struct _prop_array_iterator *pai; 390 391 if (! prop_object_is_array(pa)) 392 return (NULL); 393 394 pai = _PROP_CALLOC(sizeof(*pai), M_TEMP); 395 if (pai == NULL) 396 return (NULL); 397 pai->pai_base.pi_next_object = _prop_array_iterator_next_object; 398 pai->pai_base.pi_reset = _prop_array_iterator_reset; 399 prop_object_retain(pa); 400 pai->pai_base.pi_obj = pa; 401 pai->pai_base.pi_version = pa->pa_version; 402 403 _prop_array_iterator_reset(pai); 404 405 return (&pai->pai_base); 406 } 407 408 /* 409 * prop_array_make_immutable -- 410 * Make the array immutable. 411 */ 412 void 413 prop_array_make_immutable(prop_array_t pa) 414 { 415 416 if (prop_array_is_immutable(pa) == FALSE) 417 pa->pa_flags |= PA_F_IMMUTABLE; 418 } 419 420 /* 421 * prop_array_mutable -- 422 * Returns TRUE if the array is mutable. 423 */ 424 boolean_t 425 prop_array_mutable(prop_array_t pa) 426 { 427 428 return (prop_array_is_immutable(pa) == FALSE); 429 } 430 431 /* 432 * prop_array_get -- 433 * Return the object stored at the specified array index. 434 */ 435 prop_object_t 436 prop_array_get(prop_array_t pa, unsigned int idx) 437 { 438 prop_object_t po; 439 440 if (! prop_object_is_array(pa)) 441 return (NULL); 442 443 if (idx >= pa->pa_count) 444 return (NULL); 445 po = pa->pa_array[idx]; 446 _PROP_ASSERT(po != NULL); 447 return (po); 448 } 449 450 /* 451 * prop_array_set -- 452 * Store a reference to an object at the specified array index. 453 * This method is not allowed to create holes in the array; the 454 * caller must either be setting the object just beyond the existing 455 * count or replacing an already existing object reference. 456 */ 457 boolean_t 458 prop_array_set(prop_array_t pa, unsigned int idx, prop_object_t po) 459 { 460 prop_object_t opo; 461 462 if (! prop_object_is_array(pa)) 463 return (FALSE); 464 465 if (prop_array_is_immutable(pa)) 466 return (FALSE); 467 468 if (idx == pa->pa_count) 469 return (prop_array_add(pa, po)); 470 471 _PROP_ASSERT(idx < pa->pa_count); 472 473 opo = pa->pa_array[idx]; 474 _PROP_ASSERT(opo != NULL); 475 476 prop_object_retain(po); 477 pa->pa_array[idx] = po; 478 pa->pa_version++; 479 480 prop_object_release(opo); 481 482 return (TRUE); 483 } 484 485 /* 486 * prop_array_add -- 487 * Add a refrerence to an object to the specified array, appending 488 * to the end and growing the array's capacity, if necessary. 489 */ 490 boolean_t 491 prop_array_add(prop_array_t pa, prop_object_t po) 492 { 493 494 if (! prop_object_is_array(pa)) 495 return (FALSE); 496 497 _PROP_ASSERT(pa->pa_count <= pa->pa_capacity); 498 499 if (prop_array_is_immutable(pa) || 500 (pa->pa_count == pa->pa_capacity && 501 _prop_array_expand(pa, pa->pa_capacity + EXPAND_STEP) == FALSE)) 502 return (FALSE); 503 504 prop_object_retain(po); 505 pa->pa_array[pa->pa_count++] = po; 506 pa->pa_version++; 507 508 return (TRUE); 509 } 510 511 /* 512 * prop_array_remove -- 513 * Remove the reference to an object from an array at the specified 514 * index. The array will be compacted following the removal. 515 */ 516 void 517 prop_array_remove(prop_array_t pa, unsigned int idx) 518 { 519 prop_object_t po; 520 521 if (! prop_object_is_array(pa)) 522 return; 523 524 _PROP_ASSERT(idx < pa->pa_count); 525 526 /* XXX Should this be a _PROP_ASSERT()? */ 527 if (prop_array_is_immutable(pa)) 528 return; 529 530 po = pa->pa_array[idx]; 531 _PROP_ASSERT(po != NULL); 532 533 for (++idx; idx < pa->pa_count; idx++) 534 pa->pa_array[idx - 1] = pa->pa_array[idx]; 535 pa->pa_count--; 536 pa->pa_version++; 537 538 prop_object_release(po); 539 } 540 541 /* 542 * prop_array_equals -- 543 * Return TRUE if the two arrays are equivalent. Note we do a 544 * by-value comparison of the objects in the array. 545 */ 546 boolean_t 547 prop_array_equals(prop_array_t array1, prop_array_t array2) 548 { 549 550 return (_prop_array_equals(array1, array2)); 551 } 552 553 /* 554 * prop_array_externalize -- 555 * Externalize an array, return a NUL-terminated buffer 556 * containing the XML-style representation. The buffer is allocated 557 * with the M_TEMP memory type. 558 */ 559 char * 560 prop_array_externalize(prop_array_t pa) 561 { 562 struct _prop_object_externalize_context *ctx; 563 char *cp; 564 565 ctx = _prop_object_externalize_context_alloc(); 566 if (ctx == NULL) 567 return (NULL); 568 569 if (_prop_object_externalize_header(ctx) == FALSE || 570 (*pa->pa_obj.po_type->pot_extern)(ctx, pa) == FALSE || 571 _prop_object_externalize_footer(ctx) == FALSE) { 572 /* We are responsible for releasing the buffer. */ 573 _PROP_FREE(ctx->poec_buf, M_TEMP); 574 _prop_object_externalize_context_free(ctx); 575 return (NULL); 576 } 577 578 cp = ctx->poec_buf; 579 _prop_object_externalize_context_free(ctx); 580 581 return (cp); 582 } 583 584 /* 585 * _prop_array_internalize -- 586 * Parse an <array>...</array> and return the object created from the 587 * external representation. 588 */ 589 prop_object_t 590 _prop_array_internalize(struct _prop_object_internalize_context *ctx) 591 { 592 prop_array_t array; 593 prop_object_t obj; 594 595 /* We don't currently understand any attributes. */ 596 if (ctx->poic_tagattr != NULL) 597 return (NULL); 598 599 array = prop_array_create(); 600 if (array == NULL) 601 return (NULL); 602 603 if (ctx->poic_is_empty_element) 604 return (array); 605 606 for (;;) { 607 /* Fetch the next tag. */ 608 if (_prop_object_internalize_find_tag(ctx, NULL, 609 _PROP_TAG_TYPE_EITHER) == FALSE) 610 goto bad; 611 612 /* Check to see if this is the end of the array. */ 613 if (_PROP_TAG_MATCH(ctx, "array") && 614 ctx->poic_tag_type == _PROP_TAG_TYPE_END) 615 break; 616 617 /* Fetch the object. */ 618 obj = _prop_object_internalize_by_tag(ctx); 619 if (obj == NULL) 620 goto bad; 621 622 if (prop_array_add(array, obj) == FALSE) { 623 prop_object_release(obj); 624 goto bad; 625 } 626 prop_object_release(obj); 627 } 628 629 return (array); 630 631 bad: 632 prop_object_release(array); 633 return (NULL); 634 } 635 636 /* 637 * prop_array_internalize -- 638 * Create an array by parsing the XML-style representation. 639 */ 640 prop_array_t 641 prop_array_internalize(const char *xml) 642 { 643 prop_array_t array = NULL; 644 struct _prop_object_internalize_context *ctx; 645 646 ctx = _prop_object_internalize_context_alloc(xml); 647 if (ctx == NULL) 648 return (NULL); 649 650 /* We start with a <plist> tag. */ 651 if (_prop_object_internalize_find_tag(ctx, "plist", 652 _PROP_TAG_TYPE_START) == FALSE) 653 goto out; 654 655 /* Plist elements cannot be empty. */ 656 if (ctx->poic_is_empty_element) 657 goto out; 658 659 /* 660 * We don't understand any plist attributes, but Apple XML 661 * property lists often have a "version" attribute. If we 662 * see that one, we simply ignore it. 663 */ 664 if (ctx->poic_tagattr != NULL && 665 !_PROP_TAGATTR_MATCH(ctx, "version")) 666 goto out; 667 668 /* Next we expect to see <array>. */ 669 if (_prop_object_internalize_find_tag(ctx, "array", 670 _PROP_TAG_TYPE_START) == FALSE) 671 goto out; 672 673 array = _prop_array_internalize(ctx); 674 if (array == NULL) 675 goto out; 676 677 /* We've advanced past </array>. Now we want </plist>. */ 678 if (_prop_object_internalize_find_tag(ctx, "plist", 679 _PROP_TAG_TYPE_END) == FALSE) { 680 prop_object_release(array); 681 array = NULL; 682 } 683 684 out: 685 _prop_object_internalize_context_free(ctx); 686 return (array); 687 } 688 689 #if !defined(_KERNEL) && !defined(_STANDALONE) 690 /* 691 * prop_array_externalize_to_file -- 692 * Externalize an array to the specified file. 693 */ 694 boolean_t 695 prop_array_externalize_to_file(prop_array_t array, const char *fname) 696 { 697 char *xml; 698 boolean_t rv; 699 int save_errno = 0; /* XXXGCC -Wuninitialized [mips, ...] */ 700 701 xml = prop_array_externalize(array); 702 if (xml == NULL) 703 return (FALSE); 704 rv = _prop_object_externalize_write_file(fname, xml, strlen(xml)); 705 if (rv == FALSE) 706 save_errno = errno; 707 _PROP_FREE(xml, M_TEMP); 708 if (rv == FALSE) 709 errno = save_errno; 710 711 return (rv); 712 } 713 714 /* 715 * prop_array_internalize_from_file -- 716 * Internalize an array from a file. 717 */ 718 prop_array_t 719 prop_array_internalize_from_file(const char *fname) 720 { 721 struct _prop_object_internalize_mapped_file *mf; 722 prop_array_t array; 723 724 mf = _prop_object_internalize_map_file(fname); 725 if (mf == NULL) 726 return (NULL); 727 array = prop_array_internalize(mf->poimf_xml); 728 _prop_object_internalize_unmap_file(mf); 729 730 return (array); 731 } 732 #endif /* _KERNEL && !_STANDALONE */ 733