xref: /minix3/common/lib/libprop/prop_object.c (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
1*0a6a1f1dSLionel Sambuc /*	$NetBSD: prop_object.c,v 1.30 2015/05/12 14:59:35 christos Exp $	*/
26b6d114aSBen Gras 
36b6d114aSBen Gras /*-
46b6d114aSBen Gras  * Copyright (c) 2006, 2007 The NetBSD Foundation, Inc.
56b6d114aSBen Gras  * All rights reserved.
66b6d114aSBen Gras  *
76b6d114aSBen Gras  * This code is derived from software contributed to The NetBSD Foundation
86b6d114aSBen Gras  * by Jason R. Thorpe.
96b6d114aSBen Gras  *
106b6d114aSBen Gras  * Redistribution and use in source and binary forms, with or without
116b6d114aSBen Gras  * modification, are permitted provided that the following conditions
126b6d114aSBen Gras  * are met:
136b6d114aSBen Gras  * 1. Redistributions of source code must retain the above copyright
146b6d114aSBen Gras  *    notice, this list of conditions and the following disclaimer.
156b6d114aSBen Gras  * 2. Redistributions in binary form must reproduce the above copyright
166b6d114aSBen Gras  *    notice, this list of conditions and the following disclaimer in the
176b6d114aSBen Gras  *    documentation and/or other materials provided with the distribution.
186b6d114aSBen Gras  *
196b6d114aSBen Gras  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
206b6d114aSBen Gras  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
216b6d114aSBen Gras  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
226b6d114aSBen Gras  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
236b6d114aSBen Gras  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
246b6d114aSBen Gras  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
256b6d114aSBen Gras  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
266b6d114aSBen Gras  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
276b6d114aSBen Gras  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
286b6d114aSBen Gras  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
296b6d114aSBen Gras  * POSSIBILITY OF SUCH DAMAGE.
306b6d114aSBen Gras  */
316b6d114aSBen Gras 
326b6d114aSBen Gras #include "prop_object_impl.h"
33f14fb602SLionel Sambuc #include <prop/prop_object.h>
34f14fb602SLionel Sambuc 
35f14fb602SLionel Sambuc #ifdef _PROP_NEED_REFCNT_MTX
36f14fb602SLionel Sambuc static pthread_mutex_t _prop_refcnt_mtx = PTHREAD_MUTEX_INITIALIZER;
37f14fb602SLionel Sambuc #endif /* _PROP_NEED_REFCNT_MTX */
386b6d114aSBen Gras 
396b6d114aSBen Gras #if !defined(_KERNEL) && !defined(_STANDALONE)
406b6d114aSBen Gras #include <sys/mman.h>
416b6d114aSBen Gras #include <sys/stat.h>
426b6d114aSBen Gras #include <errno.h>
436b6d114aSBen Gras #include <fcntl.h>
446b6d114aSBen Gras #include <limits.h>
456b6d114aSBen Gras #include <unistd.h>
46be89757eSLionel Sambuc #if defined(__minix)
476b6d114aSBen Gras #include <assert.h>
48be89757eSLionel Sambuc #endif /* defined(__minix) */
496b6d114aSBen Gras #endif
506b6d114aSBen Gras 
516b6d114aSBen Gras #ifdef _STANDALONE
526b6d114aSBen Gras void *
_prop_standalone_calloc(size_t size)536b6d114aSBen Gras _prop_standalone_calloc(size_t size)
546b6d114aSBen Gras {
556b6d114aSBen Gras 	void *rv;
566b6d114aSBen Gras 
576b6d114aSBen Gras 	rv = alloc(size);
586b6d114aSBen Gras 	if (rv != NULL)
596b6d114aSBen Gras 		memset(rv, 0, size);
606b6d114aSBen Gras 
616b6d114aSBen Gras 	return (rv);
626b6d114aSBen Gras }
636b6d114aSBen Gras 
646b6d114aSBen Gras void *
_prop_standalone_realloc(void * v,size_t size)656b6d114aSBen Gras _prop_standalone_realloc(void *v, size_t size)
666b6d114aSBen Gras {
676b6d114aSBen Gras 	void *rv;
686b6d114aSBen Gras 
696b6d114aSBen Gras 	rv = alloc(size);
706b6d114aSBen Gras 	if (rv != NULL) {
716b6d114aSBen Gras 		memcpy(rv, v, size);	/* XXX */
726b6d114aSBen Gras 		dealloc(v, 0);		/* XXX */
736b6d114aSBen Gras 	}
746b6d114aSBen Gras 
756b6d114aSBen Gras 	return (rv);
766b6d114aSBen Gras }
776b6d114aSBen Gras #endif /* _STANDALONE */
786b6d114aSBen Gras 
796b6d114aSBen Gras /*
806b6d114aSBen Gras  * _prop_object_init --
816b6d114aSBen Gras  *	Initialize an object.  Called when sub-classes create
826b6d114aSBen Gras  *	an instance.
836b6d114aSBen Gras  */
846b6d114aSBen Gras void
_prop_object_init(struct _prop_object * po,const struct _prop_object_type * pot)856b6d114aSBen Gras _prop_object_init(struct _prop_object *po, const struct _prop_object_type *pot)
866b6d114aSBen Gras {
876b6d114aSBen Gras 
886b6d114aSBen Gras 	po->po_type = pot;
896b6d114aSBen Gras 	po->po_refcnt = 1;
906b6d114aSBen Gras }
916b6d114aSBen Gras 
926b6d114aSBen Gras /*
936b6d114aSBen Gras  * _prop_object_fini --
946b6d114aSBen Gras  *	Finalize an object.  Called when sub-classes destroy
956b6d114aSBen Gras  *	an instance.
966b6d114aSBen Gras  */
976b6d114aSBen Gras /*ARGSUSED*/
986b6d114aSBen Gras void
_prop_object_fini(struct _prop_object * po _PROP_ARG_UNUSED)996b6d114aSBen Gras _prop_object_fini(struct _prop_object *po _PROP_ARG_UNUSED)
1006b6d114aSBen Gras {
1016b6d114aSBen Gras 	/* Nothing to do, currently. */
1026b6d114aSBen Gras }
1036b6d114aSBen Gras 
1046b6d114aSBen Gras /*
1056b6d114aSBen Gras  * _prop_object_externalize_start_tag --
1066b6d114aSBen Gras  *	Append an XML-style start tag to the externalize buffer.
1076b6d114aSBen Gras  */
1086b6d114aSBen Gras bool
_prop_object_externalize_start_tag(struct _prop_object_externalize_context * ctx,const char * tag)1096b6d114aSBen Gras _prop_object_externalize_start_tag(
1106b6d114aSBen Gras     struct _prop_object_externalize_context *ctx, const char *tag)
1116b6d114aSBen Gras {
1126b6d114aSBen Gras 	unsigned int i;
1136b6d114aSBen Gras 
1146b6d114aSBen Gras 	for (i = 0; i < ctx->poec_depth; i++) {
1156b6d114aSBen Gras 		if (_prop_object_externalize_append_char(ctx, '\t') == false)
1166b6d114aSBen Gras 			return (false);
1176b6d114aSBen Gras 	}
1186b6d114aSBen Gras 	if (_prop_object_externalize_append_char(ctx, '<') == false ||
1196b6d114aSBen Gras 	    _prop_object_externalize_append_cstring(ctx, tag) == false ||
1206b6d114aSBen Gras 	    _prop_object_externalize_append_char(ctx, '>') == false)
1216b6d114aSBen Gras 		return (false);
1226b6d114aSBen Gras 
1236b6d114aSBen Gras 	return (true);
1246b6d114aSBen Gras }
1256b6d114aSBen Gras 
1266b6d114aSBen Gras /*
1276b6d114aSBen Gras  * _prop_object_externalize_end_tag --
1286b6d114aSBen Gras  *	Append an XML-style end tag to the externalize buffer.
1296b6d114aSBen Gras  */
1306b6d114aSBen Gras bool
_prop_object_externalize_end_tag(struct _prop_object_externalize_context * ctx,const char * tag)1316b6d114aSBen Gras _prop_object_externalize_end_tag(
1326b6d114aSBen Gras     struct _prop_object_externalize_context *ctx, const char *tag)
1336b6d114aSBen Gras {
1346b6d114aSBen Gras 
1356b6d114aSBen Gras 	if (_prop_object_externalize_append_char(ctx, '<') == false ||
1366b6d114aSBen Gras 	    _prop_object_externalize_append_char(ctx, '/') == false ||
1376b6d114aSBen Gras 	    _prop_object_externalize_append_cstring(ctx, tag) == false ||
1386b6d114aSBen Gras 	    _prop_object_externalize_append_char(ctx, '>') == false ||
1396b6d114aSBen Gras 	    _prop_object_externalize_append_char(ctx, '\n') == false)
1406b6d114aSBen Gras 		return (false);
1416b6d114aSBen Gras 
1426b6d114aSBen Gras 	return (true);
1436b6d114aSBen Gras }
1446b6d114aSBen Gras 
1456b6d114aSBen Gras /*
1466b6d114aSBen Gras  * _prop_object_externalize_empty_tag --
1476b6d114aSBen Gras  *	Append an XML-style empty tag to the externalize buffer.
1486b6d114aSBen Gras  */
1496b6d114aSBen Gras bool
_prop_object_externalize_empty_tag(struct _prop_object_externalize_context * ctx,const char * tag)1506b6d114aSBen Gras _prop_object_externalize_empty_tag(
1516b6d114aSBen Gras     struct _prop_object_externalize_context *ctx, const char *tag)
1526b6d114aSBen Gras {
1536b6d114aSBen Gras 	unsigned int i;
1546b6d114aSBen Gras 
1556b6d114aSBen Gras 	for (i = 0; i < ctx->poec_depth; i++) {
1566b6d114aSBen Gras 		if (_prop_object_externalize_append_char(ctx, '\t') == false)
1576b6d114aSBen Gras 			return (false);
1586b6d114aSBen Gras 	}
1596b6d114aSBen Gras 
1606b6d114aSBen Gras 	if (_prop_object_externalize_append_char(ctx, '<') == false ||
1616b6d114aSBen Gras 	    _prop_object_externalize_append_cstring(ctx, tag) == false ||
1626b6d114aSBen Gras 	    _prop_object_externalize_append_char(ctx, '/') == false ||
1636b6d114aSBen Gras 	    _prop_object_externalize_append_char(ctx, '>') == false ||
1646b6d114aSBen Gras 	    _prop_object_externalize_append_char(ctx, '\n') == false)
1656b6d114aSBen Gras 	    	return (false);
1666b6d114aSBen Gras 
1676b6d114aSBen Gras 	return (true);
1686b6d114aSBen Gras }
1696b6d114aSBen Gras 
1706b6d114aSBen Gras /*
1716b6d114aSBen Gras  * _prop_object_externalize_append_cstring --
1726b6d114aSBen Gras  *	Append a C string to the externalize buffer.
1736b6d114aSBen Gras  */
1746b6d114aSBen Gras bool
_prop_object_externalize_append_cstring(struct _prop_object_externalize_context * ctx,const char * cp)1756b6d114aSBen Gras _prop_object_externalize_append_cstring(
1766b6d114aSBen Gras     struct _prop_object_externalize_context *ctx, const char *cp)
1776b6d114aSBen Gras {
1786b6d114aSBen Gras 
1796b6d114aSBen Gras 	while (*cp != '\0') {
1806b6d114aSBen Gras 		if (_prop_object_externalize_append_char(ctx,
1816b6d114aSBen Gras 						(unsigned char) *cp) == false)
1826b6d114aSBen Gras 			return (false);
1836b6d114aSBen Gras 		cp++;
1846b6d114aSBen Gras 	}
1856b6d114aSBen Gras 
1866b6d114aSBen Gras 	return (true);
1876b6d114aSBen Gras }
1886b6d114aSBen Gras 
1896b6d114aSBen Gras /*
1906b6d114aSBen Gras  * _prop_object_externalize_append_encoded_cstring --
1916b6d114aSBen Gras  *	Append an encoded C string to the externalize buffer.
1926b6d114aSBen Gras  */
1936b6d114aSBen Gras bool
_prop_object_externalize_append_encoded_cstring(struct _prop_object_externalize_context * ctx,const char * cp)1946b6d114aSBen Gras _prop_object_externalize_append_encoded_cstring(
1956b6d114aSBen Gras     struct _prop_object_externalize_context *ctx, const char *cp)
1966b6d114aSBen Gras {
1976b6d114aSBen Gras 
1986b6d114aSBen Gras 	while (*cp != '\0') {
1996b6d114aSBen Gras 		switch (*cp) {
2006b6d114aSBen Gras 		case '<':
2016b6d114aSBen Gras 			if (_prop_object_externalize_append_cstring(ctx,
2026b6d114aSBen Gras 					"&lt;") == false)
2036b6d114aSBen Gras 				return (false);
2046b6d114aSBen Gras 			break;
2056b6d114aSBen Gras 		case '>':
2066b6d114aSBen Gras 			if (_prop_object_externalize_append_cstring(ctx,
2076b6d114aSBen Gras 					"&gt;") == false)
2086b6d114aSBen Gras 				return (false);
2096b6d114aSBen Gras 			break;
2106b6d114aSBen Gras 		case '&':
2116b6d114aSBen Gras 			if (_prop_object_externalize_append_cstring(ctx,
2126b6d114aSBen Gras 					"&amp;") == false)
2136b6d114aSBen Gras 				return (false);
2146b6d114aSBen Gras 			break;
2156b6d114aSBen Gras 		default:
2166b6d114aSBen Gras 			if (_prop_object_externalize_append_char(ctx,
2176b6d114aSBen Gras 					(unsigned char) *cp) == false)
2186b6d114aSBen Gras 				return (false);
2196b6d114aSBen Gras 			break;
2206b6d114aSBen Gras 		}
2216b6d114aSBen Gras 		cp++;
2226b6d114aSBen Gras 	}
2236b6d114aSBen Gras 
2246b6d114aSBen Gras 	return (true);
2256b6d114aSBen Gras }
2266b6d114aSBen Gras 
2276b6d114aSBen Gras #define	BUF_EXPAND		256
2286b6d114aSBen Gras 
2296b6d114aSBen Gras /*
2306b6d114aSBen Gras  * _prop_object_externalize_append_char --
2316b6d114aSBen Gras  *	Append a single character to the externalize buffer.
2326b6d114aSBen Gras  */
2336b6d114aSBen Gras bool
_prop_object_externalize_append_char(struct _prop_object_externalize_context * ctx,unsigned char c)2346b6d114aSBen Gras _prop_object_externalize_append_char(
2356b6d114aSBen Gras     struct _prop_object_externalize_context *ctx, unsigned char c)
2366b6d114aSBen Gras {
2376b6d114aSBen Gras 
2386b6d114aSBen Gras 	_PROP_ASSERT(ctx->poec_capacity != 0);
2396b6d114aSBen Gras 	_PROP_ASSERT(ctx->poec_buf != NULL);
2406b6d114aSBen Gras 	_PROP_ASSERT(ctx->poec_len <= ctx->poec_capacity);
2416b6d114aSBen Gras 
2426b6d114aSBen Gras 	if (ctx->poec_len == ctx->poec_capacity) {
2436b6d114aSBen Gras 		char *cp = _PROP_REALLOC(ctx->poec_buf,
2446b6d114aSBen Gras 					 ctx->poec_capacity + BUF_EXPAND,
2456b6d114aSBen Gras 					 M_TEMP);
2466b6d114aSBen Gras 		if (cp == NULL)
2476b6d114aSBen Gras 			return (false);
2486b6d114aSBen Gras 		ctx->poec_capacity = ctx->poec_capacity + BUF_EXPAND;
2496b6d114aSBen Gras 		ctx->poec_buf = cp;
2506b6d114aSBen Gras 	}
2516b6d114aSBen Gras 
2526b6d114aSBen Gras 	ctx->poec_buf[ctx->poec_len++] = c;
2536b6d114aSBen Gras 
2546b6d114aSBen Gras 	return (true);
2556b6d114aSBen Gras }
2566b6d114aSBen Gras 
2576b6d114aSBen Gras /*
2586b6d114aSBen Gras  * _prop_object_externalize_header --
2596b6d114aSBen Gras  *	Append the standard XML header to the externalize buffer.
2606b6d114aSBen Gras  */
2616b6d114aSBen Gras bool
_prop_object_externalize_header(struct _prop_object_externalize_context * ctx)2626b6d114aSBen Gras _prop_object_externalize_header(struct _prop_object_externalize_context *ctx)
2636b6d114aSBen Gras {
2646b6d114aSBen Gras 	static const char _plist_xml_header[] =
2656b6d114aSBen Gras "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
2666b6d114aSBen Gras "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n";
2676b6d114aSBen Gras 
2686b6d114aSBen Gras 	if (_prop_object_externalize_append_cstring(ctx,
2696b6d114aSBen Gras 						 _plist_xml_header) == false ||
2706b6d114aSBen Gras 	    _prop_object_externalize_start_tag(ctx,
2716b6d114aSBen Gras 				       "plist version=\"1.0\"") == false ||
2726b6d114aSBen Gras 	    _prop_object_externalize_append_char(ctx, '\n') == false)
2736b6d114aSBen Gras 		return (false);
2746b6d114aSBen Gras 
2756b6d114aSBen Gras 	return (true);
2766b6d114aSBen Gras }
2776b6d114aSBen Gras 
2786b6d114aSBen Gras /*
2796b6d114aSBen Gras  * _prop_object_externalize_footer --
2806b6d114aSBen Gras  *	Append the standard XML footer to the externalize buffer.  This
2816b6d114aSBen Gras  *	also NUL-terminates the buffer.
2826b6d114aSBen Gras  */
2836b6d114aSBen Gras bool
_prop_object_externalize_footer(struct _prop_object_externalize_context * ctx)2846b6d114aSBen Gras _prop_object_externalize_footer(struct _prop_object_externalize_context *ctx)
2856b6d114aSBen Gras {
2866b6d114aSBen Gras 
2876b6d114aSBen Gras 	if (_prop_object_externalize_end_tag(ctx, "plist") == false ||
2886b6d114aSBen Gras 	    _prop_object_externalize_append_char(ctx, '\0') == false)
2896b6d114aSBen Gras 		return (false);
2906b6d114aSBen Gras 
2916b6d114aSBen Gras 	return (true);
2926b6d114aSBen Gras }
2936b6d114aSBen Gras 
2946b6d114aSBen Gras /*
2956b6d114aSBen Gras  * _prop_object_externalize_context_alloc --
2966b6d114aSBen Gras  *	Allocate an externalize context.
2976b6d114aSBen Gras  */
2986b6d114aSBen Gras struct _prop_object_externalize_context *
_prop_object_externalize_context_alloc(void)2996b6d114aSBen Gras _prop_object_externalize_context_alloc(void)
3006b6d114aSBen Gras {
3016b6d114aSBen Gras 	struct _prop_object_externalize_context *ctx;
3026b6d114aSBen Gras 
3036b6d114aSBen Gras 	ctx = _PROP_MALLOC(sizeof(*ctx), M_TEMP);
3046b6d114aSBen Gras 	if (ctx != NULL) {
3056b6d114aSBen Gras 		ctx->poec_buf = _PROP_MALLOC(BUF_EXPAND, M_TEMP);
3066b6d114aSBen Gras 		if (ctx->poec_buf == NULL) {
3076b6d114aSBen Gras 			_PROP_FREE(ctx, M_TEMP);
3086b6d114aSBen Gras 			return (NULL);
3096b6d114aSBen Gras 		}
3106b6d114aSBen Gras 		ctx->poec_len = 0;
3116b6d114aSBen Gras 		ctx->poec_capacity = BUF_EXPAND;
3126b6d114aSBen Gras 		ctx->poec_depth = 0;
3136b6d114aSBen Gras 	}
3146b6d114aSBen Gras 	return (ctx);
3156b6d114aSBen Gras }
3166b6d114aSBen Gras 
3176b6d114aSBen Gras /*
3186b6d114aSBen Gras  * _prop_object_externalize_context_free --
3196b6d114aSBen Gras  *	Free an externalize context.
3206b6d114aSBen Gras  */
3216b6d114aSBen Gras void
_prop_object_externalize_context_free(struct _prop_object_externalize_context * ctx)3226b6d114aSBen Gras _prop_object_externalize_context_free(
3236b6d114aSBen Gras 		struct _prop_object_externalize_context *ctx)
3246b6d114aSBen Gras {
3256b6d114aSBen Gras 
3266b6d114aSBen Gras 	/* Buffer is always freed by the caller. */
3276b6d114aSBen Gras 	_PROP_FREE(ctx, M_TEMP);
3286b6d114aSBen Gras }
3296b6d114aSBen Gras 
3306b6d114aSBen Gras /*
3316b6d114aSBen Gras  * _prop_object_internalize_skip_comment --
3326b6d114aSBen Gras  *	Skip the body and end tag of a comment.
3336b6d114aSBen Gras  */
3346b6d114aSBen Gras static bool
_prop_object_internalize_skip_comment(struct _prop_object_internalize_context * ctx)3356b6d114aSBen Gras _prop_object_internalize_skip_comment(
3366b6d114aSBen Gras 				struct _prop_object_internalize_context *ctx)
3376b6d114aSBen Gras {
3386b6d114aSBen Gras 	const char *cp = ctx->poic_cp;
3396b6d114aSBen Gras 
3406b6d114aSBen Gras 	while (!_PROP_EOF(*cp)) {
3416b6d114aSBen Gras 		if (cp[0] == '-' &&
3426b6d114aSBen Gras 		    cp[1] == '-' &&
3436b6d114aSBen Gras 		    cp[2] == '>') {
3446b6d114aSBen Gras 			ctx->poic_cp = cp + 3;
3456b6d114aSBen Gras 			return (true);
3466b6d114aSBen Gras 		}
3476b6d114aSBen Gras 		cp++;
3486b6d114aSBen Gras 	}
3496b6d114aSBen Gras 
3506b6d114aSBen Gras 	return (false);		/* ran out of buffer */
3516b6d114aSBen Gras }
3526b6d114aSBen Gras 
3536b6d114aSBen Gras /*
3546b6d114aSBen Gras  * _prop_object_internalize_find_tag --
3556b6d114aSBen Gras  *	Find the next tag in an XML stream.  Optionally compare the found
3566b6d114aSBen Gras  *	tag to an expected tag name.  State of the context is undefined
3576b6d114aSBen Gras  *	if this routine returns false.  Upon success, the context points
3586b6d114aSBen Gras  *	to the first octet after the tag.
3596b6d114aSBen Gras  */
3606b6d114aSBen Gras bool
_prop_object_internalize_find_tag(struct _prop_object_internalize_context * ctx,const char * tag,_prop_tag_type_t type)3616b6d114aSBen Gras _prop_object_internalize_find_tag(struct _prop_object_internalize_context *ctx,
3626b6d114aSBen Gras 		      const char *tag, _prop_tag_type_t type)
3636b6d114aSBen Gras {
3646b6d114aSBen Gras 	const char *cp;
3656b6d114aSBen Gras 	size_t taglen;
3666b6d114aSBen Gras 
3676b6d114aSBen Gras 	if (tag != NULL)
3686b6d114aSBen Gras 		taglen = strlen(tag);
3696b6d114aSBen Gras 	else
3706b6d114aSBen Gras 		taglen = 0;
3716b6d114aSBen Gras 
3726b6d114aSBen Gras  start_over:
3736b6d114aSBen Gras 	cp = ctx->poic_cp;
3746b6d114aSBen Gras 
3756b6d114aSBen Gras 	/*
3766b6d114aSBen Gras 	 * Find the start of the tag.
3776b6d114aSBen Gras 	 */
3786b6d114aSBen Gras 	while (_PROP_ISSPACE(*cp))
3796b6d114aSBen Gras 		cp++;
3806b6d114aSBen Gras 	if (_PROP_EOF(*cp))
3816b6d114aSBen Gras 		return (false);
3826b6d114aSBen Gras 
3836b6d114aSBen Gras 	if (*cp != '<')
3846b6d114aSBen Gras 		return (false);
3856b6d114aSBen Gras 
3866b6d114aSBen Gras 	ctx->poic_tag_start = cp++;
3876b6d114aSBen Gras 	if (_PROP_EOF(*cp))
3886b6d114aSBen Gras 		return (false);
3896b6d114aSBen Gras 
3906b6d114aSBen Gras 	if (*cp == '!') {
3916b6d114aSBen Gras 		if (cp[1] != '-' || cp[2] != '-')
3926b6d114aSBen Gras 			return (false);
3936b6d114aSBen Gras 		/*
3946b6d114aSBen Gras 		 * Comment block -- only allowed if we are allowed to
3956b6d114aSBen Gras 		 * return a start tag.
3966b6d114aSBen Gras 		 */
3976b6d114aSBen Gras 		if (type == _PROP_TAG_TYPE_END)
3986b6d114aSBen Gras 			return (false);
3996b6d114aSBen Gras 		ctx->poic_cp = cp + 3;
4006b6d114aSBen Gras 		if (_prop_object_internalize_skip_comment(ctx) == false)
4016b6d114aSBen Gras 			return (false);
4026b6d114aSBen Gras 		goto start_over;
4036b6d114aSBen Gras 	}
4046b6d114aSBen Gras 
4056b6d114aSBen Gras 	if (*cp == '/') {
4066b6d114aSBen Gras 		if (type != _PROP_TAG_TYPE_END &&
4076b6d114aSBen Gras 		    type != _PROP_TAG_TYPE_EITHER)
4086b6d114aSBen Gras 			return (false);
4096b6d114aSBen Gras 		cp++;
4106b6d114aSBen Gras 		if (_PROP_EOF(*cp))
4116b6d114aSBen Gras 			return (false);
4126b6d114aSBen Gras 		ctx->poic_tag_type = _PROP_TAG_TYPE_END;
4136b6d114aSBen Gras 	} else {
4146b6d114aSBen Gras 		if (type != _PROP_TAG_TYPE_START &&
4156b6d114aSBen Gras 		    type != _PROP_TAG_TYPE_EITHER)
4166b6d114aSBen Gras 			return (false);
4176b6d114aSBen Gras 		ctx->poic_tag_type = _PROP_TAG_TYPE_START;
4186b6d114aSBen Gras 	}
4196b6d114aSBen Gras 
4206b6d114aSBen Gras 	ctx->poic_tagname = cp;
4216b6d114aSBen Gras 
422*0a6a1f1dSLionel Sambuc 	while (!_PROP_ISSPACE(*cp) && *cp != '/' && *cp != '>') {
4236b6d114aSBen Gras 		if (_PROP_EOF(*cp))
4246b6d114aSBen Gras 			return (false);
425*0a6a1f1dSLionel Sambuc 		cp++;
426*0a6a1f1dSLionel Sambuc 	}
4276b6d114aSBen Gras 
4286b6d114aSBen Gras 	ctx->poic_tagname_len = cp - ctx->poic_tagname;
4296b6d114aSBen Gras 
4306b6d114aSBen Gras 	/* Make sure this is the tag we're looking for. */
4316b6d114aSBen Gras 	if (tag != NULL &&
4326b6d114aSBen Gras 	    (taglen != ctx->poic_tagname_len ||
4336b6d114aSBen Gras 	     memcmp(tag, ctx->poic_tagname, taglen) != 0))
4346b6d114aSBen Gras 		return (false);
4356b6d114aSBen Gras 
4366b6d114aSBen Gras 	/* Check for empty tag. */
4376b6d114aSBen Gras 	if (*cp == '/') {
4386b6d114aSBen Gras 		if (ctx->poic_tag_type != _PROP_TAG_TYPE_START)
4396b6d114aSBen Gras 			return(false);		/* only valid on start tags */
4406b6d114aSBen Gras 		ctx->poic_is_empty_element = true;
4416b6d114aSBen Gras 		cp++;
4426b6d114aSBen Gras 		if (_PROP_EOF(*cp) || *cp != '>')
4436b6d114aSBen Gras 			return (false);
4446b6d114aSBen Gras 	} else
4456b6d114aSBen Gras 		ctx->poic_is_empty_element = false;
4466b6d114aSBen Gras 
4476b6d114aSBen Gras 	/* Easy case of no arguments. */
4486b6d114aSBen Gras 	if (*cp == '>') {
4496b6d114aSBen Gras 		ctx->poic_tagattr = NULL;
4506b6d114aSBen Gras 		ctx->poic_tagattr_len = 0;
4516b6d114aSBen Gras 		ctx->poic_tagattrval = NULL;
4526b6d114aSBen Gras 		ctx->poic_tagattrval_len = 0;
4536b6d114aSBen Gras 		ctx->poic_cp = cp + 1;
4546b6d114aSBen Gras 		return (true);
4556b6d114aSBen Gras 	}
4566b6d114aSBen Gras 
4576b6d114aSBen Gras 	_PROP_ASSERT(!_PROP_EOF(*cp));
4586b6d114aSBen Gras 	cp++;
4596b6d114aSBen Gras 	if (_PROP_EOF(*cp))
4606b6d114aSBen Gras 		return (false);
4616b6d114aSBen Gras 
4626b6d114aSBen Gras 	while (_PROP_ISSPACE(*cp))
4636b6d114aSBen Gras 		cp++;
4646b6d114aSBen Gras 	if (_PROP_EOF(*cp))
4656b6d114aSBen Gras 		return (false);
4666b6d114aSBen Gras 
4676b6d114aSBen Gras 	ctx->poic_tagattr = cp;
4686b6d114aSBen Gras 
469*0a6a1f1dSLionel Sambuc 	while (!_PROP_ISSPACE(*cp) && *cp != '=') {
4706b6d114aSBen Gras 		if (_PROP_EOF(*cp))
4716b6d114aSBen Gras 			return (false);
472*0a6a1f1dSLionel Sambuc 		cp++;
473*0a6a1f1dSLionel Sambuc 	}
4746b6d114aSBen Gras 
4756b6d114aSBen Gras 	ctx->poic_tagattr_len = cp - ctx->poic_tagattr;
4766b6d114aSBen Gras 
4776b6d114aSBen Gras 	cp++;
4786b6d114aSBen Gras 	if (*cp != '\"')
4796b6d114aSBen Gras 		return (false);
4806b6d114aSBen Gras 	cp++;
4816b6d114aSBen Gras 	if (_PROP_EOF(*cp))
4826b6d114aSBen Gras 		return (false);
4836b6d114aSBen Gras 
4846b6d114aSBen Gras 	ctx->poic_tagattrval = cp;
485*0a6a1f1dSLionel Sambuc 	while (*cp != '\"') {
4866b6d114aSBen Gras 		if (_PROP_EOF(*cp))
4876b6d114aSBen Gras 			return (false);
488*0a6a1f1dSLionel Sambuc 		cp++;
489*0a6a1f1dSLionel Sambuc 	}
4906b6d114aSBen Gras 	ctx->poic_tagattrval_len = cp - ctx->poic_tagattrval;
4916b6d114aSBen Gras 
4926b6d114aSBen Gras 	cp++;
4936b6d114aSBen Gras 	if (*cp != '>')
4946b6d114aSBen Gras 		return (false);
4956b6d114aSBen Gras 
4966b6d114aSBen Gras 	ctx->poic_cp = cp + 1;
4976b6d114aSBen Gras 	return (true);
4986b6d114aSBen Gras }
4996b6d114aSBen Gras 
5006b6d114aSBen Gras /*
5016b6d114aSBen Gras  * _prop_object_internalize_decode_string --
5026b6d114aSBen Gras  *	Decode an encoded string.
5036b6d114aSBen Gras  */
5046b6d114aSBen Gras bool
_prop_object_internalize_decode_string(struct _prop_object_internalize_context * ctx,char * target,size_t targsize,size_t * sizep,const char ** cpp)5056b6d114aSBen Gras _prop_object_internalize_decode_string(
5066b6d114aSBen Gras 				struct _prop_object_internalize_context *ctx,
5076b6d114aSBen Gras 				char *target, size_t targsize, size_t *sizep,
5086b6d114aSBen Gras 				const char **cpp)
5096b6d114aSBen Gras {
5106b6d114aSBen Gras 	const char *src;
5116b6d114aSBen Gras 	size_t tarindex;
5126b6d114aSBen Gras 	char c;
5136b6d114aSBen Gras 
5146b6d114aSBen Gras 	tarindex = 0;
5156b6d114aSBen Gras 	src = ctx->poic_cp;
5166b6d114aSBen Gras 
5176b6d114aSBen Gras 	for (;;) {
5186b6d114aSBen Gras 		if (_PROP_EOF(*src))
5196b6d114aSBen Gras 			return (false);
5206b6d114aSBen Gras 		if (*src == '<') {
5216b6d114aSBen Gras 			break;
5226b6d114aSBen Gras 		}
5236b6d114aSBen Gras 
5246b6d114aSBen Gras 		if ((c = *src) == '&') {
5256b6d114aSBen Gras 			if (src[1] == 'a' &&
5266b6d114aSBen Gras 			    src[2] == 'm' &&
5276b6d114aSBen Gras 			    src[3] == 'p' &&
5286b6d114aSBen Gras 			    src[4] == ';') {
5296b6d114aSBen Gras 			    	c = '&';
5306b6d114aSBen Gras 				src += 5;
5316b6d114aSBen Gras 			} else if (src[1] == 'l' &&
5326b6d114aSBen Gras 				   src[2] == 't' &&
5336b6d114aSBen Gras 				   src[3] == ';') {
5346b6d114aSBen Gras 				c = '<';
5356b6d114aSBen Gras 				src += 4;
5366b6d114aSBen Gras 			} else if (src[1] == 'g' &&
5376b6d114aSBen Gras 				   src[2] == 't' &&
5386b6d114aSBen Gras 				   src[3] == ';') {
5396b6d114aSBen Gras 				c = '>';
5406b6d114aSBen Gras 				src += 4;
5416b6d114aSBen Gras 			} else if (src[1] == 'a' &&
5426b6d114aSBen Gras 				   src[2] == 'p' &&
5436b6d114aSBen Gras 				   src[3] == 'o' &&
5446b6d114aSBen Gras 				   src[4] == 's' &&
5456b6d114aSBen Gras 				   src[5] == ';') {
5466b6d114aSBen Gras 				c = '\'';
5476b6d114aSBen Gras 				src += 6;
5486b6d114aSBen Gras 			} else if (src[1] == 'q' &&
5496b6d114aSBen Gras 				   src[2] == 'u' &&
5506b6d114aSBen Gras 				   src[3] == 'o' &&
5516b6d114aSBen Gras 				   src[4] == 't' &&
5526b6d114aSBen Gras 				   src[5] == ';') {
5536b6d114aSBen Gras 				c = '\"';
5546b6d114aSBen Gras 				src += 6;
5556b6d114aSBen Gras 			} else
5566b6d114aSBen Gras 				return (false);
5576b6d114aSBen Gras 		} else
5586b6d114aSBen Gras 			src++;
5596b6d114aSBen Gras 		if (target) {
5606b6d114aSBen Gras 			if (tarindex >= targsize)
5616b6d114aSBen Gras 				return (false);
5626b6d114aSBen Gras 			target[tarindex] = c;
5636b6d114aSBen Gras 		}
5646b6d114aSBen Gras 		tarindex++;
5656b6d114aSBen Gras 	}
5666b6d114aSBen Gras 
5676b6d114aSBen Gras 	_PROP_ASSERT(*src == '<');
5686b6d114aSBen Gras 	if (sizep != NULL)
5696b6d114aSBen Gras 		*sizep = tarindex;
5706b6d114aSBen Gras 	if (cpp != NULL)
5716b6d114aSBen Gras 		*cpp = src;
5726b6d114aSBen Gras 
5736b6d114aSBen Gras 	return (true);
5746b6d114aSBen Gras }
5756b6d114aSBen Gras 
5766b6d114aSBen Gras /*
5776b6d114aSBen Gras  * _prop_object_internalize_match --
5786b6d114aSBen Gras  *	Returns true if the two character streams match.
5796b6d114aSBen Gras  */
5806b6d114aSBen Gras bool
_prop_object_internalize_match(const char * str1,size_t len1,const char * str2,size_t len2)5816b6d114aSBen Gras _prop_object_internalize_match(const char *str1, size_t len1,
5826b6d114aSBen Gras 			       const char *str2, size_t len2)
5836b6d114aSBen Gras {
5846b6d114aSBen Gras 
5856b6d114aSBen Gras 	return (len1 == len2 && memcmp(str1, str2, len1) == 0);
5866b6d114aSBen Gras }
5876b6d114aSBen Gras 
5886b6d114aSBen Gras #define	INTERNALIZER(t, f)			\
5896b6d114aSBen Gras {	t,	sizeof(t) - 1,		f	}
5906b6d114aSBen Gras 
5916b6d114aSBen Gras static const struct _prop_object_internalizer {
5926b6d114aSBen Gras 	const char			*poi_tag;
5936b6d114aSBen Gras 	size_t				poi_taglen;
5946b6d114aSBen Gras 	prop_object_internalizer_t	poi_intern;
5956b6d114aSBen Gras } _prop_object_internalizer_table[] = {
5966b6d114aSBen Gras 	INTERNALIZER("array", _prop_array_internalize),
5976b6d114aSBen Gras 
5986b6d114aSBen Gras 	INTERNALIZER("true", _prop_bool_internalize),
5996b6d114aSBen Gras 	INTERNALIZER("false", _prop_bool_internalize),
6006b6d114aSBen Gras 
6016b6d114aSBen Gras 	INTERNALIZER("data", _prop_data_internalize),
6026b6d114aSBen Gras 
6036b6d114aSBen Gras 	INTERNALIZER("dict", _prop_dictionary_internalize),
6046b6d114aSBen Gras 
6056b6d114aSBen Gras 	INTERNALIZER("integer", _prop_number_internalize),
6066b6d114aSBen Gras 
6076b6d114aSBen Gras 	INTERNALIZER("string", _prop_string_internalize),
6086b6d114aSBen Gras 
6096b6d114aSBen Gras 	{ 0, 0, NULL }
6106b6d114aSBen Gras };
6116b6d114aSBen Gras 
6126b6d114aSBen Gras #undef INTERNALIZER
6136b6d114aSBen Gras 
6146b6d114aSBen Gras /*
6156b6d114aSBen Gras  * _prop_object_internalize_by_tag --
6166b6d114aSBen Gras  *	Determine the object type from the tag in the context and
6176b6d114aSBen Gras  *	internalize it.
6186b6d114aSBen Gras  */
6196b6d114aSBen Gras prop_object_t
_prop_object_internalize_by_tag(struct _prop_object_internalize_context * ctx)6206b6d114aSBen Gras _prop_object_internalize_by_tag(struct _prop_object_internalize_context *ctx)
6216b6d114aSBen Gras {
6226b6d114aSBen Gras 	const struct _prop_object_internalizer *poi;
6236b6d114aSBen Gras 	prop_object_t obj, parent_obj;
6246b6d114aSBen Gras 	void *data, *iter;
6256b6d114aSBen Gras 	prop_object_internalizer_continue_t iter_func;
6266b6d114aSBen Gras 	struct _prop_stack stack;
6276b6d114aSBen Gras 
6286b6d114aSBen Gras 	_prop_stack_init(&stack);
6296b6d114aSBen Gras 
6306b6d114aSBen Gras match_start:
6316b6d114aSBen Gras 	for (poi = _prop_object_internalizer_table;
6326b6d114aSBen Gras 	     poi->poi_tag != NULL; poi++) {
6336b6d114aSBen Gras 		if (_prop_object_internalize_match(ctx->poic_tagname,
6346b6d114aSBen Gras 						   ctx->poic_tagname_len,
6356b6d114aSBen Gras 						   poi->poi_tag,
6366b6d114aSBen Gras 						   poi->poi_taglen))
6376b6d114aSBen Gras 			break;
6386b6d114aSBen Gras 	}
6396b6d114aSBen Gras 	if ((poi == NULL) || (poi->poi_tag == NULL)) {
6406b6d114aSBen Gras 		while (_prop_stack_pop(&stack, &obj, &iter, &data, NULL)) {
6416b6d114aSBen Gras 			iter_func = (prop_object_internalizer_continue_t)iter;
6426b6d114aSBen Gras 			(*iter_func)(&stack, &obj, ctx, data, NULL);
6436b6d114aSBen Gras 		}
6446b6d114aSBen Gras 
6456b6d114aSBen Gras 		return (NULL);
6466b6d114aSBen Gras 	}
6476b6d114aSBen Gras 
6486b6d114aSBen Gras 	obj = NULL;
6496b6d114aSBen Gras 	if (!(*poi->poi_intern)(&stack, &obj, ctx))
6506b6d114aSBen Gras 		goto match_start;
6516b6d114aSBen Gras 
6526b6d114aSBen Gras 	parent_obj = obj;
6536b6d114aSBen Gras 	while (_prop_stack_pop(&stack, &parent_obj, &iter, &data, NULL)) {
6546b6d114aSBen Gras 		iter_func = (prop_object_internalizer_continue_t)iter;
6556b6d114aSBen Gras 		if (!(*iter_func)(&stack, &parent_obj, ctx, data, obj))
6566b6d114aSBen Gras 			goto match_start;
6576b6d114aSBen Gras 		obj = parent_obj;
6586b6d114aSBen Gras 	}
6596b6d114aSBen Gras 
6606b6d114aSBen Gras 	return (parent_obj);
6616b6d114aSBen Gras }
6626b6d114aSBen Gras 
6636b6d114aSBen Gras prop_object_t
_prop_generic_internalize(const char * xml,const char * master_tag)6646b6d114aSBen Gras _prop_generic_internalize(const char *xml, const char *master_tag)
6656b6d114aSBen Gras {
6666b6d114aSBen Gras 	prop_object_t obj = NULL;
6676b6d114aSBen Gras 	struct _prop_object_internalize_context *ctx;
6686b6d114aSBen Gras 
6696b6d114aSBen Gras 	ctx = _prop_object_internalize_context_alloc(xml);
6706b6d114aSBen Gras 	if (ctx == NULL)
6716b6d114aSBen Gras 		return (NULL);
6726b6d114aSBen Gras 
6736b6d114aSBen Gras 	/* We start with a <plist> tag. */
6746b6d114aSBen Gras 	if (_prop_object_internalize_find_tag(ctx, "plist",
6756b6d114aSBen Gras 					      _PROP_TAG_TYPE_START) == false)
6766b6d114aSBen Gras 		goto out;
6776b6d114aSBen Gras 
6786b6d114aSBen Gras 	/* Plist elements cannot be empty. */
6796b6d114aSBen Gras 	if (ctx->poic_is_empty_element)
6806b6d114aSBen Gras 		goto out;
6816b6d114aSBen Gras 
6826b6d114aSBen Gras 	/*
6836b6d114aSBen Gras 	 * We don't understand any plist attributes, but Apple XML
6846b6d114aSBen Gras 	 * property lists often have a "version" attribute.  If we
6856b6d114aSBen Gras 	 * see that one, we simply ignore it.
6866b6d114aSBen Gras 	 */
6876b6d114aSBen Gras 	if (ctx->poic_tagattr != NULL &&
6886b6d114aSBen Gras 	    !_PROP_TAGATTR_MATCH(ctx, "version"))
6896b6d114aSBen Gras 		goto out;
6906b6d114aSBen Gras 
6916b6d114aSBen Gras 	/* Next we expect to see opening master_tag. */
6926b6d114aSBen Gras 	if (_prop_object_internalize_find_tag(ctx, master_tag,
6936b6d114aSBen Gras 					      _PROP_TAG_TYPE_START) == false)
6946b6d114aSBen Gras 		goto out;
6956b6d114aSBen Gras 
6966b6d114aSBen Gras 	obj = _prop_object_internalize_by_tag(ctx);
6976b6d114aSBen Gras 	if (obj == NULL)
6986b6d114aSBen Gras 		goto out;
6996b6d114aSBen Gras 
7006b6d114aSBen Gras 	/*
7016b6d114aSBen Gras 	 * We've advanced past the closing master_tag.
7026b6d114aSBen Gras 	 * Now we want </plist>.
7036b6d114aSBen Gras 	 */
7046b6d114aSBen Gras 	if (_prop_object_internalize_find_tag(ctx, "plist",
7056b6d114aSBen Gras 					      _PROP_TAG_TYPE_END) == false) {
7066b6d114aSBen Gras 		prop_object_release(obj);
7076b6d114aSBen Gras 		obj = NULL;
7086b6d114aSBen Gras 	}
7096b6d114aSBen Gras 
7106b6d114aSBen Gras  out:
7116b6d114aSBen Gras  	_prop_object_internalize_context_free(ctx);
7126b6d114aSBen Gras 	return (obj);
7136b6d114aSBen Gras }
7146b6d114aSBen Gras 
7156b6d114aSBen Gras /*
7166b6d114aSBen Gras  * _prop_object_internalize_context_alloc --
7176b6d114aSBen Gras  *	Allocate an internalize context.
7186b6d114aSBen Gras  */
7196b6d114aSBen Gras struct _prop_object_internalize_context *
_prop_object_internalize_context_alloc(const char * xml)7206b6d114aSBen Gras _prop_object_internalize_context_alloc(const char *xml)
7216b6d114aSBen Gras {
7226b6d114aSBen Gras 	struct _prop_object_internalize_context *ctx;
7236b6d114aSBen Gras 
7246b6d114aSBen Gras 	ctx = _PROP_MALLOC(sizeof(struct _prop_object_internalize_context),
7256b6d114aSBen Gras 			   M_TEMP);
7266b6d114aSBen Gras 	if (ctx == NULL)
7276b6d114aSBen Gras 		return (NULL);
7286b6d114aSBen Gras 
7296b6d114aSBen Gras 	ctx->poic_xml = ctx->poic_cp = xml;
7306b6d114aSBen Gras 
7316b6d114aSBen Gras 	/*
7326b6d114aSBen Gras 	 * Skip any whitespace and XML preamble stuff that we don't
7336b6d114aSBen Gras 	 * know about / care about.
7346b6d114aSBen Gras 	 */
7356b6d114aSBen Gras 	for (;;) {
7366b6d114aSBen Gras 		while (_PROP_ISSPACE(*xml))
7376b6d114aSBen Gras 			xml++;
7386b6d114aSBen Gras 		if (_PROP_EOF(*xml) || *xml != '<')
7396b6d114aSBen Gras 			goto bad;
7406b6d114aSBen Gras 
7416b6d114aSBen Gras #define	MATCH(str)	(memcmp(&xml[1], str, sizeof(str) - 1) == 0)
7426b6d114aSBen Gras 
7436b6d114aSBen Gras 		/*
7446b6d114aSBen Gras 		 * Skip over the XML preamble that Apple XML property
7456b6d114aSBen Gras 		 * lists usually include at the top of the file.
7466b6d114aSBen Gras 		 */
7476b6d114aSBen Gras 		if (MATCH("?xml ") ||
7486b6d114aSBen Gras 		    MATCH("!DOCTYPE plist")) {
7496b6d114aSBen Gras 			while (*xml != '>' && !_PROP_EOF(*xml))
7506b6d114aSBen Gras 				xml++;
7516b6d114aSBen Gras 			if (_PROP_EOF(*xml))
7526b6d114aSBen Gras 				goto bad;
7536b6d114aSBen Gras 			xml++;	/* advance past the '>' */
7546b6d114aSBen Gras 			continue;
7556b6d114aSBen Gras 		}
7566b6d114aSBen Gras 
7576b6d114aSBen Gras 		if (MATCH("<!--")) {
7586b6d114aSBen Gras 			ctx->poic_cp = xml + 4;
7596b6d114aSBen Gras 			if (_prop_object_internalize_skip_comment(ctx) == false)
7606b6d114aSBen Gras 				goto bad;
7616b6d114aSBen Gras 			xml = ctx->poic_cp;
7626b6d114aSBen Gras 			continue;
7636b6d114aSBen Gras 		}
7646b6d114aSBen Gras 
7656b6d114aSBen Gras #undef MATCH
7666b6d114aSBen Gras 
7676b6d114aSBen Gras 		/*
7686b6d114aSBen Gras 		 * We don't think we should skip it, so let's hope we can
7696b6d114aSBen Gras 		 * parse it.
7706b6d114aSBen Gras 		 */
7716b6d114aSBen Gras 		break;
7726b6d114aSBen Gras 	}
7736b6d114aSBen Gras 
7746b6d114aSBen Gras 	ctx->poic_cp = xml;
7756b6d114aSBen Gras 	return (ctx);
7766b6d114aSBen Gras  bad:
7776b6d114aSBen Gras 	_PROP_FREE(ctx, M_TEMP);
7786b6d114aSBen Gras 	return (NULL);
7796b6d114aSBen Gras }
7806b6d114aSBen Gras 
7816b6d114aSBen Gras /*
7826b6d114aSBen Gras  * _prop_object_internalize_context_free --
7836b6d114aSBen Gras  *	Free an internalize context.
7846b6d114aSBen Gras  */
7856b6d114aSBen Gras void
_prop_object_internalize_context_free(struct _prop_object_internalize_context * ctx)7866b6d114aSBen Gras _prop_object_internalize_context_free(
7876b6d114aSBen Gras 		struct _prop_object_internalize_context *ctx)
7886b6d114aSBen Gras {
7896b6d114aSBen Gras 
7906b6d114aSBen Gras 	_PROP_FREE(ctx, M_TEMP);
7916b6d114aSBen Gras }
7926b6d114aSBen Gras 
7936b6d114aSBen Gras #if !defined(_KERNEL) && !defined(_STANDALONE)
7946b6d114aSBen Gras /*
7956b6d114aSBen Gras  * _prop_object_externalize_file_dirname --
7966b6d114aSBen Gras  *	dirname(3), basically.  We have to roll our own because the
7976b6d114aSBen Gras  *	system dirname(3) isn't reentrant.
7986b6d114aSBen Gras  */
7996b6d114aSBen Gras static void
_prop_object_externalize_file_dirname(const char * path,char * result)8006b6d114aSBen Gras _prop_object_externalize_file_dirname(const char *path, char *result)
8016b6d114aSBen Gras {
8026b6d114aSBen Gras 	const char *lastp;
8036b6d114aSBen Gras 	size_t len;
8046b6d114aSBen Gras 
8056b6d114aSBen Gras 	/*
8066b6d114aSBen Gras 	 * If `path' is a NULL pointer or points to an empty string,
8076b6d114aSBen Gras 	 * return ".".
8086b6d114aSBen Gras 	 */
8096b6d114aSBen Gras 	if (path == NULL || *path == '\0')
8106b6d114aSBen Gras 		goto singledot;
8116b6d114aSBen Gras 
8126b6d114aSBen Gras 	/* String trailing slashes, if any. */
8136b6d114aSBen Gras 	lastp = path + strlen(path) - 1;
8146b6d114aSBen Gras 	while (lastp != path && *lastp == '/')
8156b6d114aSBen Gras 		lastp--;
8166b6d114aSBen Gras 
8176b6d114aSBen Gras 	/* Terminate path at the last occurrence of '/'. */
8186b6d114aSBen Gras 	do {
8196b6d114aSBen Gras 		if (*lastp == '/') {
8206b6d114aSBen Gras 			/* Strip trailing slashes, if any. */
8216b6d114aSBen Gras 			while (lastp != path && *lastp == '/')
8226b6d114aSBen Gras 				lastp--;
8236b6d114aSBen Gras 
8246b6d114aSBen Gras 			/* ...and copy the result into the result buffer. */
8256b6d114aSBen Gras 			len = (lastp - path) + 1 /* last char */;
8266b6d114aSBen Gras 			if (len > (PATH_MAX - 1))
8276b6d114aSBen Gras 				len = PATH_MAX - 1;
8286b6d114aSBen Gras 
8296b6d114aSBen Gras 			memcpy(result, path, len);
8306b6d114aSBen Gras 			result[len] = '\0';
8316b6d114aSBen Gras 			return;
8326b6d114aSBen Gras 		}
8336b6d114aSBen Gras 	} while (--lastp >= path);
8346b6d114aSBen Gras 
8356b6d114aSBen Gras  	/* No /'s found, return ".". */
8366b6d114aSBen Gras  singledot:
8376b6d114aSBen Gras 	strcpy(result, ".");
8386b6d114aSBen Gras }
8396b6d114aSBen Gras 
8406b6d114aSBen Gras /*
8416b6d114aSBen Gras  * _prop_object_externalize_write_file --
8426b6d114aSBen Gras  *	Write an externalized dictionary to the specified file.
8436b6d114aSBen Gras  *	The file is written atomically from the caller's perspective,
8446b6d114aSBen Gras  *	and the mode set to 0666 modified by the caller's umask.
8456b6d114aSBen Gras  */
8466b6d114aSBen Gras bool
_prop_object_externalize_write_file(const char * fname,const char * xml,size_t len)8476b6d114aSBen Gras _prop_object_externalize_write_file(const char *fname, const char *xml,
8486b6d114aSBen Gras     size_t len)
8496b6d114aSBen Gras {
8506b6d114aSBen Gras 	char tname[PATH_MAX];
8516b6d114aSBen Gras 	int fd;
8526b6d114aSBen Gras 	int save_errno;
8536b6d114aSBen Gras 	mode_t myumask;
8546b6d114aSBen Gras 
8556b6d114aSBen Gras 	if (len > SSIZE_MAX) {
8566b6d114aSBen Gras 		errno = EFBIG;
8576b6d114aSBen Gras 		return (false);
8586b6d114aSBen Gras 	}
8596b6d114aSBen Gras 
8606b6d114aSBen Gras 	/*
8616b6d114aSBen Gras 	 * Get the directory name where the file is to be written
8626b6d114aSBen Gras 	 * and create the temporary file.
8636b6d114aSBen Gras 	 */
8646b6d114aSBen Gras 	_prop_object_externalize_file_dirname(fname, tname);
865f14fb602SLionel Sambuc #define PLISTTMP "/.plistXXXXXX"
866f14fb602SLionel Sambuc 	if (strlen(tname) + strlen(PLISTTMP) >= sizeof(tname)) {
8676b6d114aSBen Gras 		errno = ENAMETOOLONG;
8686b6d114aSBen Gras 		return (false);
8696b6d114aSBen Gras 	}
870f14fb602SLionel Sambuc 	strcat(tname, PLISTTMP);
871f14fb602SLionel Sambuc #undef PLISTTMP
872f14fb602SLionel Sambuc 
8736b6d114aSBen Gras 	if ((fd = mkstemp(tname)) == -1)
8746b6d114aSBen Gras 		return (false);
8756b6d114aSBen Gras 
8766b6d114aSBen Gras 	if (write(fd, xml, len) != (ssize_t)len)
8776b6d114aSBen Gras 		goto bad;
8786b6d114aSBen Gras 
8796b6d114aSBen Gras 	if (fsync(fd) == -1)
8806b6d114aSBen Gras 		goto bad;
8816b6d114aSBen Gras 
8826b6d114aSBen Gras 	myumask = umask(0);
8836b6d114aSBen Gras 	(void)umask(myumask);
8846b6d114aSBen Gras 	if (fchmod(fd, 0666 & ~myumask) == -1)
8856b6d114aSBen Gras 		goto bad;
8866b6d114aSBen Gras 
8876b6d114aSBen Gras 	(void) close(fd);
8886b6d114aSBen Gras 	fd = -1;
8896b6d114aSBen Gras 
8906b6d114aSBen Gras 	if (rename(tname, fname) == -1)
8916b6d114aSBen Gras 		goto bad;
8926b6d114aSBen Gras 
8936b6d114aSBen Gras 	return (true);
8946b6d114aSBen Gras 
8956b6d114aSBen Gras  bad:
8966b6d114aSBen Gras 	save_errno = errno;
8976b6d114aSBen Gras 	if (fd != -1)
8986b6d114aSBen Gras 		(void) close(fd);
8996b6d114aSBen Gras 	(void) unlink(tname);
9006b6d114aSBen Gras 	errno = save_errno;
9016b6d114aSBen Gras 	return (false);
9026b6d114aSBen Gras }
9036b6d114aSBen Gras 
9046b6d114aSBen Gras /*
9056b6d114aSBen Gras  * _prop_object_internalize_map_file --
9066b6d114aSBen Gras  *	Map a file for the purpose of internalizing it.
9076b6d114aSBen Gras  */
9086b6d114aSBen Gras struct _prop_object_internalize_mapped_file *
_prop_object_internalize_map_file(const char * fname)9096b6d114aSBen Gras _prop_object_internalize_map_file(const char *fname)
9106b6d114aSBen Gras {
9116b6d114aSBen Gras 	struct stat sb;
9126b6d114aSBen Gras 	struct _prop_object_internalize_mapped_file *mf;
9136b6d114aSBen Gras 	size_t pgsize = (size_t)sysconf(_SC_PAGESIZE);
9146b6d114aSBen Gras 	size_t pgmask = pgsize - 1;
9156b6d114aSBen Gras 	bool need_guard = false;
9166b6d114aSBen Gras 	int fd;
9176b6d114aSBen Gras 
9186b6d114aSBen Gras 	mf = _PROP_MALLOC(sizeof(*mf), M_TEMP);
9196b6d114aSBen Gras 	if (mf == NULL)
9206b6d114aSBen Gras 		return (NULL);
9216b6d114aSBen Gras 
9226b6d114aSBen Gras 	fd = open(fname, O_RDONLY, 0400);
9236b6d114aSBen Gras 	if (fd == -1) {
9246b6d114aSBen Gras 		_PROP_FREE(mf, M_TEMP);
9256b6d114aSBen Gras 		return (NULL);
9266b6d114aSBen Gras 	}
9276b6d114aSBen Gras 
9286b6d114aSBen Gras 	if (fstat(fd, &sb) == -1) {
9296b6d114aSBen Gras 		(void) close(fd);
9306b6d114aSBen Gras 		_PROP_FREE(mf, M_TEMP);
9316b6d114aSBen Gras 		return (NULL);
9326b6d114aSBen Gras 	}
9336b6d114aSBen Gras 	mf->poimf_mapsize = ((size_t)sb.st_size + pgmask) & ~pgmask;
9346b6d114aSBen Gras 	if (mf->poimf_mapsize < (size_t)sb.st_size) {
9356b6d114aSBen Gras 		(void) close(fd);
9366b6d114aSBen Gras 		_PROP_FREE(mf, M_TEMP);
9376b6d114aSBen Gras 		return (NULL);
9386b6d114aSBen Gras 	}
9396b6d114aSBen Gras 
9406b6d114aSBen Gras 	/*
9416b6d114aSBen Gras 	 * If the file length is an integral number of pages, then we
9426b6d114aSBen Gras 	 * need to map a guard page at the end in order to provide the
9436b6d114aSBen Gras 	 * necessary NUL-termination of the buffer.
9446b6d114aSBen Gras 	 */
9456b6d114aSBen Gras 	if ((sb.st_size & pgmask) == 0)
9466b6d114aSBen Gras 		need_guard = true;
9476b6d114aSBen Gras 
9486b6d114aSBen Gras 	mf->poimf_xml = mmap(NULL, need_guard ? mf->poimf_mapsize + pgsize
9496b6d114aSBen Gras 			    		      : mf->poimf_mapsize,
9506b6d114aSBen Gras 			    PROT_READ, MAP_FILE|MAP_SHARED, fd, (off_t)0);
9516b6d114aSBen Gras 	(void) close(fd);
9526b6d114aSBen Gras 	if (mf->poimf_xml == MAP_FAILED) {
9536b6d114aSBen Gras 		_PROP_FREE(mf, M_TEMP);
9546b6d114aSBen Gras 		return (NULL);
9556b6d114aSBen Gras 	}
956*0a6a1f1dSLionel Sambuc #if !defined(__minix)
9576b6d114aSBen Gras 	(void) madvise(mf->poimf_xml, mf->poimf_mapsize, MADV_SEQUENTIAL);
9586b6d114aSBen Gras 
9596b6d114aSBen Gras 	if (need_guard) {
9606b6d114aSBen Gras 		if (mmap(mf->poimf_xml + mf->poimf_mapsize,
9616b6d114aSBen Gras 			 pgsize, PROT_READ,
9626b6d114aSBen Gras 			 MAP_ANON|MAP_PRIVATE|MAP_FIXED, -1,
9636b6d114aSBen Gras 			 (off_t)0) == MAP_FAILED) {
9646b6d114aSBen Gras 			(void) munmap(mf->poimf_xml, mf->poimf_mapsize);
9656b6d114aSBen Gras 			_PROP_FREE(mf, M_TEMP);
9666b6d114aSBen Gras 			return (NULL);
9676b6d114aSBen Gras 		}
9686b6d114aSBen Gras 		mf->poimf_mapsize += pgsize;
9696b6d114aSBen Gras 	}
970*0a6a1f1dSLionel Sambuc #endif /* !defined(__minix) */
9716b6d114aSBen Gras 
9726b6d114aSBen Gras 	return (mf);
9736b6d114aSBen Gras }
9746b6d114aSBen Gras 
9756b6d114aSBen Gras /*
9766b6d114aSBen Gras  * _prop_object_internalize_unmap_file --
9776b6d114aSBen Gras  *	Unmap a file previously mapped for internalizing.
9786b6d114aSBen Gras  */
9796b6d114aSBen Gras void
9804684ddb6SLionel Sambuc #if defined(__minix)
9814684ddb6SLionel Sambuc __dead
9824684ddb6SLionel Sambuc #endif /* defined(__minix) */
_prop_object_internalize_unmap_file(struct _prop_object_internalize_mapped_file * mf)9836b6d114aSBen Gras _prop_object_internalize_unmap_file(
9846b6d114aSBen Gras     struct _prop_object_internalize_mapped_file *mf)
9856b6d114aSBen Gras {
9866b6d114aSBen Gras 
987*0a6a1f1dSLionel Sambuc #if !defined(__minix)
9886b6d114aSBen Gras 	(void) madvise(mf->poimf_xml, mf->poimf_mapsize, MADV_DONTNEED);
9896b6d114aSBen Gras 	(void) munmap(mf->poimf_xml, mf->poimf_mapsize);
9906b6d114aSBen Gras 	_PROP_FREE(mf, M_TEMP);
9916b6d114aSBen Gras #else
9923260d16fSLionel Sambuc 	abort();
993*0a6a1f1dSLionel Sambuc #endif /*  !defined(__minix) */
9946b6d114aSBen Gras }
9956b6d114aSBen Gras #endif /* !_KERNEL && !_STANDALONE */
9966b6d114aSBen Gras 
9976b6d114aSBen Gras /*
9986b6d114aSBen Gras  * prop_object_retain --
9996b6d114aSBen Gras  *	Increment the reference count on an object.
10006b6d114aSBen Gras  */
10016b6d114aSBen Gras void
prop_object_retain(prop_object_t obj)10026b6d114aSBen Gras prop_object_retain(prop_object_t obj)
10036b6d114aSBen Gras {
10046b6d114aSBen Gras 	struct _prop_object *po = obj;
100584d9c625SLionel Sambuc 	uint32_t ncnt __unused;
10066b6d114aSBen Gras 
1007f14fb602SLionel Sambuc 	_PROP_ATOMIC_INC32_NV(&po->po_refcnt, ncnt);
10086b6d114aSBen Gras 	_PROP_ASSERT(ncnt != 0);
10096b6d114aSBen Gras }
10106b6d114aSBen Gras 
10116b6d114aSBen Gras /*
10126b6d114aSBen Gras  * prop_object_release_emergency
10136b6d114aSBen Gras  *	A direct free with prop_object_release failed.
10146b6d114aSBen Gras  *	Walk down the tree until a leaf is found and
10156b6d114aSBen Gras  *	free that. Do not recurse to avoid stack overflows.
10166b6d114aSBen Gras  *
10176b6d114aSBen Gras  *	This is a slow edge condition, but necessary to
10186b6d114aSBen Gras  *	guarantee that an object can always be freed.
10196b6d114aSBen Gras  */
10206b6d114aSBen Gras static void
prop_object_release_emergency(prop_object_t obj)10216b6d114aSBen Gras prop_object_release_emergency(prop_object_t obj)
10226b6d114aSBen Gras {
10236b6d114aSBen Gras 	struct _prop_object *po;
10246b6d114aSBen Gras 	void (*unlock)(void);
10256b6d114aSBen Gras 	prop_object_t parent = NULL;
10266b6d114aSBen Gras 	uint32_t ocnt;
10276b6d114aSBen Gras 
10286b6d114aSBen Gras 	for (;;) {
10296b6d114aSBen Gras 		po = obj;
10306b6d114aSBen Gras 		_PROP_ASSERT(obj);
10316b6d114aSBen Gras 
10326b6d114aSBen Gras 		if (po->po_type->pot_lock != NULL)
10336b6d114aSBen Gras 		po->po_type->pot_lock();
10346b6d114aSBen Gras 
10356b6d114aSBen Gras 		/* Save pointerto unlock function */
10366b6d114aSBen Gras 		unlock = po->po_type->pot_unlock;
10376b6d114aSBen Gras 
10386b6d114aSBen Gras 		/* Dance a bit to make sure we always get the non-racy ocnt */
1039f14fb602SLionel Sambuc 		_PROP_ATOMIC_DEC32_NV(&po->po_refcnt, ocnt);
10406b6d114aSBen Gras 		ocnt++;
10416b6d114aSBen Gras 		_PROP_ASSERT(ocnt != 0);
10426b6d114aSBen Gras 
10436b6d114aSBen Gras 		if (ocnt != 1) {
10446b6d114aSBen Gras 			if (unlock != NULL)
10456b6d114aSBen Gras 				unlock();
10466b6d114aSBen Gras 			break;
10476b6d114aSBen Gras 		}
10486b6d114aSBen Gras 
10496b6d114aSBen Gras 		_PROP_ASSERT(po->po_type);
10506b6d114aSBen Gras 		if ((po->po_type->pot_free)(NULL, &obj) ==
10516b6d114aSBen Gras 		    _PROP_OBJECT_FREE_DONE) {
10526b6d114aSBen Gras 			if (unlock != NULL)
10536b6d114aSBen Gras 				unlock();
10546b6d114aSBen Gras 			break;
10556b6d114aSBen Gras 		}
10566b6d114aSBen Gras 
10576b6d114aSBen Gras 		if (unlock != NULL)
10586b6d114aSBen Gras 			unlock();
10596b6d114aSBen Gras 
10606b6d114aSBen Gras 		parent = po;
1061f14fb602SLionel Sambuc 		_PROP_ATOMIC_INC32(&po->po_refcnt);
10626b6d114aSBen Gras 	}
10636b6d114aSBen Gras 	_PROP_ASSERT(parent);
10646b6d114aSBen Gras 	/* One object was just freed. */
10656b6d114aSBen Gras 	po = parent;
10666b6d114aSBen Gras 	(*po->po_type->pot_emergency_free)(parent);
10676b6d114aSBen Gras }
10686b6d114aSBen Gras 
10696b6d114aSBen Gras /*
10706b6d114aSBen Gras  * prop_object_release --
10716b6d114aSBen Gras  *	Decrement the reference count on an object.
10726b6d114aSBen Gras  *
10736b6d114aSBen Gras  *	Free the object if we are releasing the final
10746b6d114aSBen Gras  *	reference.
10756b6d114aSBen Gras  */
10766b6d114aSBen Gras void
prop_object_release(prop_object_t obj)10776b6d114aSBen Gras prop_object_release(prop_object_t obj)
10786b6d114aSBen Gras {
10796b6d114aSBen Gras 	struct _prop_object *po;
10806b6d114aSBen Gras 	struct _prop_stack stack;
10816b6d114aSBen Gras 	void (*unlock)(void);
10826b6d114aSBen Gras 	int ret;
10836b6d114aSBen Gras 	uint32_t ocnt;
10846b6d114aSBen Gras 
10856b6d114aSBen Gras 	_prop_stack_init(&stack);
10866b6d114aSBen Gras 
10876b6d114aSBen Gras 	do {
10886b6d114aSBen Gras 		do {
10896b6d114aSBen Gras 			po = obj;
10906b6d114aSBen Gras 			_PROP_ASSERT(obj);
10916b6d114aSBen Gras 
10926b6d114aSBen Gras 			if (po->po_type->pot_lock != NULL)
10936b6d114aSBen Gras 				po->po_type->pot_lock();
10946b6d114aSBen Gras 
10956b6d114aSBen Gras 			/* Save pointer to object unlock function */
10966b6d114aSBen Gras 			unlock = po->po_type->pot_unlock;
10976b6d114aSBen Gras 
1098f14fb602SLionel Sambuc 			_PROP_ATOMIC_DEC32_NV(&po->po_refcnt, ocnt);
10996b6d114aSBen Gras 			ocnt++;
11006b6d114aSBen Gras 			_PROP_ASSERT(ocnt != 0);
11016b6d114aSBen Gras 
11026b6d114aSBen Gras 			if (ocnt != 1) {
11036b6d114aSBen Gras 				ret = 0;
11046b6d114aSBen Gras 				if (unlock != NULL)
11056b6d114aSBen Gras 					unlock();
11066b6d114aSBen Gras 				break;
11076b6d114aSBen Gras 			}
11086b6d114aSBen Gras 
11096b6d114aSBen Gras 			ret = (po->po_type->pot_free)(&stack, &obj);
11106b6d114aSBen Gras 
11116b6d114aSBen Gras 			if (unlock != NULL)
11126b6d114aSBen Gras 				unlock();
11136b6d114aSBen Gras 
11146b6d114aSBen Gras 			if (ret == _PROP_OBJECT_FREE_DONE)
11156b6d114aSBen Gras 				break;
11166b6d114aSBen Gras 
1117f14fb602SLionel Sambuc 			_PROP_ATOMIC_INC32(&po->po_refcnt);
11186b6d114aSBen Gras 		} while (ret == _PROP_OBJECT_FREE_RECURSE);
11196b6d114aSBen Gras 		if (ret == _PROP_OBJECT_FREE_FAILED)
11206b6d114aSBen Gras 			prop_object_release_emergency(obj);
11216b6d114aSBen Gras 	} while (_prop_stack_pop(&stack, &obj, NULL, NULL, NULL));
11226b6d114aSBen Gras }
11236b6d114aSBen Gras 
11246b6d114aSBen Gras /*
11256b6d114aSBen Gras  * prop_object_type --
11266b6d114aSBen Gras  *	Return the type of an object.
11276b6d114aSBen Gras  */
11286b6d114aSBen Gras prop_type_t
prop_object_type(prop_object_t obj)11296b6d114aSBen Gras prop_object_type(prop_object_t obj)
11306b6d114aSBen Gras {
11316b6d114aSBen Gras 	struct _prop_object *po = obj;
11326b6d114aSBen Gras 
11336b6d114aSBen Gras 	if (obj == NULL)
11346b6d114aSBen Gras 		return (PROP_TYPE_UNKNOWN);
11356b6d114aSBen Gras 
11366b6d114aSBen Gras 	return (po->po_type->pot_type);
11376b6d114aSBen Gras }
11386b6d114aSBen Gras 
11396b6d114aSBen Gras /*
11406b6d114aSBen Gras  * prop_object_equals --
11416b6d114aSBen Gras  *	Returns true if thw two objects are equivalent.
11426b6d114aSBen Gras  */
11436b6d114aSBen Gras bool
prop_object_equals(prop_object_t obj1,prop_object_t obj2)11446b6d114aSBen Gras prop_object_equals(prop_object_t obj1, prop_object_t obj2)
11456b6d114aSBen Gras {
11466b6d114aSBen Gras 	return (prop_object_equals_with_error(obj1, obj2, NULL));
11476b6d114aSBen Gras }
11486b6d114aSBen Gras 
11496b6d114aSBen Gras bool
prop_object_equals_with_error(prop_object_t obj1,prop_object_t obj2,bool * error_flag)11506b6d114aSBen Gras prop_object_equals_with_error(prop_object_t obj1, prop_object_t obj2,
11516b6d114aSBen Gras     bool *error_flag)
11526b6d114aSBen Gras {
11536b6d114aSBen Gras 	struct _prop_object *po1;
11546b6d114aSBen Gras 	struct _prop_object *po2;
11556b6d114aSBen Gras 	void *stored_pointer1, *stored_pointer2;
11566b6d114aSBen Gras 	prop_object_t next_obj1, next_obj2;
11576b6d114aSBen Gras 	struct _prop_stack stack;
11586b6d114aSBen Gras 	_prop_object_equals_rv_t ret;
11596b6d114aSBen Gras 
11606b6d114aSBen Gras 	_prop_stack_init(&stack);
11616b6d114aSBen Gras 	if (error_flag)
11626b6d114aSBen Gras 		*error_flag = false;
11636b6d114aSBen Gras 
11646b6d114aSBen Gras  start_subtree:
11656b6d114aSBen Gras 	stored_pointer1 = NULL;
11666b6d114aSBen Gras 	stored_pointer2 = NULL;
11676b6d114aSBen Gras 	po1 = obj1;
11686b6d114aSBen Gras 	po2 = obj2;
11696b6d114aSBen Gras 
11706b6d114aSBen Gras 	if (po1->po_type != po2->po_type)
11716b6d114aSBen Gras 		return (false);
11726b6d114aSBen Gras 
11736b6d114aSBen Gras  continue_subtree:
11746b6d114aSBen Gras 	ret = (*po1->po_type->pot_equals)(obj1, obj2,
11756b6d114aSBen Gras 					  &stored_pointer1, &stored_pointer2,
11766b6d114aSBen Gras 					  &next_obj1, &next_obj2);
11776b6d114aSBen Gras 	if (ret == _PROP_OBJECT_EQUALS_FALSE)
11786b6d114aSBen Gras 		goto finish;
11796b6d114aSBen Gras 	if (ret == _PROP_OBJECT_EQUALS_TRUE) {
11806b6d114aSBen Gras 		if (!_prop_stack_pop(&stack, &obj1, &obj2,
11816b6d114aSBen Gras 				     &stored_pointer1, &stored_pointer2))
11826b6d114aSBen Gras 			return true;
11836b6d114aSBen Gras 		po1 = obj1;
11846b6d114aSBen Gras 		po2 = obj2;
11856b6d114aSBen Gras 		goto continue_subtree;
11866b6d114aSBen Gras 	}
11876b6d114aSBen Gras 	_PROP_ASSERT(ret == _PROP_OBJECT_EQUALS_RECURSE);
11886b6d114aSBen Gras 
11896b6d114aSBen Gras 	if (!_prop_stack_push(&stack, obj1, obj2,
11906b6d114aSBen Gras 			      stored_pointer1, stored_pointer2)) {
11916b6d114aSBen Gras 		if (error_flag)
11926b6d114aSBen Gras 			*error_flag = true;
11936b6d114aSBen Gras 		goto finish;
11946b6d114aSBen Gras 	}
11956b6d114aSBen Gras 	obj1 = next_obj1;
11966b6d114aSBen Gras 	obj2 = next_obj2;
11976b6d114aSBen Gras 	goto start_subtree;
11986b6d114aSBen Gras 
11996b6d114aSBen Gras finish:
12006b6d114aSBen Gras 	while (_prop_stack_pop(&stack, &obj1, &obj2, NULL, NULL)) {
12016b6d114aSBen Gras 		po1 = obj1;
12026b6d114aSBen Gras 		(*po1->po_type->pot_equals_finish)(obj1, obj2);
12036b6d114aSBen Gras 	}
12046b6d114aSBen Gras 	return (false);
12056b6d114aSBen Gras }
12066b6d114aSBen Gras 
12076b6d114aSBen Gras /*
12086b6d114aSBen Gras  * prop_object_iterator_next --
12096b6d114aSBen Gras  *	Return the next item during an iteration.
12106b6d114aSBen Gras  */
12116b6d114aSBen Gras prop_object_t
prop_object_iterator_next(prop_object_iterator_t pi)12126b6d114aSBen Gras prop_object_iterator_next(prop_object_iterator_t pi)
12136b6d114aSBen Gras {
12146b6d114aSBen Gras 
12156b6d114aSBen Gras 	return ((*pi->pi_next_object)(pi));
12166b6d114aSBen Gras }
12176b6d114aSBen Gras 
12186b6d114aSBen Gras /*
12196b6d114aSBen Gras  * prop_object_iterator_reset --
12206b6d114aSBen Gras  *	Reset the iterator to the first object so as to restart
12216b6d114aSBen Gras  *	iteration.
12226b6d114aSBen Gras  */
12236b6d114aSBen Gras void
prop_object_iterator_reset(prop_object_iterator_t pi)12246b6d114aSBen Gras prop_object_iterator_reset(prop_object_iterator_t pi)
12256b6d114aSBen Gras {
12266b6d114aSBen Gras 
12276b6d114aSBen Gras 	(*pi->pi_reset)(pi);
12286b6d114aSBen Gras }
12296b6d114aSBen Gras 
12306b6d114aSBen Gras /*
12316b6d114aSBen Gras  * prop_object_iterator_release --
12326b6d114aSBen Gras  *	Release the object iterator.
12336b6d114aSBen Gras  */
12346b6d114aSBen Gras void
prop_object_iterator_release(prop_object_iterator_t pi)12356b6d114aSBen Gras prop_object_iterator_release(prop_object_iterator_t pi)
12366b6d114aSBen Gras {
12376b6d114aSBen Gras 
12386b6d114aSBen Gras 	prop_object_release(pi->pi_obj);
12396b6d114aSBen Gras 	_PROP_FREE(pi, M_TEMP);
12406b6d114aSBen Gras }
1241