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 "<") == FALSE)
193 return (FALSE);
194 break;
195 case '>':
196 if (_prop_object_externalize_append_cstring(ctx,
197 ">") == FALSE)
198 return (FALSE);
199 break;
200 case '&':
201 if (_prop_object_externalize_append_cstring(ctx,
202 "&") == 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