xref: /netbsd-src/common/lib/libprop/prop_object.c (revision 39dccbf2f18da72903d2b7da76d9ab07e27875b5)
1*39dccbf2Sjoerg /*	$NetBSD: prop_object.c,v 1.13 2007/07/16 19:20:17 joerg Exp $	*/
2774eb1a3Sthorpej 
3774eb1a3Sthorpej /*-
4774eb1a3Sthorpej  * Copyright (c) 2006 The NetBSD Foundation, Inc.
5774eb1a3Sthorpej  * All rights reserved.
6774eb1a3Sthorpej  *
7774eb1a3Sthorpej  * This code is derived from software contributed to The NetBSD Foundation
8774eb1a3Sthorpej  * by Jason R. Thorpe.
9774eb1a3Sthorpej  *
10774eb1a3Sthorpej  * Redistribution and use in source and binary forms, with or without
11774eb1a3Sthorpej  * modification, are permitted provided that the following conditions
12774eb1a3Sthorpej  * are met:
13774eb1a3Sthorpej  * 1. Redistributions of source code must retain the above copyright
14774eb1a3Sthorpej  *    notice, this list of conditions and the following disclaimer.
15774eb1a3Sthorpej  * 2. Redistributions in binary form must reproduce the above copyright
16774eb1a3Sthorpej  *    notice, this list of conditions and the following disclaimer in the
17774eb1a3Sthorpej  *    documentation and/or other materials provided with the distribution.
18774eb1a3Sthorpej  * 3. All advertising materials mentioning features or use of this software
19774eb1a3Sthorpej  *    must display the following acknowledgement:
20774eb1a3Sthorpej  *      This product includes software developed by the NetBSD
21774eb1a3Sthorpej  *      Foundation, Inc. and its contributors.
22774eb1a3Sthorpej  * 4. Neither the name of The NetBSD Foundation nor the names of its
23774eb1a3Sthorpej  *    contributors may be used to endorse or promote products derived
24774eb1a3Sthorpej  *    from this software without specific prior written permission.
25774eb1a3Sthorpej  *
26774eb1a3Sthorpej  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27774eb1a3Sthorpej  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28774eb1a3Sthorpej  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29774eb1a3Sthorpej  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30774eb1a3Sthorpej  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31774eb1a3Sthorpej  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32774eb1a3Sthorpej  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33774eb1a3Sthorpej  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34774eb1a3Sthorpej  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35774eb1a3Sthorpej  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36774eb1a3Sthorpej  * POSSIBILITY OF SUCH DAMAGE.
37774eb1a3Sthorpej  */
38774eb1a3Sthorpej 
39774eb1a3Sthorpej #include <prop/prop_object.h>
40774eb1a3Sthorpej #include "prop_object_impl.h"
41774eb1a3Sthorpej 
42d21620b2Sthorpej #if !defined(_KERNEL) && !defined(_STANDALONE)
43d21620b2Sthorpej #include <sys/mman.h>
44d21620b2Sthorpej #include <sys/stat.h>
45d21620b2Sthorpej #include <errno.h>
46d21620b2Sthorpej #include <fcntl.h>
47d21620b2Sthorpej #include <limits.h>
48d21620b2Sthorpej #include <unistd.h>
49d21620b2Sthorpej #endif
50d21620b2Sthorpej 
51774eb1a3Sthorpej #ifdef _STANDALONE
52774eb1a3Sthorpej void *
53774eb1a3Sthorpej _prop_standalone_calloc(size_t size)
54774eb1a3Sthorpej {
55774eb1a3Sthorpej 	void *rv;
56774eb1a3Sthorpej 
57774eb1a3Sthorpej 	rv = alloc(size);
58774eb1a3Sthorpej 	if (rv != NULL)
59774eb1a3Sthorpej 		memset(rv, 0, size);
60774eb1a3Sthorpej 
61774eb1a3Sthorpej 	return (rv);
62774eb1a3Sthorpej }
633e69f1b2Sthorpej 
643e69f1b2Sthorpej void *
653e69f1b2Sthorpej _prop_standalone_realloc(void *v, size_t size)
663e69f1b2Sthorpej {
673e69f1b2Sthorpej 	void *rv;
683e69f1b2Sthorpej 
693e69f1b2Sthorpej 	rv = alloc(size);
703e69f1b2Sthorpej 	if (rv != NULL) {
713e69f1b2Sthorpej 		memcpy(rv, v, size);	/* XXX */
723e69f1b2Sthorpej 		dealloc(v, 0);		/* XXX */
733e69f1b2Sthorpej 	}
743e69f1b2Sthorpej 
753e69f1b2Sthorpej 	return (rv);
763e69f1b2Sthorpej }
77774eb1a3Sthorpej #endif /* _STANDALONE */
78774eb1a3Sthorpej 
79774eb1a3Sthorpej /*
80774eb1a3Sthorpej  * _prop_object_init --
81774eb1a3Sthorpej  *	Initialize an object.  Called when sub-classes create
82774eb1a3Sthorpej  *	an instance.
83774eb1a3Sthorpej  */
84774eb1a3Sthorpej void
853e69f1b2Sthorpej _prop_object_init(struct _prop_object *po, const struct _prop_object_type *pot)
86774eb1a3Sthorpej {
87774eb1a3Sthorpej 
883e69f1b2Sthorpej 	po->po_type = pot;
89774eb1a3Sthorpej 	po->po_refcnt = 1;
90774eb1a3Sthorpej }
91774eb1a3Sthorpej 
92774eb1a3Sthorpej /*
93774eb1a3Sthorpej  * _prop_object_fini --
94774eb1a3Sthorpej  *	Finalize an object.  Called when sub-classes destroy
95774eb1a3Sthorpej  *	an instance.
96774eb1a3Sthorpej  */
97d21620b2Sthorpej /*ARGSUSED*/
98774eb1a3Sthorpej void
99d9fd2cbcSthorpej _prop_object_fini(struct _prop_object *po _PROP_ARG_UNUSED)
100774eb1a3Sthorpej {
101774eb1a3Sthorpej 	/* Nothing to do, currently. */
102774eb1a3Sthorpej }
103774eb1a3Sthorpej 
104774eb1a3Sthorpej /*
105774eb1a3Sthorpej  * _prop_object_externalize_start_tag --
106774eb1a3Sthorpej  *	Append an XML-style start tag to the externalize buffer.
107774eb1a3Sthorpej  */
108774eb1a3Sthorpej boolean_t
109774eb1a3Sthorpej _prop_object_externalize_start_tag(
110774eb1a3Sthorpej     struct _prop_object_externalize_context *ctx, const char *tag)
111774eb1a3Sthorpej {
112774eb1a3Sthorpej 	unsigned int i;
113774eb1a3Sthorpej 
114774eb1a3Sthorpej 	for (i = 0; i < ctx->poec_depth; i++) {
115774eb1a3Sthorpej 		if (_prop_object_externalize_append_char(ctx, '\t') == FALSE)
116774eb1a3Sthorpej 			return (FALSE);
117774eb1a3Sthorpej 	}
118774eb1a3Sthorpej 	if (_prop_object_externalize_append_char(ctx, '<') == FALSE ||
119774eb1a3Sthorpej 	    _prop_object_externalize_append_cstring(ctx, tag) == FALSE ||
120774eb1a3Sthorpej 	    _prop_object_externalize_append_char(ctx, '>') == FALSE)
121774eb1a3Sthorpej 		return (FALSE);
122774eb1a3Sthorpej 
123774eb1a3Sthorpej 	return (TRUE);
124774eb1a3Sthorpej }
125774eb1a3Sthorpej 
126774eb1a3Sthorpej /*
127774eb1a3Sthorpej  * _prop_object_externalize_end_tag --
128774eb1a3Sthorpej  *	Append an XML-style end tag to the externalize buffer.
129774eb1a3Sthorpej  */
130774eb1a3Sthorpej boolean_t
131774eb1a3Sthorpej _prop_object_externalize_end_tag(
132774eb1a3Sthorpej     struct _prop_object_externalize_context *ctx, const char *tag)
133774eb1a3Sthorpej {
134774eb1a3Sthorpej 
135774eb1a3Sthorpej 	if (_prop_object_externalize_append_char(ctx, '<') == FALSE ||
136774eb1a3Sthorpej 	    _prop_object_externalize_append_char(ctx, '/') == FALSE ||
137774eb1a3Sthorpej 	    _prop_object_externalize_append_cstring(ctx, tag) == FALSE ||
138774eb1a3Sthorpej 	    _prop_object_externalize_append_char(ctx, '>') == FALSE ||
139774eb1a3Sthorpej 	    _prop_object_externalize_append_char(ctx, '\n') == FALSE)
140774eb1a3Sthorpej 		return (FALSE);
141774eb1a3Sthorpej 
142774eb1a3Sthorpej 	return (TRUE);
143774eb1a3Sthorpej }
144774eb1a3Sthorpej 
145774eb1a3Sthorpej /*
146774eb1a3Sthorpej  * _prop_object_externalize_empty_tag --
147774eb1a3Sthorpej  *	Append an XML-style empty tag to the externalize buffer.
148774eb1a3Sthorpej  */
149774eb1a3Sthorpej boolean_t
150774eb1a3Sthorpej _prop_object_externalize_empty_tag(
151774eb1a3Sthorpej     struct _prop_object_externalize_context *ctx, const char *tag)
152774eb1a3Sthorpej {
153774eb1a3Sthorpej 	unsigned int i;
154774eb1a3Sthorpej 
155774eb1a3Sthorpej 	for (i = 0; i < ctx->poec_depth; i++) {
156774eb1a3Sthorpej 		if (_prop_object_externalize_append_char(ctx, '\t') == FALSE)
157774eb1a3Sthorpej 			return (FALSE);
158774eb1a3Sthorpej 	}
159774eb1a3Sthorpej 
160774eb1a3Sthorpej 	if (_prop_object_externalize_append_char(ctx, '<') == FALSE ||
161774eb1a3Sthorpej 	    _prop_object_externalize_append_cstring(ctx, tag) == FALSE ||
162774eb1a3Sthorpej 	    _prop_object_externalize_append_char(ctx, '/') == FALSE ||
163774eb1a3Sthorpej 	    _prop_object_externalize_append_char(ctx, '>') == FALSE ||
164774eb1a3Sthorpej 	    _prop_object_externalize_append_char(ctx, '\n') == FALSE)
165774eb1a3Sthorpej 	    	return (FALSE);
166774eb1a3Sthorpej 
167774eb1a3Sthorpej 	return (TRUE);
168774eb1a3Sthorpej }
169774eb1a3Sthorpej 
170774eb1a3Sthorpej /*
171774eb1a3Sthorpej  * _prop_object_externalize_append_cstring --
172774eb1a3Sthorpej  *	Append a C string to the externalize buffer.
173774eb1a3Sthorpej  */
174774eb1a3Sthorpej boolean_t
175774eb1a3Sthorpej _prop_object_externalize_append_cstring(
176774eb1a3Sthorpej     struct _prop_object_externalize_context *ctx, const char *cp)
177774eb1a3Sthorpej {
178774eb1a3Sthorpej 
179774eb1a3Sthorpej 	while (*cp != '\0') {
180774eb1a3Sthorpej 		if (_prop_object_externalize_append_char(ctx,
181774eb1a3Sthorpej 						(unsigned char) *cp) == FALSE)
182774eb1a3Sthorpej 			return (FALSE);
183774eb1a3Sthorpej 		cp++;
184774eb1a3Sthorpej 	}
185774eb1a3Sthorpej 
186774eb1a3Sthorpej 	return (TRUE);
187774eb1a3Sthorpej }
188774eb1a3Sthorpej 
189774eb1a3Sthorpej /*
190774eb1a3Sthorpej  * _prop_object_externalize_append_encoded_cstring --
191774eb1a3Sthorpej  *	Append an encoded C string to the externalize buffer.
192774eb1a3Sthorpej  */
193774eb1a3Sthorpej boolean_t
194774eb1a3Sthorpej _prop_object_externalize_append_encoded_cstring(
195774eb1a3Sthorpej     struct _prop_object_externalize_context *ctx, const char *cp)
196774eb1a3Sthorpej {
197774eb1a3Sthorpej 
198774eb1a3Sthorpej 	while (*cp != '\0') {
199774eb1a3Sthorpej 		switch (*cp) {
200774eb1a3Sthorpej 		case '<':
201774eb1a3Sthorpej 			if (_prop_object_externalize_append_cstring(ctx,
202774eb1a3Sthorpej 					"&lt;") == FALSE)
203774eb1a3Sthorpej 				return (FALSE);
204774eb1a3Sthorpej 			break;
205774eb1a3Sthorpej 		case '>':
206774eb1a3Sthorpej 			if (_prop_object_externalize_append_cstring(ctx,
207774eb1a3Sthorpej 					"&gt;") == FALSE)
208774eb1a3Sthorpej 				return (FALSE);
209774eb1a3Sthorpej 			break;
210774eb1a3Sthorpej 		case '&':
211774eb1a3Sthorpej 			if (_prop_object_externalize_append_cstring(ctx,
212774eb1a3Sthorpej 					"&amp;") == FALSE)
213774eb1a3Sthorpej 				return (FALSE);
214774eb1a3Sthorpej 			break;
215774eb1a3Sthorpej 		default:
216774eb1a3Sthorpej 			if (_prop_object_externalize_append_char(ctx,
217774eb1a3Sthorpej 					(unsigned char) *cp) == FALSE)
218774eb1a3Sthorpej 				return (FALSE);
219774eb1a3Sthorpej 			break;
220774eb1a3Sthorpej 		}
221774eb1a3Sthorpej 		cp++;
222774eb1a3Sthorpej 	}
223774eb1a3Sthorpej 
224774eb1a3Sthorpej 	return (TRUE);
225774eb1a3Sthorpej }
226774eb1a3Sthorpej 
227ab821170Smartin #define	BUF_EXPAND		256
228774eb1a3Sthorpej 
229774eb1a3Sthorpej /*
230774eb1a3Sthorpej  * _prop_object_externalize_append_char --
231774eb1a3Sthorpej  *	Append a single character to the externalize buffer.
232774eb1a3Sthorpej  */
233774eb1a3Sthorpej boolean_t
234774eb1a3Sthorpej _prop_object_externalize_append_char(
235774eb1a3Sthorpej     struct _prop_object_externalize_context *ctx, unsigned char c)
236774eb1a3Sthorpej {
237774eb1a3Sthorpej 
238774eb1a3Sthorpej 	_PROP_ASSERT(ctx->poec_capacity != 0);
239774eb1a3Sthorpej 	_PROP_ASSERT(ctx->poec_buf != NULL);
240774eb1a3Sthorpej 	_PROP_ASSERT(ctx->poec_len <= ctx->poec_capacity);
241774eb1a3Sthorpej 
242774eb1a3Sthorpej 	if (ctx->poec_len == ctx->poec_capacity) {
2433e69f1b2Sthorpej 		char *cp = _PROP_REALLOC(ctx->poec_buf,
2443e69f1b2Sthorpej 					 ctx->poec_capacity + BUF_EXPAND,
245774eb1a3Sthorpej 					 M_TEMP);
246774eb1a3Sthorpej 		if (cp == NULL)
247774eb1a3Sthorpej 			return (FALSE);
248774eb1a3Sthorpej 		ctx->poec_capacity = ctx->poec_capacity + BUF_EXPAND;
249774eb1a3Sthorpej 		ctx->poec_buf = cp;
250774eb1a3Sthorpej 	}
251774eb1a3Sthorpej 
252774eb1a3Sthorpej 	ctx->poec_buf[ctx->poec_len++] = c;
253774eb1a3Sthorpej 
254774eb1a3Sthorpej 	return (TRUE);
255774eb1a3Sthorpej }
256774eb1a3Sthorpej 
257774eb1a3Sthorpej /*
258d21620b2Sthorpej  * _prop_object_externalize_header --
259d21620b2Sthorpej  *	Append the standard XML header to the externalize buffer.
260d21620b2Sthorpej  */
261d21620b2Sthorpej boolean_t
262d21620b2Sthorpej _prop_object_externalize_header(struct _prop_object_externalize_context *ctx)
263d21620b2Sthorpej {
264d21620b2Sthorpej 	static const char _plist_xml_header[] =
265d21620b2Sthorpej "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
266d21620b2Sthorpej "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n";
267d21620b2Sthorpej 
268d21620b2Sthorpej 	if (_prop_object_externalize_append_cstring(ctx,
269d21620b2Sthorpej 						 _plist_xml_header) == FALSE ||
270d21620b2Sthorpej 	    _prop_object_externalize_start_tag(ctx,
271d21620b2Sthorpej 				       "plist version=\"1.0\"") == FALSE ||
272d21620b2Sthorpej 	    _prop_object_externalize_append_char(ctx, '\n') == FALSE)
273d21620b2Sthorpej 		return (FALSE);
274d21620b2Sthorpej 
275d21620b2Sthorpej 	return (TRUE);
276d21620b2Sthorpej }
277d21620b2Sthorpej 
278d21620b2Sthorpej /*
279d21620b2Sthorpej  * _prop_object_externalize_footer --
280d21620b2Sthorpej  *	Append the standard XML footer to the externalize buffer.  This
281d21620b2Sthorpej  *	also NUL-terminates the buffer.
282d21620b2Sthorpej  */
283d21620b2Sthorpej boolean_t
284d21620b2Sthorpej _prop_object_externalize_footer(struct _prop_object_externalize_context *ctx)
285d21620b2Sthorpej {
286d21620b2Sthorpej 
287d21620b2Sthorpej 	if (_prop_object_externalize_end_tag(ctx, "plist") == FALSE ||
288d21620b2Sthorpej 	    _prop_object_externalize_append_char(ctx, '\0') == FALSE)
289d21620b2Sthorpej 		return (FALSE);
290d21620b2Sthorpej 
291d21620b2Sthorpej 	return (TRUE);
292d21620b2Sthorpej }
293d21620b2Sthorpej 
294d21620b2Sthorpej /*
295774eb1a3Sthorpej  * _prop_object_externalize_context_alloc --
296774eb1a3Sthorpej  *	Allocate an externalize context.
297774eb1a3Sthorpej  */
298774eb1a3Sthorpej struct _prop_object_externalize_context *
299774eb1a3Sthorpej _prop_object_externalize_context_alloc(void)
300774eb1a3Sthorpej {
301774eb1a3Sthorpej 	struct _prop_object_externalize_context *ctx;
302774eb1a3Sthorpej 
303774eb1a3Sthorpej 	ctx = _PROP_MALLOC(sizeof(*ctx), M_TEMP);
304774eb1a3Sthorpej 	if (ctx != NULL) {
305774eb1a3Sthorpej 		ctx->poec_buf = _PROP_MALLOC(BUF_EXPAND, M_TEMP);
306774eb1a3Sthorpej 		if (ctx->poec_buf == NULL) {
307774eb1a3Sthorpej 			_PROP_FREE(ctx, M_TEMP);
308774eb1a3Sthorpej 			return (NULL);
309774eb1a3Sthorpej 		}
310774eb1a3Sthorpej 		ctx->poec_len = 0;
311774eb1a3Sthorpej 		ctx->poec_capacity = BUF_EXPAND;
312774eb1a3Sthorpej 		ctx->poec_depth = 0;
313774eb1a3Sthorpej 	}
314774eb1a3Sthorpej 	return (ctx);
315774eb1a3Sthorpej }
316774eb1a3Sthorpej 
317774eb1a3Sthorpej /*
318774eb1a3Sthorpej  * _prop_object_externalize_context_free --
319774eb1a3Sthorpej  *	Free an externalize context.
320774eb1a3Sthorpej  */
321774eb1a3Sthorpej void
322774eb1a3Sthorpej _prop_object_externalize_context_free(
323774eb1a3Sthorpej 		struct _prop_object_externalize_context *ctx)
324774eb1a3Sthorpej {
325774eb1a3Sthorpej 
326774eb1a3Sthorpej 	/* Buffer is always freed by the caller. */
327774eb1a3Sthorpej 	_PROP_FREE(ctx, M_TEMP);
328774eb1a3Sthorpej }
329774eb1a3Sthorpej 
330774eb1a3Sthorpej /*
331774eb1a3Sthorpej  * _prop_object_internalize_skip_comment --
332774eb1a3Sthorpej  *	Skip the body and end tag of a comment.
333774eb1a3Sthorpej  */
334774eb1a3Sthorpej static boolean_t
335774eb1a3Sthorpej _prop_object_internalize_skip_comment(
336774eb1a3Sthorpej 				struct _prop_object_internalize_context *ctx)
337774eb1a3Sthorpej {
338774eb1a3Sthorpej 	const char *cp = ctx->poic_cp;
339774eb1a3Sthorpej 
340774eb1a3Sthorpej 	while (!_PROP_EOF(*cp)) {
341774eb1a3Sthorpej 		if (cp[0] == '-' &&
342774eb1a3Sthorpej 		    cp[1] == '-' &&
343774eb1a3Sthorpej 		    cp[2] == '>') {
344774eb1a3Sthorpej 			ctx->poic_cp = cp + 3;
345774eb1a3Sthorpej 			return (TRUE);
346774eb1a3Sthorpej 		}
347774eb1a3Sthorpej 		cp++;
348774eb1a3Sthorpej 	}
349774eb1a3Sthorpej 
350774eb1a3Sthorpej 	return (FALSE);		/* ran out of buffer */
351774eb1a3Sthorpej }
352774eb1a3Sthorpej 
353774eb1a3Sthorpej /*
354774eb1a3Sthorpej  * _prop_object_internalize_find_tag --
355774eb1a3Sthorpej  *	Find the next tag in an XML stream.  Optionally compare the found
356774eb1a3Sthorpej  *	tag to an expected tag name.  State of the context is undefined
357774eb1a3Sthorpej  *	if this routine returns FALSE.  Upon success, the context points
358774eb1a3Sthorpej  *	to the first octet after the tag.
359774eb1a3Sthorpej  */
360774eb1a3Sthorpej boolean_t
361774eb1a3Sthorpej _prop_object_internalize_find_tag(struct _prop_object_internalize_context *ctx,
362774eb1a3Sthorpej 		      const char *tag, _prop_tag_type_t type)
363774eb1a3Sthorpej {
364774eb1a3Sthorpej 	const char *cp;
365774eb1a3Sthorpej 	size_t taglen;
366774eb1a3Sthorpej 
367774eb1a3Sthorpej 	if (tag != NULL)
368774eb1a3Sthorpej 		taglen = strlen(tag);
369774eb1a3Sthorpej 	else
370774eb1a3Sthorpej 		taglen = 0;
371774eb1a3Sthorpej 
372774eb1a3Sthorpej  start_over:
373774eb1a3Sthorpej 	cp = ctx->poic_cp;
374774eb1a3Sthorpej 
375774eb1a3Sthorpej 	/*
376774eb1a3Sthorpej 	 * Find the start of the tag.
377774eb1a3Sthorpej 	 */
378774eb1a3Sthorpej 	while (_PROP_ISSPACE(*cp))
379774eb1a3Sthorpej 		cp++;
380774eb1a3Sthorpej 	if (_PROP_EOF(*cp))
381774eb1a3Sthorpej 		return (FALSE);
382774eb1a3Sthorpej 
383774eb1a3Sthorpej 	if (*cp != '<')
384774eb1a3Sthorpej 		return (FALSE);
385774eb1a3Sthorpej 
386774eb1a3Sthorpej 	ctx->poic_tag_start = cp++;
387774eb1a3Sthorpej 	if (_PROP_EOF(*cp))
388774eb1a3Sthorpej 		return (FALSE);
389774eb1a3Sthorpej 
390774eb1a3Sthorpej 	if (*cp == '!') {
391774eb1a3Sthorpej 		if (cp[1] != '-' || cp[2] != '-')
392774eb1a3Sthorpej 			return (FALSE);
393774eb1a3Sthorpej 		/*
394774eb1a3Sthorpej 		 * Comment block -- only allowed if we are allowed to
395774eb1a3Sthorpej 		 * return a start tag.
396774eb1a3Sthorpej 		 */
397774eb1a3Sthorpej 		if (type == _PROP_TAG_TYPE_END)
398774eb1a3Sthorpej 			return (FALSE);
399774eb1a3Sthorpej 		ctx->poic_cp = cp + 3;
400774eb1a3Sthorpej 		if (_prop_object_internalize_skip_comment(ctx) == FALSE)
401774eb1a3Sthorpej 			return (FALSE);
402774eb1a3Sthorpej 		goto start_over;
403774eb1a3Sthorpej 	}
404774eb1a3Sthorpej 
405774eb1a3Sthorpej 	if (*cp == '/') {
406774eb1a3Sthorpej 		if (type != _PROP_TAG_TYPE_END &&
407774eb1a3Sthorpej 		    type != _PROP_TAG_TYPE_EITHER)
408774eb1a3Sthorpej 			return (FALSE);
409774eb1a3Sthorpej 		cp++;
410774eb1a3Sthorpej 		if (_PROP_EOF(*cp))
411774eb1a3Sthorpej 			return (FALSE);
412774eb1a3Sthorpej 		ctx->poic_tag_type = _PROP_TAG_TYPE_END;
413774eb1a3Sthorpej 	} else {
414774eb1a3Sthorpej 		if (type != _PROP_TAG_TYPE_START &&
415774eb1a3Sthorpej 		    type != _PROP_TAG_TYPE_EITHER)
416774eb1a3Sthorpej 			return (FALSE);
417774eb1a3Sthorpej 		ctx->poic_tag_type = _PROP_TAG_TYPE_START;
418774eb1a3Sthorpej 	}
419774eb1a3Sthorpej 
420774eb1a3Sthorpej 	ctx->poic_tagname = cp;
421774eb1a3Sthorpej 
422774eb1a3Sthorpej 	while (!_PROP_ISSPACE(*cp) && *cp != '/' && *cp != '>')
423774eb1a3Sthorpej 		cp++;
424774eb1a3Sthorpej 	if (_PROP_EOF(*cp))
425774eb1a3Sthorpej 		return (FALSE);
426774eb1a3Sthorpej 
427774eb1a3Sthorpej 	ctx->poic_tagname_len = cp - ctx->poic_tagname;
428774eb1a3Sthorpej 
429774eb1a3Sthorpej 	/* Make sure this is the tag we're looking for. */
430774eb1a3Sthorpej 	if (tag != NULL &&
431774eb1a3Sthorpej 	    (taglen != ctx->poic_tagname_len ||
432774eb1a3Sthorpej 	     memcmp(tag, ctx->poic_tagname, taglen) != 0))
433774eb1a3Sthorpej 		return (FALSE);
434774eb1a3Sthorpej 
435774eb1a3Sthorpej 	/* Check for empty tag. */
436774eb1a3Sthorpej 	if (*cp == '/') {
437774eb1a3Sthorpej 		if (ctx->poic_tag_type != _PROP_TAG_TYPE_START)
438774eb1a3Sthorpej 			return(FALSE);		/* only valid on start tags */
439774eb1a3Sthorpej 		ctx->poic_is_empty_element = TRUE;
440774eb1a3Sthorpej 		cp++;
441774eb1a3Sthorpej 		if (_PROP_EOF(*cp) || *cp != '>')
442774eb1a3Sthorpej 			return (FALSE);
443774eb1a3Sthorpej 	} else
444774eb1a3Sthorpej 		ctx->poic_is_empty_element = FALSE;
445774eb1a3Sthorpej 
446774eb1a3Sthorpej 	/* Easy case of no arguments. */
447774eb1a3Sthorpej 	if (*cp == '>') {
448774eb1a3Sthorpej 		ctx->poic_tagattr = NULL;
449774eb1a3Sthorpej 		ctx->poic_tagattr_len = 0;
450774eb1a3Sthorpej 		ctx->poic_tagattrval = NULL;
451774eb1a3Sthorpej 		ctx->poic_tagattrval_len = 0;
452774eb1a3Sthorpej 		ctx->poic_cp = cp + 1;
453774eb1a3Sthorpej 		return (TRUE);
454774eb1a3Sthorpej 	}
455774eb1a3Sthorpej 
456774eb1a3Sthorpej 	_PROP_ASSERT(!_PROP_EOF(*cp));
457774eb1a3Sthorpej 	cp++;
458774eb1a3Sthorpej 	if (_PROP_EOF(*cp))
459774eb1a3Sthorpej 		return (FALSE);
460774eb1a3Sthorpej 
461774eb1a3Sthorpej 	while (_PROP_ISSPACE(*cp))
462774eb1a3Sthorpej 		cp++;
463774eb1a3Sthorpej 	if (_PROP_EOF(*cp))
464774eb1a3Sthorpej 		return (FALSE);
465774eb1a3Sthorpej 
466774eb1a3Sthorpej 	ctx->poic_tagattr = cp;
467774eb1a3Sthorpej 
468774eb1a3Sthorpej 	while (!_PROP_ISSPACE(*cp) && *cp != '=')
469774eb1a3Sthorpej 		cp++;
470774eb1a3Sthorpej 	if (_PROP_EOF(*cp))
471774eb1a3Sthorpej 		return (FALSE);
472774eb1a3Sthorpej 
473774eb1a3Sthorpej 	ctx->poic_tagattr_len = cp - ctx->poic_tagattr;
474774eb1a3Sthorpej 
475774eb1a3Sthorpej 	cp++;
476774eb1a3Sthorpej 	if (*cp != '\"')
477774eb1a3Sthorpej 		return (FALSE);
478774eb1a3Sthorpej 	cp++;
479774eb1a3Sthorpej 	if (_PROP_EOF(*cp))
480774eb1a3Sthorpej 		return (FALSE);
481774eb1a3Sthorpej 
482774eb1a3Sthorpej 	ctx->poic_tagattrval = cp;
483774eb1a3Sthorpej 	while (*cp != '\"')
484774eb1a3Sthorpej 		cp++;
485774eb1a3Sthorpej 	if (_PROP_EOF(*cp))
486774eb1a3Sthorpej 		return (FALSE);
487774eb1a3Sthorpej 	ctx->poic_tagattrval_len = cp - ctx->poic_tagattrval;
488774eb1a3Sthorpej 
489774eb1a3Sthorpej 	cp++;
490774eb1a3Sthorpej 	if (*cp != '>')
491774eb1a3Sthorpej 		return (FALSE);
492774eb1a3Sthorpej 
493774eb1a3Sthorpej 	ctx->poic_cp = cp + 1;
494774eb1a3Sthorpej 	return (TRUE);
495774eb1a3Sthorpej }
496774eb1a3Sthorpej 
497774eb1a3Sthorpej /*
498774eb1a3Sthorpej  * _prop_object_internalize_decode_string --
499774eb1a3Sthorpej  *	Decode an encoded string.
500774eb1a3Sthorpej  */
501774eb1a3Sthorpej boolean_t
502774eb1a3Sthorpej _prop_object_internalize_decode_string(
503774eb1a3Sthorpej 				struct _prop_object_internalize_context *ctx,
504774eb1a3Sthorpej 				char *target, size_t targsize, size_t *sizep,
505774eb1a3Sthorpej 				const char **cpp)
506774eb1a3Sthorpej {
507774eb1a3Sthorpej 	const char *src;
508774eb1a3Sthorpej 	size_t tarindex;
509774eb1a3Sthorpej 	char c;
510774eb1a3Sthorpej 
511774eb1a3Sthorpej 	tarindex = 0;
512774eb1a3Sthorpej 	src = ctx->poic_cp;
513774eb1a3Sthorpej 
514774eb1a3Sthorpej 	for (;;) {
515774eb1a3Sthorpej 		if (_PROP_EOF(*src))
516774eb1a3Sthorpej 			return (FALSE);
517774eb1a3Sthorpej 		if (*src == '<') {
518774eb1a3Sthorpej 			break;
519774eb1a3Sthorpej 		}
520774eb1a3Sthorpej 
521774eb1a3Sthorpej 		if ((c = *src) == '&') {
522774eb1a3Sthorpej 			if (src[1] == 'a' &&
523774eb1a3Sthorpej 			    src[2] == 'm' &&
524774eb1a3Sthorpej 			    src[3] == 'p' &&
525774eb1a3Sthorpej 			    src[4] == ';') {
526774eb1a3Sthorpej 			    	c = '&';
527774eb1a3Sthorpej 				src += 5;
528774eb1a3Sthorpej 			} else if (src[1] == 'l' &&
529774eb1a3Sthorpej 				   src[2] == 't' &&
530774eb1a3Sthorpej 				   src[3] == ';') {
531774eb1a3Sthorpej 				c = '<';
532774eb1a3Sthorpej 				src += 4;
533774eb1a3Sthorpej 			} else if (src[1] == 'g' &&
534774eb1a3Sthorpej 				   src[2] == 't' &&
535774eb1a3Sthorpej 				   src[3] == ';') {
536774eb1a3Sthorpej 				c = '>';
537774eb1a3Sthorpej 				src += 4;
538774eb1a3Sthorpej 			} else if (src[1] == 'a' &&
539774eb1a3Sthorpej 				   src[2] == 'p' &&
540774eb1a3Sthorpej 				   src[3] == 'o' &&
541774eb1a3Sthorpej 				   src[4] == 's' &&
542774eb1a3Sthorpej 				   src[5] == ';') {
543774eb1a3Sthorpej 				c = '\'';
544774eb1a3Sthorpej 				src += 6;
545774eb1a3Sthorpej 			} else if (src[1] == 'q' &&
546774eb1a3Sthorpej 				   src[2] == 'u' &&
547774eb1a3Sthorpej 				   src[3] == 'o' &&
548774eb1a3Sthorpej 				   src[4] == 't' &&
549774eb1a3Sthorpej 				   src[5] == ';') {
550774eb1a3Sthorpej 				c = '\"';
551774eb1a3Sthorpej 				src += 6;
552774eb1a3Sthorpej 			} else
553774eb1a3Sthorpej 				return (FALSE);
554774eb1a3Sthorpej 		} else
555774eb1a3Sthorpej 			src++;
556774eb1a3Sthorpej 		if (target) {
557774eb1a3Sthorpej 			if (tarindex >= targsize)
558774eb1a3Sthorpej 				return (FALSE);
559774eb1a3Sthorpej 			target[tarindex] = c;
560774eb1a3Sthorpej 		}
561774eb1a3Sthorpej 		tarindex++;
562774eb1a3Sthorpej 	}
563774eb1a3Sthorpej 
564774eb1a3Sthorpej 	_PROP_ASSERT(*src == '<');
565774eb1a3Sthorpej 	if (sizep != NULL)
566774eb1a3Sthorpej 		*sizep = tarindex;
567774eb1a3Sthorpej 	if (cpp != NULL)
568774eb1a3Sthorpej 		*cpp = src;
569774eb1a3Sthorpej 
570774eb1a3Sthorpej 	return (TRUE);
571774eb1a3Sthorpej }
572774eb1a3Sthorpej 
573774eb1a3Sthorpej /*
574774eb1a3Sthorpej  * _prop_object_internalize_match --
575774eb1a3Sthorpej  *	Returns true if the two character streams match.
576774eb1a3Sthorpej  */
577774eb1a3Sthorpej boolean_t
578774eb1a3Sthorpej _prop_object_internalize_match(const char *str1, size_t len1,
579774eb1a3Sthorpej 			       const char *str2, size_t len2)
580774eb1a3Sthorpej {
581774eb1a3Sthorpej 
582774eb1a3Sthorpej 	return (len1 == len2 && memcmp(str1, str2, len1) == 0);
583774eb1a3Sthorpej }
584774eb1a3Sthorpej 
585774eb1a3Sthorpej #define	INTERNALIZER(t, f)			\
586774eb1a3Sthorpej {	t,	sizeof(t) - 1,		f	}
587774eb1a3Sthorpej 
588774eb1a3Sthorpej static const struct _prop_object_internalizer {
589774eb1a3Sthorpej 	const char	*poi_tag;
590774eb1a3Sthorpej 	size_t		poi_taglen;
591774eb1a3Sthorpej 	prop_object_t	(*poi_intern)(
592774eb1a3Sthorpej 				struct _prop_object_internalize_context *);
593774eb1a3Sthorpej } _prop_object_internalizer_table[] = {
594774eb1a3Sthorpej 	INTERNALIZER("array", _prop_array_internalize),
595774eb1a3Sthorpej 
596774eb1a3Sthorpej 	INTERNALIZER("true", _prop_bool_internalize),
597774eb1a3Sthorpej 	INTERNALIZER("false", _prop_bool_internalize),
598774eb1a3Sthorpej 
599774eb1a3Sthorpej 	INTERNALIZER("data", _prop_data_internalize),
600774eb1a3Sthorpej 
601774eb1a3Sthorpej 	INTERNALIZER("dict", _prop_dictionary_internalize),
602774eb1a3Sthorpej 
603774eb1a3Sthorpej 	INTERNALIZER("integer", _prop_number_internalize),
604774eb1a3Sthorpej 
605774eb1a3Sthorpej 	INTERNALIZER("string", _prop_string_internalize),
606774eb1a3Sthorpej 
6070616c072Schristos 	{ 0, 0, NULL }
608774eb1a3Sthorpej };
609774eb1a3Sthorpej 
610774eb1a3Sthorpej #undef INTERNALIZER
611774eb1a3Sthorpej 
612774eb1a3Sthorpej /*
613774eb1a3Sthorpej  * _prop_object_internalize_by_tag --
614774eb1a3Sthorpej  *	Determine the object type from the tag in the context and
615774eb1a3Sthorpej  *	internalize it.
616774eb1a3Sthorpej  */
617774eb1a3Sthorpej prop_object_t
618774eb1a3Sthorpej _prop_object_internalize_by_tag(struct _prop_object_internalize_context *ctx)
619774eb1a3Sthorpej {
620774eb1a3Sthorpej 	const struct _prop_object_internalizer *poi;
621774eb1a3Sthorpej 
622774eb1a3Sthorpej 	for (poi = _prop_object_internalizer_table;
623774eb1a3Sthorpej 	     poi->poi_tag != NULL; poi++) {
624774eb1a3Sthorpej 		if (_prop_object_internalize_match(ctx->poic_tagname,
625774eb1a3Sthorpej 						   ctx->poic_tagname_len,
626774eb1a3Sthorpej 						   poi->poi_tag,
627774eb1a3Sthorpej 						   poi->poi_taglen))
628774eb1a3Sthorpej 			return ((*poi->poi_intern)(ctx));
629774eb1a3Sthorpej 	}
630774eb1a3Sthorpej 
631774eb1a3Sthorpej 	return (NULL);
632774eb1a3Sthorpej }
633774eb1a3Sthorpej 
634*39dccbf2Sjoerg prop_object_t
635*39dccbf2Sjoerg _prop_generic_internalize(const char *xml, const char *master_tag)
636*39dccbf2Sjoerg {
637*39dccbf2Sjoerg 	prop_object_t obj = NULL;
638*39dccbf2Sjoerg 	struct _prop_object_internalize_context *ctx;
639*39dccbf2Sjoerg 
640*39dccbf2Sjoerg 	ctx = _prop_object_internalize_context_alloc(xml);
641*39dccbf2Sjoerg 	if (ctx == NULL)
642*39dccbf2Sjoerg 		return (NULL);
643*39dccbf2Sjoerg 
644*39dccbf2Sjoerg 	/* We start with a <plist> tag. */
645*39dccbf2Sjoerg 	if (_prop_object_internalize_find_tag(ctx, "plist",
646*39dccbf2Sjoerg 					      _PROP_TAG_TYPE_START) == FALSE)
647*39dccbf2Sjoerg 		goto out;
648*39dccbf2Sjoerg 
649*39dccbf2Sjoerg 	/* Plist elements cannot be empty. */
650*39dccbf2Sjoerg 	if (ctx->poic_is_empty_element)
651*39dccbf2Sjoerg 		goto out;
652*39dccbf2Sjoerg 
653*39dccbf2Sjoerg 	/*
654*39dccbf2Sjoerg 	 * We don't understand any plist attributes, but Apple XML
655*39dccbf2Sjoerg 	 * property lists often have a "version" attribute.  If we
656*39dccbf2Sjoerg 	 * see that one, we simply ignore it.
657*39dccbf2Sjoerg 	 */
658*39dccbf2Sjoerg 	if (ctx->poic_tagattr != NULL &&
659*39dccbf2Sjoerg 	    !_PROP_TAGATTR_MATCH(ctx, "version"))
660*39dccbf2Sjoerg 		goto out;
661*39dccbf2Sjoerg 
662*39dccbf2Sjoerg 	/* Next we expect to see opening master_tag. */
663*39dccbf2Sjoerg 	if (_prop_object_internalize_find_tag(ctx, master_tag,
664*39dccbf2Sjoerg 					      _PROP_TAG_TYPE_START) == FALSE)
665*39dccbf2Sjoerg 		goto out;
666*39dccbf2Sjoerg 
667*39dccbf2Sjoerg 	obj = _prop_object_internalize_by_tag(ctx);
668*39dccbf2Sjoerg 	if (obj == NULL)
669*39dccbf2Sjoerg 		goto out;
670*39dccbf2Sjoerg 
671*39dccbf2Sjoerg 	/*
672*39dccbf2Sjoerg 	 * We've advanced past the closing master_tag.
673*39dccbf2Sjoerg 	 * Now we want </plist>.
674*39dccbf2Sjoerg 	 */
675*39dccbf2Sjoerg 	if (_prop_object_internalize_find_tag(ctx, "plist",
676*39dccbf2Sjoerg 					      _PROP_TAG_TYPE_END) == FALSE) {
677*39dccbf2Sjoerg 		prop_object_release(obj);
678*39dccbf2Sjoerg 		obj = NULL;
679*39dccbf2Sjoerg 	}
680*39dccbf2Sjoerg 
681*39dccbf2Sjoerg  out:
682*39dccbf2Sjoerg  	_prop_object_internalize_context_free(ctx);
683*39dccbf2Sjoerg 	return (obj);
684*39dccbf2Sjoerg }
685*39dccbf2Sjoerg 
686774eb1a3Sthorpej /*
687774eb1a3Sthorpej  * _prop_object_internalize_context_alloc --
688774eb1a3Sthorpej  *	Allocate an internalize context.
689774eb1a3Sthorpej  */
690774eb1a3Sthorpej struct _prop_object_internalize_context *
691774eb1a3Sthorpej _prop_object_internalize_context_alloc(const char *xml)
692774eb1a3Sthorpej {
693774eb1a3Sthorpej 	struct _prop_object_internalize_context *ctx;
694774eb1a3Sthorpej 
695774eb1a3Sthorpej 	ctx = _PROP_MALLOC(sizeof(struct _prop_object_internalize_context),
696774eb1a3Sthorpej 			   M_TEMP);
697774eb1a3Sthorpej 	if (ctx == NULL)
698774eb1a3Sthorpej 		return (NULL);
699774eb1a3Sthorpej 
700774eb1a3Sthorpej 	ctx->poic_xml = ctx->poic_cp = xml;
701774eb1a3Sthorpej 
702774eb1a3Sthorpej 	/*
703774eb1a3Sthorpej 	 * Skip any whitespace and XML preamble stuff that we don't
704774eb1a3Sthorpej 	 * know about / care about.
705774eb1a3Sthorpej 	 */
706774eb1a3Sthorpej 	for (;;) {
707774eb1a3Sthorpej 		while (_PROP_ISSPACE(*xml))
708774eb1a3Sthorpej 			xml++;
709774eb1a3Sthorpej 		if (_PROP_EOF(*xml) || *xml != '<')
710774eb1a3Sthorpej 			goto bad;
711774eb1a3Sthorpej 
712774eb1a3Sthorpej #define	MATCH(str)	(memcmp(&xml[1], str, sizeof(str) - 1) == 0)
713774eb1a3Sthorpej 
714774eb1a3Sthorpej 		/*
715774eb1a3Sthorpej 		 * Skip over the XML preamble that Apple XML property
716774eb1a3Sthorpej 		 * lists usually include at the top of the file.
717774eb1a3Sthorpej 		 */
718774eb1a3Sthorpej 		if (MATCH("?xml ") ||
719774eb1a3Sthorpej 		    MATCH("!DOCTYPE plist")) {
720774eb1a3Sthorpej 			while (*xml != '>' && !_PROP_EOF(*xml))
721774eb1a3Sthorpej 				xml++;
722774eb1a3Sthorpej 			if (_PROP_EOF(*xml))
723774eb1a3Sthorpej 				goto bad;
724774eb1a3Sthorpej 			xml++;	/* advance past the '>' */
725774eb1a3Sthorpej 			continue;
726774eb1a3Sthorpej 		}
727774eb1a3Sthorpej 
728774eb1a3Sthorpej 		if (MATCH("<!--")) {
729774eb1a3Sthorpej 			ctx->poic_cp = xml + 4;
730774eb1a3Sthorpej 			if (_prop_object_internalize_skip_comment(ctx) == FALSE)
731774eb1a3Sthorpej 				goto bad;
732774eb1a3Sthorpej 			xml = ctx->poic_cp;
733774eb1a3Sthorpej 			continue;
734774eb1a3Sthorpej 		}
735774eb1a3Sthorpej 
736774eb1a3Sthorpej #undef MATCH
737774eb1a3Sthorpej 
738774eb1a3Sthorpej 		/*
739774eb1a3Sthorpej 		 * We don't think we should skip it, so let's hope we can
740774eb1a3Sthorpej 		 * parse it.
741774eb1a3Sthorpej 		 */
742774eb1a3Sthorpej 		break;
743774eb1a3Sthorpej 	}
744774eb1a3Sthorpej 
745774eb1a3Sthorpej 	ctx->poic_cp = xml;
746774eb1a3Sthorpej 	return (ctx);
747774eb1a3Sthorpej  bad:
748774eb1a3Sthorpej 	_PROP_FREE(ctx, M_TEMP);
749774eb1a3Sthorpej 	return (NULL);
750774eb1a3Sthorpej }
751774eb1a3Sthorpej 
752774eb1a3Sthorpej /*
753774eb1a3Sthorpej  * _prop_object_internalize_context_free --
754774eb1a3Sthorpej  *	Free an internalize context.
755774eb1a3Sthorpej  */
756774eb1a3Sthorpej void
757774eb1a3Sthorpej _prop_object_internalize_context_free(
758774eb1a3Sthorpej 		struct _prop_object_internalize_context *ctx)
759774eb1a3Sthorpej {
760774eb1a3Sthorpej 
761774eb1a3Sthorpej 	_PROP_FREE(ctx, M_TEMP);
762774eb1a3Sthorpej }
763774eb1a3Sthorpej 
764d21620b2Sthorpej #if !defined(_KERNEL) && !defined(_STANDALONE)
765d21620b2Sthorpej /*
766d21620b2Sthorpej  * _prop_object_externalize_file_dirname --
767d21620b2Sthorpej  *	dirname(3), basically.  We have to roll our own because the
768d21620b2Sthorpej  *	system dirname(3) isn't reentrant.
769d21620b2Sthorpej  */
770d21620b2Sthorpej static void
771d21620b2Sthorpej _prop_object_externalize_file_dirname(const char *path, char *result)
772d21620b2Sthorpej {
773d21620b2Sthorpej 	const char *lastp;
774d21620b2Sthorpej 	size_t len;
775d21620b2Sthorpej 
776d21620b2Sthorpej 	/*
777d21620b2Sthorpej 	 * If `path' is a NULL pointer or points to an empty string,
778d21620b2Sthorpej 	 * return ".".
779d21620b2Sthorpej 	 */
780d21620b2Sthorpej 	if (path == NULL || *path == '\0')
781d21620b2Sthorpej 		goto singledot;
782d21620b2Sthorpej 
783d21620b2Sthorpej 	/* String trailing slashes, if any. */
784d21620b2Sthorpej 	lastp = path + strlen(path) - 1;
785d21620b2Sthorpej 	while (lastp != path && *lastp == '/')
786d21620b2Sthorpej 		lastp--;
787d21620b2Sthorpej 
788d21620b2Sthorpej 	/* Terminate path at the last occurrence of '/'. */
789d21620b2Sthorpej 	do {
790d21620b2Sthorpej 		if (*lastp == '/') {
791d21620b2Sthorpej 			/* Strip trailing slashes, if any. */
792d21620b2Sthorpej 			while (lastp != path && *lastp == '/')
793d21620b2Sthorpej 				lastp--;
794d21620b2Sthorpej 
795d21620b2Sthorpej 			/* ...and copy the result into the result buffer. */
796d21620b2Sthorpej 			len = (lastp - path) + 1 /* last char */;
797d21620b2Sthorpej 			if (len > (PATH_MAX - 1))
798d21620b2Sthorpej 				len = PATH_MAX - 1;
799d21620b2Sthorpej 
800d21620b2Sthorpej 			memcpy(result, path, len);
801d21620b2Sthorpej 			result[len] = '\0';
802d21620b2Sthorpej 			return;
803d21620b2Sthorpej 		}
804d21620b2Sthorpej 	} while (--lastp >= path);
805d21620b2Sthorpej 
806d21620b2Sthorpej  	/* No /'s found, return ".". */
807d21620b2Sthorpej  singledot:
808d21620b2Sthorpej 	strcpy(result, ".");
809d21620b2Sthorpej }
810d21620b2Sthorpej 
811d21620b2Sthorpej /*
812d21620b2Sthorpej  * _prop_object_externalize_write_file --
813d21620b2Sthorpej  *	Write an externalized dictionary to the specified file.
814d21620b2Sthorpej  *	The file is written atomically from the caller's perspective,
815d21620b2Sthorpej  *	and the mode set to 0666 modified by the caller's umask.
816d21620b2Sthorpej  */
817d21620b2Sthorpej boolean_t
818d21620b2Sthorpej _prop_object_externalize_write_file(const char *fname, const char *xml,
819d21620b2Sthorpej     size_t len)
820d21620b2Sthorpej {
821d21620b2Sthorpej 	char tname[PATH_MAX];
822d21620b2Sthorpej 	int fd;
823d21620b2Sthorpej 	int save_errno;
824d21620b2Sthorpej 
825d21620b2Sthorpej 	if (len > SSIZE_MAX) {
826d21620b2Sthorpej 		errno = EFBIG;
827d21620b2Sthorpej 		return (FALSE);
828d21620b2Sthorpej 	}
829d21620b2Sthorpej 
830d21620b2Sthorpej 	/*
831d21620b2Sthorpej 	 * Get the directory name where the file is to be written
832d21620b2Sthorpej 	 * and create the temporary file.
833d21620b2Sthorpej 	 *
834d21620b2Sthorpej 	 * We don't use mkstemp() because mkstemp() always creates the
835d21620b2Sthorpej 	 * file with mode 0600.  We do, however, use mktemp() safely.
836d21620b2Sthorpej 	 */
837d21620b2Sthorpej  again:
838d21620b2Sthorpej 	_prop_object_externalize_file_dirname(fname, tname);
839d21620b2Sthorpej 	if (strlcat(tname, "/.plistXXXXXX", sizeof(tname)) >= sizeof(tname)) {
840d21620b2Sthorpej 		errno = ENAMETOOLONG;
841d21620b2Sthorpej 		return (FALSE);
842d21620b2Sthorpej 	}
843d21620b2Sthorpej 	if (mktemp(tname) == NULL)
844d21620b2Sthorpej 		return (FALSE);
845d21620b2Sthorpej 	if ((fd = open(tname, O_CREAT|O_RDWR|O_EXCL, 0666)) == -1) {
846d21620b2Sthorpej 		if (errno == EEXIST)
847d21620b2Sthorpej 			goto again;
848d21620b2Sthorpej 		return (FALSE);
849d21620b2Sthorpej 	}
850d21620b2Sthorpej 
851d21620b2Sthorpej 	if (write(fd, xml, len) != (ssize_t)len)
852d21620b2Sthorpej 		goto bad;
853d21620b2Sthorpej 
854d21620b2Sthorpej 	if (fsync(fd) == -1)
855d21620b2Sthorpej 		goto bad;
856d21620b2Sthorpej 
857d21620b2Sthorpej 	(void) close(fd);
858d21620b2Sthorpej 	fd = -1;
859d21620b2Sthorpej 
860d21620b2Sthorpej 	if (rename(tname, fname) == -1)
861d21620b2Sthorpej 		goto bad;
862d21620b2Sthorpej 
863d21620b2Sthorpej 	return (TRUE);
864d21620b2Sthorpej 
865d21620b2Sthorpej  bad:
866d21620b2Sthorpej 	save_errno = errno;
867d21620b2Sthorpej 	if (fd != -1)
868d21620b2Sthorpej 		(void) close(fd);
869d21620b2Sthorpej 	(void) unlink(tname);
870d21620b2Sthorpej 	errno = save_errno;
871d21620b2Sthorpej 	return (FALSE);
872d21620b2Sthorpej }
873d21620b2Sthorpej 
874d21620b2Sthorpej /*
875d21620b2Sthorpej  * _prop_object_internalize_map_file --
876d21620b2Sthorpej  *	Map a file for the purpose of internalizing it.
877d21620b2Sthorpej  */
878d21620b2Sthorpej struct _prop_object_internalize_mapped_file *
879d21620b2Sthorpej _prop_object_internalize_map_file(const char *fname)
880d21620b2Sthorpej {
881d21620b2Sthorpej 	struct stat sb;
882d21620b2Sthorpej 	struct _prop_object_internalize_mapped_file *mf;
8834df50368She 	size_t pgsize = (size_t)sysconf(_SC_PAGESIZE);
884d21620b2Sthorpej 	size_t pgmask = pgsize - 1;
885d21620b2Sthorpej 	boolean_t need_guard = FALSE;
886d21620b2Sthorpej 	int fd;
887d21620b2Sthorpej 
888d21620b2Sthorpej 	mf = _PROP_MALLOC(sizeof(*mf), M_TEMP);
889d21620b2Sthorpej 	if (mf == NULL)
890d21620b2Sthorpej 		return (NULL);
891d21620b2Sthorpej 
892d21620b2Sthorpej 	fd = open(fname, O_RDONLY, 0400);
893d21620b2Sthorpej 	if (fd == -1) {
894d21620b2Sthorpej 		_PROP_FREE(mf, M_TEMP);
895d21620b2Sthorpej 		return (NULL);
896d21620b2Sthorpej 	}
897d21620b2Sthorpej 
898d21620b2Sthorpej 	if (fstat(fd, &sb) == -1) {
899d21620b2Sthorpej 		(void) close(fd);
900d21620b2Sthorpej 		_PROP_FREE(mf, M_TEMP);
901d21620b2Sthorpej 		return (NULL);
902d21620b2Sthorpej 	}
903d21620b2Sthorpej 	mf->poimf_mapsize = ((size_t)sb.st_size + pgmask) & ~pgmask;
904d21620b2Sthorpej 	if (mf->poimf_mapsize < sb.st_size) {
905d21620b2Sthorpej 		(void) close(fd);
906d21620b2Sthorpej 		_PROP_FREE(mf, M_TEMP);
907d21620b2Sthorpej 		return (NULL);
908d21620b2Sthorpej 	}
909d21620b2Sthorpej 
910d21620b2Sthorpej 	/*
911d21620b2Sthorpej 	 * If the file length is an integral number of pages, then we
912d21620b2Sthorpej 	 * need to map a guard page at the end in order to provide the
913d21620b2Sthorpej 	 * necessary NUL-termination of the buffer.
914d21620b2Sthorpej 	 */
915d21620b2Sthorpej 	if ((sb.st_size & pgmask) == 0)
916d21620b2Sthorpej 		need_guard = TRUE;
917d21620b2Sthorpej 
918d21620b2Sthorpej 	mf->poimf_xml = mmap(NULL, need_guard ? mf->poimf_mapsize + pgsize
919d21620b2Sthorpej 			    		      : mf->poimf_mapsize,
920d21620b2Sthorpej 			    PROT_READ, MAP_FILE|MAP_SHARED, fd, (off_t)0);
921d21620b2Sthorpej 	(void) close(fd);
922d21620b2Sthorpej 	if (mf->poimf_xml == MAP_FAILED) {
923d21620b2Sthorpej 		_PROP_FREE(mf, M_TEMP);
924d21620b2Sthorpej 		return (NULL);
925d21620b2Sthorpej 	}
926d21620b2Sthorpej 	(void) madvise(mf->poimf_xml, mf->poimf_mapsize, MADV_SEQUENTIAL);
927d21620b2Sthorpej 
928d21620b2Sthorpej 	if (need_guard) {
929d21620b2Sthorpej 		if (mmap(mf->poimf_xml + mf->poimf_mapsize,
930d21620b2Sthorpej 			 pgsize, PROT_READ,
931d21620b2Sthorpej 			 MAP_ANON|MAP_PRIVATE|MAP_FIXED, -1,
932d21620b2Sthorpej 			 (off_t)0) == MAP_FAILED) {
933d21620b2Sthorpej 			(void) munmap(mf->poimf_xml, mf->poimf_mapsize);
934d21620b2Sthorpej 			_PROP_FREE(mf, M_TEMP);
935d21620b2Sthorpej 			return (NULL);
936d21620b2Sthorpej 		}
937d21620b2Sthorpej 		mf->poimf_mapsize += pgsize;
938d21620b2Sthorpej 	}
939d21620b2Sthorpej 
940d21620b2Sthorpej 	return (mf);
941d21620b2Sthorpej }
942d21620b2Sthorpej 
943d21620b2Sthorpej /*
944d21620b2Sthorpej  * _prop_object_internalize_unmap_file --
945d21620b2Sthorpej  *	Unmap a file previously mapped for internalizing.
946d21620b2Sthorpej  */
947d21620b2Sthorpej void
948d21620b2Sthorpej _prop_object_internalize_unmap_file(
949d21620b2Sthorpej     struct _prop_object_internalize_mapped_file *mf)
950d21620b2Sthorpej {
951d21620b2Sthorpej 
952d21620b2Sthorpej 	(void) madvise(mf->poimf_xml, mf->poimf_mapsize, MADV_DONTNEED);
953d21620b2Sthorpej 	(void) munmap(mf->poimf_xml, mf->poimf_mapsize);
954d21620b2Sthorpej 	_PROP_FREE(mf, M_TEMP);
955d21620b2Sthorpej }
956d21620b2Sthorpej #endif /* !_KERNEL && !_STANDALONE */
957d21620b2Sthorpej 
958774eb1a3Sthorpej /*
959774eb1a3Sthorpej  * Retain / release serialization --
960774eb1a3Sthorpej  *
961774eb1a3Sthorpej  * Eventually we would like to use atomic operations.  But until we have
962774eb1a3Sthorpej  * an MI API for them that is common to userland and the kernel, we will
963774eb1a3Sthorpej  * use a lock instead.
964774eb1a3Sthorpej  *
965774eb1a3Sthorpej  * We use a single global mutex for all serialization.  In the kernel, because
966774eb1a3Sthorpej  * we are still under a biglock, this will basically never contend (properties
967774eb1a3Sthorpej  * cannot be manipulated at interrupt level).  In userland, this will cost
968774eb1a3Sthorpej  * nothing for single-threaded programs.  For multi-threaded programs, there
969774eb1a3Sthorpej  * could be contention, but it probably won't cost that much unless the program
970774eb1a3Sthorpej  * makes heavy use of property lists.
971774eb1a3Sthorpej  */
972eb2acb85Sthorpej _PROP_MUTEX_DECL_STATIC(_prop_refcnt_mutex)
973eff71884Sthorpej #define	_PROP_REFCNT_LOCK()	_PROP_MUTEX_LOCK(_prop_refcnt_mutex)
974eff71884Sthorpej #define	_PROP_REFCNT_UNLOCK()	_PROP_MUTEX_UNLOCK(_prop_refcnt_mutex)
975774eb1a3Sthorpej 
976774eb1a3Sthorpej /*
977774eb1a3Sthorpej  * prop_object_retain --
978774eb1a3Sthorpej  *	Increment the reference count on an object.
979774eb1a3Sthorpej  */
980774eb1a3Sthorpej void
981774eb1a3Sthorpej prop_object_retain(prop_object_t obj)
982774eb1a3Sthorpej {
983774eb1a3Sthorpej 	struct _prop_object *po = obj;
984774eb1a3Sthorpej 	uint32_t ocnt;
985774eb1a3Sthorpej 
986774eb1a3Sthorpej 	_PROP_REFCNT_LOCK();
987774eb1a3Sthorpej 	ocnt = po->po_refcnt++;
988774eb1a3Sthorpej 	_PROP_REFCNT_UNLOCK();
989774eb1a3Sthorpej 
990774eb1a3Sthorpej 	_PROP_ASSERT(ocnt != 0xffffffffU);
991774eb1a3Sthorpej }
992774eb1a3Sthorpej 
993774eb1a3Sthorpej /*
994774eb1a3Sthorpej  * prop_object_release --
995774eb1a3Sthorpej  *	Decrement the reference count on an object.
996774eb1a3Sthorpej  *
997774eb1a3Sthorpej  *	Free the object if we are releasing the final
998774eb1a3Sthorpej  *	reference.
999774eb1a3Sthorpej  */
1000774eb1a3Sthorpej void
1001774eb1a3Sthorpej prop_object_release(prop_object_t obj)
1002774eb1a3Sthorpej {
1003774eb1a3Sthorpej 	struct _prop_object *po = obj;
1004774eb1a3Sthorpej 	uint32_t ocnt;
1005774eb1a3Sthorpej 
1006774eb1a3Sthorpej 	_PROP_REFCNT_LOCK();
1007774eb1a3Sthorpej 	ocnt = po->po_refcnt--;
1008774eb1a3Sthorpej 	_PROP_REFCNT_UNLOCK();
1009774eb1a3Sthorpej 
1010774eb1a3Sthorpej 	_PROP_ASSERT(ocnt != 0);
1011774eb1a3Sthorpej 	if (ocnt == 1)
10123e69f1b2Sthorpej 		(*po->po_type->pot_free)(po);
1013774eb1a3Sthorpej }
1014774eb1a3Sthorpej 
1015774eb1a3Sthorpej /*
1016774eb1a3Sthorpej  * prop_object_type --
1017774eb1a3Sthorpej  *	Return the type of an object.
1018774eb1a3Sthorpej  */
1019774eb1a3Sthorpej prop_type_t
1020774eb1a3Sthorpej prop_object_type(prop_object_t obj)
1021774eb1a3Sthorpej {
1022774eb1a3Sthorpej 	struct _prop_object *po = obj;
1023774eb1a3Sthorpej 
1024d21620b2Sthorpej 	if (obj == NULL)
1025d21620b2Sthorpej 		return (PROP_TYPE_UNKNOWN);
1026d21620b2Sthorpej 
10273e69f1b2Sthorpej 	return (po->po_type->pot_type);
10283e69f1b2Sthorpej }
10293e69f1b2Sthorpej 
10303e69f1b2Sthorpej /*
10313e69f1b2Sthorpej  * prop_object_equals --
10323e69f1b2Sthorpej  *	Returns TRUE if thw two objects are equivalent.
10333e69f1b2Sthorpej  */
10343e69f1b2Sthorpej boolean_t
10353e69f1b2Sthorpej prop_object_equals(prop_object_t obj1, prop_object_t obj2)
10363e69f1b2Sthorpej {
10373e69f1b2Sthorpej 	struct _prop_object *po1 = obj1;
10383e69f1b2Sthorpej 	struct _prop_object *po2 = obj2;
10393e69f1b2Sthorpej 
10403e69f1b2Sthorpej 	if (po1->po_type != po2->po_type)
10413e69f1b2Sthorpej 		return (FALSE);
10423e69f1b2Sthorpej 
10433e69f1b2Sthorpej 	return ((*po1->po_type->pot_equals)(obj1, obj2));
1044774eb1a3Sthorpej }
1045774eb1a3Sthorpej 
1046774eb1a3Sthorpej /*
1047774eb1a3Sthorpej  * prop_object_iterator_next --
1048774eb1a3Sthorpej  *	Return the next item during an iteration.
1049774eb1a3Sthorpej  */
1050774eb1a3Sthorpej prop_object_t
1051774eb1a3Sthorpej prop_object_iterator_next(prop_object_iterator_t pi)
1052774eb1a3Sthorpej {
1053774eb1a3Sthorpej 
1054774eb1a3Sthorpej 	return ((*pi->pi_next_object)(pi));
1055774eb1a3Sthorpej }
1056774eb1a3Sthorpej 
1057774eb1a3Sthorpej /*
1058774eb1a3Sthorpej  * prop_object_iterator_reset --
1059774eb1a3Sthorpej  *	Reset the iterator to the first object so as to restart
1060774eb1a3Sthorpej  *	iteration.
1061774eb1a3Sthorpej  */
1062774eb1a3Sthorpej void
1063774eb1a3Sthorpej prop_object_iterator_reset(prop_object_iterator_t pi)
1064774eb1a3Sthorpej {
1065774eb1a3Sthorpej 
1066774eb1a3Sthorpej 	(*pi->pi_reset)(pi);
1067774eb1a3Sthorpej }
1068774eb1a3Sthorpej 
1069774eb1a3Sthorpej /*
1070774eb1a3Sthorpej  * prop_object_iterator_release --
1071774eb1a3Sthorpej  *	Release the object iterator.
1072774eb1a3Sthorpej  */
1073774eb1a3Sthorpej void
1074774eb1a3Sthorpej prop_object_iterator_release(prop_object_iterator_t pi)
1075774eb1a3Sthorpej {
1076774eb1a3Sthorpej 
1077774eb1a3Sthorpej 	prop_object_release(pi->pi_obj);
1078774eb1a3Sthorpej 	_PROP_FREE(pi, M_TEMP);
1079774eb1a3Sthorpej }
1080