prop_array.c revision 1.8
1/*	$NetBSD: prop_array.c,v 1.8 2007/07/16 19:20:17 joerg 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
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 void		_prop_array_free(void *);
64static boolean_t	_prop_array_externalize(
65				struct _prop_object_externalize_context *,
66				void *);
67static boolean_t	_prop_array_equals(void *, void *);
68
69static 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
81struct _prop_array_iterator {
82	struct _prop_object_iterator pai_base;
83	unsigned int		pai_index;
84};
85
86#define	EXPAND_STEP		16
87
88static 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
113static 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
166static 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
206static 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
239static 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
264static 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
291static 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 */
311prop_array_t
312prop_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 */
322prop_array_t
323prop_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 */
336prop_array_t
337prop_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 */
366prop_array_t
367prop_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 */
382unsigned int
383prop_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 */
401unsigned int
402prop_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 */
422boolean_t
423prop_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 */
445prop_object_iterator_t
446prop_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 */
469void
470prop_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 */
483boolean_t
484prop_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 */
499prop_object_t
500prop_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
517static 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 */
546boolean_t
547prop_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 */
588boolean_t
589prop_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 */
608void
609prop_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 */
644boolean_t
645prop_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 */
657char *
658prop_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 */
687prop_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 */
738prop_array_t
739prop_array_internalize(const char *xml)
740{
741	return _prop_generic_internalize(xml, "array");
742}
743
744#if !defined(_KERNEL) && !defined(_STANDALONE)
745/*
746 * prop_array_externalize_to_file --
747 *	Externalize an array to the specified file.
748 */
749boolean_t
750prop_array_externalize_to_file(prop_array_t array, const char *fname)
751{
752	char *xml;
753	boolean_t rv;
754	int save_errno = 0;	/* XXXGCC -Wuninitialized [mips, ...] */
755
756	xml = prop_array_externalize(array);
757	if (xml == NULL)
758		return (FALSE);
759	rv = _prop_object_externalize_write_file(fname, xml, strlen(xml));
760	if (rv == FALSE)
761		save_errno = errno;
762	_PROP_FREE(xml, M_TEMP);
763	if (rv == FALSE)
764		errno = save_errno;
765
766	return (rv);
767}
768
769/*
770 * prop_array_internalize_from_file --
771 *	Internalize an array from a file.
772 */
773prop_array_t
774prop_array_internalize_from_file(const char *fname)
775{
776	struct _prop_object_internalize_mapped_file *mf;
777	prop_array_t array;
778
779	mf = _prop_object_internalize_map_file(fname);
780	if (mf == NULL)
781		return (NULL);
782	array = prop_array_internalize(mf->poimf_xml);
783	_prop_object_internalize_unmap_file(mf);
784
785	return (array);
786}
787#endif /* _KERNEL && !_STANDALONE */
788