Home | History | Annotate | Line # | Download | only in libprop
prop_object.c revision 1.2
      1 /*	$NetBSD: prop_object.c,v 1.2 2006/05/18 03:05:19 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 #ifdef _STANDALONE
     43 void *
     44 _prop_standalone_calloc(size_t size)
     45 {
     46 	void *rv;
     47 
     48 	rv = alloc(size);
     49 	if (rv != NULL)
     50 		memset(rv, 0, size);
     51 
     52 	return (rv);
     53 }
     54 
     55 void *
     56 _prop_standalone_realloc(void *v, size_t size)
     57 {
     58 	void *rv;
     59 
     60 	rv = alloc(size);
     61 	if (rv != NULL) {
     62 		memcpy(rv, v, size);	/* XXX */
     63 		dealloc(v, 0);		/* XXX */
     64 	}
     65 
     66 	return (rv);
     67 }
     68 #endif /* _STANDALONE */
     69 
     70 /*
     71  * _prop_object_init --
     72  *	Initialize an object.  Called when sub-classes create
     73  *	an instance.
     74  */
     75 void
     76 _prop_object_init(struct _prop_object *po, const struct _prop_object_type *pot)
     77 {
     78 
     79 	po->po_type = pot;
     80 	po->po_refcnt = 1;
     81 }
     82 
     83 /*
     84  * _prop_object_fini --
     85  *	Finalize an object.  Called when sub-classes destroy
     86  *	an instance.
     87  */
     88 void
     89 _prop_object_fini(struct _prop_object *po)
     90 {
     91 	/* Nothing to do, currently. */
     92 }
     93 
     94 /*
     95  * _prop_object_externalize_start_tag --
     96  *	Append an XML-style start tag to the externalize buffer.
     97  */
     98 boolean_t
     99 _prop_object_externalize_start_tag(
    100     struct _prop_object_externalize_context *ctx, const char *tag)
    101 {
    102 	unsigned int i;
    103 
    104 	for (i = 0; i < ctx->poec_depth; i++) {
    105 		if (_prop_object_externalize_append_char(ctx, '\t') == FALSE)
    106 			return (FALSE);
    107 	}
    108 	if (_prop_object_externalize_append_char(ctx, '<') == FALSE ||
    109 	    _prop_object_externalize_append_cstring(ctx, tag) == FALSE ||
    110 	    _prop_object_externalize_append_char(ctx, '>') == FALSE)
    111 		return (FALSE);
    112 
    113 	return (TRUE);
    114 }
    115 
    116 /*
    117  * _prop_object_externalize_end_tag --
    118  *	Append an XML-style end tag to the externalize buffer.
    119  */
    120 boolean_t
    121 _prop_object_externalize_end_tag(
    122     struct _prop_object_externalize_context *ctx, const char *tag)
    123 {
    124 
    125 	if (_prop_object_externalize_append_char(ctx, '<') == FALSE ||
    126 	    _prop_object_externalize_append_char(ctx, '/') == FALSE ||
    127 	    _prop_object_externalize_append_cstring(ctx, tag) == FALSE ||
    128 	    _prop_object_externalize_append_char(ctx, '>') == FALSE ||
    129 	    _prop_object_externalize_append_char(ctx, '\n') == FALSE)
    130 		return (FALSE);
    131 
    132 	return (TRUE);
    133 }
    134 
    135 /*
    136  * _prop_object_externalize_empty_tag --
    137  *	Append an XML-style empty tag to the externalize buffer.
    138  */
    139 boolean_t
    140 _prop_object_externalize_empty_tag(
    141     struct _prop_object_externalize_context *ctx, const char *tag)
    142 {
    143 	unsigned int i;
    144 
    145 	for (i = 0; i < ctx->poec_depth; i++) {
    146 		if (_prop_object_externalize_append_char(ctx, '\t') == FALSE)
    147 			return (FALSE);
    148 	}
    149 
    150 	if (_prop_object_externalize_append_char(ctx, '<') == FALSE ||
    151 	    _prop_object_externalize_append_cstring(ctx, tag) == FALSE ||
    152 	    _prop_object_externalize_append_char(ctx, '/') == FALSE ||
    153 	    _prop_object_externalize_append_char(ctx, '>') == FALSE ||
    154 	    _prop_object_externalize_append_char(ctx, '\n') == FALSE)
    155 	    	return (FALSE);
    156 
    157 	return (TRUE);
    158 }
    159 
    160 /*
    161  * _prop_object_externalize_append_cstring --
    162  *	Append a C string to the externalize buffer.
    163  */
    164 boolean_t
    165 _prop_object_externalize_append_cstring(
    166     struct _prop_object_externalize_context *ctx, const char *cp)
    167 {
    168 
    169 	while (*cp != '\0') {
    170 		if (_prop_object_externalize_append_char(ctx,
    171 						(unsigned char) *cp) == FALSE)
    172 			return (FALSE);
    173 		cp++;
    174 	}
    175 
    176 	return (TRUE);
    177 }
    178 
    179 /*
    180  * _prop_object_externalize_append_encoded_cstring --
    181  *	Append an encoded C string to the externalize buffer.
    182  */
    183 boolean_t
    184 _prop_object_externalize_append_encoded_cstring(
    185     struct _prop_object_externalize_context *ctx, const char *cp)
    186 {
    187 
    188 	while (*cp != '\0') {
    189 		switch (*cp) {
    190 		case '<':
    191 			if (_prop_object_externalize_append_cstring(ctx,
    192 					"&lt;") == FALSE)
    193 				return (FALSE);
    194 			break;
    195 		case '>':
    196 			if (_prop_object_externalize_append_cstring(ctx,
    197 					"&gt;") == FALSE)
    198 				return (FALSE);
    199 			break;
    200 		case '&':
    201 			if (_prop_object_externalize_append_cstring(ctx,
    202 					"&amp;") == FALSE)
    203 				return (FALSE);
    204 			break;
    205 		default:
    206 			if (_prop_object_externalize_append_char(ctx,
    207 					(unsigned char) *cp) == FALSE)
    208 				return (FALSE);
    209 			break;
    210 		}
    211 		cp++;
    212 	}
    213 
    214 	return (TRUE);
    215 }
    216 
    217 #define	BUF_EXPAND		256
    218 
    219 /*
    220  * _prop_object_externalize_append_char --
    221  *	Append a single character to the externalize buffer.
    222  */
    223 boolean_t
    224 _prop_object_externalize_append_char(
    225     struct _prop_object_externalize_context *ctx, unsigned char c)
    226 {
    227 
    228 	_PROP_ASSERT(ctx->poec_capacity != 0);
    229 	_PROP_ASSERT(ctx->poec_buf != NULL);
    230 	_PROP_ASSERT(ctx->poec_len <= ctx->poec_capacity);
    231 
    232 	if (ctx->poec_len == ctx->poec_capacity) {
    233 		char *cp = _PROP_REALLOC(ctx->poec_buf,
    234 					 ctx->poec_capacity + BUF_EXPAND,
    235 					 M_TEMP);
    236 		if (cp == NULL)
    237 			return (FALSE);
    238 		ctx->poec_capacity = ctx->poec_capacity + BUF_EXPAND;
    239 		ctx->poec_buf = cp;
    240 	}
    241 
    242 	ctx->poec_buf[ctx->poec_len++] = c;
    243 
    244 	return (TRUE);
    245 }
    246 
    247 /*
    248  * _prop_object_externalize_context_alloc --
    249  *	Allocate an externalize context.
    250  */
    251 struct _prop_object_externalize_context *
    252 _prop_object_externalize_context_alloc(void)
    253 {
    254 	struct _prop_object_externalize_context *ctx;
    255 
    256 	ctx = _PROP_MALLOC(sizeof(*ctx), M_TEMP);
    257 	if (ctx != NULL) {
    258 		ctx->poec_buf = _PROP_MALLOC(BUF_EXPAND, M_TEMP);
    259 		if (ctx->poec_buf == NULL) {
    260 			_PROP_FREE(ctx, M_TEMP);
    261 			return (NULL);
    262 		}
    263 		ctx->poec_len = 0;
    264 		ctx->poec_capacity = BUF_EXPAND;
    265 		ctx->poec_depth = 0;
    266 	}
    267 	return (ctx);
    268 }
    269 
    270 /*
    271  * _prop_object_externalize_context_free --
    272  *	Free an externalize context.
    273  */
    274 void
    275 _prop_object_externalize_context_free(
    276 		struct _prop_object_externalize_context *ctx)
    277 {
    278 
    279 	/* Buffer is always freed by the caller. */
    280 	_PROP_FREE(ctx, M_TEMP);
    281 }
    282 
    283 /*
    284  * _prop_object_internalize_skip_comment --
    285  *	Skip the body and end tag of a comment.
    286  */
    287 static boolean_t
    288 _prop_object_internalize_skip_comment(
    289 				struct _prop_object_internalize_context *ctx)
    290 {
    291 	const char *cp = ctx->poic_cp;
    292 
    293 	while (!_PROP_EOF(*cp)) {
    294 		if (cp[0] == '-' &&
    295 		    cp[1] == '-' &&
    296 		    cp[2] == '>') {
    297 			ctx->poic_cp = cp + 3;
    298 			return (TRUE);
    299 		}
    300 		cp++;
    301 	}
    302 
    303 	return (FALSE);		/* ran out of buffer */
    304 }
    305 
    306 /*
    307  * _prop_object_internalize_find_tag --
    308  *	Find the next tag in an XML stream.  Optionally compare the found
    309  *	tag to an expected tag name.  State of the context is undefined
    310  *	if this routine returns FALSE.  Upon success, the context points
    311  *	to the first octet after the tag.
    312  */
    313 boolean_t
    314 _prop_object_internalize_find_tag(struct _prop_object_internalize_context *ctx,
    315 		      const char *tag, _prop_tag_type_t type)
    316 {
    317 	const char *cp;
    318 	size_t taglen;
    319 
    320 	if (tag != NULL)
    321 		taglen = strlen(tag);
    322 	else
    323 		taglen = 0;
    324 
    325  start_over:
    326 	cp = ctx->poic_cp;
    327 
    328 	/*
    329 	 * Find the start of the tag.
    330 	 */
    331 	while (_PROP_ISSPACE(*cp))
    332 		cp++;
    333 	if (_PROP_EOF(*cp))
    334 		return (FALSE);
    335 
    336 	if (*cp != '<')
    337 		return (FALSE);
    338 
    339 	ctx->poic_tag_start = cp++;
    340 	if (_PROP_EOF(*cp))
    341 		return (FALSE);
    342 
    343 	if (*cp == '!') {
    344 		if (cp[1] != '-' || cp[2] != '-')
    345 			return (FALSE);
    346 		/*
    347 		 * Comment block -- only allowed if we are allowed to
    348 		 * return a start tag.
    349 		 */
    350 		if (type == _PROP_TAG_TYPE_END)
    351 			return (FALSE);
    352 		ctx->poic_cp = cp + 3;
    353 		if (_prop_object_internalize_skip_comment(ctx) == FALSE)
    354 			return (FALSE);
    355 		goto start_over;
    356 	}
    357 
    358 	if (*cp == '/') {
    359 		if (type != _PROP_TAG_TYPE_END &&
    360 		    type != _PROP_TAG_TYPE_EITHER)
    361 			return (FALSE);
    362 		cp++;
    363 		if (_PROP_EOF(*cp))
    364 			return (FALSE);
    365 		ctx->poic_tag_type = _PROP_TAG_TYPE_END;
    366 	} else {
    367 		if (type != _PROP_TAG_TYPE_START &&
    368 		    type != _PROP_TAG_TYPE_EITHER)
    369 			return (FALSE);
    370 		ctx->poic_tag_type = _PROP_TAG_TYPE_START;
    371 	}
    372 
    373 	ctx->poic_tagname = cp;
    374 
    375 	while (!_PROP_ISSPACE(*cp) && *cp != '/' && *cp != '>')
    376 		cp++;
    377 	if (_PROP_EOF(*cp))
    378 		return (FALSE);
    379 
    380 	ctx->poic_tagname_len = cp - ctx->poic_tagname;
    381 
    382 	/* Make sure this is the tag we're looking for. */
    383 	if (tag != NULL &&
    384 	    (taglen != ctx->poic_tagname_len ||
    385 	     memcmp(tag, ctx->poic_tagname, taglen) != 0))
    386 		return (FALSE);
    387 
    388 	/* Check for empty tag. */
    389 	if (*cp == '/') {
    390 		if (ctx->poic_tag_type != _PROP_TAG_TYPE_START)
    391 			return(FALSE);		/* only valid on start tags */
    392 		ctx->poic_is_empty_element = TRUE;
    393 		cp++;
    394 		if (_PROP_EOF(*cp) || *cp != '>')
    395 			return (FALSE);
    396 	} else
    397 		ctx->poic_is_empty_element = FALSE;
    398 
    399 	/* Easy case of no arguments. */
    400 	if (*cp == '>') {
    401 		ctx->poic_tagattr = NULL;
    402 		ctx->poic_tagattr_len = 0;
    403 		ctx->poic_tagattrval = NULL;
    404 		ctx->poic_tagattrval_len = 0;
    405 		ctx->poic_cp = cp + 1;
    406 		return (TRUE);
    407 	}
    408 
    409 	_PROP_ASSERT(!_PROP_EOF(*cp));
    410 	cp++;
    411 	if (_PROP_EOF(*cp))
    412 		return (FALSE);
    413 
    414 	while (_PROP_ISSPACE(*cp))
    415 		cp++;
    416 	if (_PROP_EOF(*cp))
    417 		return (FALSE);
    418 
    419 	ctx->poic_tagattr = cp;
    420 
    421 	while (!_PROP_ISSPACE(*cp) && *cp != '=')
    422 		cp++;
    423 	if (_PROP_EOF(*cp))
    424 		return (FALSE);
    425 
    426 	ctx->poic_tagattr_len = cp - ctx->poic_tagattr;
    427 
    428 	cp++;
    429 	if (*cp != '\"')
    430 		return (FALSE);
    431 	cp++;
    432 	if (_PROP_EOF(*cp))
    433 		return (FALSE);
    434 
    435 	ctx->poic_tagattrval = cp;
    436 	while (*cp != '\"')
    437 		cp++;
    438 	if (_PROP_EOF(*cp))
    439 		return (FALSE);
    440 	ctx->poic_tagattrval_len = cp - ctx->poic_tagattrval;
    441 
    442 	cp++;
    443 	if (*cp != '>')
    444 		return (FALSE);
    445 
    446 	ctx->poic_cp = cp + 1;
    447 	return (TRUE);
    448 }
    449 
    450 /*
    451  * _prop_object_internalize_decode_string --
    452  *	Decode an encoded string.
    453  */
    454 boolean_t
    455 _prop_object_internalize_decode_string(
    456 				struct _prop_object_internalize_context *ctx,
    457 				char *target, size_t targsize, size_t *sizep,
    458 				const char **cpp)
    459 {
    460 	const char *src;
    461 	size_t tarindex;
    462 	char c;
    463 
    464 	tarindex = 0;
    465 	src = ctx->poic_cp;
    466 
    467 	for (;;) {
    468 		if (_PROP_EOF(*src))
    469 			return (FALSE);
    470 		if (*src == '<') {
    471 			break;
    472 		}
    473 
    474 		if ((c = *src) == '&') {
    475 			if (src[1] == 'a' &&
    476 			    src[2] == 'm' &&
    477 			    src[3] == 'p' &&
    478 			    src[4] == ';') {
    479 			    	c = '&';
    480 				src += 5;
    481 			} else if (src[1] == 'l' &&
    482 				   src[2] == 't' &&
    483 				   src[3] == ';') {
    484 				c = '<';
    485 				src += 4;
    486 			} else if (src[1] == 'g' &&
    487 				   src[2] == 't' &&
    488 				   src[3] == ';') {
    489 				c = '>';
    490 				src += 4;
    491 			} else if (src[1] == 'a' &&
    492 				   src[2] == 'p' &&
    493 				   src[3] == 'o' &&
    494 				   src[4] == 's' &&
    495 				   src[5] == ';') {
    496 				c = '\'';
    497 				src += 6;
    498 			} else if (src[1] == 'q' &&
    499 				   src[2] == 'u' &&
    500 				   src[3] == 'o' &&
    501 				   src[4] == 't' &&
    502 				   src[5] == ';') {
    503 				c = '\"';
    504 				src += 6;
    505 			} else
    506 				return (FALSE);
    507 		} else
    508 			src++;
    509 		if (target) {
    510 			if (tarindex >= targsize)
    511 				return (FALSE);
    512 			target[tarindex] = c;
    513 		}
    514 		tarindex++;
    515 	}
    516 
    517 	_PROP_ASSERT(*src == '<');
    518 	if (sizep != NULL)
    519 		*sizep = tarindex;
    520 	if (cpp != NULL)
    521 		*cpp = src;
    522 
    523 	return (TRUE);
    524 }
    525 
    526 /*
    527  * _prop_object_internalize_match --
    528  *	Returns true if the two character streams match.
    529  */
    530 boolean_t
    531 _prop_object_internalize_match(const char *str1, size_t len1,
    532 			       const char *str2, size_t len2)
    533 {
    534 
    535 	return (len1 == len2 && memcmp(str1, str2, len1) == 0);
    536 }
    537 
    538 #define	INTERNALIZER(t, f)			\
    539 {	t,	sizeof(t) - 1,		f	}
    540 
    541 static const struct _prop_object_internalizer {
    542 	const char	*poi_tag;
    543 	size_t		poi_taglen;
    544 	prop_object_t	(*poi_intern)(
    545 				struct _prop_object_internalize_context *);
    546 } _prop_object_internalizer_table[] = {
    547 	INTERNALIZER("array", _prop_array_internalize),
    548 
    549 	INTERNALIZER("true", _prop_bool_internalize),
    550 	INTERNALIZER("false", _prop_bool_internalize),
    551 
    552 	INTERNALIZER("data", _prop_data_internalize),
    553 
    554 	INTERNALIZER("dict", _prop_dictionary_internalize),
    555 
    556 	INTERNALIZER("integer", _prop_number_internalize),
    557 
    558 	INTERNALIZER("string", _prop_string_internalize),
    559 
    560 	{ 0 }
    561 };
    562 
    563 #undef INTERNALIZER
    564 
    565 /*
    566  * _prop_object_internalize_by_tag --
    567  *	Determine the object type from the tag in the context and
    568  *	internalize it.
    569  */
    570 prop_object_t
    571 _prop_object_internalize_by_tag(struct _prop_object_internalize_context *ctx)
    572 {
    573 	const struct _prop_object_internalizer *poi;
    574 
    575 	for (poi = _prop_object_internalizer_table;
    576 	     poi->poi_tag != NULL; poi++) {
    577 		if (_prop_object_internalize_match(ctx->poic_tagname,
    578 						   ctx->poic_tagname_len,
    579 						   poi->poi_tag,
    580 						   poi->poi_taglen))
    581 			return ((*poi->poi_intern)(ctx));
    582 	}
    583 
    584 	return (NULL);
    585 }
    586 
    587 /*
    588  * _prop_object_internalize_context_alloc --
    589  *	Allocate an internalize context.
    590  */
    591 struct _prop_object_internalize_context *
    592 _prop_object_internalize_context_alloc(const char *xml)
    593 {
    594 	struct _prop_object_internalize_context *ctx;
    595 
    596 	ctx = _PROP_MALLOC(sizeof(struct _prop_object_internalize_context),
    597 			   M_TEMP);
    598 	if (ctx == NULL)
    599 		return (NULL);
    600 
    601 	ctx->poic_xml = ctx->poic_cp = xml;
    602 
    603 	/*
    604 	 * Skip any whitespace and XML preamble stuff that we don't
    605 	 * know about / care about.
    606 	 */
    607 	for (;;) {
    608 		while (_PROP_ISSPACE(*xml))
    609 			xml++;
    610 		if (_PROP_EOF(*xml) || *xml != '<')
    611 			goto bad;
    612 
    613 #define	MATCH(str)	(memcmp(&xml[1], str, sizeof(str) - 1) == 0)
    614 
    615 		/*
    616 		 * Skip over the XML preamble that Apple XML property
    617 		 * lists usually include at the top of the file.
    618 		 */
    619 		if (MATCH("?xml ") ||
    620 		    MATCH("!DOCTYPE plist")) {
    621 			while (*xml != '>' && !_PROP_EOF(*xml))
    622 				xml++;
    623 			if (_PROP_EOF(*xml))
    624 				goto bad;
    625 			xml++;	/* advance past the '>' */
    626 			continue;
    627 		}
    628 
    629 		if (MATCH("<!--")) {
    630 			ctx->poic_cp = xml + 4;
    631 			if (_prop_object_internalize_skip_comment(ctx) == FALSE)
    632 				goto bad;
    633 			xml = ctx->poic_cp;
    634 			continue;
    635 		}
    636 
    637 #undef MATCH
    638 
    639 		/*
    640 		 * We don't think we should skip it, so let's hope we can
    641 		 * parse it.
    642 		 */
    643 		break;
    644 	}
    645 
    646 	ctx->poic_cp = xml;
    647 	return (ctx);
    648  bad:
    649 	_PROP_FREE(ctx, M_TEMP);
    650 	return (NULL);
    651 }
    652 
    653 /*
    654  * _prop_object_internalize_context_free --
    655  *	Free an internalize context.
    656  */
    657 void
    658 _prop_object_internalize_context_free(
    659 		struct _prop_object_internalize_context *ctx)
    660 {
    661 
    662 	_PROP_FREE(ctx, M_TEMP);
    663 }
    664 
    665 /*
    666  * Retain / release serialization --
    667  *
    668  * Eventually we would like to use atomic operations.  But until we have
    669  * an MI API for them that is common to userland and the kernel, we will
    670  * use a lock instead.
    671  *
    672  * We use a single global mutex for all serialization.  In the kernel, because
    673  * we are still under a biglock, this will basically never contend (properties
    674  * cannot be manipulated at interrupt level).  In userland, this will cost
    675  * nothing for single-threaded programs.  For multi-threaded programs, there
    676  * could be contention, but it probably won't cost that much unless the program
    677  * makes heavy use of property lists.
    678  */
    679 #if defined(_KERNEL)
    680 #include <sys/lock.h>
    681 static struct simplelock _prop_refcnt_mutex = SIMPLELOCK_INITIALIZER;
    682 
    683 #define	_PROP_REFCNT_LOCK()	simple_lock(&_prop_refcnt_mutex)
    684 #define	_PROP_REFCNT_UNLOCK()	simple_unlock(&_prop_refcnt_mutex)
    685 #elif defined(_STANDALONE)
    686 /*
    687  * No locking necessary for standalone environments.
    688  */
    689 #define	_PROP_REFCNT_LOCK()	/* nothing */
    690 #define	_PROP_REFCNT_UNLOCK()	/* nothing */
    691 #elif defined(__NetBSD__) && defined(_LIBPROP)
    692 /*
    693  * Use the same mechanism as libc; we get pthread mutexes for threaded
    694  * programs and do-nothing stubs for non-threaded programs.
    695  */
    696 #include "reentrant.h"
    697 static mutex_t _prop_refcnt_mutex = MUTEX_INITIALIZER;
    698 
    699 #define	_PROP_REFCNT_LOCK()	mutex_lock(&_prop_refcnt_mutex)
    700 #define	_PROP_REFCNT_UNLOCK()	mutex_unlock(&_prop_refcnt_mutex)
    701 #elif defined(HAVE_NBTOOL_CONFIG_H)
    702 /* None of NetBSD's build tools are multi-threaded. */
    703 #define	_PROP_REFCNT_LOCK()	/* nothing */
    704 #define	_PROP_REFCNT_UNLOCK()	/* nothing */
    705 #else
    706 /* Use pthread mutexes everywhere else. */
    707 #include <pthread.h>
    708 static pthread_mutex_t _prop_refcnt_mutex = PTHREAD_MUTEX_INITIALIZER;
    709 
    710 #define	_PROP_REFCNT_LOCK()	pthread_mutex_lock(&_prop_refcnt_mutex)
    711 #define	_PROP_REFCNT_UNLOCK()	pthread_mutex_unlock(&_prop_refcnt_mutex)
    712 #endif
    713 
    714 /*
    715  * prop_object_retain --
    716  *	Increment the reference count on an object.
    717  */
    718 void
    719 prop_object_retain(prop_object_t obj)
    720 {
    721 	struct _prop_object *po = obj;
    722 	uint32_t ocnt;
    723 
    724 	_PROP_REFCNT_LOCK();
    725 	ocnt = po->po_refcnt++;
    726 	_PROP_REFCNT_UNLOCK();
    727 
    728 	_PROP_ASSERT(ocnt != 0xffffffffU);
    729 }
    730 
    731 /*
    732  * prop_object_release --
    733  *	Decrement the reference count on an object.
    734  *
    735  *	Free the object if we are releasing the final
    736  *	reference.
    737  */
    738 void
    739 prop_object_release(prop_object_t obj)
    740 {
    741 	struct _prop_object *po = obj;
    742 	uint32_t ocnt;
    743 
    744 	_PROP_REFCNT_LOCK();
    745 	ocnt = po->po_refcnt--;
    746 	_PROP_REFCNT_UNLOCK();
    747 
    748 	_PROP_ASSERT(ocnt != 0);
    749 	if (ocnt == 1)
    750 		(*po->po_type->pot_free)(po);
    751 }
    752 
    753 /*
    754  * prop_object_type --
    755  *	Return the type of an object.
    756  */
    757 prop_type_t
    758 prop_object_type(prop_object_t obj)
    759 {
    760 	struct _prop_object *po = obj;
    761 
    762 	return (po->po_type->pot_type);
    763 }
    764 
    765 /*
    766  * prop_object_equals --
    767  *	Returns TRUE if thw two objects are equivalent.
    768  */
    769 boolean_t
    770 prop_object_equals(prop_object_t obj1, prop_object_t obj2)
    771 {
    772 	struct _prop_object *po1 = obj1;
    773 	struct _prop_object *po2 = obj2;
    774 
    775 	if (po1->po_type != po2->po_type)
    776 		return (FALSE);
    777 
    778 	return ((*po1->po_type->pot_equals)(obj1, obj2));
    779 }
    780 
    781 /*
    782  * prop_object_iterator_next --
    783  *	Return the next item during an iteration.
    784  */
    785 prop_object_t
    786 prop_object_iterator_next(prop_object_iterator_t pi)
    787 {
    788 
    789 	return ((*pi->pi_next_object)(pi));
    790 }
    791 
    792 /*
    793  * prop_object_iterator_reset --
    794  *	Reset the iterator to the first object so as to restart
    795  *	iteration.
    796  */
    797 void
    798 prop_object_iterator_reset(prop_object_iterator_t pi)
    799 {
    800 
    801 	(*pi->pi_reset)(pi);
    802 }
    803 
    804 /*
    805  * prop_object_iterator_release --
    806  *	Release the object iterator.
    807  */
    808 void
    809 prop_object_iterator_release(prop_object_iterator_t pi)
    810 {
    811 
    812 	prop_object_release(pi->pi_obj);
    813 	_PROP_FREE(pi, M_TEMP);
    814 }
    815