Home | History | Annotate | Line # | Download | only in libprop
prop_object.c revision 1.7
      1 /*	$NetBSD: prop_object.c,v 1.7 2006/10/12 04:41:51 thorpej Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2006 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Jason R. Thorpe.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  * 3. All advertising materials mentioning features or use of this software
     19  *    must display the following acknowledgement:
     20  *      This product includes software developed by the NetBSD
     21  *      Foundation, Inc. and its contributors.
     22  * 4. Neither the name of The NetBSD Foundation nor the names of its
     23  *    contributors may be used to endorse or promote products derived
     24  *    from this software without specific prior written permission.
     25  *
     26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     36  * POSSIBILITY OF SUCH DAMAGE.
     37  */
     38 
     39 #include <prop/prop_object.h>
     40 #include "prop_object_impl.h"
     41 
     42 #if !defined(_KERNEL) && !defined(_STANDALONE)
     43 #include <sys/mman.h>
     44 #include <sys/stat.h>
     45 #include <errno.h>
     46 #include <fcntl.h>
     47 #include <limits.h>
     48 #include <unistd.h>
     49 #endif
     50 
     51 #ifdef _STANDALONE
     52 void *
     53 _prop_standalone_calloc(size_t size)
     54 {
     55 	void *rv;
     56 
     57 	rv = alloc(size);
     58 	if (rv != NULL)
     59 		memset(rv, 0, size);
     60 
     61 	return (rv);
     62 }
     63 
     64 void *
     65 _prop_standalone_realloc(void *v, size_t size)
     66 {
     67 	void *rv;
     68 
     69 	rv = alloc(size);
     70 	if (rv != NULL) {
     71 		memcpy(rv, v, size);	/* XXX */
     72 		dealloc(v, 0);		/* XXX */
     73 	}
     74 
     75 	return (rv);
     76 }
     77 #endif /* _STANDALONE */
     78 
     79 /*
     80  * _prop_object_init --
     81  *	Initialize an object.  Called when sub-classes create
     82  *	an instance.
     83  */
     84 void
     85 _prop_object_init(struct _prop_object *po, const struct _prop_object_type *pot)
     86 {
     87 
     88 	po->po_type = pot;
     89 	po->po_refcnt = 1;
     90 }
     91 
     92 /*
     93  * _prop_object_fini --
     94  *	Finalize an object.  Called when sub-classes destroy
     95  *	an instance.
     96  */
     97 /*ARGSUSED*/
     98 void
     99 _prop_object_fini(struct _prop_object *po)
    100 {
    101 	/* Nothing to do, currently. */
    102 	(void) po;
    103 }
    104 
    105 /*
    106  * _prop_object_externalize_start_tag --
    107  *	Append an XML-style start tag to the externalize buffer.
    108  */
    109 boolean_t
    110 _prop_object_externalize_start_tag(
    111     struct _prop_object_externalize_context *ctx, const char *tag)
    112 {
    113 	unsigned int i;
    114 
    115 	for (i = 0; i < ctx->poec_depth; i++) {
    116 		if (_prop_object_externalize_append_char(ctx, '\t') == FALSE)
    117 			return (FALSE);
    118 	}
    119 	if (_prop_object_externalize_append_char(ctx, '<') == FALSE ||
    120 	    _prop_object_externalize_append_cstring(ctx, tag) == FALSE ||
    121 	    _prop_object_externalize_append_char(ctx, '>') == FALSE)
    122 		return (FALSE);
    123 
    124 	return (TRUE);
    125 }
    126 
    127 /*
    128  * _prop_object_externalize_end_tag --
    129  *	Append an XML-style end tag to the externalize buffer.
    130  */
    131 boolean_t
    132 _prop_object_externalize_end_tag(
    133     struct _prop_object_externalize_context *ctx, const char *tag)
    134 {
    135 
    136 	if (_prop_object_externalize_append_char(ctx, '<') == FALSE ||
    137 	    _prop_object_externalize_append_char(ctx, '/') == FALSE ||
    138 	    _prop_object_externalize_append_cstring(ctx, tag) == FALSE ||
    139 	    _prop_object_externalize_append_char(ctx, '>') == FALSE ||
    140 	    _prop_object_externalize_append_char(ctx, '\n') == FALSE)
    141 		return (FALSE);
    142 
    143 	return (TRUE);
    144 }
    145 
    146 /*
    147  * _prop_object_externalize_empty_tag --
    148  *	Append an XML-style empty tag to the externalize buffer.
    149  */
    150 boolean_t
    151 _prop_object_externalize_empty_tag(
    152     struct _prop_object_externalize_context *ctx, const char *tag)
    153 {
    154 	unsigned int i;
    155 
    156 	for (i = 0; i < ctx->poec_depth; i++) {
    157 		if (_prop_object_externalize_append_char(ctx, '\t') == FALSE)
    158 			return (FALSE);
    159 	}
    160 
    161 	if (_prop_object_externalize_append_char(ctx, '<') == FALSE ||
    162 	    _prop_object_externalize_append_cstring(ctx, tag) == FALSE ||
    163 	    _prop_object_externalize_append_char(ctx, '/') == FALSE ||
    164 	    _prop_object_externalize_append_char(ctx, '>') == FALSE ||
    165 	    _prop_object_externalize_append_char(ctx, '\n') == FALSE)
    166 	    	return (FALSE);
    167 
    168 	return (TRUE);
    169 }
    170 
    171 /*
    172  * _prop_object_externalize_append_cstring --
    173  *	Append a C string to the externalize buffer.
    174  */
    175 boolean_t
    176 _prop_object_externalize_append_cstring(
    177     struct _prop_object_externalize_context *ctx, const char *cp)
    178 {
    179 
    180 	while (*cp != '\0') {
    181 		if (_prop_object_externalize_append_char(ctx,
    182 						(unsigned char) *cp) == FALSE)
    183 			return (FALSE);
    184 		cp++;
    185 	}
    186 
    187 	return (TRUE);
    188 }
    189 
    190 /*
    191  * _prop_object_externalize_append_encoded_cstring --
    192  *	Append an encoded C string to the externalize buffer.
    193  */
    194 boolean_t
    195 _prop_object_externalize_append_encoded_cstring(
    196     struct _prop_object_externalize_context *ctx, const char *cp)
    197 {
    198 
    199 	while (*cp != '\0') {
    200 		switch (*cp) {
    201 		case '<':
    202 			if (_prop_object_externalize_append_cstring(ctx,
    203 					"&lt;") == FALSE)
    204 				return (FALSE);
    205 			break;
    206 		case '>':
    207 			if (_prop_object_externalize_append_cstring(ctx,
    208 					"&gt;") == FALSE)
    209 				return (FALSE);
    210 			break;
    211 		case '&':
    212 			if (_prop_object_externalize_append_cstring(ctx,
    213 					"&amp;") == FALSE)
    214 				return (FALSE);
    215 			break;
    216 		default:
    217 			if (_prop_object_externalize_append_char(ctx,
    218 					(unsigned char) *cp) == FALSE)
    219 				return (FALSE);
    220 			break;
    221 		}
    222 		cp++;
    223 	}
    224 
    225 	return (TRUE);
    226 }
    227 
    228 #define	BUF_EXPAND		256
    229 
    230 /*
    231  * _prop_object_externalize_append_char --
    232  *	Append a single character to the externalize buffer.
    233  */
    234 boolean_t
    235 _prop_object_externalize_append_char(
    236     struct _prop_object_externalize_context *ctx, unsigned char c)
    237 {
    238 
    239 	_PROP_ASSERT(ctx->poec_capacity != 0);
    240 	_PROP_ASSERT(ctx->poec_buf != NULL);
    241 	_PROP_ASSERT(ctx->poec_len <= ctx->poec_capacity);
    242 
    243 	if (ctx->poec_len == ctx->poec_capacity) {
    244 		char *cp = _PROP_REALLOC(ctx->poec_buf,
    245 					 ctx->poec_capacity + BUF_EXPAND,
    246 					 M_TEMP);
    247 		if (cp == NULL)
    248 			return (FALSE);
    249 		ctx->poec_capacity = ctx->poec_capacity + BUF_EXPAND;
    250 		ctx->poec_buf = cp;
    251 	}
    252 
    253 	ctx->poec_buf[ctx->poec_len++] = c;
    254 
    255 	return (TRUE);
    256 }
    257 
    258 /*
    259  * _prop_object_externalize_header --
    260  *	Append the standard XML header to the externalize buffer.
    261  */
    262 boolean_t
    263 _prop_object_externalize_header(struct _prop_object_externalize_context *ctx)
    264 {
    265 	static const char _plist_xml_header[] =
    266 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
    267 "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n";
    268 
    269 	if (_prop_object_externalize_append_cstring(ctx,
    270 						 _plist_xml_header) == FALSE ||
    271 	    _prop_object_externalize_start_tag(ctx,
    272 				       "plist version=\"1.0\"") == FALSE ||
    273 	    _prop_object_externalize_append_char(ctx, '\n') == FALSE)
    274 		return (FALSE);
    275 
    276 	return (TRUE);
    277 }
    278 
    279 /*
    280  * _prop_object_externalize_footer --
    281  *	Append the standard XML footer to the externalize buffer.  This
    282  *	also NUL-terminates the buffer.
    283  */
    284 boolean_t
    285 _prop_object_externalize_footer(struct _prop_object_externalize_context *ctx)
    286 {
    287 
    288 	if (_prop_object_externalize_end_tag(ctx, "plist") == FALSE ||
    289 	    _prop_object_externalize_append_char(ctx, '\0') == FALSE)
    290 		return (FALSE);
    291 
    292 	return (TRUE);
    293 }
    294 
    295 /*
    296  * _prop_object_externalize_context_alloc --
    297  *	Allocate an externalize context.
    298  */
    299 struct _prop_object_externalize_context *
    300 _prop_object_externalize_context_alloc(void)
    301 {
    302 	struct _prop_object_externalize_context *ctx;
    303 
    304 	ctx = _PROP_MALLOC(sizeof(*ctx), M_TEMP);
    305 	if (ctx != NULL) {
    306 		ctx->poec_buf = _PROP_MALLOC(BUF_EXPAND, M_TEMP);
    307 		if (ctx->poec_buf == NULL) {
    308 			_PROP_FREE(ctx, M_TEMP);
    309 			return (NULL);
    310 		}
    311 		ctx->poec_len = 0;
    312 		ctx->poec_capacity = BUF_EXPAND;
    313 		ctx->poec_depth = 0;
    314 	}
    315 	return (ctx);
    316 }
    317 
    318 /*
    319  * _prop_object_externalize_context_free --
    320  *	Free an externalize context.
    321  */
    322 void
    323 _prop_object_externalize_context_free(
    324 		struct _prop_object_externalize_context *ctx)
    325 {
    326 
    327 	/* Buffer is always freed by the caller. */
    328 	_PROP_FREE(ctx, M_TEMP);
    329 }
    330 
    331 /*
    332  * _prop_object_internalize_skip_comment --
    333  *	Skip the body and end tag of a comment.
    334  */
    335 static boolean_t
    336 _prop_object_internalize_skip_comment(
    337 				struct _prop_object_internalize_context *ctx)
    338 {
    339 	const char *cp = ctx->poic_cp;
    340 
    341 	while (!_PROP_EOF(*cp)) {
    342 		if (cp[0] == '-' &&
    343 		    cp[1] == '-' &&
    344 		    cp[2] == '>') {
    345 			ctx->poic_cp = cp + 3;
    346 			return (TRUE);
    347 		}
    348 		cp++;
    349 	}
    350 
    351 	return (FALSE);		/* ran out of buffer */
    352 }
    353 
    354 /*
    355  * _prop_object_internalize_find_tag --
    356  *	Find the next tag in an XML stream.  Optionally compare the found
    357  *	tag to an expected tag name.  State of the context is undefined
    358  *	if this routine returns FALSE.  Upon success, the context points
    359  *	to the first octet after the tag.
    360  */
    361 boolean_t
    362 _prop_object_internalize_find_tag(struct _prop_object_internalize_context *ctx,
    363 		      const char *tag, _prop_tag_type_t type)
    364 {
    365 	const char *cp;
    366 	size_t taglen;
    367 
    368 	if (tag != NULL)
    369 		taglen = strlen(tag);
    370 	else
    371 		taglen = 0;
    372 
    373  start_over:
    374 	cp = ctx->poic_cp;
    375 
    376 	/*
    377 	 * Find the start of the tag.
    378 	 */
    379 	while (_PROP_ISSPACE(*cp))
    380 		cp++;
    381 	if (_PROP_EOF(*cp))
    382 		return (FALSE);
    383 
    384 	if (*cp != '<')
    385 		return (FALSE);
    386 
    387 	ctx->poic_tag_start = cp++;
    388 	if (_PROP_EOF(*cp))
    389 		return (FALSE);
    390 
    391 	if (*cp == '!') {
    392 		if (cp[1] != '-' || cp[2] != '-')
    393 			return (FALSE);
    394 		/*
    395 		 * Comment block -- only allowed if we are allowed to
    396 		 * return a start tag.
    397 		 */
    398 		if (type == _PROP_TAG_TYPE_END)
    399 			return (FALSE);
    400 		ctx->poic_cp = cp + 3;
    401 		if (_prop_object_internalize_skip_comment(ctx) == FALSE)
    402 			return (FALSE);
    403 		goto start_over;
    404 	}
    405 
    406 	if (*cp == '/') {
    407 		if (type != _PROP_TAG_TYPE_END &&
    408 		    type != _PROP_TAG_TYPE_EITHER)
    409 			return (FALSE);
    410 		cp++;
    411 		if (_PROP_EOF(*cp))
    412 			return (FALSE);
    413 		ctx->poic_tag_type = _PROP_TAG_TYPE_END;
    414 	} else {
    415 		if (type != _PROP_TAG_TYPE_START &&
    416 		    type != _PROP_TAG_TYPE_EITHER)
    417 			return (FALSE);
    418 		ctx->poic_tag_type = _PROP_TAG_TYPE_START;
    419 	}
    420 
    421 	ctx->poic_tagname = cp;
    422 
    423 	while (!_PROP_ISSPACE(*cp) && *cp != '/' && *cp != '>')
    424 		cp++;
    425 	if (_PROP_EOF(*cp))
    426 		return (FALSE);
    427 
    428 	ctx->poic_tagname_len = cp - ctx->poic_tagname;
    429 
    430 	/* Make sure this is the tag we're looking for. */
    431 	if (tag != NULL &&
    432 	    (taglen != ctx->poic_tagname_len ||
    433 	     memcmp(tag, ctx->poic_tagname, taglen) != 0))
    434 		return (FALSE);
    435 
    436 	/* Check for empty tag. */
    437 	if (*cp == '/') {
    438 		if (ctx->poic_tag_type != _PROP_TAG_TYPE_START)
    439 			return(FALSE);		/* only valid on start tags */
    440 		ctx->poic_is_empty_element = TRUE;
    441 		cp++;
    442 		if (_PROP_EOF(*cp) || *cp != '>')
    443 			return (FALSE);
    444 	} else
    445 		ctx->poic_is_empty_element = FALSE;
    446 
    447 	/* Easy case of no arguments. */
    448 	if (*cp == '>') {
    449 		ctx->poic_tagattr = NULL;
    450 		ctx->poic_tagattr_len = 0;
    451 		ctx->poic_tagattrval = NULL;
    452 		ctx->poic_tagattrval_len = 0;
    453 		ctx->poic_cp = cp + 1;
    454 		return (TRUE);
    455 	}
    456 
    457 	_PROP_ASSERT(!_PROP_EOF(*cp));
    458 	cp++;
    459 	if (_PROP_EOF(*cp))
    460 		return (FALSE);
    461 
    462 	while (_PROP_ISSPACE(*cp))
    463 		cp++;
    464 	if (_PROP_EOF(*cp))
    465 		return (FALSE);
    466 
    467 	ctx->poic_tagattr = cp;
    468 
    469 	while (!_PROP_ISSPACE(*cp) && *cp != '=')
    470 		cp++;
    471 	if (_PROP_EOF(*cp))
    472 		return (FALSE);
    473 
    474 	ctx->poic_tagattr_len = cp - ctx->poic_tagattr;
    475 
    476 	cp++;
    477 	if (*cp != '\"')
    478 		return (FALSE);
    479 	cp++;
    480 	if (_PROP_EOF(*cp))
    481 		return (FALSE);
    482 
    483 	ctx->poic_tagattrval = cp;
    484 	while (*cp != '\"')
    485 		cp++;
    486 	if (_PROP_EOF(*cp))
    487 		return (FALSE);
    488 	ctx->poic_tagattrval_len = cp - ctx->poic_tagattrval;
    489 
    490 	cp++;
    491 	if (*cp != '>')
    492 		return (FALSE);
    493 
    494 	ctx->poic_cp = cp + 1;
    495 	return (TRUE);
    496 }
    497 
    498 /*
    499  * _prop_object_internalize_decode_string --
    500  *	Decode an encoded string.
    501  */
    502 boolean_t
    503 _prop_object_internalize_decode_string(
    504 				struct _prop_object_internalize_context *ctx,
    505 				char *target, size_t targsize, size_t *sizep,
    506 				const char **cpp)
    507 {
    508 	const char *src;
    509 	size_t tarindex;
    510 	char c;
    511 
    512 	tarindex = 0;
    513 	src = ctx->poic_cp;
    514 
    515 	for (;;) {
    516 		if (_PROP_EOF(*src))
    517 			return (FALSE);
    518 		if (*src == '<') {
    519 			break;
    520 		}
    521 
    522 		if ((c = *src) == '&') {
    523 			if (src[1] == 'a' &&
    524 			    src[2] == 'm' &&
    525 			    src[3] == 'p' &&
    526 			    src[4] == ';') {
    527 			    	c = '&';
    528 				src += 5;
    529 			} else if (src[1] == 'l' &&
    530 				   src[2] == 't' &&
    531 				   src[3] == ';') {
    532 				c = '<';
    533 				src += 4;
    534 			} else if (src[1] == 'g' &&
    535 				   src[2] == 't' &&
    536 				   src[3] == ';') {
    537 				c = '>';
    538 				src += 4;
    539 			} else if (src[1] == 'a' &&
    540 				   src[2] == 'p' &&
    541 				   src[3] == 'o' &&
    542 				   src[4] == 's' &&
    543 				   src[5] == ';') {
    544 				c = '\'';
    545 				src += 6;
    546 			} else if (src[1] == 'q' &&
    547 				   src[2] == 'u' &&
    548 				   src[3] == 'o' &&
    549 				   src[4] == 't' &&
    550 				   src[5] == ';') {
    551 				c = '\"';
    552 				src += 6;
    553 			} else
    554 				return (FALSE);
    555 		} else
    556 			src++;
    557 		if (target) {
    558 			if (tarindex >= targsize)
    559 				return (FALSE);
    560 			target[tarindex] = c;
    561 		}
    562 		tarindex++;
    563 	}
    564 
    565 	_PROP_ASSERT(*src == '<');
    566 	if (sizep != NULL)
    567 		*sizep = tarindex;
    568 	if (cpp != NULL)
    569 		*cpp = src;
    570 
    571 	return (TRUE);
    572 }
    573 
    574 /*
    575  * _prop_object_internalize_match --
    576  *	Returns true if the two character streams match.
    577  */
    578 boolean_t
    579 _prop_object_internalize_match(const char *str1, size_t len1,
    580 			       const char *str2, size_t len2)
    581 {
    582 
    583 	return (len1 == len2 && memcmp(str1, str2, len1) == 0);
    584 }
    585 
    586 #define	INTERNALIZER(t, f)			\
    587 {	t,	sizeof(t) - 1,		f	}
    588 
    589 static const struct _prop_object_internalizer {
    590 	const char	*poi_tag;
    591 	size_t		poi_taglen;
    592 	prop_object_t	(*poi_intern)(
    593 				struct _prop_object_internalize_context *);
    594 } _prop_object_internalizer_table[] = {
    595 	INTERNALIZER("array", _prop_array_internalize),
    596 
    597 	INTERNALIZER("true", _prop_bool_internalize),
    598 	INTERNALIZER("false", _prop_bool_internalize),
    599 
    600 	INTERNALIZER("data", _prop_data_internalize),
    601 
    602 	INTERNALIZER("dict", _prop_dictionary_internalize),
    603 
    604 	INTERNALIZER("integer", _prop_number_internalize),
    605 
    606 	INTERNALIZER("string", _prop_string_internalize),
    607 
    608 	{ 0, 0, NULL }
    609 };
    610 
    611 #undef INTERNALIZER
    612 
    613 /*
    614  * _prop_object_internalize_by_tag --
    615  *	Determine the object type from the tag in the context and
    616  *	internalize it.
    617  */
    618 prop_object_t
    619 _prop_object_internalize_by_tag(struct _prop_object_internalize_context *ctx)
    620 {
    621 	const struct _prop_object_internalizer *poi;
    622 
    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 			return ((*poi->poi_intern)(ctx));
    630 	}
    631 
    632 	return (NULL);
    633 }
    634 
    635 /*
    636  * _prop_object_internalize_context_alloc --
    637  *	Allocate an internalize context.
    638  */
    639 struct _prop_object_internalize_context *
    640 _prop_object_internalize_context_alloc(const char *xml)
    641 {
    642 	struct _prop_object_internalize_context *ctx;
    643 
    644 	ctx = _PROP_MALLOC(sizeof(struct _prop_object_internalize_context),
    645 			   M_TEMP);
    646 	if (ctx == NULL)
    647 		return (NULL);
    648 
    649 	ctx->poic_xml = ctx->poic_cp = xml;
    650 
    651 	/*
    652 	 * Skip any whitespace and XML preamble stuff that we don't
    653 	 * know about / care about.
    654 	 */
    655 	for (;;) {
    656 		while (_PROP_ISSPACE(*xml))
    657 			xml++;
    658 		if (_PROP_EOF(*xml) || *xml != '<')
    659 			goto bad;
    660 
    661 #define	MATCH(str)	(memcmp(&xml[1], str, sizeof(str) - 1) == 0)
    662 
    663 		/*
    664 		 * Skip over the XML preamble that Apple XML property
    665 		 * lists usually include at the top of the file.
    666 		 */
    667 		if (MATCH("?xml ") ||
    668 		    MATCH("!DOCTYPE plist")) {
    669 			while (*xml != '>' && !_PROP_EOF(*xml))
    670 				xml++;
    671 			if (_PROP_EOF(*xml))
    672 				goto bad;
    673 			xml++;	/* advance past the '>' */
    674 			continue;
    675 		}
    676 
    677 		if (MATCH("<!--")) {
    678 			ctx->poic_cp = xml + 4;
    679 			if (_prop_object_internalize_skip_comment(ctx) == FALSE)
    680 				goto bad;
    681 			xml = ctx->poic_cp;
    682 			continue;
    683 		}
    684 
    685 #undef MATCH
    686 
    687 		/*
    688 		 * We don't think we should skip it, so let's hope we can
    689 		 * parse it.
    690 		 */
    691 		break;
    692 	}
    693 
    694 	ctx->poic_cp = xml;
    695 	return (ctx);
    696  bad:
    697 	_PROP_FREE(ctx, M_TEMP);
    698 	return (NULL);
    699 }
    700 
    701 /*
    702  * _prop_object_internalize_context_free --
    703  *	Free an internalize context.
    704  */
    705 void
    706 _prop_object_internalize_context_free(
    707 		struct _prop_object_internalize_context *ctx)
    708 {
    709 
    710 	_PROP_FREE(ctx, M_TEMP);
    711 }
    712 
    713 #if !defined(_KERNEL) && !defined(_STANDALONE)
    714 /*
    715  * _prop_object_externalize_file_dirname --
    716  *	dirname(3), basically.  We have to roll our own because the
    717  *	system dirname(3) isn't reentrant.
    718  */
    719 static void
    720 _prop_object_externalize_file_dirname(const char *path, char *result)
    721 {
    722 	const char *lastp;
    723 	size_t len;
    724 
    725 	/*
    726 	 * If `path' is a NULL pointer or points to an empty string,
    727 	 * return ".".
    728 	 */
    729 	if (path == NULL || *path == '\0')
    730 		goto singledot;
    731 
    732 	/* String trailing slashes, if any. */
    733 	lastp = path + strlen(path) - 1;
    734 	while (lastp != path && *lastp == '/')
    735 		lastp--;
    736 
    737 	/* Terminate path at the last occurrence of '/'. */
    738 	do {
    739 		if (*lastp == '/') {
    740 			/* Strip trailing slashes, if any. */
    741 			while (lastp != path && *lastp == '/')
    742 				lastp--;
    743 
    744 			/* ...and copy the result into the result buffer. */
    745 			len = (lastp - path) + 1 /* last char */;
    746 			if (len > (PATH_MAX - 1))
    747 				len = PATH_MAX - 1;
    748 
    749 			memcpy(result, path, len);
    750 			result[len] = '\0';
    751 			return;
    752 		}
    753 	} while (--lastp >= path);
    754 
    755  	/* No /'s found, return ".". */
    756  singledot:
    757 	strcpy(result, ".");
    758 }
    759 
    760 /*
    761  * _prop_object_externalize_write_file --
    762  *	Write an externalized dictionary to the specified file.
    763  *	The file is written atomically from the caller's perspective,
    764  *	and the mode set to 0666 modified by the caller's umask.
    765  */
    766 boolean_t
    767 _prop_object_externalize_write_file(const char *fname, const char *xml,
    768     size_t len)
    769 {
    770 	char tname[PATH_MAX];
    771 	int fd;
    772 	int save_errno;
    773 
    774 	if (len > SSIZE_MAX) {
    775 		errno = EFBIG;
    776 		return (FALSE);
    777 	}
    778 
    779 	/*
    780 	 * Get the directory name where the file is to be written
    781 	 * and create the temporary file.
    782 	 *
    783 	 * We don't use mkstemp() because mkstemp() always creates the
    784 	 * file with mode 0600.  We do, however, use mktemp() safely.
    785 	 */
    786  again:
    787 	_prop_object_externalize_file_dirname(fname, tname);
    788 	if (strlcat(tname, "/.plistXXXXXX", sizeof(tname)) >= sizeof(tname)) {
    789 		errno = ENAMETOOLONG;
    790 		return (FALSE);
    791 	}
    792 	if (mktemp(tname) == NULL)
    793 		return (FALSE);
    794 	if ((fd = open(tname, O_CREAT|O_RDWR|O_EXCL, 0666)) == -1) {
    795 		if (errno == EEXIST)
    796 			goto again;
    797 		return (FALSE);
    798 	}
    799 
    800 	if (write(fd, xml, len) != (ssize_t)len)
    801 		goto bad;
    802 
    803 	if (fsync(fd) == -1)
    804 		goto bad;
    805 
    806 	(void) close(fd);
    807 	fd = -1;
    808 
    809 	if (rename(tname, fname) == -1)
    810 		goto bad;
    811 
    812 	return (TRUE);
    813 
    814  bad:
    815 	save_errno = errno;
    816 	if (fd != -1)
    817 		(void) close(fd);
    818 	(void) unlink(tname);
    819 	errno = save_errno;
    820 	return (FALSE);
    821 }
    822 
    823 /*
    824  * _prop_object_internalize_map_file --
    825  *	Map a file for the purpose of internalizing it.
    826  */
    827 struct _prop_object_internalize_mapped_file *
    828 _prop_object_internalize_map_file(const char *fname)
    829 {
    830 	struct stat sb;
    831 	struct _prop_object_internalize_mapped_file *mf;
    832 	size_t pgsize = sysconf(_SC_PAGESIZE);
    833 	size_t pgmask = pgsize - 1;
    834 	boolean_t need_guard = FALSE;
    835 	int fd;
    836 
    837 	mf = _PROP_MALLOC(sizeof(*mf), M_TEMP);
    838 	if (mf == NULL)
    839 		return (NULL);
    840 
    841 	fd = open(fname, O_RDONLY, 0400);
    842 	if (fd == -1) {
    843 		_PROP_FREE(mf, M_TEMP);
    844 		return (NULL);
    845 	}
    846 
    847 	if (fstat(fd, &sb) == -1) {
    848 		(void) close(fd);
    849 		_PROP_FREE(mf, M_TEMP);
    850 		return (NULL);
    851 	}
    852 	mf->poimf_mapsize = ((size_t)sb.st_size + pgmask) & ~pgmask;
    853 	if (mf->poimf_mapsize < sb.st_size) {
    854 		(void) close(fd);
    855 		_PROP_FREE(mf, M_TEMP);
    856 		return (NULL);
    857 	}
    858 
    859 	/*
    860 	 * If the file length is an integral number of pages, then we
    861 	 * need to map a guard page at the end in order to provide the
    862 	 * necessary NUL-termination of the buffer.
    863 	 */
    864 	if ((sb.st_size & pgmask) == 0)
    865 		need_guard = TRUE;
    866 
    867 	mf->poimf_xml = mmap(NULL, need_guard ? mf->poimf_mapsize + pgsize
    868 			    		      : mf->poimf_mapsize,
    869 			    PROT_READ, MAP_FILE|MAP_SHARED, fd, (off_t)0);
    870 	(void) close(fd);
    871 	if (mf->poimf_xml == MAP_FAILED) {
    872 		_PROP_FREE(mf, M_TEMP);
    873 		return (NULL);
    874 	}
    875 	(void) madvise(mf->poimf_xml, mf->poimf_mapsize, MADV_SEQUENTIAL);
    876 
    877 	if (need_guard) {
    878 		if (mmap(mf->poimf_xml + mf->poimf_mapsize,
    879 			 pgsize, PROT_READ,
    880 			 MAP_ANON|MAP_PRIVATE|MAP_FIXED, -1,
    881 			 (off_t)0) == MAP_FAILED) {
    882 			(void) munmap(mf->poimf_xml, mf->poimf_mapsize);
    883 			_PROP_FREE(mf, M_TEMP);
    884 			return (NULL);
    885 		}
    886 		mf->poimf_mapsize += pgsize;
    887 	}
    888 
    889 	return (mf);
    890 }
    891 
    892 /*
    893  * _prop_object_internalize_unmap_file --
    894  *	Unmap a file previously mapped for internalizing.
    895  */
    896 void
    897 _prop_object_internalize_unmap_file(
    898     struct _prop_object_internalize_mapped_file *mf)
    899 {
    900 
    901 	(void) madvise(mf->poimf_xml, mf->poimf_mapsize, MADV_DONTNEED);
    902 	(void) munmap(mf->poimf_xml, mf->poimf_mapsize);
    903 	_PROP_FREE(mf, M_TEMP);
    904 }
    905 #endif /* !_KERNEL && !_STANDALONE */
    906 
    907 /*
    908  * Retain / release serialization --
    909  *
    910  * Eventually we would like to use atomic operations.  But until we have
    911  * an MI API for them that is common to userland and the kernel, we will
    912  * use a lock instead.
    913  *
    914  * We use a single global mutex for all serialization.  In the kernel, because
    915  * we are still under a biglock, this will basically never contend (properties
    916  * cannot be manipulated at interrupt level).  In userland, this will cost
    917  * nothing for single-threaded programs.  For multi-threaded programs, there
    918  * could be contention, but it probably won't cost that much unless the program
    919  * makes heavy use of property lists.
    920  */
    921 _PROP_MUTEX_DECL_STATIC(_prop_refcnt_mutex)
    922 #define	_PROP_REFCNT_LOCK()	_PROP_MUTEX_LOCK(_prop_refcnt_mutex)
    923 #define	_PROP_REFCNT_UNLOCK()	_PROP_MUTEX_UNLOCK(_prop_refcnt_mutex)
    924 
    925 /*
    926  * prop_object_retain --
    927  *	Increment the reference count on an object.
    928  */
    929 void
    930 prop_object_retain(prop_object_t obj)
    931 {
    932 	struct _prop_object *po = obj;
    933 	uint32_t ocnt;
    934 
    935 	_PROP_REFCNT_LOCK();
    936 	ocnt = po->po_refcnt++;
    937 	_PROP_REFCNT_UNLOCK();
    938 
    939 	_PROP_ASSERT(ocnt != 0xffffffffU);
    940 }
    941 
    942 /*
    943  * prop_object_release --
    944  *	Decrement the reference count on an object.
    945  *
    946  *	Free the object if we are releasing the final
    947  *	reference.
    948  */
    949 void
    950 prop_object_release(prop_object_t obj)
    951 {
    952 	struct _prop_object *po = obj;
    953 	uint32_t ocnt;
    954 
    955 	_PROP_REFCNT_LOCK();
    956 	ocnt = po->po_refcnt--;
    957 	_PROP_REFCNT_UNLOCK();
    958 
    959 	_PROP_ASSERT(ocnt != 0);
    960 	if (ocnt == 1)
    961 		(*po->po_type->pot_free)(po);
    962 }
    963 
    964 /*
    965  * prop_object_type --
    966  *	Return the type of an object.
    967  */
    968 prop_type_t
    969 prop_object_type(prop_object_t obj)
    970 {
    971 	struct _prop_object *po = obj;
    972 
    973 	if (obj == NULL)
    974 		return (PROP_TYPE_UNKNOWN);
    975 
    976 	return (po->po_type->pot_type);
    977 }
    978 
    979 /*
    980  * prop_object_equals --
    981  *	Returns TRUE if thw two objects are equivalent.
    982  */
    983 boolean_t
    984 prop_object_equals(prop_object_t obj1, prop_object_t obj2)
    985 {
    986 	struct _prop_object *po1 = obj1;
    987 	struct _prop_object *po2 = obj2;
    988 
    989 	if (po1->po_type != po2->po_type)
    990 		return (FALSE);
    991 
    992 	return ((*po1->po_type->pot_equals)(obj1, obj2));
    993 }
    994 
    995 /*
    996  * prop_object_iterator_next --
    997  *	Return the next item during an iteration.
    998  */
    999 prop_object_t
   1000 prop_object_iterator_next(prop_object_iterator_t pi)
   1001 {
   1002 
   1003 	return ((*pi->pi_next_object)(pi));
   1004 }
   1005 
   1006 /*
   1007  * prop_object_iterator_reset --
   1008  *	Reset the iterator to the first object so as to restart
   1009  *	iteration.
   1010  */
   1011 void
   1012 prop_object_iterator_reset(prop_object_iterator_t pi)
   1013 {
   1014 
   1015 	(*pi->pi_reset)(pi);
   1016 }
   1017 
   1018 /*
   1019  * prop_object_iterator_release --
   1020  *	Release the object iterator.
   1021  */
   1022 void
   1023 prop_object_iterator_release(prop_object_iterator_t pi)
   1024 {
   1025 
   1026 	prop_object_release(pi->pi_obj);
   1027 	_PROP_FREE(pi, M_TEMP);
   1028 }
   1029