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