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