xref: /netbsd-src/common/lib/libprop/prop_object.c (revision fad4c9f71477ae11cea2ee75ec82151ac770a534)
1 /*	$NetBSD: prop_object.c,v 1.3 2006/05/18 16:23:55 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 _PROP_MUTEX_DECL(_prop_refcnt_mutex)
680 #define	_PROP_REFCNT_LOCK()	_PROP_MUTEX_LOCK(_prop_refcnt_mutex)
681 #define	_PROP_REFCNT_UNLOCK()	_PROP_MUTEX_UNLOCK(_prop_refcnt_mutex)
682 
683 /*
684  * prop_object_retain --
685  *	Increment the reference count on an object.
686  */
687 void
688 prop_object_retain(prop_object_t obj)
689 {
690 	struct _prop_object *po = obj;
691 	uint32_t ocnt;
692 
693 	_PROP_REFCNT_LOCK();
694 	ocnt = po->po_refcnt++;
695 	_PROP_REFCNT_UNLOCK();
696 
697 	_PROP_ASSERT(ocnt != 0xffffffffU);
698 }
699 
700 /*
701  * prop_object_release --
702  *	Decrement the reference count on an object.
703  *
704  *	Free the object if we are releasing the final
705  *	reference.
706  */
707 void
708 prop_object_release(prop_object_t obj)
709 {
710 	struct _prop_object *po = obj;
711 	uint32_t ocnt;
712 
713 	_PROP_REFCNT_LOCK();
714 	ocnt = po->po_refcnt--;
715 	_PROP_REFCNT_UNLOCK();
716 
717 	_PROP_ASSERT(ocnt != 0);
718 	if (ocnt == 1)
719 		(*po->po_type->pot_free)(po);
720 }
721 
722 /*
723  * prop_object_type --
724  *	Return the type of an object.
725  */
726 prop_type_t
727 prop_object_type(prop_object_t obj)
728 {
729 	struct _prop_object *po = obj;
730 
731 	return (po->po_type->pot_type);
732 }
733 
734 /*
735  * prop_object_equals --
736  *	Returns TRUE if thw two objects are equivalent.
737  */
738 boolean_t
739 prop_object_equals(prop_object_t obj1, prop_object_t obj2)
740 {
741 	struct _prop_object *po1 = obj1;
742 	struct _prop_object *po2 = obj2;
743 
744 	if (po1->po_type != po2->po_type)
745 		return (FALSE);
746 
747 	return ((*po1->po_type->pot_equals)(obj1, obj2));
748 }
749 
750 /*
751  * prop_object_iterator_next --
752  *	Return the next item during an iteration.
753  */
754 prop_object_t
755 prop_object_iterator_next(prop_object_iterator_t pi)
756 {
757 
758 	return ((*pi->pi_next_object)(pi));
759 }
760 
761 /*
762  * prop_object_iterator_reset --
763  *	Reset the iterator to the first object so as to restart
764  *	iteration.
765  */
766 void
767 prop_object_iterator_reset(prop_object_iterator_t pi)
768 {
769 
770 	(*pi->pi_reset)(pi);
771 }
772 
773 /*
774  * prop_object_iterator_release --
775  *	Release the object iterator.
776  */
777 void
778 prop_object_iterator_release(prop_object_iterator_t pi)
779 {
780 
781 	prop_object_release(pi->pi_obj);
782 	_PROP_FREE(pi, M_TEMP);
783 }
784