xref: /minix3/common/lib/libprop/prop_object.c (revision ef01931f760fe8114e6dd99a6864c92b3a85ae12)
1 /*	$NetBSD: prop_object.c,v 1.27 2011/04/20 20:00:07 martin Exp $	*/
2 
3 /*-
4  * Copyright (c) 2006, 2007 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Jason R. Thorpe.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <prop/prop_object.h>
33 #include "prop_object_impl.h"
34 
35 #if !defined(_KERNEL) && !defined(_STANDALONE)
36 #include <sys/mman.h>
37 #include <sys/stat.h>
38 #include <errno.h>
39 #include <fcntl.h>
40 #include <limits.h>
41 #include <unistd.h>
42 #include <assert.h>
43 #endif
44 #include <sys/atomic.h>
45 
46 #ifdef _STANDALONE
47 void *
48 _prop_standalone_calloc(size_t size)
49 {
50 	void *rv;
51 
52 	rv = alloc(size);
53 	if (rv != NULL)
54 		memset(rv, 0, size);
55 
56 	return (rv);
57 }
58 
59 void *
60 _prop_standalone_realloc(void *v, size_t size)
61 {
62 	void *rv;
63 
64 	rv = alloc(size);
65 	if (rv != NULL) {
66 		memcpy(rv, v, size);	/* XXX */
67 		dealloc(v, 0);		/* XXX */
68 	}
69 
70 	return (rv);
71 }
72 #endif /* _STANDALONE */
73 
74 /*
75  * _prop_object_init --
76  *	Initialize an object.  Called when sub-classes create
77  *	an instance.
78  */
79 void
80 _prop_object_init(struct _prop_object *po, const struct _prop_object_type *pot)
81 {
82 
83 	po->po_type = pot;
84 	po->po_refcnt = 1;
85 }
86 
87 /*
88  * _prop_object_fini --
89  *	Finalize an object.  Called when sub-classes destroy
90  *	an instance.
91  */
92 /*ARGSUSED*/
93 void
94 _prop_object_fini(struct _prop_object *po _PROP_ARG_UNUSED)
95 {
96 	/* Nothing to do, currently. */
97 }
98 
99 /*
100  * _prop_object_externalize_start_tag --
101  *	Append an XML-style start tag to the externalize buffer.
102  */
103 bool
104 _prop_object_externalize_start_tag(
105     struct _prop_object_externalize_context *ctx, const char *tag)
106 {
107 	unsigned int i;
108 
109 	for (i = 0; i < ctx->poec_depth; i++) {
110 		if (_prop_object_externalize_append_char(ctx, '\t') == false)
111 			return (false);
112 	}
113 	if (_prop_object_externalize_append_char(ctx, '<') == false ||
114 	    _prop_object_externalize_append_cstring(ctx, tag) == false ||
115 	    _prop_object_externalize_append_char(ctx, '>') == false)
116 		return (false);
117 
118 	return (true);
119 }
120 
121 /*
122  * _prop_object_externalize_end_tag --
123  *	Append an XML-style end tag to the externalize buffer.
124  */
125 bool
126 _prop_object_externalize_end_tag(
127     struct _prop_object_externalize_context *ctx, const char *tag)
128 {
129 
130 	if (_prop_object_externalize_append_char(ctx, '<') == false ||
131 	    _prop_object_externalize_append_char(ctx, '/') == false ||
132 	    _prop_object_externalize_append_cstring(ctx, tag) == false ||
133 	    _prop_object_externalize_append_char(ctx, '>') == false ||
134 	    _prop_object_externalize_append_char(ctx, '\n') == false)
135 		return (false);
136 
137 	return (true);
138 }
139 
140 /*
141  * _prop_object_externalize_empty_tag --
142  *	Append an XML-style empty tag to the externalize buffer.
143  */
144 bool
145 _prop_object_externalize_empty_tag(
146     struct _prop_object_externalize_context *ctx, const char *tag)
147 {
148 	unsigned int i;
149 
150 	for (i = 0; i < ctx->poec_depth; i++) {
151 		if (_prop_object_externalize_append_char(ctx, '\t') == false)
152 			return (false);
153 	}
154 
155 	if (_prop_object_externalize_append_char(ctx, '<') == false ||
156 	    _prop_object_externalize_append_cstring(ctx, tag) == false ||
157 	    _prop_object_externalize_append_char(ctx, '/') == false ||
158 	    _prop_object_externalize_append_char(ctx, '>') == false ||
159 	    _prop_object_externalize_append_char(ctx, '\n') == false)
160 	    	return (false);
161 
162 	return (true);
163 }
164 
165 /*
166  * _prop_object_externalize_append_cstring --
167  *	Append a C string to the externalize buffer.
168  */
169 bool
170 _prop_object_externalize_append_cstring(
171     struct _prop_object_externalize_context *ctx, const char *cp)
172 {
173 
174 	while (*cp != '\0') {
175 		if (_prop_object_externalize_append_char(ctx,
176 						(unsigned char) *cp) == false)
177 			return (false);
178 		cp++;
179 	}
180 
181 	return (true);
182 }
183 
184 /*
185  * _prop_object_externalize_append_encoded_cstring --
186  *	Append an encoded C string to the externalize buffer.
187  */
188 bool
189 _prop_object_externalize_append_encoded_cstring(
190     struct _prop_object_externalize_context *ctx, const char *cp)
191 {
192 
193 	while (*cp != '\0') {
194 		switch (*cp) {
195 		case '<':
196 			if (_prop_object_externalize_append_cstring(ctx,
197 					"&lt;") == false)
198 				return (false);
199 			break;
200 		case '>':
201 			if (_prop_object_externalize_append_cstring(ctx,
202 					"&gt;") == false)
203 				return (false);
204 			break;
205 		case '&':
206 			if (_prop_object_externalize_append_cstring(ctx,
207 					"&amp;") == false)
208 				return (false);
209 			break;
210 		default:
211 			if (_prop_object_externalize_append_char(ctx,
212 					(unsigned char) *cp) == false)
213 				return (false);
214 			break;
215 		}
216 		cp++;
217 	}
218 
219 	return (true);
220 }
221 
222 #define	BUF_EXPAND		256
223 
224 /*
225  * _prop_object_externalize_append_char --
226  *	Append a single character to the externalize buffer.
227  */
228 bool
229 _prop_object_externalize_append_char(
230     struct _prop_object_externalize_context *ctx, unsigned char c)
231 {
232 
233 	_PROP_ASSERT(ctx->poec_capacity != 0);
234 	_PROP_ASSERT(ctx->poec_buf != NULL);
235 	_PROP_ASSERT(ctx->poec_len <= ctx->poec_capacity);
236 
237 	if (ctx->poec_len == ctx->poec_capacity) {
238 		char *cp = _PROP_REALLOC(ctx->poec_buf,
239 					 ctx->poec_capacity + BUF_EXPAND,
240 					 M_TEMP);
241 		if (cp == NULL)
242 			return (false);
243 		ctx->poec_capacity = ctx->poec_capacity + BUF_EXPAND;
244 		ctx->poec_buf = cp;
245 	}
246 
247 	ctx->poec_buf[ctx->poec_len++] = c;
248 
249 	return (true);
250 }
251 
252 /*
253  * _prop_object_externalize_header --
254  *	Append the standard XML header to the externalize buffer.
255  */
256 bool
257 _prop_object_externalize_header(struct _prop_object_externalize_context *ctx)
258 {
259 	static const char _plist_xml_header[] =
260 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
261 "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n";
262 
263 	if (_prop_object_externalize_append_cstring(ctx,
264 						 _plist_xml_header) == false ||
265 	    _prop_object_externalize_start_tag(ctx,
266 				       "plist version=\"1.0\"") == false ||
267 	    _prop_object_externalize_append_char(ctx, '\n') == false)
268 		return (false);
269 
270 	return (true);
271 }
272 
273 /*
274  * _prop_object_externalize_footer --
275  *	Append the standard XML footer to the externalize buffer.  This
276  *	also NUL-terminates the buffer.
277  */
278 bool
279 _prop_object_externalize_footer(struct _prop_object_externalize_context *ctx)
280 {
281 
282 	if (_prop_object_externalize_end_tag(ctx, "plist") == false ||
283 	    _prop_object_externalize_append_char(ctx, '\0') == false)
284 		return (false);
285 
286 	return (true);
287 }
288 
289 /*
290  * _prop_object_externalize_context_alloc --
291  *	Allocate an externalize context.
292  */
293 struct _prop_object_externalize_context *
294 _prop_object_externalize_context_alloc(void)
295 {
296 	struct _prop_object_externalize_context *ctx;
297 
298 	ctx = _PROP_MALLOC(sizeof(*ctx), M_TEMP);
299 	if (ctx != NULL) {
300 		ctx->poec_buf = _PROP_MALLOC(BUF_EXPAND, M_TEMP);
301 		if (ctx->poec_buf == NULL) {
302 			_PROP_FREE(ctx, M_TEMP);
303 			return (NULL);
304 		}
305 		ctx->poec_len = 0;
306 		ctx->poec_capacity = BUF_EXPAND;
307 		ctx->poec_depth = 0;
308 	}
309 	return (ctx);
310 }
311 
312 /*
313  * _prop_object_externalize_context_free --
314  *	Free an externalize context.
315  */
316 void
317 _prop_object_externalize_context_free(
318 		struct _prop_object_externalize_context *ctx)
319 {
320 
321 	/* Buffer is always freed by the caller. */
322 	_PROP_FREE(ctx, M_TEMP);
323 }
324 
325 /*
326  * _prop_object_internalize_skip_comment --
327  *	Skip the body and end tag of a comment.
328  */
329 static bool
330 _prop_object_internalize_skip_comment(
331 				struct _prop_object_internalize_context *ctx)
332 {
333 	const char *cp = ctx->poic_cp;
334 
335 	while (!_PROP_EOF(*cp)) {
336 		if (cp[0] == '-' &&
337 		    cp[1] == '-' &&
338 		    cp[2] == '>') {
339 			ctx->poic_cp = cp + 3;
340 			return (true);
341 		}
342 		cp++;
343 	}
344 
345 	return (false);		/* ran out of buffer */
346 }
347 
348 /*
349  * _prop_object_internalize_find_tag --
350  *	Find the next tag in an XML stream.  Optionally compare the found
351  *	tag to an expected tag name.  State of the context is undefined
352  *	if this routine returns false.  Upon success, the context points
353  *	to the first octet after the tag.
354  */
355 bool
356 _prop_object_internalize_find_tag(struct _prop_object_internalize_context *ctx,
357 		      const char *tag, _prop_tag_type_t type)
358 {
359 	const char *cp;
360 	size_t taglen;
361 
362 	if (tag != NULL)
363 		taglen = strlen(tag);
364 	else
365 		taglen = 0;
366 
367  start_over:
368 	cp = ctx->poic_cp;
369 
370 	/*
371 	 * Find the start of the tag.
372 	 */
373 	while (_PROP_ISSPACE(*cp))
374 		cp++;
375 	if (_PROP_EOF(*cp))
376 		return (false);
377 
378 	if (*cp != '<')
379 		return (false);
380 
381 	ctx->poic_tag_start = cp++;
382 	if (_PROP_EOF(*cp))
383 		return (false);
384 
385 	if (*cp == '!') {
386 		if (cp[1] != '-' || cp[2] != '-')
387 			return (false);
388 		/*
389 		 * Comment block -- only allowed if we are allowed to
390 		 * return a start tag.
391 		 */
392 		if (type == _PROP_TAG_TYPE_END)
393 			return (false);
394 		ctx->poic_cp = cp + 3;
395 		if (_prop_object_internalize_skip_comment(ctx) == false)
396 			return (false);
397 		goto start_over;
398 	}
399 
400 	if (*cp == '/') {
401 		if (type != _PROP_TAG_TYPE_END &&
402 		    type != _PROP_TAG_TYPE_EITHER)
403 			return (false);
404 		cp++;
405 		if (_PROP_EOF(*cp))
406 			return (false);
407 		ctx->poic_tag_type = _PROP_TAG_TYPE_END;
408 	} else {
409 		if (type != _PROP_TAG_TYPE_START &&
410 		    type != _PROP_TAG_TYPE_EITHER)
411 			return (false);
412 		ctx->poic_tag_type = _PROP_TAG_TYPE_START;
413 	}
414 
415 	ctx->poic_tagname = cp;
416 
417 	while (!_PROP_ISSPACE(*cp) && *cp != '/' && *cp != '>')
418 		cp++;
419 	if (_PROP_EOF(*cp))
420 		return (false);
421 
422 	ctx->poic_tagname_len = cp - ctx->poic_tagname;
423 
424 	/* Make sure this is the tag we're looking for. */
425 	if (tag != NULL &&
426 	    (taglen != ctx->poic_tagname_len ||
427 	     memcmp(tag, ctx->poic_tagname, taglen) != 0))
428 		return (false);
429 
430 	/* Check for empty tag. */
431 	if (*cp == '/') {
432 		if (ctx->poic_tag_type != _PROP_TAG_TYPE_START)
433 			return(false);		/* only valid on start tags */
434 		ctx->poic_is_empty_element = true;
435 		cp++;
436 		if (_PROP_EOF(*cp) || *cp != '>')
437 			return (false);
438 	} else
439 		ctx->poic_is_empty_element = false;
440 
441 	/* Easy case of no arguments. */
442 	if (*cp == '>') {
443 		ctx->poic_tagattr = NULL;
444 		ctx->poic_tagattr_len = 0;
445 		ctx->poic_tagattrval = NULL;
446 		ctx->poic_tagattrval_len = 0;
447 		ctx->poic_cp = cp + 1;
448 		return (true);
449 	}
450 
451 	_PROP_ASSERT(!_PROP_EOF(*cp));
452 	cp++;
453 	if (_PROP_EOF(*cp))
454 		return (false);
455 
456 	while (_PROP_ISSPACE(*cp))
457 		cp++;
458 	if (_PROP_EOF(*cp))
459 		return (false);
460 
461 	ctx->poic_tagattr = cp;
462 
463 	while (!_PROP_ISSPACE(*cp) && *cp != '=')
464 		cp++;
465 	if (_PROP_EOF(*cp))
466 		return (false);
467 
468 	ctx->poic_tagattr_len = cp - ctx->poic_tagattr;
469 
470 	cp++;
471 	if (*cp != '\"')
472 		return (false);
473 	cp++;
474 	if (_PROP_EOF(*cp))
475 		return (false);
476 
477 	ctx->poic_tagattrval = cp;
478 	while (*cp != '\"')
479 		cp++;
480 	if (_PROP_EOF(*cp))
481 		return (false);
482 	ctx->poic_tagattrval_len = cp - ctx->poic_tagattrval;
483 
484 	cp++;
485 	if (*cp != '>')
486 		return (false);
487 
488 	ctx->poic_cp = cp + 1;
489 	return (true);
490 }
491 
492 /*
493  * _prop_object_internalize_decode_string --
494  *	Decode an encoded string.
495  */
496 bool
497 _prop_object_internalize_decode_string(
498 				struct _prop_object_internalize_context *ctx,
499 				char *target, size_t targsize, size_t *sizep,
500 				const char **cpp)
501 {
502 	const char *src;
503 	size_t tarindex;
504 	char c;
505 
506 	tarindex = 0;
507 	src = ctx->poic_cp;
508 
509 	for (;;) {
510 		if (_PROP_EOF(*src))
511 			return (false);
512 		if (*src == '<') {
513 			break;
514 		}
515 
516 		if ((c = *src) == '&') {
517 			if (src[1] == 'a' &&
518 			    src[2] == 'm' &&
519 			    src[3] == 'p' &&
520 			    src[4] == ';') {
521 			    	c = '&';
522 				src += 5;
523 			} else if (src[1] == 'l' &&
524 				   src[2] == 't' &&
525 				   src[3] == ';') {
526 				c = '<';
527 				src += 4;
528 			} else if (src[1] == 'g' &&
529 				   src[2] == 't' &&
530 				   src[3] == ';') {
531 				c = '>';
532 				src += 4;
533 			} else if (src[1] == 'a' &&
534 				   src[2] == 'p' &&
535 				   src[3] == 'o' &&
536 				   src[4] == 's' &&
537 				   src[5] == ';') {
538 				c = '\'';
539 				src += 6;
540 			} else if (src[1] == 'q' &&
541 				   src[2] == 'u' &&
542 				   src[3] == 'o' &&
543 				   src[4] == 't' &&
544 				   src[5] == ';') {
545 				c = '\"';
546 				src += 6;
547 			} else
548 				return (false);
549 		} else
550 			src++;
551 		if (target) {
552 			if (tarindex >= targsize)
553 				return (false);
554 			target[tarindex] = c;
555 		}
556 		tarindex++;
557 	}
558 
559 	_PROP_ASSERT(*src == '<');
560 	if (sizep != NULL)
561 		*sizep = tarindex;
562 	if (cpp != NULL)
563 		*cpp = src;
564 
565 	return (true);
566 }
567 
568 /*
569  * _prop_object_internalize_match --
570  *	Returns true if the two character streams match.
571  */
572 bool
573 _prop_object_internalize_match(const char *str1, size_t len1,
574 			       const char *str2, size_t len2)
575 {
576 
577 	return (len1 == len2 && memcmp(str1, str2, len1) == 0);
578 }
579 
580 #define	INTERNALIZER(t, f)			\
581 {	t,	sizeof(t) - 1,		f	}
582 
583 static const struct _prop_object_internalizer {
584 	const char			*poi_tag;
585 	size_t				poi_taglen;
586 	prop_object_internalizer_t	poi_intern;
587 } _prop_object_internalizer_table[] = {
588 	INTERNALIZER("array", _prop_array_internalize),
589 
590 	INTERNALIZER("true", _prop_bool_internalize),
591 	INTERNALIZER("false", _prop_bool_internalize),
592 
593 	INTERNALIZER("data", _prop_data_internalize),
594 
595 	INTERNALIZER("dict", _prop_dictionary_internalize),
596 
597 	INTERNALIZER("integer", _prop_number_internalize),
598 
599 	INTERNALIZER("string", _prop_string_internalize),
600 
601 	{ 0, 0, NULL }
602 };
603 
604 #undef INTERNALIZER
605 
606 /*
607  * _prop_object_internalize_by_tag --
608  *	Determine the object type from the tag in the context and
609  *	internalize it.
610  */
611 prop_object_t
612 _prop_object_internalize_by_tag(struct _prop_object_internalize_context *ctx)
613 {
614 	const struct _prop_object_internalizer *poi;
615 	prop_object_t obj, parent_obj;
616 	void *data, *iter;
617 	prop_object_internalizer_continue_t iter_func;
618 	struct _prop_stack stack;
619 
620 	_prop_stack_init(&stack);
621 
622 match_start:
623 	for (poi = _prop_object_internalizer_table;
624 	     poi->poi_tag != NULL; poi++) {
625 		if (_prop_object_internalize_match(ctx->poic_tagname,
626 						   ctx->poic_tagname_len,
627 						   poi->poi_tag,
628 						   poi->poi_taglen))
629 			break;
630 	}
631 	if ((poi == NULL) || (poi->poi_tag == NULL)) {
632 		while (_prop_stack_pop(&stack, &obj, &iter, &data, NULL)) {
633 			iter_func = (prop_object_internalizer_continue_t)iter;
634 			(*iter_func)(&stack, &obj, ctx, data, NULL);
635 		}
636 
637 		return (NULL);
638 	}
639 
640 	obj = NULL;
641 	if (!(*poi->poi_intern)(&stack, &obj, ctx))
642 		goto match_start;
643 
644 	parent_obj = obj;
645 	while (_prop_stack_pop(&stack, &parent_obj, &iter, &data, NULL)) {
646 		iter_func = (prop_object_internalizer_continue_t)iter;
647 		if (!(*iter_func)(&stack, &parent_obj, ctx, data, obj))
648 			goto match_start;
649 		obj = parent_obj;
650 	}
651 
652 	return (parent_obj);
653 }
654 
655 prop_object_t
656 _prop_generic_internalize(const char *xml, const char *master_tag)
657 {
658 	prop_object_t obj = NULL;
659 	struct _prop_object_internalize_context *ctx;
660 
661 	ctx = _prop_object_internalize_context_alloc(xml);
662 	if (ctx == NULL)
663 		return (NULL);
664 
665 	/* We start with a <plist> tag. */
666 	if (_prop_object_internalize_find_tag(ctx, "plist",
667 					      _PROP_TAG_TYPE_START) == false)
668 		goto out;
669 
670 	/* Plist elements cannot be empty. */
671 	if (ctx->poic_is_empty_element)
672 		goto out;
673 
674 	/*
675 	 * We don't understand any plist attributes, but Apple XML
676 	 * property lists often have a "version" attribute.  If we
677 	 * see that one, we simply ignore it.
678 	 */
679 	if (ctx->poic_tagattr != NULL &&
680 	    !_PROP_TAGATTR_MATCH(ctx, "version"))
681 		goto out;
682 
683 	/* Next we expect to see opening master_tag. */
684 	if (_prop_object_internalize_find_tag(ctx, master_tag,
685 					      _PROP_TAG_TYPE_START) == false)
686 		goto out;
687 
688 	obj = _prop_object_internalize_by_tag(ctx);
689 	if (obj == NULL)
690 		goto out;
691 
692 	/*
693 	 * We've advanced past the closing master_tag.
694 	 * Now we want </plist>.
695 	 */
696 	if (_prop_object_internalize_find_tag(ctx, "plist",
697 					      _PROP_TAG_TYPE_END) == false) {
698 		prop_object_release(obj);
699 		obj = NULL;
700 	}
701 
702  out:
703  	_prop_object_internalize_context_free(ctx);
704 	return (obj);
705 }
706 
707 /*
708  * _prop_object_internalize_context_alloc --
709  *	Allocate an internalize context.
710  */
711 struct _prop_object_internalize_context *
712 _prop_object_internalize_context_alloc(const char *xml)
713 {
714 	struct _prop_object_internalize_context *ctx;
715 
716 	ctx = _PROP_MALLOC(sizeof(struct _prop_object_internalize_context),
717 			   M_TEMP);
718 	if (ctx == NULL)
719 		return (NULL);
720 
721 	ctx->poic_xml = ctx->poic_cp = xml;
722 
723 	/*
724 	 * Skip any whitespace and XML preamble stuff that we don't
725 	 * know about / care about.
726 	 */
727 	for (;;) {
728 		while (_PROP_ISSPACE(*xml))
729 			xml++;
730 		if (_PROP_EOF(*xml) || *xml != '<')
731 			goto bad;
732 
733 #define	MATCH(str)	(memcmp(&xml[1], str, sizeof(str) - 1) == 0)
734 
735 		/*
736 		 * Skip over the XML preamble that Apple XML property
737 		 * lists usually include at the top of the file.
738 		 */
739 		if (MATCH("?xml ") ||
740 		    MATCH("!DOCTYPE plist")) {
741 			while (*xml != '>' && !_PROP_EOF(*xml))
742 				xml++;
743 			if (_PROP_EOF(*xml))
744 				goto bad;
745 			xml++;	/* advance past the '>' */
746 			continue;
747 		}
748 
749 		if (MATCH("<!--")) {
750 			ctx->poic_cp = xml + 4;
751 			if (_prop_object_internalize_skip_comment(ctx) == false)
752 				goto bad;
753 			xml = ctx->poic_cp;
754 			continue;
755 		}
756 
757 #undef MATCH
758 
759 		/*
760 		 * We don't think we should skip it, so let's hope we can
761 		 * parse it.
762 		 */
763 		break;
764 	}
765 
766 	ctx->poic_cp = xml;
767 	return (ctx);
768  bad:
769 	_PROP_FREE(ctx, M_TEMP);
770 	return (NULL);
771 }
772 
773 /*
774  * _prop_object_internalize_context_free --
775  *	Free an internalize context.
776  */
777 void
778 _prop_object_internalize_context_free(
779 		struct _prop_object_internalize_context *ctx)
780 {
781 
782 	_PROP_FREE(ctx, M_TEMP);
783 }
784 
785 #if !defined(_KERNEL) && !defined(_STANDALONE)
786 /*
787  * _prop_object_externalize_file_dirname --
788  *	dirname(3), basically.  We have to roll our own because the
789  *	system dirname(3) isn't reentrant.
790  */
791 static void
792 _prop_object_externalize_file_dirname(const char *path, char *result)
793 {
794 	const char *lastp;
795 	size_t len;
796 
797 	/*
798 	 * If `path' is a NULL pointer or points to an empty string,
799 	 * return ".".
800 	 */
801 	if (path == NULL || *path == '\0')
802 		goto singledot;
803 
804 	/* String trailing slashes, if any. */
805 	lastp = path + strlen(path) - 1;
806 	while (lastp != path && *lastp == '/')
807 		lastp--;
808 
809 	/* Terminate path at the last occurrence of '/'. */
810 	do {
811 		if (*lastp == '/') {
812 			/* Strip trailing slashes, if any. */
813 			while (lastp != path && *lastp == '/')
814 				lastp--;
815 
816 			/* ...and copy the result into the result buffer. */
817 			len = (lastp - path) + 1 /* last char */;
818 			if (len > (PATH_MAX - 1))
819 				len = PATH_MAX - 1;
820 
821 			memcpy(result, path, len);
822 			result[len] = '\0';
823 			return;
824 		}
825 	} while (--lastp >= path);
826 
827  	/* No /'s found, return ".". */
828  singledot:
829 	strcpy(result, ".");
830 }
831 
832 /*
833  * _prop_object_externalize_write_file --
834  *	Write an externalized dictionary to the specified file.
835  *	The file is written atomically from the caller's perspective,
836  *	and the mode set to 0666 modified by the caller's umask.
837  */
838 bool
839 _prop_object_externalize_write_file(const char *fname, const char *xml,
840     size_t len)
841 {
842 	char tname[PATH_MAX];
843 	int fd;
844 	int save_errno;
845 	mode_t myumask;
846 
847 	if (len > SSIZE_MAX) {
848 		errno = EFBIG;
849 		return (false);
850 	}
851 
852 	/*
853 	 * Get the directory name where the file is to be written
854 	 * and create the temporary file.
855 	 */
856 	_prop_object_externalize_file_dirname(fname, tname);
857 	if (strlcat(tname, "/.plistXXXXXX", sizeof(tname)) >= sizeof(tname)) {
858 		errno = ENAMETOOLONG;
859 		return (false);
860 	}
861 	if ((fd = mkstemp(tname)) == -1)
862 		return (false);
863 
864 	if (write(fd, xml, len) != (ssize_t)len)
865 		goto bad;
866 
867 	if (fsync(fd) == -1)
868 		goto bad;
869 
870 	myumask = umask(0);
871 	(void)umask(myumask);
872 	if (fchmod(fd, 0666 & ~myumask) == -1)
873 		goto bad;
874 
875 	(void) close(fd);
876 	fd = -1;
877 
878 	if (rename(tname, fname) == -1)
879 		goto bad;
880 
881 	return (true);
882 
883  bad:
884 	save_errno = errno;
885 	if (fd != -1)
886 		(void) close(fd);
887 	(void) unlink(tname);
888 	errno = save_errno;
889 	return (false);
890 }
891 
892 /*
893  * _prop_object_internalize_map_file --
894  *	Map a file for the purpose of internalizing it.
895  */
896 struct _prop_object_internalize_mapped_file *
897 _prop_object_internalize_map_file(const char *fname)
898 {
899 	struct stat sb;
900 	struct _prop_object_internalize_mapped_file *mf;
901 	size_t pgsize = (size_t)sysconf(_SC_PAGESIZE);
902 	size_t pgmask = pgsize - 1;
903 	bool need_guard = false;
904 	int fd;
905 
906 	mf = _PROP_MALLOC(sizeof(*mf), M_TEMP);
907 	if (mf == NULL)
908 		return (NULL);
909 
910 	fd = open(fname, O_RDONLY, 0400);
911 	if (fd == -1) {
912 		_PROP_FREE(mf, M_TEMP);
913 		return (NULL);
914 	}
915 
916 	if (fstat(fd, &sb) == -1) {
917 		(void) close(fd);
918 		_PROP_FREE(mf, M_TEMP);
919 		return (NULL);
920 	}
921 	mf->poimf_mapsize = ((size_t)sb.st_size + pgmask) & ~pgmask;
922 	if (mf->poimf_mapsize < (size_t)sb.st_size) {
923 		(void) close(fd);
924 		_PROP_FREE(mf, M_TEMP);
925 		return (NULL);
926 	}
927 
928 	/*
929 	 * If the file length is an integral number of pages, then we
930 	 * need to map a guard page at the end in order to provide the
931 	 * necessary NUL-termination of the buffer.
932 	 */
933 	if ((sb.st_size & pgmask) == 0)
934 		need_guard = true;
935 
936 #ifndef __minix
937 	mf->poimf_xml = mmap(NULL, need_guard ? mf->poimf_mapsize + pgsize
938 			    		      : mf->poimf_mapsize,
939 			    PROT_READ, MAP_FILE|MAP_SHARED, fd, (off_t)0);
940 #else
941 	mf->poimf_xml = MAP_FAILED;
942 #endif
943 	(void) close(fd);
944 	if (mf->poimf_xml == MAP_FAILED) {
945 		_PROP_FREE(mf, M_TEMP);
946 		return (NULL);
947 	}
948 
949 #ifndef __minix
950 	(void) madvise(mf->poimf_xml, mf->poimf_mapsize, MADV_SEQUENTIAL);
951 
952 	if (need_guard) {
953 		if (mmap(mf->poimf_xml + mf->poimf_mapsize,
954 			 pgsize, PROT_READ,
955 			 MAP_ANON|MAP_PRIVATE|MAP_FIXED, -1,
956 			 (off_t)0) == MAP_FAILED) {
957 			(void) munmap(mf->poimf_xml, mf->poimf_mapsize);
958 			_PROP_FREE(mf, M_TEMP);
959 			return (NULL);
960 		}
961 		mf->poimf_mapsize += pgsize;
962 	}
963 #endif
964 
965 	return (mf);
966 }
967 
968 /*
969  * _prop_object_internalize_unmap_file --
970  *	Unmap a file previously mapped for internalizing.
971  */
972 void
973 _prop_object_internalize_unmap_file(
974     struct _prop_object_internalize_mapped_file *mf)
975 {
976 
977 #ifndef __minix
978 	(void) madvise(mf->poimf_xml, mf->poimf_mapsize, MADV_DONTNEED);
979 	(void) munmap(mf->poimf_xml, mf->poimf_mapsize);
980 	_PROP_FREE(mf, M_TEMP);
981 #else
982 	assert(0);
983 #endif
984 }
985 #endif /* !_KERNEL && !_STANDALONE */
986 
987 /*
988  * prop_object_retain --
989  *	Increment the reference count on an object.
990  */
991 void
992 prop_object_retain(prop_object_t obj)
993 {
994 	struct _prop_object *po = obj;
995 	uint32_t ncnt;
996 
997 	ncnt = atomic_inc_32_nv(&po->po_refcnt);
998 	_PROP_ASSERT(ncnt != 0);
999 }
1000 
1001 /*
1002  * prop_object_release_emergency
1003  *	A direct free with prop_object_release failed.
1004  *	Walk down the tree until a leaf is found and
1005  *	free that. Do not recurse to avoid stack overflows.
1006  *
1007  *	This is a slow edge condition, but necessary to
1008  *	guarantee that an object can always be freed.
1009  */
1010 static void
1011 prop_object_release_emergency(prop_object_t obj)
1012 {
1013 	struct _prop_object *po;
1014 	void (*unlock)(void);
1015 	prop_object_t parent = NULL;
1016 	uint32_t ocnt;
1017 
1018 	for (;;) {
1019 		po = obj;
1020 		_PROP_ASSERT(obj);
1021 
1022 		if (po->po_type->pot_lock != NULL)
1023 		po->po_type->pot_lock();
1024 
1025 		/* Save pointerto unlock function */
1026 		unlock = po->po_type->pot_unlock;
1027 
1028 		/* Dance a bit to make sure we always get the non-racy ocnt */
1029 		ocnt = atomic_dec_32_nv(&po->po_refcnt);
1030 		ocnt++;
1031 		_PROP_ASSERT(ocnt != 0);
1032 
1033 		if (ocnt != 1) {
1034 			if (unlock != NULL)
1035 				unlock();
1036 			break;
1037 		}
1038 
1039 		_PROP_ASSERT(po->po_type);
1040 		if ((po->po_type->pot_free)(NULL, &obj) ==
1041 		    _PROP_OBJECT_FREE_DONE) {
1042 			if (unlock != NULL)
1043 				unlock();
1044 			break;
1045 		}
1046 
1047 		if (unlock != NULL)
1048 			unlock();
1049 
1050 		parent = po;
1051 		atomic_inc_32(&po->po_refcnt);
1052 	}
1053 	_PROP_ASSERT(parent);
1054 	/* One object was just freed. */
1055 	po = parent;
1056 	(*po->po_type->pot_emergency_free)(parent);
1057 }
1058 
1059 /*
1060  * prop_object_release --
1061  *	Decrement the reference count on an object.
1062  *
1063  *	Free the object if we are releasing the final
1064  *	reference.
1065  */
1066 void
1067 prop_object_release(prop_object_t obj)
1068 {
1069 	struct _prop_object *po;
1070 	struct _prop_stack stack;
1071 	void (*unlock)(void);
1072 	int ret;
1073 	uint32_t ocnt;
1074 
1075 	_prop_stack_init(&stack);
1076 
1077 	do {
1078 		do {
1079 			po = obj;
1080 			_PROP_ASSERT(obj);
1081 
1082 			if (po->po_type->pot_lock != NULL)
1083 				po->po_type->pot_lock();
1084 
1085 			/* Save pointer to object unlock function */
1086 			unlock = po->po_type->pot_unlock;
1087 
1088 			ocnt = atomic_dec_32_nv(&po->po_refcnt);
1089 			ocnt++;
1090 			_PROP_ASSERT(ocnt != 0);
1091 
1092 			if (ocnt != 1) {
1093 				ret = 0;
1094 				if (unlock != NULL)
1095 					unlock();
1096 				break;
1097 			}
1098 
1099 			ret = (po->po_type->pot_free)(&stack, &obj);
1100 
1101 			if (unlock != NULL)
1102 				unlock();
1103 
1104 			if (ret == _PROP_OBJECT_FREE_DONE)
1105 				break;
1106 
1107 			atomic_inc_32(&po->po_refcnt);
1108 		} while (ret == _PROP_OBJECT_FREE_RECURSE);
1109 		if (ret == _PROP_OBJECT_FREE_FAILED)
1110 			prop_object_release_emergency(obj);
1111 	} while (_prop_stack_pop(&stack, &obj, NULL, NULL, NULL));
1112 }
1113 
1114 /*
1115  * prop_object_type --
1116  *	Return the type of an object.
1117  */
1118 prop_type_t
1119 prop_object_type(prop_object_t obj)
1120 {
1121 	struct _prop_object *po = obj;
1122 
1123 	if (obj == NULL)
1124 		return (PROP_TYPE_UNKNOWN);
1125 
1126 	return (po->po_type->pot_type);
1127 }
1128 
1129 /*
1130  * prop_object_equals --
1131  *	Returns true if thw two objects are equivalent.
1132  */
1133 bool
1134 prop_object_equals(prop_object_t obj1, prop_object_t obj2)
1135 {
1136 	return (prop_object_equals_with_error(obj1, obj2, NULL));
1137 }
1138 
1139 bool
1140 prop_object_equals_with_error(prop_object_t obj1, prop_object_t obj2,
1141     bool *error_flag)
1142 {
1143 	struct _prop_object *po1;
1144 	struct _prop_object *po2;
1145 	void *stored_pointer1, *stored_pointer2;
1146 	prop_object_t next_obj1, next_obj2;
1147 	struct _prop_stack stack;
1148 	_prop_object_equals_rv_t ret;
1149 
1150 	_prop_stack_init(&stack);
1151 	if (error_flag)
1152 		*error_flag = false;
1153 
1154  start_subtree:
1155 	stored_pointer1 = NULL;
1156 	stored_pointer2 = NULL;
1157 	po1 = obj1;
1158 	po2 = obj2;
1159 
1160 	if (po1->po_type != po2->po_type)
1161 		return (false);
1162 
1163  continue_subtree:
1164 	ret = (*po1->po_type->pot_equals)(obj1, obj2,
1165 					  &stored_pointer1, &stored_pointer2,
1166 					  &next_obj1, &next_obj2);
1167 	if (ret == _PROP_OBJECT_EQUALS_FALSE)
1168 		goto finish;
1169 	if (ret == _PROP_OBJECT_EQUALS_TRUE) {
1170 		if (!_prop_stack_pop(&stack, &obj1, &obj2,
1171 				     &stored_pointer1, &stored_pointer2))
1172 			return true;
1173 		po1 = obj1;
1174 		po2 = obj2;
1175 		goto continue_subtree;
1176 	}
1177 	_PROP_ASSERT(ret == _PROP_OBJECT_EQUALS_RECURSE);
1178 
1179 	if (!_prop_stack_push(&stack, obj1, obj2,
1180 			      stored_pointer1, stored_pointer2)) {
1181 		if (error_flag)
1182 			*error_flag = true;
1183 		goto finish;
1184 	}
1185 	obj1 = next_obj1;
1186 	obj2 = next_obj2;
1187 	goto start_subtree;
1188 
1189 finish:
1190 	while (_prop_stack_pop(&stack, &obj1, &obj2, NULL, NULL)) {
1191 		po1 = obj1;
1192 		(*po1->po_type->pot_equals_finish)(obj1, obj2);
1193 	}
1194 	return (false);
1195 }
1196 
1197 /*
1198  * prop_object_iterator_next --
1199  *	Return the next item during an iteration.
1200  */
1201 prop_object_t
1202 prop_object_iterator_next(prop_object_iterator_t pi)
1203 {
1204 
1205 	return ((*pi->pi_next_object)(pi));
1206 }
1207 
1208 /*
1209  * prop_object_iterator_reset --
1210  *	Reset the iterator to the first object so as to restart
1211  *	iteration.
1212  */
1213 void
1214 prop_object_iterator_reset(prop_object_iterator_t pi)
1215 {
1216 
1217 	(*pi->pi_reset)(pi);
1218 }
1219 
1220 /*
1221  * prop_object_iterator_release --
1222  *	Release the object iterator.
1223  */
1224 void
1225 prop_object_iterator_release(prop_object_iterator_t pi)
1226 {
1227 
1228 	prop_object_release(pi->pi_obj);
1229 	_PROP_FREE(pi, M_TEMP);
1230 }
1231