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