]> Zhao Yanbai Git Server - minix.git/commitdiff
import netbsd libprop
authorBen Gras <ben@minix3.org>
Thu, 22 Dec 2011 16:42:19 +0000 (17:42 +0100)
committerBen Gras <ben@minix3.org>
Thu, 22 Dec 2011 17:03:13 +0000 (18:03 +0100)
48 files changed:
common/lib/libprop/Makefile.inc [new file with mode: 0644]
common/lib/libprop/prop_array.3 [new file with mode: 0644]
common/lib/libprop/prop_array.c [new file with mode: 0644]
common/lib/libprop/prop_array_util.3 [new file with mode: 0644]
common/lib/libprop/prop_array_util.c [new file with mode: 0644]
common/lib/libprop/prop_bool.3 [new file with mode: 0644]
common/lib/libprop/prop_bool.c [new file with mode: 0644]
common/lib/libprop/prop_copyin_ioctl.9 [new file with mode: 0644]
common/lib/libprop/prop_data.3 [new file with mode: 0644]
common/lib/libprop/prop_data.c [new file with mode: 0644]
common/lib/libprop/prop_dictionary.3 [new file with mode: 0644]
common/lib/libprop/prop_dictionary.c [new file with mode: 0644]
common/lib/libprop/prop_dictionary_util.3 [new file with mode: 0644]
common/lib/libprop/prop_dictionary_util.c [new file with mode: 0644]
common/lib/libprop/prop_ingest.3 [new file with mode: 0644]
common/lib/libprop/prop_ingest.c [new file with mode: 0644]
common/lib/libprop/prop_kern.c [new file with mode: 0644]
common/lib/libprop/prop_number.3 [new file with mode: 0644]
common/lib/libprop/prop_number.c [new file with mode: 0644]
common/lib/libprop/prop_object.3 [new file with mode: 0644]
common/lib/libprop/prop_object.c [new file with mode: 0644]
common/lib/libprop/prop_object_impl.h [new file with mode: 0644]
common/lib/libprop/prop_rb.c [new file with mode: 0644]
common/lib/libprop/prop_rb_impl.h [new file with mode: 0644]
common/lib/libprop/prop_send_ioctl.3 [new file with mode: 0644]
common/lib/libprop/prop_send_syscall.3 [new file with mode: 0644]
common/lib/libprop/prop_stack.c [new file with mode: 0644]
common/lib/libprop/prop_stack.h [new file with mode: 0644]
common/lib/libprop/prop_string.3 [new file with mode: 0644]
common/lib/libprop/prop_string.c [new file with mode: 0644]
common/lib/libprop/proplib.3 [new file with mode: 0644]
etc/mtree/minix.tree
lib/Makefile
lib/libprop/Makefile [new file with mode: 0644]
lib/libprop/shlib_version [new file with mode: 0644]
nbsd_include/Makefile
nbsd_include/prop/Makefile [new file with mode: 0644]
nbsd_include/prop/plistref.h [new file with mode: 0644]
nbsd_include/prop/prop_array.h [new file with mode: 0644]
nbsd_include/prop/prop_bool.h [new file with mode: 0644]
nbsd_include/prop/prop_data.h [new file with mode: 0644]
nbsd_include/prop/prop_dictionary.h [new file with mode: 0644]
nbsd_include/prop/prop_ingest.h [new file with mode: 0644]
nbsd_include/prop/prop_number.h [new file with mode: 0644]
nbsd_include/prop/prop_object.h [new file with mode: 0644]
nbsd_include/prop/prop_string.h [new file with mode: 0644]
nbsd_include/prop/proplib.h [new file with mode: 0644]
tools/nbsd_ports

diff --git a/common/lib/libprop/Makefile.inc b/common/lib/libprop/Makefile.inc
new file mode 100644 (file)
index 0000000..6568593
--- /dev/null
@@ -0,0 +1,9 @@
+#      $NetBSD: Makefile.inc,v 1.8 2008/06/30 20:14:09 matt Exp $
+
+.PATH: ${.PARSEDIR}
+
+SRCS+= prop_array.c prop_array_util.c prop_bool.c prop_data.c \
+        prop_dictionary.c prop_dictionary_util.c prop_ingest.c \
+        prop_kern.c prop_number.c prop_object.c prop_stack.c prop_string.c
+
+#SRCS+=        prop_rb.c
diff --git a/common/lib/libprop/prop_array.3 b/common/lib/libprop/prop_array.3
new file mode 100644 (file)
index 0000000..510d620
--- /dev/null
@@ -0,0 +1,308 @@
+.\"    $NetBSD: prop_array.3,v 1.13 2011/09/30 22:08:18 jym Exp $
+.\"
+.\" Copyright (c) 2006, 2009 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by Jason R. Thorpe.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd October 10, 2009
+.Dt PROP_ARRAY 3
+.Os
+.Sh NAME
+.Nm prop_array ,
+.Nm prop_array_create ,
+.Nm prop_array_create_with_capacity ,
+.Nm prop_array_copy ,
+.Nm prop_array_copy_mutable ,
+.Nm prop_array_capacity ,
+.Nm prop_array_count ,
+.Nm prop_array_ensure_capacity ,
+.Nm prop_array_iterator ,
+.Nm prop_array_make_immutable ,
+.Nm prop_array_mutable ,
+.Nm prop_array_get ,
+.Nm prop_array_set ,
+.Nm prop_array_add ,
+.Nm prop_array_remove ,
+.Nm prop_array_externalize ,
+.Nm prop_array_internalize ,
+.Nm prop_array_externalize_to_file ,
+.Nm prop_array_internalize_from_file ,
+.Nm prop_array_externalize_to_pref ,
+.Nm prop_array_internalize_from_pref ,
+.Nm prop_array_equals
+.Nd array property collection object
+.Sh LIBRARY
+.Lb libprop
+.Sh SYNOPSIS
+.In prop/proplib.h
+.\"
+.Ft prop_array_t
+.Fn prop_array_create "void"
+.Ft prop_array_t
+.Fn prop_array_create_with_capacity "unsigned int capacity"
+.\"
+.Ft prop_array_t
+.Fn prop_array_copy "prop_array_t array"
+.Ft prop_array_t
+.Fn prop_array_copy_mutable "prop_array_t array"
+.\"
+.Ft unsigned int
+.Fn prop_array_capacity "prop_array_t array"
+.Ft unsigned int
+.Fn prop_array_count "prop_array_t array"
+.Ft bool
+.Fn prop_array_ensure_capacity "prop_array_t array" "unsigned int capacity"
+.\"
+.Ft prop_object_iterator_t
+.Fn prop_array_iterator "prop_array_t array"
+.\"
+.Ft void
+.Fn prop_array_make_immutable "prop_array_t array"
+.Ft bool
+.Fn prop_array_mutable "prop_array_t array"
+.\"
+.Ft prop_object_t
+.Fn prop_array_get "prop_array_t array" "unsigned int index"
+.Ft bool
+.Fn prop_array_set "prop_array_t array" "unsigned int index" "prop_object_t obj"
+.Ft bool
+.Fn prop_array_add "prop_array_t array" "prop_object_t obj"
+.Ft void
+.Fn prop_array_remove "prop_array_t array" "unsigned int index"
+.\"
+.Ft char *
+.Fn prop_array_externalize "prop_array_t array"
+.Ft prop_array_t
+.Fn prop_array_internalize "const char *xml"
+.\"
+.Ft bool
+.Fn prop_array_externalize_to_file "prop_array_t array" "const char *path"
+.Ft prop_array_t
+.Fn prop_array_internalize_from_file "const char *path"
+.\"
+.Ft bool
+.Fn prop_array_externalize_to_pref "prop_array_t array" "struct plistref *pref"
+.Ft bool
+.Fn prop_array_internalize_from_pref "const struct plistref *pref" \
+    "prop_array_t *arrayp"
+.\"
+.Ft bool
+.Fn prop_array_equals "prop_array_t array1" "prop_array_t array2"
+.Sh DESCRIPTION
+The
+.Nm prop_array
+family of functions operate on the array property collection object type.
+An array is an ordered set; an iterated array will return objects in the
+same order with which they were stored.
+.Bl -tag -width "xxxxx"
+.It Fn prop_array_create "void"
+Create an empty array.
+The array initially has no capacity.
+Returns
+.Dv NULL
+on failure.
+.It Fn prop_array_create_with_capacity "unsigned int capacity"
+Create an array with the capacity to store
+.Fa capacity
+objects.
+Returns
+.Dv NULL
+on failure.
+.It Fn prop_array_copy "prop_array_t array"
+Copy an array.
+The new array has an initial capacity equal to the number of objects stored
+in the array being copied.
+The new array contains references to the original array's objects, not
+copies of those objects
+.Pq i.e. a shallow copy is made .
+If the original array is immutable, the resulting array is also immutable.
+Returns
+.Dv NULL
+on failure.
+.It Fn prop_array_copy_mutable "prop_array_t array"
+Like
+.Fn prop_array_copy ,
+except the resulting array is always mutable.
+.It Fn prop_array_capacity "prop_array_t array"
+Returns the total capacity of the array, including objects already stored
+in the array.
+If the supplied object isn't an array, zero is returned.
+.It Fn prop_array_count "prop_array_t array"
+Returns the number of objects stored in the array.
+If the supplied object isn't an array, zero is returned.
+.It Fn prop_array_ensure_capacity "prop_array_t array" "unsigned int capacity"
+Ensure that the array has a total capacity of
+.Fa capacity ,
+including objects already stored in the array.
+Returns
+.Dv true
+if the capacity of the array is greater or equal to
+.Fa capacity
+or if expansion of the array's capacity was successful
+and
+.Dv false
+otherwise.
+.It Fn prop_array_iterator "prop_array_t array"
+Create an iterator for the array.
+The array is retained by the iterator.
+An array iterator returns the object references stored in the array.
+Storing to or removing from the array invalidates any active iterators for
+the array.
+Returns
+.Dv NULL
+on failure.
+.It Fn prop_array_make_immutable "prop_array_t array"
+Make
+.Fa array
+immutable.
+.It Fn prop_array_mutable "prop_array_t array"
+Returns
+.Dv true
+if the array is mutable.
+.It Fn prop_array_get "prop_array_t array" "unsigned int index"
+Return the object stored at the array index
+.Fa index .
+Returns
+.Dv NULL
+on failure.
+.It Fn prop_array_set "prop_array_t array" "unsigned int index" \
+       "prop_object_t obj"
+Store a reference to the object
+.Fa obj
+at the array index
+.Fa index .
+This function is not allowed to create holes in the array;
+the caller must either be setting the object just beyond the existing
+count or replacing an already existing object reference.
+The object will be retained by the array.
+If an existing object reference is being replaced, that object will be
+released.
+Returns
+.Dv true
+if storing the object was successful and
+.Dv false
+otherwise.
+.It Fn prop_array_add "prop_array_t array" "prop_object_t obj"
+Add a reference to the object
+.Fa obj
+to the array, appending to the end and growing the array's capacity if
+necessary.
+The object will be retained by the array.
+Returns
+.Dv true
+if storing the object was successful and
+.Dv false
+otherwise.
+.Pp
+During expansion, array's capacity is augmented by the
+.Dv EXPAND_STEP
+constant, as defined in
+.Pa libprop/prop_array.c
+file, e.g.
+.Pp
+.Dl #define    EXPAND_STEP             16
+.It Fn prop_array_remove "prop_array_t array" "unsigned int index"
+Remove the reference to the object stored at array index
+.Fa index .
+The object will be released and the array compacted following
+the removal.
+.It Fn prop_array_externalize "prop_array_t array"
+Externalizes an array, returning a NUL-terminated buffer containing
+the XML representation of the array.
+The caller is responsible for freeing the returned buffer.
+If converting to the external representation fails for any reason,
+.Dv NULL
+is returned.
+.Pp
+In user space, the buffer is allocated using
+.Xr malloc 3 .
+In the kernel, the buffer is allocated using
+.Xr malloc 9
+using the malloc type
+.Dv M_TEMP .
+.It Fn prop_array_internalize "const char *xml"
+Parse the XML representation of a property list in the NUL-terminated
+buffer
+.Fa xml
+and return the corresponding array.
+Returns
+.Dv NULL
+if parsing fails for any reason.
+.It Fn prop_array_externalize_to_file "prop_array_t array" "const char *path"
+Externalizes an array and writes it to the file specified by
+.Fa path .
+The file is saved with the mode
+.Dv 0666
+as modified by the process's file creation mask
+.Pq see Xr umask 2
+and is written atomically.
+Returns
+.Dv false
+if externalizing or writing the array fails for any reason.
+.It Fn prop_array_internalize_from_file "const char *path"
+Reads the XML property list contained in the file specified by
+.Fa path ,
+internalizes it, and returns the corresponding array.
+Returns
+.Dv NULL
+on failure.
+.It Fn prop_array_externalize_to_pref "prop_array_t array" \
+    "struct plistref *pref"
+Externalizes an array and packs it into the plistref specified by
+.Fa pref .
+Returns
+.Dv false
+if externalizing the array fails for any reason.
+.It Fn prop_array_internalize_from_pref "const struct plistref *pref" \
+    "prop_array_t *arrayp"
+Reads the plistref specified by
+.Fa pref ,
+internalizes it, and returns the corresponding array.
+Returns
+.Dv false
+if internalizing or writing the array fails for any reason.
+.It Fn prop_array_equals "prop_array_t array1" "prop_array_t array2"
+Returns
+.Dv true
+if the two arrays are equivalent.
+If at least one of the supplied objects isn't an array,
+.Dv false
+is returned.
+Note: Objects contained in the array are compared by value, not by reference.
+.El
+.Sh SEE ALSO
+.Xr prop_bool 3 ,
+.Xr prop_data 3 ,
+.Xr prop_dictionary 3 ,
+.Xr prop_number 3 ,
+.Xr prop_object 3 ,
+.Xr prop_string 3 ,
+.Xr proplib 3
+.Sh HISTORY
+The
+.Nm proplib
+property container object library first appeared in
+.Nx 4.0 .
diff --git a/common/lib/libprop/prop_array.c b/common/lib/libprop/prop_array.c
new file mode 100644 (file)
index 0000000..ecb6a86
--- /dev/null
@@ -0,0 +1,911 @@
+/*     $NetBSD: prop_array.c,v 1.20 2008/08/11 05:54:21 christos Exp $ */
+
+/*-
+ * Copyright (c) 2006, 2007 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jason R. Thorpe.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <prop/prop_array.h>
+#include "prop_object_impl.h"
+
+#if !defined(_KERNEL) && !defined(_STANDALONE)
+#include <errno.h>
+#endif
+
+struct _prop_array {
+       struct _prop_object     pa_obj;
+       _PROP_RWLOCK_DECL(pa_rwlock)
+       prop_object_t *         pa_array;
+       unsigned int            pa_capacity;
+       unsigned int            pa_count;
+       int                     pa_flags;
+
+       uint32_t                pa_version;
+};
+
+#define PA_F_IMMUTABLE         0x01    /* array is immutable */
+
+_PROP_POOL_INIT(_prop_array_pool, sizeof(struct _prop_array), "proparay")
+_PROP_MALLOC_DEFINE(M_PROP_ARRAY, "prop array",
+                   "property array container object")
+
+static _prop_object_free_rv_t
+               _prop_array_free(prop_stack_t, prop_object_t *);
+static void    _prop_array_emergency_free(prop_object_t);
+static bool    _prop_array_externalize(
+                               struct _prop_object_externalize_context *,
+                               void *);
+static _prop_object_equals_rv_t
+               _prop_array_equals(prop_object_t, prop_object_t,
+                                  void **, void **,
+                                  prop_object_t *, prop_object_t *);
+static void    _prop_array_equals_finish(prop_object_t, prop_object_t);
+static prop_object_iterator_t
+               _prop_array_iterator_locked(prop_array_t);
+static prop_object_t
+               _prop_array_iterator_next_object_locked(void *);
+static void    _prop_array_iterator_reset_locked(void *);
+
+static const struct _prop_object_type _prop_object_type_array = {
+       .pot_type               =       PROP_TYPE_ARRAY,
+       .pot_free               =       _prop_array_free,
+       .pot_emergency_free     =       _prop_array_emergency_free,
+       .pot_extern             =       _prop_array_externalize,
+       .pot_equals             =       _prop_array_equals,
+       .pot_equals_finish      =       _prop_array_equals_finish,
+};
+
+#define prop_object_is_array(x)                \
+       ((x) != NULL && (x)->pa_obj.po_type == &_prop_object_type_array)
+
+#define prop_array_is_immutable(x) (((x)->pa_flags & PA_F_IMMUTABLE) != 0)
+
+struct _prop_array_iterator {
+       struct _prop_object_iterator pai_base;
+       unsigned int            pai_index;
+};
+
+#define EXPAND_STEP            16
+
+static _prop_object_free_rv_t
+_prop_array_free(prop_stack_t stack, prop_object_t *obj)
+{
+       prop_array_t pa = *obj;
+       prop_object_t po;
+
+       _PROP_ASSERT(pa->pa_count <= pa->pa_capacity);
+       _PROP_ASSERT((pa->pa_capacity == 0 && pa->pa_array == NULL) ||
+                    (pa->pa_capacity != 0 && pa->pa_array != NULL));
+
+       /* The easy case is an empty array, just free and return. */
+       if (pa->pa_count == 0) {
+               if (pa->pa_array != NULL)
+                       _PROP_FREE(pa->pa_array, M_PROP_ARRAY);
+
+               _PROP_RWLOCK_DESTROY(pa->pa_rwlock);
+
+               _PROP_POOL_PUT(_prop_array_pool, pa);
+
+               return (_PROP_OBJECT_FREE_DONE);
+       }
+
+       po = pa->pa_array[pa->pa_count - 1];
+       _PROP_ASSERT(po != NULL);
+
+       if (stack == NULL) {
+               /*
+                * If we are in emergency release mode,
+                * just let caller recurse down.
+                */
+               *obj = po;
+               return (_PROP_OBJECT_FREE_FAILED);
+       }
+
+       /* Otherwise, try to push the current object on the stack. */
+       if (!_prop_stack_push(stack, pa, NULL, NULL, NULL)) {
+               /* Push failed, entering emergency release mode. */
+               return (_PROP_OBJECT_FREE_FAILED);
+       }
+       /* Object pushed on stack, caller will release it. */
+       --pa->pa_count;
+       *obj = po;
+       return (_PROP_OBJECT_FREE_RECURSE);
+}
+
+static void
+_prop_array_emergency_free(prop_object_t obj)
+{
+       prop_array_t pa = obj;
+
+       _PROP_ASSERT(pa->pa_count != 0);
+       --pa->pa_count;
+}
+
+static bool
+_prop_array_externalize(struct _prop_object_externalize_context *ctx,
+                       void *v)
+{
+       prop_array_t pa = v;
+       struct _prop_object *po;
+       prop_object_iterator_t pi;
+       unsigned int i;
+       bool rv = false;
+
+       _PROP_RWLOCK_RDLOCK(pa->pa_rwlock);
+
+       if (pa->pa_count == 0) {
+               _PROP_RWLOCK_UNLOCK(pa->pa_rwlock);
+               return (_prop_object_externalize_empty_tag(ctx, "array"));
+       }
+
+       /* XXXJRT Hint "count" for the internalize step? */
+       if (_prop_object_externalize_start_tag(ctx, "array") == false ||
+           _prop_object_externalize_append_char(ctx, '\n') == false)
+               goto out;
+
+       pi = _prop_array_iterator_locked(pa);
+       if (pi == NULL)
+               goto out;
+
+       ctx->poec_depth++;
+       _PROP_ASSERT(ctx->poec_depth != 0);
+
+       while ((po = _prop_array_iterator_next_object_locked(pi)) != NULL) {
+               if ((*po->po_type->pot_extern)(ctx, po) == false) {
+                       prop_object_iterator_release(pi);
+                       goto out;
+               }
+       }
+
+       prop_object_iterator_release(pi);
+
+       ctx->poec_depth--;
+       for (i = 0; i < ctx->poec_depth; i++) {
+               if (_prop_object_externalize_append_char(ctx, '\t') == false)
+                       goto out;
+       }
+       if (_prop_object_externalize_end_tag(ctx, "array") == false)
+               goto out;
+
+       rv = true;
+
+ out:
+       _PROP_RWLOCK_UNLOCK(pa->pa_rwlock);
+       return (rv);
+}
+
+/* ARGSUSED */
+static _prop_object_equals_rv_t
+_prop_array_equals(prop_object_t v1, prop_object_t v2,
+    void **stored_pointer1, void **stored_pointer2,
+    prop_object_t *next_obj1, prop_object_t *next_obj2)
+{
+       prop_array_t array1 = v1;
+       prop_array_t array2 = v2;
+       uintptr_t idx;
+       _prop_object_equals_rv_t rv = _PROP_OBJECT_EQUALS_FALSE;
+
+       if (array1 == array2)
+               return (_PROP_OBJECT_EQUALS_TRUE);
+
+       _PROP_ASSERT(*stored_pointer1 == *stored_pointer2);
+       idx = (uintptr_t)*stored_pointer1;
+
+       /* For the first iteration, lock the objects. */
+       if (idx == 0) {
+               if ((uintptr_t)array1 < (uintptr_t)array2) {
+                       _PROP_RWLOCK_RDLOCK(array1->pa_rwlock);
+                       _PROP_RWLOCK_RDLOCK(array2->pa_rwlock);
+               } else {
+                       _PROP_RWLOCK_RDLOCK(array2->pa_rwlock);
+                       _PROP_RWLOCK_RDLOCK(array1->pa_rwlock);
+               }
+       }
+
+       if (array1->pa_count != array2->pa_count)
+               goto out;
+       if (idx == array1->pa_count) {
+               rv = _PROP_OBJECT_EQUALS_TRUE;
+               goto out;
+       }
+       _PROP_ASSERT(idx < array1->pa_count);
+
+       *stored_pointer1 = (void *)(idx + 1);
+       *stored_pointer2 = (void *)(idx + 1);
+
+       *next_obj1 = array1->pa_array[idx];
+       *next_obj2 = array2->pa_array[idx];
+
+       return (_PROP_OBJECT_EQUALS_RECURSE);
+
+ out:
+       _PROP_RWLOCK_UNLOCK(array1->pa_rwlock);
+       _PROP_RWLOCK_UNLOCK(array2->pa_rwlock);
+       return (rv);
+}
+
+static void
+_prop_array_equals_finish(prop_object_t v1, prop_object_t v2)
+{
+       _PROP_RWLOCK_UNLOCK(((prop_array_t)v1)->pa_rwlock);
+       _PROP_RWLOCK_UNLOCK(((prop_array_t)v2)->pa_rwlock);
+}
+
+static prop_array_t
+_prop_array_alloc(unsigned int capacity)
+{
+       prop_array_t pa;
+       prop_object_t *array;
+
+       if (capacity != 0) {
+               array = _PROP_CALLOC(capacity * sizeof(prop_object_t),
+                                    M_PROP_ARRAY);
+               if (array == NULL)
+                       return (NULL);
+       } else
+               array = NULL;
+
+       pa = _PROP_POOL_GET(_prop_array_pool);
+       if (pa != NULL) {
+               _prop_object_init(&pa->pa_obj, &_prop_object_type_array);
+               pa->pa_obj.po_type = &_prop_object_type_array;
+
+               _PROP_RWLOCK_INIT(pa->pa_rwlock);
+               pa->pa_array = array;
+               pa->pa_capacity = capacity;
+               pa->pa_count = 0;
+               pa->pa_flags = 0;
+
+               pa->pa_version = 0;
+       } else if (array != NULL)
+               _PROP_FREE(array, M_PROP_ARRAY);
+
+       return (pa);
+}
+
+static bool
+_prop_array_expand(prop_array_t pa, unsigned int capacity)
+{
+       prop_object_t *array, *oarray;
+
+       /*
+        * Array must be WRITE-LOCKED.
+        */
+
+       oarray = pa->pa_array;
+
+       array = _PROP_CALLOC(capacity * sizeof(*array), M_PROP_ARRAY);
+       if (array == NULL)
+               return (false);
+       if (oarray != NULL)
+               memcpy(array, oarray, pa->pa_capacity * sizeof(*array));
+       pa->pa_array = array;
+       pa->pa_capacity = capacity;
+
+       if (oarray != NULL)
+               _PROP_FREE(oarray, M_PROP_ARRAY);
+
+       return (true);
+}
+
+static prop_object_t
+_prop_array_iterator_next_object_locked(void *v)
+{
+       struct _prop_array_iterator *pai = v;
+       prop_array_t pa = pai->pai_base.pi_obj;
+       prop_object_t po = NULL;
+
+       _PROP_ASSERT(prop_object_is_array(pa));
+
+       if (pa->pa_version != pai->pai_base.pi_version)
+               goto out;       /* array changed during iteration */
+
+       _PROP_ASSERT(pai->pai_index <= pa->pa_count);
+
+       if (pai->pai_index == pa->pa_count)
+               goto out;       /* we've iterated all objects */
+
+       po = pa->pa_array[pai->pai_index];
+       pai->pai_index++;
+
+ out:
+       return (po);
+}
+
+static prop_object_t
+_prop_array_iterator_next_object(void *v)
+{
+       struct _prop_array_iterator *pai = v;
+       prop_array_t pa __unused = pai->pai_base.pi_obj;
+       prop_object_t po;
+
+       _PROP_ASSERT(prop_object_is_array(pa));
+
+       _PROP_RWLOCK_RDLOCK(pa->pa_rwlock);
+       po = _prop_array_iterator_next_object_locked(pai);
+       _PROP_RWLOCK_UNLOCK(pa->pa_rwlock);
+       return (po);
+}
+
+static void
+_prop_array_iterator_reset_locked(void *v)
+{
+       struct _prop_array_iterator *pai = v;
+       prop_array_t pa = pai->pai_base.pi_obj;
+
+       _PROP_ASSERT(prop_object_is_array(pa));
+
+       pai->pai_index = 0;
+       pai->pai_base.pi_version = pa->pa_version;
+}
+
+static void
+_prop_array_iterator_reset(void *v)
+{
+       struct _prop_array_iterator *pai = v;
+       prop_array_t pa __unused = pai->pai_base.pi_obj;
+
+       _PROP_ASSERT(prop_object_is_array(pa));
+
+       _PROP_RWLOCK_RDLOCK(pa->pa_rwlock);
+       _prop_array_iterator_reset_locked(pai);
+       _PROP_RWLOCK_UNLOCK(pa->pa_rwlock);
+}
+
+/*
+ * prop_array_create --
+ *     Create an empty array.
+ */
+prop_array_t
+prop_array_create(void)
+{
+
+       return (_prop_array_alloc(0));
+}
+
+/*
+ * prop_array_create_with_capacity --
+ *     Create an array with the capacity to store N objects.
+ */
+prop_array_t
+prop_array_create_with_capacity(unsigned int capacity)
+{
+
+       return (_prop_array_alloc(capacity));
+}
+
+/*
+ * prop_array_copy --
+ *     Copy an array.  The new array has an initial capacity equal to
+ *     the number of objects stored in the original array.  The new
+ *     array contains references to the original array's objects, not
+ *     copies of those objects (i.e. a shallow copy).
+ */
+prop_array_t
+prop_array_copy(prop_array_t opa)
+{
+       prop_array_t pa;
+       prop_object_t po;
+       unsigned int idx;
+
+       if (! prop_object_is_array(opa))
+               return (NULL);
+
+       _PROP_RWLOCK_RDLOCK(opa->pa_rwlock);
+
+       pa = _prop_array_alloc(opa->pa_count);
+       if (pa != NULL) {
+               for (idx = 0; idx < opa->pa_count; idx++) {
+                       po = opa->pa_array[idx];
+                       prop_object_retain(po);
+                       pa->pa_array[idx] = po;
+               }
+               pa->pa_count = opa->pa_count;
+               pa->pa_flags = opa->pa_flags;
+       }
+       _PROP_RWLOCK_UNLOCK(opa->pa_rwlock);
+       return (pa);
+}
+
+/*
+ * prop_array_copy_mutable --
+ *     Like prop_array_copy(), but the resulting array is mutable.
+ */
+prop_array_t
+prop_array_copy_mutable(prop_array_t opa)
+{
+       prop_array_t pa;
+
+       pa = prop_array_copy(opa);
+       if (pa != NULL)
+               pa->pa_flags &= ~PA_F_IMMUTABLE;
+
+       return (pa);
+}
+
+/*
+ * prop_array_capacity --
+ *     Return the capacity of the array.
+ */
+unsigned int
+prop_array_capacity(prop_array_t pa)
+{
+       unsigned int rv;
+
+       if (! prop_object_is_array(pa))
+               return (0);
+
+       _PROP_RWLOCK_RDLOCK(pa->pa_rwlock);
+       rv = pa->pa_capacity;
+       _PROP_RWLOCK_UNLOCK(pa->pa_rwlock);
+
+       return (rv);
+}
+
+/*
+ * prop_array_count --
+ *     Return the number of objects stored in the array.
+ */
+unsigned int
+prop_array_count(prop_array_t pa)
+{
+       unsigned int rv;
+
+       if (! prop_object_is_array(pa))
+               return (0);
+
+       _PROP_RWLOCK_RDLOCK(pa->pa_rwlock);
+       rv = pa->pa_count;
+       _PROP_RWLOCK_UNLOCK(pa->pa_rwlock);
+
+       return (rv);
+}
+
+/*
+ * prop_array_ensure_capacity --
+ *     Ensure that the array has the capacity to store the specified
+ *     total number of objects (inluding the objects already stored
+ *     in the array).
+ */
+bool
+prop_array_ensure_capacity(prop_array_t pa, unsigned int capacity)
+{
+       bool rv;
+
+       if (! prop_object_is_array(pa))
+               return (false);
+
+       _PROP_RWLOCK_WRLOCK(pa->pa_rwlock);
+       if (capacity > pa->pa_capacity)
+               rv = _prop_array_expand(pa, capacity);
+       else
+               rv = true;
+       _PROP_RWLOCK_UNLOCK(pa->pa_rwlock);
+
+       return (rv);
+}
+
+static prop_object_iterator_t
+_prop_array_iterator_locked(prop_array_t pa)
+{
+       struct _prop_array_iterator *pai;
+
+       if (! prop_object_is_array(pa))
+               return (NULL);
+
+       pai = _PROP_CALLOC(sizeof(*pai), M_TEMP);
+       if (pai == NULL)
+               return (NULL);
+       pai->pai_base.pi_next_object = _prop_array_iterator_next_object;
+       pai->pai_base.pi_reset = _prop_array_iterator_reset;
+       prop_object_retain(pa);
+       pai->pai_base.pi_obj = pa;
+       _prop_array_iterator_reset_locked(pai);
+
+       return (&pai->pai_base);
+}
+
+/*
+ * prop_array_iterator --
+ *     Return an iterator for the array.  The array is retained by
+ *     the iterator.
+ */
+prop_object_iterator_t
+prop_array_iterator(prop_array_t pa)
+{
+       prop_object_iterator_t pi;
+
+       _PROP_RWLOCK_RDLOCK(pa->pa_rwlock);
+       pi = _prop_array_iterator_locked(pa);
+       _PROP_RWLOCK_UNLOCK(pa->pa_rwlock);
+       return (pi);
+}
+
+/*
+ * prop_array_make_immutable --
+ *     Make the array immutable.
+ */
+void
+prop_array_make_immutable(prop_array_t pa)
+{
+
+       _PROP_RWLOCK_WRLOCK(pa->pa_rwlock);
+       if (prop_array_is_immutable(pa) == false)
+               pa->pa_flags |= PA_F_IMMUTABLE;
+       _PROP_RWLOCK_UNLOCK(pa->pa_rwlock);
+}
+
+/*
+ * prop_array_mutable --
+ *     Returns true if the array is mutable.
+ */
+bool
+prop_array_mutable(prop_array_t pa)
+{
+       bool rv;
+
+       _PROP_RWLOCK_RDLOCK(pa->pa_rwlock);
+       rv = prop_array_is_immutable(pa) == false;
+       _PROP_RWLOCK_UNLOCK(pa->pa_rwlock);
+
+       return (rv);
+}
+
+/*
+ * prop_array_get --
+ *     Return the object stored at the specified array index.
+ */
+prop_object_t
+prop_array_get(prop_array_t pa, unsigned int idx)
+{
+       prop_object_t po = NULL;
+
+       if (! prop_object_is_array(pa))
+               return (NULL);
+
+       _PROP_RWLOCK_RDLOCK(pa->pa_rwlock);
+       if (idx >= pa->pa_count)
+               goto out;
+       po = pa->pa_array[idx];
+       _PROP_ASSERT(po != NULL);
+ out:
+       _PROP_RWLOCK_UNLOCK(pa->pa_rwlock);
+       return (po);
+}
+
+static bool
+_prop_array_add(prop_array_t pa, prop_object_t po)
+{
+
+       /*
+        * Array must be WRITE-LOCKED.
+        */
+
+       _PROP_ASSERT(pa->pa_count <= pa->pa_capacity);
+
+       if (prop_array_is_immutable(pa) ||
+           (pa->pa_count == pa->pa_capacity &&
+           _prop_array_expand(pa, pa->pa_capacity + EXPAND_STEP) == false))
+               return (false);
+
+       prop_object_retain(po);
+       pa->pa_array[pa->pa_count++] = po;
+       pa->pa_version++;
+
+       return (true);
+}
+
+/*
+ * prop_array_set --
+ *     Store a reference to an object at the specified array index.
+ *     This method is not allowed to create holes in the array; the
+ *     caller must either be setting the object just beyond the existing
+ *     count or replacing an already existing object reference.
+ */
+bool
+prop_array_set(prop_array_t pa, unsigned int idx, prop_object_t po)
+{
+       prop_object_t opo;
+       bool rv = false;
+
+       if (! prop_object_is_array(pa))
+               return (false);
+
+       _PROP_RWLOCK_WRLOCK(pa->pa_rwlock);
+
+       if (prop_array_is_immutable(pa))
+               goto out;
+
+       if (idx == pa->pa_count) {
+               rv = _prop_array_add(pa, po);
+               goto out;
+       }
+
+       _PROP_ASSERT(idx < pa->pa_count);
+
+       opo = pa->pa_array[idx];
+       _PROP_ASSERT(opo != NULL);
+
+       prop_object_retain(po);
+       pa->pa_array[idx] = po;
+       pa->pa_version++;
+
+       prop_object_release(opo);
+
+       rv = true;
+
+ out:
+       _PROP_RWLOCK_UNLOCK(pa->pa_rwlock);
+       return (rv);
+}
+
+/*
+ * prop_array_add --
+ *     Add a reference to an object to the specified array, appending
+ *     to the end and growing the array's capacity, if necessary.
+ */
+bool
+prop_array_add(prop_array_t pa, prop_object_t po)
+{
+       bool rv;
+
+       if (! prop_object_is_array(pa))
+               return (false);
+
+       _PROP_RWLOCK_WRLOCK(pa->pa_rwlock);
+       rv = _prop_array_add(pa, po);
+       _PROP_RWLOCK_UNLOCK(pa->pa_rwlock);
+
+       return (rv);
+}
+
+/*
+ * prop_array_remove --
+ *     Remove the reference to an object from an array at the specified
+ *     index.  The array will be compacted following the removal.
+ */
+void
+prop_array_remove(prop_array_t pa, unsigned int idx)
+{
+       prop_object_t po;
+
+       if (! prop_object_is_array(pa))
+               return;
+
+       _PROP_RWLOCK_WRLOCK(pa->pa_rwlock);
+
+       _PROP_ASSERT(idx < pa->pa_count);
+
+       /* XXX Should this be a _PROP_ASSERT()? */
+       if (prop_array_is_immutable(pa)) {
+               _PROP_RWLOCK_UNLOCK(pa->pa_rwlock);
+               return;
+       }
+
+       po = pa->pa_array[idx];
+       _PROP_ASSERT(po != NULL);
+
+       for (++idx; idx < pa->pa_count; idx++)
+               pa->pa_array[idx - 1] = pa->pa_array[idx];
+       pa->pa_count--;
+       pa->pa_version++;
+
+       _PROP_RWLOCK_UNLOCK(pa->pa_rwlock);
+
+       prop_object_release(po);
+}
+
+/*
+ * prop_array_equals --
+ *     Return true if the two arrays are equivalent.  Note we do a
+ *     by-value comparison of the objects in the array.
+ */
+bool
+prop_array_equals(prop_array_t array1, prop_array_t array2)
+{
+       if (!prop_object_is_array(array1) || !prop_object_is_array(array2))
+               return (false);
+
+       return (prop_object_equals(array1, array2));
+}
+
+/*
+ * prop_array_externalize --
+ *     Externalize an array, return a NUL-terminated buffer
+ *     containing the XML-style representation.  The buffer is allocated
+ *     with the M_TEMP memory type.
+ */
+char *
+prop_array_externalize(prop_array_t pa)
+{
+       struct _prop_object_externalize_context *ctx;
+       char *cp;
+
+       ctx = _prop_object_externalize_context_alloc();
+       if (ctx == NULL)
+               return (NULL);
+
+       if (_prop_object_externalize_header(ctx) == false ||
+           (*pa->pa_obj.po_type->pot_extern)(ctx, pa) == false ||
+           _prop_object_externalize_footer(ctx) == false) {
+               /* We are responsible for releasing the buffer. */
+               _PROP_FREE(ctx->poec_buf, M_TEMP);
+               _prop_object_externalize_context_free(ctx);
+               return (NULL);
+       }
+
+       cp = ctx->poec_buf;
+       _prop_object_externalize_context_free(ctx);
+
+       return (cp);
+}
+
+/*
+ * _prop_array_internalize --
+ *     Parse an <array>...</array> and return the object created from the
+ *     external representation.
+ */
+static bool _prop_array_internalize_body(prop_stack_t, prop_object_t *,
+    struct _prop_object_internalize_context *);
+
+bool
+_prop_array_internalize(prop_stack_t stack, prop_object_t *obj,
+    struct _prop_object_internalize_context *ctx)
+{
+       /* We don't currently understand any attributes. */
+       if (ctx->poic_tagattr != NULL)
+               return (true);
+
+       *obj = prop_array_create();
+       /*
+        * We are done if the create failed or no child elements exist.
+        */
+       if (*obj == NULL || ctx->poic_is_empty_element)
+               return (true);
+
+       /*
+        * Opening tag is found, now continue to the first element.
+        */
+       return (_prop_array_internalize_body(stack, obj, ctx));
+}
+
+static bool
+_prop_array_internalize_continue(prop_stack_t stack,
+    prop_object_t *obj,
+    struct _prop_object_internalize_context *ctx,
+    void *data, prop_object_t child)
+{
+       prop_array_t array;
+
+       _PROP_ASSERT(data == NULL);
+
+       if (child == NULL)
+               goto bad; /* Element could not be parsed. */
+
+       array = *obj;
+
+       if (prop_array_add(array, child) == false) {
+               prop_object_release(child);
+               goto bad;
+       }
+       prop_object_release(child);
+
+       /*
+        * Current element is processed and added, look for next.
+        */
+       return (_prop_array_internalize_body(stack, obj, ctx));
+
+ bad:
+       prop_object_release(*obj);
+       *obj = NULL;
+       return (true);
+}
+
+static bool
+_prop_array_internalize_body(prop_stack_t stack, prop_object_t *obj,
+    struct _prop_object_internalize_context *ctx)
+{
+       prop_array_t array = *obj;
+
+       _PROP_ASSERT(array != NULL);
+
+       /* Fetch the next tag. */
+       if (_prop_object_internalize_find_tag(ctx, NULL,
+                               _PROP_TAG_TYPE_EITHER) == false)
+               goto bad;
+
+       /* Check to see if this is the end of the array. */
+       if (_PROP_TAG_MATCH(ctx, "array") &&
+           ctx->poic_tag_type == _PROP_TAG_TYPE_END) {
+               /* It is, so don't iterate any further. */
+               return (true);
+       }
+
+       if (_prop_stack_push(stack, array,
+                            _prop_array_internalize_continue, NULL, NULL))
+               return (false);
+
+ bad:
+       prop_object_release(array);
+       *obj = NULL;
+       return (true);
+}
+
+/*
+ * prop_array_internalize --
+ *     Create an array by parsing the XML-style representation.
+ */
+prop_array_t
+prop_array_internalize(const char *xml)
+{
+       return _prop_generic_internalize(xml, "array");
+}
+
+#if !defined(_KERNEL) && !defined(_STANDALONE)
+/*
+ * prop_array_externalize_to_file --
+ *     Externalize an array to the specified file.
+ */
+bool
+prop_array_externalize_to_file(prop_array_t array, const char *fname)
+{
+       char *xml;
+       bool rv;
+       int save_errno = 0;     /* XXXGCC -Wuninitialized [mips, ...] */
+
+       xml = prop_array_externalize(array);
+       if (xml == NULL)
+               return (false);
+       rv = _prop_object_externalize_write_file(fname, xml, strlen(xml));
+       if (rv == false)
+               save_errno = errno;
+       _PROP_FREE(xml, M_TEMP);
+       if (rv == false)
+               errno = save_errno;
+
+       return (rv);
+}
+
+/*
+ * prop_array_internalize_from_file --
+ *     Internalize an array from a file.
+ */
+prop_array_t
+prop_array_internalize_from_file(const char *fname)
+{
+       struct _prop_object_internalize_mapped_file *mf;
+       prop_array_t array;
+
+       mf = _prop_object_internalize_map_file(fname);
+       if (mf == NULL)
+               return (NULL);
+       array = prop_array_internalize(mf->poimf_xml);
+       _prop_object_internalize_unmap_file(mf);
+
+       return (array);
+}
+#endif /* _KERNEL && !_STANDALONE */
diff --git a/common/lib/libprop/prop_array_util.3 b/common/lib/libprop/prop_array_util.3
new file mode 100644 (file)
index 0000000..89323a5
--- /dev/null
@@ -0,0 +1,228 @@
+.\"    $NetBSD: prop_array_util.3,v 1.7 2011/10/17 09:24:54 wiz Exp $
+.\"
+.\" Copyright (c) 2006 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by Jason R. Thorpe.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd March 12, 2011
+.Dt PROP_ARRAY_UTIL 3
+.Os
+.Sh NAME
+.Nm prop_array_util ,
+.Nm prop_array_get_bool ,
+.Nm prop_array_set_bool ,
+.Nm prop_array_get_int8 ,
+.Nm prop_array_get_uint8 ,
+.Nm prop_array_set_int8 ,
+.Nm prop_array_set_uint8 ,
+.Nm prop_array_get_int16 ,
+.Nm prop_array_get_uint16 ,
+.Nm prop_array_set_int16 ,
+.Nm prop_array_set_uint16 ,
+.Nm prop_array_get_int32 ,
+.Nm prop_array_get_uint32 ,
+.Nm prop_array_set_int32 ,
+.Nm prop_array_set_uint32 ,
+.Nm prop_array_get_int64 ,
+.Nm prop_array_get_uint64 ,
+.Nm prop_array_set_int64 ,
+.Nm prop_array_set_uint64 ,
+.Nm prop_array_add_int8 ,
+.Nm prop_array_add_uint8 ,
+.Nm prop_array_add_int16 ,
+.Nm prop_array_add_uint16 ,
+.Nm prop_array_add_int32 ,
+.Nm prop_array_add_uint32 ,
+.Nm prop_array_add_int64 ,
+.Nm prop_array_add_uint64 ,
+.Nm prop_array_get_cstring ,
+.Nm prop_array_set_cstring ,
+.Nm prop_array_get_cstring_nocopy ,
+.Nm prop_array_set_cstring_nocopy ,
+.Nm prop_array_add_and_rel
+.Nd array property collection object utility functions
+.Sh LIBRARY
+.Lb libprop
+.Sh SYNOPSIS
+.In prop/proplib.h
+.\"
+.Ft bool
+.Fn prop_array_get_bool "prop_array_t dict" "unsigned int indx" \
+    "bool *valp"
+.Ft bool
+.Fn prop_array_set_bool "prop_array_t dict" "unsigned int indx" \
+    "bool val"
+.\"
+.Ft bool
+.Fn prop_array_get_int8 "prop_array_t dict" "unsigned int indx" \
+    "int8_t *valp"
+.Ft bool
+.Fn prop_array_get_uint8 "prop_array_t dict" "unsigned int indx" \
+    "uint8_t *valp"
+.Ft bool
+.Fn prop_array_set_int8 "prop_array_t dict" "unsigned int indx" \
+    "int8_t val"
+.Ft bool
+.Fn prop_array_set_uint8 "prop_array_t dict" "unsigned int indx" \
+    "uint8_t val"
+.\"
+.Ft bool
+.Fn prop_array_get_int16 "prop_array_t dict" "unsigned int indx" \
+    "int16_t *valp"
+.Ft bool
+.Fn prop_array_get_uint16 "prop_array_t dict" "unsigned int indx" \
+    "uint16_t *valp"
+.Ft bool
+.Fn prop_array_set_int16 "prop_array_t dict" "unsigned int indx" \
+    "int16_t val"
+.Ft bool
+.Fn prop_array_set_uint16 "prop_array_t dict" "unsigned int indx" \
+    "uint16_t val"
+.\"
+.Ft bool
+.Fn prop_array_get_int32 "prop_array_t dict" "unsigned int indx" \
+    "int32_t *valp"
+.Ft bool
+.Fn prop_array_get_uint32 "prop_array_t dict" "unsigned int indx" \
+    "uint32_t *valp"
+.Ft bool
+.Fn prop_array_set_int32 "prop_array_t dict" "unsigned int indx" \
+    "int32_t val"
+.Ft bool
+.Fn prop_array_set_uint32 "prop_array_t dict" "unsigned int indx" \
+    "uint32_t val"
+.\"
+.Ft bool
+.Fn prop_array_get_int64 "prop_array_t dict" "unsigned int indx" \
+    "int64_t *valp"
+.Ft bool
+.Fn prop_array_get_uint64 "prop_array_t dict" "unsigned int indx" \
+    "uint64_t *valp"
+.Ft bool
+.Fn prop_array_set_int64 "prop_array_t dict" "unsigned int indx" \
+    "int64_t val"
+.Ft bool
+.Fn prop_array_set_uint64 "prop_array_t dict" "unsigned int indx" \
+    "uint64_t val"
+.\"
+.Ft bool
+.Fn prop_array_set_int32 "prop_array_t dict" "unsigned int indx" \
+    "int32_t val"
+.Ft bool
+.Fn prop_array_set_uint32 "prop_array_t dict" "unsigned int indx" \
+    "uint32_t val"
+.\"
+.Ft bool
+.Fn prop_array_add_int8 "prop_array_t dict" "int8_t val"
+.Ft bool
+.Fn prop_array_add_uint8 "prop_array_t dict" "uint8_t val"
+.Ft bool
+.Fn prop_array_add_int16 "prop_array_t dict"  "int16_t val"
+.Ft bool
+.Fn prop_array_add_uint16 "prop_array_t dict" "uint16_t val"
+.Ft bool
+.Fn prop_array_add_int32 "prop_array_t dict" "int32_t val"
+.Ft bool
+.Fn prop_array_add_uint32 "prop_array_t dict" "uint32_t val"
+.Ft bool
+.Fn prop_array_add_int64 "prop_array_t dict"  "int64_t val"
+.Ft bool
+.Fn prop_array_add_uint64 "prop_array_t dict" "uint64_t val"
+.\"
+.Ft bool
+.Fn prop_array_get_cstring "prop_array_t dict" "unsigned int indx" \
+    "char **strp"
+.Ft bool
+.Fn prop_array_set_cstring "prop_array_t dict" "unsigned int indx" \
+    "const char *str"
+.\"
+.Ft bool
+.Fn prop_array_get_cstring_nocopy "prop_array_t dict" \
+    "unsigned int indx" "const char **strp"
+.Ft bool
+.Fn prop_array_set_cstring_nocopy "prop_array_t dict" \
+    "unsigned int indx" "const char *strp"
+.Ft bool
+.Fn prop_array_add_and_rel "prop_array_t dict" \
+    "prop_object_t obj"
+.Sh DESCRIPTION
+The
+.Nm prop_array_util
+family of functions are provided to make getting and setting values in
+arrays more convenient in some applications.
+.Pp
+The getters check the type of the returned object and, in some cases, also
+ensure that the returned value is within the range implied by the getter's
+value type.
+.Pp
+The setters handle object creation and release for the caller.
+.Pp
+The
+.Fn prop_array_get_cstring
+function returns dynamically allocated memory.
+See
+.Xr prop_string 3
+for more information.
+.Pp
+The
+.Fn prop_array_get_cstring_nocopy
+and
+.Fn prop_array_set_cstring_nocopy
+functions do not copy the string that is set or returned.
+See
+.Xr prop_string 3
+for more information.
+.Pp
+The
+.Fn prop_array_add_and_rel
+function adds the object to the end of the array and releases it.
+The object is also released on failure.
+.Sh RETURN VALUES
+The
+.Nm prop_array_util
+getter functions return
+.Dv true
+if the object exists in the array and the value is in-range, or
+.Dv false
+otherwise.
+.Pp
+The
+.Nm prop_array_util
+setter functions return
+.Dv true
+if creating the object and storing it in the array is successful, or
+.Dv false
+otherwise.
+.Sh SEE ALSO
+.Xr prop_array 3 ,
+.Xr prop_bool 3 ,
+.Xr prop_number 3 ,
+.Xr proplib 3
+.Sh HISTORY
+The
+.Nm proplib
+property container object library first appeared in
+.Nx 4.0 .
diff --git a/common/lib/libprop/prop_array_util.c b/common/lib/libprop/prop_array_util.c
new file mode 100644 (file)
index 0000000..8515bd5
--- /dev/null
@@ -0,0 +1,251 @@
+/*     $NetBSD: prop_array_util.c,v 1.3 2011/03/24 17:05:39 bouyer Exp $       */
+
+/*-
+ * Copyright (c) 2006 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jason R. Thorpe.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Utility routines to make it more convenient to work with values
+ * stored in array.
+ *
+ * Note: There is no special magic going on here.  We use the standard
+ * proplib(3) APIs to do all of this work.  Any application could do
+ * exactly what we're doing here.
+ */
+
+#include <prop/proplib.h>
+#include "prop_object_impl.h" /* hide kernel vs. not-kernel vs. standalone */
+
+bool
+prop_array_get_bool(prop_array_t array,
+                        unsigned int indx,
+                        bool *valp)
+{
+       prop_bool_t b;
+
+       b = prop_array_get(array, indx);
+       if (prop_object_type(b) != PROP_TYPE_BOOL)
+               return (false);
+       
+       *valp = prop_bool_true(b);
+
+       return (true);
+}
+
+bool
+prop_array_set_bool(prop_array_t array,
+                        unsigned int indx,
+                        bool val)
+{
+       prop_bool_t b;
+       int rv;
+
+       b = prop_bool_create(val);
+       if (b == NULL)
+               return (false);
+       rv = prop_array_set(array, indx, b);
+       prop_object_release(b);
+
+       return (rv);
+}
+
+#define        TEMPLATE(size)                                                  \
+bool                                                                   \
+prop_array_get_int ## size (prop_array_t array,                                \
+                                unsigned int indx,                     \
+                                int ## size ## _t *valp)               \
+{                                                                      \
+       prop_number_t num;                                              \
+                                                                       \
+       num = prop_array_get(array, indx);                              \
+       if (prop_object_type(num) != PROP_TYPE_NUMBER)                  \
+               return (false);                                         \
+                                                                       \
+       if (prop_number_unsigned(num) &&                                \
+           prop_number_unsigned_integer_value(num) >                   \
+          /*CONSTCOND*/((size) ==  8 ?  INT8_MAX :                     \
+                        (size) == 16 ? INT16_MAX :                     \
+                        (size) == 32 ? INT32_MAX : INT64_MAX)) {       \
+               return (false);                                         \
+       }                                                               \
+                                                                       \
+       if (prop_number_size(num) > (size))                             \
+               return (false);                                         \
+                                                                       \
+       *valp = (int ## size ## _t) prop_number_integer_value(num);     \
+                                                                       \
+       return (true);                                                  \
+}                                                                      \
+                                                                       \
+bool                                                                   \
+prop_array_get_uint ## size (prop_array_t array,                       \
+                                 unsigned int indx,                    \
+                                 uint ## size ## _t *valp)             \
+{                                                                      \
+       prop_number_t num;                                              \
+                                                                       \
+       num = prop_array_get(array, indx);                              \
+       if (prop_object_type(num) != PROP_TYPE_NUMBER)                  \
+               return (false);                                         \
+                                                                       \
+       if (prop_number_unsigned(num) == false &&                       \
+           prop_number_integer_value(num) < 0) {                       \
+               return (false);                                         \
+       }                                                               \
+                                                                       \
+       if (prop_number_size(num) > (size))                             \
+               return (false);                                         \
+                                                                       \
+       *valp = (uint ## size ## _t)                                    \
+           prop_number_unsigned_integer_value(num);                    \
+                                                                       \
+       return (true);                                                  \
+}                                                                      \
+                                                                       \
+bool                                                                   \
+ prop_array_set_int ## size (prop_array_t array,                       \
+                                unsigned int indx,                     \
+                                int ## size ## _t val)                 \
+{                                                                      \
+       prop_number_t num;                                              \
+       int rv;                                                         \
+                                                                       \
+       num = prop_number_create_integer((int64_t) val);                \
+       if (num == NULL)                                                \
+               return (false);                                         \
+       rv = prop_array_set(array, indx, num);                          \
+       prop_object_release(num);                                       \
+                                                                       \
+       return (rv);                                                    \
+}                                                                      \
+                                                                       \
+bool                                                                   \
+prop_array_set_uint ## size (prop_array_t array,                       \
+                                 unsigned int indx,                    \
+                                 uint ## size ## _t val)               \
+{                                                                      \
+       prop_number_t num;                                              \
+       int rv;                                                         \
+                                                                       \
+       num = prop_number_create_unsigned_integer((uint64_t) val);      \
+       if (num == NULL)                                                \
+               return (false);                                         \
+       rv = prop_array_set(array, indx, num);                          \
+       prop_object_release(num);                                       \
+                                                                       \
+       return (rv);                                                    \
+}                                                                      \
+                                                                       \
+bool                                                                   \
+prop_array_add_int ## size (prop_array_t array,                                \
+                                int ## size ## _t val)                 \
+{                                                                      \
+       prop_number_t num;                                              \
+       int rv;                                                         \
+                                                                       \
+       num = prop_number_create_integer((int64_t) val);                \
+       if (num == NULL)                                                \
+               return (false);                                         \
+       rv = prop_array_add(array, num);                                \
+       prop_object_release(num);                                       \
+                                                                       \
+       return (rv);                                                    \
+}                                                                      \
+                                                                       \
+bool                                                                   \
+prop_array_add_uint ## size (prop_array_t array,                       \
+                                 uint ## size ## _t val)               \
+{                                                                      \
+       prop_number_t num;                                              \
+       int rv;                                                         \
+                                                                       \
+       num = prop_number_create_integer((int64_t) val);                \
+       if (num == NULL)                                                \
+               return (false);                                         \
+       rv = prop_array_add(array, num);                                \
+       prop_object_release(num);                                       \
+                                                                       \
+       return (rv);                                                    \
+}
+
+TEMPLATE(8)
+TEMPLATE(16)
+TEMPLATE(32)
+TEMPLATE(64)
+
+#undef TEMPLATE
+
+#define        TEMPLATE(variant, qualifier)                                    \
+bool                                                                   \
+prop_array_get_cstring ## variant (prop_array_t array,                 \
+                                       unsigned int indx,              \
+                                       qualifier char **cpp)           \
+{                                                                      \
+       prop_string_t str;                                              \
+                                                                       \
+       str = prop_array_get(array, indx);                              \
+       if (prop_object_type(str) != PROP_TYPE_STRING)                  \
+               return (false);                                         \
+                                                                       \
+       *cpp = prop_string_cstring ## variant (str);                    \
+                                                                       \
+       return (*cpp == NULL ? false : true);                           \
+}                                                                      \
+                                                                       \
+bool                                                                   \
+prop_array_set_cstring ## variant (prop_array_t array,                 \
+                                       unsigned int indx,              \
+                                       const char *cp)                 \
+{                                                                      \
+       prop_string_t str;                                              \
+       int rv;                                                         \
+                                                                       \
+       str = prop_string_create_cstring ## variant (cp);               \
+       if (str == NULL)                                                \
+               return (false);                                         \
+       rv = prop_array_set(array, indx, str);                          \
+       prop_object_release(str);                                       \
+                                                                       \
+       return (rv);                                                    \
+}
+
+TEMPLATE(,)
+TEMPLATE(_nocopy,const)
+
+#undef TEMPLATE
+
+bool
+prop_array_add_and_rel(prop_array_t array, prop_object_t po)
+{
+       bool ret;
+       if (po == NULL)
+               return false;
+       ret = prop_array_add(array, po);
+       prop_object_release(po);
+       return ret;
+}
diff --git a/common/lib/libprop/prop_bool.3 b/common/lib/libprop/prop_bool.3
new file mode 100644 (file)
index 0000000..f56382d
--- /dev/null
@@ -0,0 +1,82 @@
+.\"    $NetBSD: prop_bool.3,v 1.6 2008/08/03 03:11:28 thorpej Exp $
+.\"
+.\" Copyright (c) 2006 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by Jason R. Thorpe.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd April 22, 2006
+.Dt PROP_BOOL 3
+.Os
+.Sh NAME
+.Nm prop_bool ,
+.Nm prop_bool_create ,
+.Nm prop_bool_copy ,
+.Nm prop_bool_true
+.Nd boolean value property object
+.Sh LIBRARY
+.Lb libprop
+.Sh SYNOPSIS
+.In prop/proplib.h
+.\"
+.Ft prop_bool_t
+.Fn prop_bool_create "bool val"
+.Ft prop_bool_t
+.Fn prop_bool_copy "prop_bool_t bool"
+.\"
+.Ft bool
+.Fn prop_bool_true "prop_bool_t bool"
+.Sh DESCRIPTION
+The
+.Nm prop_bool
+family of functions operate on a boolean value property object type.
+.Bl -tag -width "xxxxx"
+.It Fn prop_bool_create "bool val"
+Create a boolean value object with the value
+.Fa val .
+.It Fn prop_bool_copy "prop_bool_t bool"
+Copy a boolean value object.
+If the supplied object isn't a boolean,
+.Dv NULL
+is returned.
+.It Fn prop_bool_true "prop_bool_t bool"
+Returns the value of the boolean value object.
+If the supplied object isn't a boolean,
+.Dv false
+is returned.
+.El
+.Sh SEE ALSO
+.Xr prop_array 3 ,
+.Xr prop_data 3 ,
+.Xr prop_dictionary 3 ,
+.Xr prop_number 3 ,
+.Xr prop_object 3 ,
+.Xr prop_string 3 ,
+.Xr proplib 3
+.Sh HISTORY
+The
+.Nm proplib
+property container object library first appeared in
+.Nx 4.0 .
diff --git a/common/lib/libprop/prop_bool.c b/common/lib/libprop/prop_bool.c
new file mode 100644 (file)
index 0000000..d9e912b
--- /dev/null
@@ -0,0 +1,222 @@
+/*     $NetBSD: prop_bool.c,v 1.17 2009/01/03 18:31:33 pooka Exp $     */
+
+/*-
+ * Copyright (c) 2006 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jason R. Thorpe.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <prop/prop_bool.h>
+#include "prop_object_impl.h"
+
+struct _prop_bool {
+       struct _prop_object     pb_obj;
+       bool            pb_value;
+};
+
+static struct _prop_bool _prop_bool_true;
+static struct _prop_bool _prop_bool_false;
+
+static _prop_object_free_rv_t
+               _prop_bool_free(prop_stack_t, prop_object_t *);
+static bool    _prop_bool_externalize(
+                               struct _prop_object_externalize_context *,
+                               void *);
+static _prop_object_equals_rv_t
+               _prop_bool_equals(prop_object_t, prop_object_t,
+                                 void **, void **,
+                                 prop_object_t *, prop_object_t *);
+
+static const struct _prop_object_type _prop_object_type_bool = {
+       .pot_type       =       PROP_TYPE_BOOL,
+       .pot_free       =       _prop_bool_free,
+       .pot_extern     =       _prop_bool_externalize,
+       .pot_equals     =       _prop_bool_equals,
+};
+
+#define        prop_object_is_bool(x)          \
+       ((x) != NULL && (x)->pb_obj.po_type == &_prop_object_type_bool)
+
+/* ARGSUSED */
+static _prop_object_free_rv_t
+_prop_bool_free(prop_stack_t stack, prop_object_t *obj)
+{
+       /*
+        * This should never happen as we "leak" our initial reference
+        * count.
+        */
+
+       /* XXX forced assertion failure? */
+       return (_PROP_OBJECT_FREE_DONE);
+}
+
+static bool
+_prop_bool_externalize(struct _prop_object_externalize_context *ctx,
+                      void *v)
+{
+       prop_bool_t pb = v;
+
+       return (_prop_object_externalize_empty_tag(ctx,
+           pb->pb_value ? "true" : "false"));
+}
+
+/* ARGSUSED */
+static _prop_object_equals_rv_t
+_prop_bool_equals(prop_object_t v1, prop_object_t v2,
+    void **stored_pointer1, void **stored_pointer2,
+    prop_object_t *next_obj1, prop_object_t *next_obj2)
+{
+       prop_bool_t b1 = v1;
+       prop_bool_t b2 = v2;
+
+       if (! (prop_object_is_bool(b1) &&
+              prop_object_is_bool(b2)))
+               return (_PROP_OBJECT_EQUALS_FALSE);
+
+       /*
+        * Since we only ever allocate one true and one false,
+        * save ourselves a couple of memory operations.
+        */
+       if (b1 == b2)
+               return (_PROP_OBJECT_EQUALS_TRUE);
+       else
+               return (_PROP_OBJECT_EQUALS_FALSE);
+}
+
+_PROP_ONCE_DECL(_prop_bool_init_once)
+
+static int
+_prop_bool_init(void)
+{
+
+       _prop_object_init(&_prop_bool_true.pb_obj,
+           &_prop_object_type_bool);
+       _prop_bool_true.pb_value = true;
+
+       _prop_object_init(&_prop_bool_false.pb_obj,
+           &_prop_object_type_bool);
+       _prop_bool_false.pb_value = false;
+
+       return 0;
+}
+
+static prop_bool_t
+_prop_bool_alloc(bool val)
+{
+       prop_bool_t pb;
+
+       _PROP_ONCE_RUN(_prop_bool_init_once, _prop_bool_init);
+       pb = val ? &_prop_bool_true : &_prop_bool_false;
+       prop_object_retain(pb);
+
+       return (pb);
+}
+
+/*
+ * prop_bool_create --
+ *     Create a prop_bool_t and initialize it with the
+ *     provided boolean value.
+ */
+prop_bool_t
+prop_bool_create(bool val)
+{
+
+       return (_prop_bool_alloc(val));
+}
+
+/*
+ * prop_bool_copy --
+ *     Copy a prop_bool_t.
+ */
+prop_bool_t
+prop_bool_copy(prop_bool_t opb)
+{
+
+       if (! prop_object_is_bool(opb))
+               return (NULL);
+
+       /*
+        * Because we only ever allocate one true and one false, this
+        * can be reduced to a simple retain operation.
+        */
+       prop_object_retain(opb);
+       return (opb);
+}
+
+/*
+ * prop_bool_true --
+ *     Get the value of a prop_bool_t.
+ */
+bool
+prop_bool_true(prop_bool_t pb)
+{
+
+       if (! prop_object_is_bool(pb))
+               return (false);
+
+       return (pb->pb_value);
+}
+
+/*
+ * prop_bool_equals --
+ *     Return true if the boolean values are equivalent.
+ */
+bool
+prop_bool_equals(prop_bool_t b1, prop_bool_t b2)
+{
+       if (!prop_object_is_bool(b1) || !prop_object_is_bool(b2))
+               return (false);
+
+       return (prop_object_equals(b1, b2));
+}
+
+/*
+ * _prop_bool_internalize --
+ *     Parse a <true/> or <false/> and return the object created from
+ *     the external representation.
+ */
+
+/* ARGSUSED */
+bool
+_prop_bool_internalize(prop_stack_t stack, prop_object_t *obj,
+    struct _prop_object_internalize_context *ctx)
+{
+       bool val;
+
+       /* No attributes, and it must be an empty element. */
+       if (ctx->poic_tagattr != NULL ||
+           ctx->poic_is_empty_element == false)
+               return (true);
+
+       if (_PROP_TAG_MATCH(ctx, "true"))
+               val = true;
+       else {
+               _PROP_ASSERT(_PROP_TAG_MATCH(ctx, "false"));
+               val = false;
+       }
+       *obj = prop_bool_create(val);
+       return (true);
+}
diff --git a/common/lib/libprop/prop_copyin_ioctl.9 b/common/lib/libprop/prop_copyin_ioctl.9
new file mode 100644 (file)
index 0000000..1ea482c
--- /dev/null
@@ -0,0 +1,194 @@
+.\"    $NetBSD: prop_copyin_ioctl.9,v 1.9 2011/01/20 10:47:33 wiz Exp $
+.\"
+.\" Copyright (c) 2006, 2009 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by Jason R. Thorpe.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd January 17, 2011
+.Dt PROP_COPYIN_IOCTL 9
+.Os
+.Sh NAME
+.Nm prop_array_copyin_ioctl ,
+.Nm prop_array_copyout_ioctl ,
+.Nm prop_array_copyin ,
+.Nm prop_array_copyout ,
+.Nm prop_dictionary_copyin_ioctl ,
+.Nm prop_dictionary_copyout_ioctl ,
+.Nm prop_dictionary_copyin ,
+.Nm prop_dictionary_copyout
+.Nd Copy property lists to and from kernel space
+.Sh SYNOPSIS
+.In prop/proplib.h
+.Ft int
+.Fn prop_array_copyin_ioctl "const struct plistref *pref" \
+    "const u_long cmd" "prop_array_t *arrayp"
+.Ft int
+.Fn prop_array_copyin "const struct plistref *pref" \
+    "prop_array_t *arrayp"
+.Ft int
+.Fn prop_array_copyout_ioctl "struct plistref *pref" \
+    "const u_long cmd" "prop_array_t array"
+.Ft int
+.Fn prop_array_copyout "struct plistref *pref" \
+    "prop_array_t array"
+.Ft int
+.Fn prop_dictionary_copyin_ioctl "const struct plistref *pref" \
+    "const u_long cmd" "prop_dictionary_t *dictp"
+.Ft int
+.Fn prop_dictionary_copyin "const struct plistref *pref" \
+    "prop_dictionary_t *dictp"
+.Ft int
+.Fn prop_dictionary_copyout_ioctl "struct plistref *pref" \
+    "const u_long cmd" "prop_dictionary_t dict"
+.Ft int
+.Fn prop_dictionary_copyout "struct plistref *pref" \
+    "prop_dictionary_t dict"
+.Sh DESCRIPTION
+The
+.Nm prop_array_copyin_ioctl ,
+.Nm prop_array_copyout_ioctl ,
+.Nm prop_dictionary_copyin_ioctl ,
+and
+.Nm prop_dictionary_copyout_ioctl
+functions implement the kernel side of a protocol for copying property lists
+to and from the kernel using
+.Xr ioctl 2 .
+The functions
+.Nm prop_array_copyin ,
+.Nm prop_array_copyout ,
+.Nm prop_dictionary_copyin ,
+and
+.Nm prop_dictionary_copyout
+implement the kernel side of a protocol for copying property lists to the
+kernel as arguments of normal system calls.
+.Pp
+A kernel routine receiving or returning a property list will be passed a
+pointer to a
+.Vt struct plistref .
+This structure encapsulates the reference to the property list in externalized
+form.
+.Sh RETURN VALUES
+If successful, functions return zero.
+Otherwise, an error number will be returned to indicate the error.
+.Sh EXAMPLES
+The following
+.Pq simplified
+example demonstrates using
+.Fn prop_dictionary_copyin_ioctl
+and
+.Fn prop_dictionary_copyout_ioctl
+in an ioctl routine:
+.Bd -literal
+extern prop_dictionary_t fooprops;
+
+int
+fooioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct lwp *l)
+{
+    prop_dictionary_t dict, odict;
+    int error;
+
+    switch (cmd) {
+    case FOOSETPROPS: {
+       const struct plistref *pref = (const struct plistref *) data;
+       error = prop_dictionary_copyin_ioctl(pref, cmd, \*[Am]dict);
+       if (error)
+               return (error);
+       odict = fooprops;
+       fooprops = dict;
+       prop_object_release(odict);
+       break;
+      }
+
+    case FOOGETPROPS: {
+       struct plistref *pref = (struct plistref *) data;
+       error = prop_dictionary_copyout_ioctl(pref, cmd, fooprops);
+       break;
+      }
+
+    default:
+       return (EPASSTHROUGH);
+    }
+    return (error);
+}
+.Ed
+.Pp
+The following
+.Pq simplified
+example demonstrates using
+.Fn prop_array_copyin
+in a routine:
+.Bd -literal
+int
+foocopyin(const struct plistref *pref))
+{
+    prop_array_t array;
+    int error;
+
+    error = prop_array_copyin(pref, \*[Am]array);
+    if (error)
+           return (error);
+    ...
+}
+.Ed
+.Sh ERRORS
+.Fn prop_array_copyin_ioctl
+and
+.Fn prop_dictionary_copyin_ioctl
+will fail if:
+.Bl -tag -width Er
+.It Bq Er EFAULT
+Bad address
+.It Bq Er EIO
+Input/output error
+.It Bq Er ENOMEM
+Cannot allocate memory
+.It Bq Er ENOTSUP
+Not supported
+.El
+.Pp
+.Fn prop_array_copyout_ioctl
+and
+.Fn prop_dictionary_copyout_ioctl
+will fail if:
+.Bl -tag -width Er
+.It Bq Er EFAULT
+Bad address
+.It Bq Er ENOMEM
+Cannot allocate memory
+.It Bq Er ENOTSUP
+Not supported
+.El
+.Sh SEE ALSO
+.Xr prop_array 3 ,
+.Xr prop_dictionary 3 ,
+.Xr prop_send_ioctl 3 ,
+.Xr prop_send_syscall 3 ,
+.Xr proplib 3
+.Sh HISTORY
+The
+.Nm proplib
+property container object library first appeared in
+.Nx 4.0 .
diff --git a/common/lib/libprop/prop_data.3 b/common/lib/libprop/prop_data.3
new file mode 100644 (file)
index 0000000..61acf46
--- /dev/null
@@ -0,0 +1,148 @@
+.\"    $NetBSD: prop_data.3,v 1.7 2009/12/14 06:03:23 dholland Exp $
+.\"
+.\" Copyright (c) 2006 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by Jason R. Thorpe.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd April 22, 2006
+.Dt PROP_DATA 3
+.Os
+.Sh NAME
+.Nm prop_data ,
+.Nm prop_data_create_data ,
+.Nm prop_data_create_data_nocopy ,
+.Nm prop_data_copy ,
+.Nm prop_data_size ,
+.Nm prop_data_data ,
+.Nm prop_data_data_nocopy ,
+.Nm prop_data_equals ,
+.Nm prop_data_equals_data
+.Nd opaque data value property object
+.Sh LIBRARY
+.Lb libprop
+.Sh SYNOPSIS
+.In prop/proplib.h
+.\"
+.Ft prop_data_t
+.Fn prop_data_create_data "const void *blob" "size_t len"
+.Ft prop_data_t
+.Fn prop_data_create_data_nocopy "const void *blob" "size_t len"
+.\"
+.Ft prop_data_t
+.Fn prop_data_copy "prop_data_t data"
+.\"
+.Ft size_t
+.Fn prop_data_size "prop_data_t data"
+.Ft void *
+.Fn prop_data_data "prop_data_t data"
+.Ft const void *
+.Fn prop_data_data_nocopy "prop_data_t data"
+.\"
+.Ft bool
+.Fn prop_data_equals "prop_data_t dat1" "prop_data_t dat2"
+.Ft bool
+.Fn prop_data_equals_data "prop_data_t data" "const void *blob" "size_t len"
+.Sh DESCRIPTION
+The
+.Nm prop_data
+family of functions operate on an opaque data value property object type.
+.Bl -tag -width "xxxxx"
+.It Fn prop_data_create_data "const void *blob" "size_t len"
+Create a data object that contains a copy of
+.Fa blob
+with size
+.Fa len .
+Returns
+.Dv NULL
+on failure.
+.It Fn prop_data_create_data_nocopy "const void *blob" "size_t len"
+Create a data object that contains a reference to
+.Fa blob
+with size
+.Fa len .
+Returns
+.Dv NULL
+on failure.
+.It Fn prop_data_copy "prop_data_t data"
+Copy a data object.
+If the data object being copied is an external data reference,
+then the copy also references the same external data.
+Returns
+.Dv NULL
+on failure.
+.It Fn prop_data_size "prop_data_t data"
+Returns the size of the data object.
+If the supplied object isn't a data object, zero is returned.
+.It Fn prop_data_data "prop_data_t data"
+Returns a copy of the data object's contents.
+The caller is responsible for freeing the returned buffer.
+If the supplied object isn't a data object or
+if the data container is empty,
+.Dv NULL
+is returned.
+.Pp
+In user space, the buffer is allocated using
+.Xr malloc 3 .
+In the kernel, the buffer is allocated using
+.Xr malloc 9
+using the malloc type
+.Dv M_TEMP .
+.It Fn prop_data_data_nocopy "prop_data_t data"
+Returns an immutable reference to the contents of the data object.
+If the supplied object isn't a data object,
+.Dv NULL
+is returned.
+.It Fn prop_data_equals "prop_data_t dat1" "prop_data_t dat2"
+Returns
+.Dv true
+if the two data objects are equivalent.
+If at least one of the supplied objects isn't a data object,
+.Dv false
+is returned.
+.It Fn prop_data_equals_data "prop_data_t data" "const void *blob" "size_t len"
+Returns
+.Dv true
+if the data object's value is equivalent to
+.Fa blob
+with size
+.Fa len .
+If the supplied object isn't a data object,
+.Dv false
+is returned.
+.El
+.Sh SEE ALSO
+.Xr prop_array 3 ,
+.Xr prop_bool 3 ,
+.Xr prop_dictionary 3 ,
+.Xr prop_number 3 ,
+.Xr prop_object 3 ,
+.Xr prop_string 3 ,
+.Xr proplib 3
+.Sh HISTORY
+The
+.Nm proplib
+property container object library first appeared in
+.Nx 4.0 .
diff --git a/common/lib/libprop/prop_data.c b/common/lib/libprop/prop_data.c
new file mode 100644 (file)
index 0000000..5ef9e35
--- /dev/null
@@ -0,0 +1,627 @@
+/*     $NetBSD: prop_data.c,v 1.14 2009/01/25 06:59:35 cyber Exp $     */
+
+/*-
+ * Copyright (c) 2006 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jason R. Thorpe.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <prop/prop_data.h>
+#include "prop_object_impl.h"
+
+#if defined(_KERNEL)
+#include <sys/systm.h>
+#elif defined(_STANDALONE)
+#include <sys/param.h>
+#include <lib/libkern/libkern.h>
+#else
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+#endif
+
+struct _prop_data {
+       struct _prop_object     pd_obj;
+       union {
+               void *          pdu_mutable;
+               const void *    pdu_immutable;
+       } pd_un;
+#define        pd_mutable              pd_un.pdu_mutable
+#define        pd_immutable            pd_un.pdu_immutable
+       size_t                  pd_size;
+       int                     pd_flags;
+};
+
+#define        PD_F_NOCOPY             0x01
+
+_PROP_POOL_INIT(_prop_data_pool, sizeof(struct _prop_data), "propdata")
+
+_PROP_MALLOC_DEFINE(M_PROP_DATA, "prop data",
+                   "property data container object")
+
+static _prop_object_free_rv_t
+               _prop_data_free(prop_stack_t, prop_object_t *);
+static bool    _prop_data_externalize(
+                               struct _prop_object_externalize_context *,
+                               void *);
+static _prop_object_equals_rv_t
+               _prop_data_equals(prop_object_t, prop_object_t,
+                                 void **, void **,
+                                 prop_object_t *, prop_object_t *);
+
+static const struct _prop_object_type _prop_object_type_data = {
+       .pot_type       =       PROP_TYPE_DATA,
+       .pot_free       =       _prop_data_free,
+       .pot_extern     =       _prop_data_externalize,
+       .pot_equals     =       _prop_data_equals,
+};
+
+#define        prop_object_is_data(x)          \
+       ((x) != NULL && (x)->pd_obj.po_type == &_prop_object_type_data)
+
+/* ARGSUSED */
+static _prop_object_free_rv_t
+_prop_data_free(prop_stack_t stack, prop_object_t *obj)
+{
+       prop_data_t pd = *obj;
+
+       if ((pd->pd_flags & PD_F_NOCOPY) == 0 && pd->pd_mutable != NULL)
+               _PROP_FREE(pd->pd_mutable, M_PROP_DATA);
+       _PROP_POOL_PUT(_prop_data_pool, pd);
+
+       return (_PROP_OBJECT_FREE_DONE);
+}
+
+static const char _prop_data_base64[] =
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+static const char _prop_data_pad64 = '=';
+
+static bool
+_prop_data_externalize(struct _prop_object_externalize_context *ctx, void *v)
+{
+       prop_data_t pd = v;
+       size_t i, srclen;
+       const uint8_t *src;
+       uint8_t output[4];
+       uint8_t input[3];
+
+       if (pd->pd_size == 0)
+               return (_prop_object_externalize_empty_tag(ctx, "data"));
+
+       if (_prop_object_externalize_start_tag(ctx, "data") == false)
+               return (false);
+
+       for (src = pd->pd_immutable, srclen = pd->pd_size;
+            srclen > 2; srclen -= 3) {
+               input[0] = *src++;
+               input[1] = *src++;
+               input[2] = *src++;
+
+               output[0] = (uint32_t)input[0] >> 2;
+               output[1] = ((uint32_t)(input[0] & 0x03) << 4) +
+                   ((uint32_t)input[1] >> 4);
+               output[2] = ((uint32_t)(input[1] & 0x0f) << 2) +
+                   ((uint32_t)input[2] >> 6);
+               output[3] = input[2] & 0x3f;
+               _PROP_ASSERT(output[0] < 64);
+               _PROP_ASSERT(output[1] < 64);
+               _PROP_ASSERT(output[2] < 64);
+               _PROP_ASSERT(output[3] < 64);
+
+               if (_prop_object_externalize_append_char(ctx,
+                               _prop_data_base64[output[0]]) == false ||
+                   _prop_object_externalize_append_char(ctx,
+                               _prop_data_base64[output[1]]) == false ||
+                   _prop_object_externalize_append_char(ctx,
+                               _prop_data_base64[output[2]]) == false ||
+                   _prop_object_externalize_append_char(ctx,
+                               _prop_data_base64[output[3]]) == false)
+                       return (false);
+       }
+
+       if (srclen != 0) {
+               input[0] = input[1] = input[2] = '\0';
+               for (i = 0; i < srclen; i++)
+                       input[i] = *src++;
+
+               output[0] = (uint32_t)input[0] >> 2;
+               output[1] = ((uint32_t)(input[0] & 0x03) << 4) +
+                   ((uint32_t)input[1] >> 4);
+               output[2] = ((uint32_t)(input[1] & 0x0f) << 2) +
+                   ((uint32_t)input[2] >> 6);
+               _PROP_ASSERT(output[0] < 64);
+               _PROP_ASSERT(output[1] < 64);
+               _PROP_ASSERT(output[2] < 64);
+
+               if (_prop_object_externalize_append_char(ctx,
+                               _prop_data_base64[output[0]]) == false ||
+                   _prop_object_externalize_append_char(ctx,
+                               _prop_data_base64[output[1]]) == false ||
+                   _prop_object_externalize_append_char(ctx,
+                               srclen == 1 ? _prop_data_pad64
+                               : _prop_data_base64[output[2]]) == false ||
+                   _prop_object_externalize_append_char(ctx,
+                               _prop_data_pad64) == false)
+                       return (false);
+       }
+
+       if (_prop_object_externalize_end_tag(ctx, "data") == false)
+               return (false);
+       
+       return (true);
+}
+
+/* ARGSUSED */
+static _prop_object_equals_rv_t
+_prop_data_equals(prop_object_t v1, prop_object_t v2,
+    void **stored_pointer1, void **stored_pointer2,
+    prop_object_t *next_obj1, prop_object_t *next_obj2)
+{
+       prop_data_t pd1 = v1;
+       prop_data_t pd2 = v2;
+
+       if (pd1 == pd2)
+               return (_PROP_OBJECT_EQUALS_TRUE);
+       if (pd1->pd_size != pd2->pd_size)
+               return (_PROP_OBJECT_EQUALS_FALSE);
+       if (pd1->pd_size == 0) {
+               _PROP_ASSERT(pd1->pd_immutable == NULL);
+               _PROP_ASSERT(pd2->pd_immutable == NULL);
+               return (_PROP_OBJECT_EQUALS_TRUE);
+       }
+       if (memcmp(pd1->pd_immutable, pd2->pd_immutable, pd1->pd_size) == 0)
+               return _PROP_OBJECT_EQUALS_TRUE;
+       else
+               return _PROP_OBJECT_EQUALS_FALSE;
+}
+
+static prop_data_t
+_prop_data_alloc(void)
+{
+       prop_data_t pd;
+
+       pd = _PROP_POOL_GET(_prop_data_pool);
+       if (pd != NULL) {
+               _prop_object_init(&pd->pd_obj, &_prop_object_type_data);
+
+               pd->pd_mutable = NULL;
+               pd->pd_size = 0;
+               pd->pd_flags = 0;
+       }
+
+       return (pd);
+}
+
+/*
+ * prop_data_create_data --
+ *     Create a data container that contains a copy of the data.
+ */
+prop_data_t
+prop_data_create_data(const void *v, size_t size)
+{
+       prop_data_t pd;
+       void *nv;
+
+       pd = _prop_data_alloc();
+       if (pd != NULL && size != 0) {
+               nv = _PROP_MALLOC(size, M_PROP_DATA);
+               if (nv == NULL) {
+                       prop_object_release(pd);
+                       return (NULL);
+               }
+               memcpy(nv, v, size);
+               pd->pd_mutable = nv;
+               pd->pd_size = size;
+       }
+       return (pd);
+}
+
+/*
+ * prop_data_create_data_nocopy --
+ *     Create an immutable data container that contains a refrence to the
+ *     provided external data.
+ */
+prop_data_t
+prop_data_create_data_nocopy(const void *v, size_t size)
+{
+       prop_data_t pd;
+       
+       pd = _prop_data_alloc();
+       if (pd != NULL) {
+               pd->pd_immutable = v;
+               pd->pd_size = size;
+               pd->pd_flags |= PD_F_NOCOPY;
+       }
+       return (pd);
+}
+
+/*
+ * prop_data_copy --
+ *     Copy a data container.  If the original data is external, then
+ *     the copy is also references the same external data.
+ */
+prop_data_t
+prop_data_copy(prop_data_t opd)
+{
+       prop_data_t pd;
+
+       if (! prop_object_is_data(opd))
+               return (NULL);
+
+       pd = _prop_data_alloc();
+       if (pd != NULL) {
+               pd->pd_size = opd->pd_size;
+               pd->pd_flags = opd->pd_flags;
+               if (opd->pd_flags & PD_F_NOCOPY)
+                       pd->pd_immutable = opd->pd_immutable;
+               else if (opd->pd_size != 0) {
+                       void *nv = _PROP_MALLOC(pd->pd_size, M_PROP_DATA);
+                       if (nv == NULL) {
+                               prop_object_release(pd);
+                               return (NULL);
+                       }
+                       memcpy(nv, opd->pd_immutable, opd->pd_size);
+                       pd->pd_mutable = nv;
+               }
+       }
+       return (pd);
+}
+
+/*
+ * prop_data_size --
+ *     Return the size of the data.
+ */
+size_t
+prop_data_size(prop_data_t pd)
+{
+
+       if (! prop_object_is_data(pd))
+               return (0);
+
+       return (pd->pd_size);
+}
+
+/*
+ * prop_data_data --
+ *     Return a copy of the contents of the data container.
+ *     The data is allocated with the M_TEMP malloc type.
+ *     If the data container is empty, NULL is returned.
+ */
+void *
+prop_data_data(prop_data_t pd)
+{
+       void *v;
+
+       if (! prop_object_is_data(pd))
+               return (NULL);
+
+       if (pd->pd_size == 0) {
+               _PROP_ASSERT(pd->pd_immutable == NULL);
+               return (NULL);
+       }
+
+       _PROP_ASSERT(pd->pd_immutable != NULL);
+
+       v = _PROP_MALLOC(pd->pd_size, M_TEMP);
+       if (v != NULL)
+               memcpy(v, pd->pd_immutable, pd->pd_size);
+       
+       return (v);
+}
+
+/*
+ * prop_data_data_nocopy --
+ *     Return an immutable reference to the contents of the data
+ *     container.
+ */
+const void *
+prop_data_data_nocopy(prop_data_t pd)
+{
+
+       if (! prop_object_is_data(pd))
+               return (NULL);
+
+       _PROP_ASSERT((pd->pd_size == 0 && pd->pd_immutable == NULL) ||
+                    (pd->pd_size != 0 && pd->pd_immutable != NULL));
+
+       return (pd->pd_immutable);
+}
+
+/*
+ * prop_data_equals --
+ *     Return true if two strings are equivalent.
+ */
+bool
+prop_data_equals(prop_data_t pd1, prop_data_t pd2)
+{
+       if (!prop_object_is_data(pd1) || !prop_object_is_data(pd2))
+               return (false);
+
+       return (prop_object_equals(pd1, pd2));
+}
+
+/*
+ * prop_data_equals_data --
+ *     Return true if the contained data is equivalent to the specified
+ *     external data.
+ */
+bool
+prop_data_equals_data(prop_data_t pd, const void *v, size_t size)
+{
+
+       if (! prop_object_is_data(pd))
+               return (false);
+
+       if (pd->pd_size != size)
+               return (false);
+       return (memcmp(pd->pd_immutable, v, size) == 0);
+}
+
+static bool
+_prop_data_internalize_decode(struct _prop_object_internalize_context *ctx,
+                            uint8_t *target, size_t targsize, size_t *sizep,
+                            const char **cpp)
+{
+       const char *src;
+       size_t tarindex;
+       int state, ch;
+       const char *pos;
+
+       state = 0;
+       tarindex = 0;
+       src = ctx->poic_cp;
+
+       for (;;) {
+               ch = (unsigned char) *src++;
+               if (_PROP_EOF(ch))
+                       return (false);
+               if (_PROP_ISSPACE(ch))
+                       continue;
+               if (ch == '<') {
+                       src--;
+                       break;
+               }
+               if (ch == _prop_data_pad64)
+                       break;
+
+               pos = strchr(_prop_data_base64, ch);
+               if (pos == NULL)
+                       return (false);
+
+               switch (state) {
+               case 0:
+                       if (target) {
+                               if (tarindex >= targsize)
+                                       return (false);
+                               target[tarindex] =
+                                   (uint8_t)((pos - _prop_data_base64) << 2);
+                       }
+                       state = 1;
+                       break;
+
+               case 1:
+                       if (target) {
+                               if (tarindex + 1 >= targsize)
+                                       return (false);
+                               target[tarindex] |=
+                                   (uint32_t)(pos - _prop_data_base64) >> 4;
+                               target[tarindex + 1] =
+                                   (uint8_t)(((pos - _prop_data_base64) & 0xf)
+                                       << 4);
+                       }
+                       tarindex++;
+                       state = 2;
+                       break;
+
+               case 2:
+                       if (target) {
+                               if (tarindex + 1 >= targsize)
+                                       return (false);
+                               target[tarindex] |=
+                                   (uint32_t)(pos - _prop_data_base64) >> 2;
+                               target[tarindex + 1] =
+                                   (uint8_t)(((pos - _prop_data_base64)
+                                       & 0x3) << 6);
+                       }
+                       tarindex++;
+                       state = 3;
+                       break;
+
+               case 3:
+                       if (target) {
+                               if (tarindex >= targsize)
+                                       return (false);
+                               target[tarindex] |= (uint8_t)
+                                   (pos - _prop_data_base64);
+                       }
+                       tarindex++;
+                       state = 0;
+                       break;
+
+               default:
+                       _PROP_ASSERT(/*CONSTCOND*/0);
+               }
+       }
+
+       /*
+        * We are done decoding the Base64 characters.  Let's see if we
+        * ended up on a byte boundary and/or with unrecognized trailing
+        * characters.
+        */
+       if (ch == _prop_data_pad64) {
+               ch = (unsigned char) *src;      /* src already advanced */
+               if (_PROP_EOF(ch))
+                       return (false);
+               switch (state) {
+               case 0:         /* Invalid = in first position */
+               case 1:         /* Invalid = in second position */
+                       return (false);
+
+               case 2:         /* Valid, one byte of info */
+                       /* Skip whitespace */
+                       for (ch = (unsigned char) *src++;
+                            ch != '<'; ch = (unsigned char) *src++) {
+                               if (_PROP_EOF(ch))
+                                       return (false);
+                               if (!_PROP_ISSPACE(ch))
+                                       break;
+                       }
+                       /* Make sure there is another trailing = */
+                       if (ch != _prop_data_pad64)
+                               return (false);
+                       ch = (unsigned char) *src;
+                       /* FALLTHROUGH */
+               
+               case 3:         /* Valid, two bytes of info */
+                       /*
+                        * We know this char is a =.  Is there anything but
+                        * whitespace after it?
+                        */
+                       for (ch = (unsigned char) *src++;
+                            ch != '<'; ch = (unsigned char) *src++) {
+                               if (_PROP_EOF(ch))
+                                       return (false);
+                               if (!_PROP_ISSPACE(ch))
+                                       return (false);
+                       }
+                       /* back up to '<' */
+                       src--;
+               }
+       } else {
+               /*
+                * We ended by seeing the end of the Base64 string.  Make
+                * sure there are no partial bytes lying around.
+                */
+               if (state != 0)
+                       return (false);
+       }
+
+       _PROP_ASSERT(*src == '<');
+       if (sizep != NULL)
+               *sizep = tarindex;
+       if (cpp != NULL)
+               *cpp = src;
+
+       return (true);
+}
+
+/*
+ * _prop_data_internalize --
+ *     Parse a <data>...</data> and return the object created from the
+ *     external representation.
+ */
+
+/* strtoul is used for parsing, enforce. */
+typedef int PROP_DATA_ASSERT[/* CONSTCOND */sizeof(size_t) == sizeof(unsigned long) ? 1 : -1];
+
+/* ARGSUSED */
+bool
+_prop_data_internalize(prop_stack_t stack, prop_object_t *obj,
+    struct _prop_object_internalize_context *ctx)
+{
+       prop_data_t data;
+       uint8_t *buf;
+       size_t len, alen;
+
+       /*
+        * We don't accept empty elements.
+        * This actually only checks for the node to be <data/>
+        * (Which actually causes another error if found.)
+        */
+       if (ctx->poic_is_empty_element)
+               return (true);
+
+       /*
+        * If we got a "size" attribute, get the size of the data blob
+        * from that.  Otherwise, we have to figure it out from the base64.
+        */
+       if (ctx->poic_tagattr != NULL) {
+               char *cp;
+
+               if (!_PROP_TAGATTR_MATCH(ctx, "size") ||
+                   ctx->poic_tagattrval_len == 0)
+                       return (true);
+
+#ifndef _KERNEL
+               errno = 0;
+#endif
+               len = strtoul(ctx->poic_tagattrval, &cp, 0);
+#ifndef _KERNEL                /* XXX can't check for ERANGE in the kernel */
+               if (len == ULONG_MAX && errno == ERANGE)
+                       return (true);
+#endif
+               if (cp != ctx->poic_tagattrval + ctx->poic_tagattrval_len)
+                       return (true);
+               _PROP_ASSERT(*cp == '\"');
+       } else if (_prop_data_internalize_decode(ctx, NULL, 0, &len,
+                                               NULL) == false)
+               return (true);
+
+       /*
+        * Always allocate one extra in case we don't land on an even byte
+        * boundary during the decode.
+        */
+       buf = _PROP_MALLOC(len + 1, M_PROP_DATA);
+       if (buf == NULL)
+               return (true);
+       
+       if (_prop_data_internalize_decode(ctx, buf, len + 1, &alen,
+                                         &ctx->poic_cp) == false) {
+               _PROP_FREE(buf, M_PROP_DATA);
+               return (true);
+       }
+       if (alen != len) {
+               _PROP_FREE(buf, M_PROP_DATA);
+               return (true);
+       }
+
+       if (_prop_object_internalize_find_tag(ctx, "data",
+                                             _PROP_TAG_TYPE_END) == false) {
+               _PROP_FREE(buf, M_PROP_DATA);
+               return (true);
+       }
+
+       data = _prop_data_alloc();
+       if (data == NULL) {
+               _PROP_FREE(buf, M_PROP_DATA);
+               return (true);
+       }
+
+       /*
+        * Handle alternate type of empty node.
+        * XML document could contain open/close tags, yet still be empty.
+        */
+       if (alen == 0) {
+               _PROP_FREE(buf, M_PROP_DATA);
+               data->pd_mutable = NULL;
+       } else {
+               data->pd_mutable = buf;
+       }
+       data->pd_size = len;
+
+       *obj = data;
+       return (true);
+}
diff --git a/common/lib/libprop/prop_dictionary.3 b/common/lib/libprop/prop_dictionary.3
new file mode 100644 (file)
index 0000000..82743e3
--- /dev/null
@@ -0,0 +1,353 @@
+.\"    $NetBSD: prop_dictionary.3,v 1.18 2011/09/30 22:08:18 jym Exp $
+.\"
+.\" Copyright (c) 2006, 2009 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by Jason R. Thorpe.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd December 5, 2009
+.Dt PROP_DICTIONARY 3
+.Os
+.Sh NAME
+.Nm prop_dictionary ,
+.Nm prop_dictionary_create ,
+.Nm prop_dictionary_create_with_capacity ,
+.Nm prop_dictionary_copy ,
+.Nm prop_dictionary_copy_mutable ,
+.Nm prop_dictionary_count ,
+.Nm prop_dictionary_ensure_capacity ,
+.Nm prop_dictionary_iterator ,
+.Nm prop_dictionary_all_keys ,
+.Nm prop_dictionary_make_immutable ,
+.Nm prop_dictionary_mutable ,
+.Nm prop_dictionary_get ,
+.Nm prop_dictionary_set ,
+.Nm prop_dictionary_remove ,
+.Nm prop_dictionary_get_keysym ,
+.Nm prop_dictionary_set_keysym ,
+.Nm prop_dictionary_remove_keysym ,
+.Nm prop_dictionary_externalize ,
+.Nm prop_dictionary_internalize ,
+.Nm prop_dictionary_externalize_to_file ,
+.Nm prop_dictionary_internalize_from_file ,
+.Nm prop_dictionary_externalize_to_pref ,
+.Nm prop_dictionary_internalize_from_pref ,
+.Nm prop_dictionary_equals ,
+.Nm prop_dictionary_keysym_cstring_nocopy ,
+.Nm prop_dictionary_keysym_equals
+.Nd dictionary property collection object
+.Sh LIBRARY
+.Lb libprop
+.Sh SYNOPSIS
+.In prop/proplib.h
+.\"
+.Ft prop_dictionary_t
+.Fn prop_dictionary_create "void"
+.Ft prop_dictionary_t
+.Fn prop_dictionary_create_with_capacity "unsigned int capacity"
+.\"
+.Ft prop_dictionary_t
+.Fn prop_dictionary_copy "prop_dictionary_t dict"
+.Ft prop_dictionary_t
+.Fn prop_dictionary_copy_mutable "prop_dictionary_t dict"
+.\"
+.Ft unsigned int
+.Fn prop_dictionary_count "prop_dictionary_t dict"
+.Ft bool
+.Fn prop_dictionary_ensure_capacity "prop_dictionary_t dict" \
+    "unsigned int capacity"
+.\"
+.Ft prop_object_iterator_t
+.Fn prop_dictionary_iterator "prop_dictionary_t dict"
+.Ft prop_array_t
+.Fn prop_dictionary_all_keys "prop_dictionary_t dict"
+.\"
+.Ft void
+.Fn prop_dictionary_make_immutable "prop_dictionary_t dict"
+.Ft bool
+.Fn prop_dictionary_mutable "prop_dictionary_t dict"
+.\"
+.Ft prop_object_t
+.Fn prop_dictionary_get "prop_dictionary_t dict" "const char *key"
+.Ft bool
+.Fn prop_dictionary_set "prop_dictionary_t dict" "const char *key" \
+    "prop_object_t obj"
+.Ft void
+.Fn prop_dictionary_remove "prop_dictionary_t dict" "const char *key"
+.\"
+.Ft prop_object_t
+.Fn prop_dictionary_get_keysym "prop_dictionary_t dict" \
+    "prop_dictionary_keysym_t keysym"
+.Ft bool
+.Fn prop_dictionary_set_keysym "prop_dictionary_t dict" \
+    "prop_dictionary_keysym_t keysym" "prop_object_t obj"
+.Ft void
+.Fn prop_dictionary_remove_keysym "prop_dictionary_t dict" \
+    "prop_dictionary_keysym_t keysym"
+.\"
+.Ft bool
+.Fn prop_dictionary_equals "prop_dictionary_t dict1" "prop_dictionary_t dict2"
+.\"
+.Ft const char *
+.Fn prop_dictionary_keysym_cstring_nocopy "prop_dictionary_keysym_t sym"
+.\"
+.Ft bool
+.Fn prop_dictionary_keysym_equals "prop_dictionary_keysym_t keysym1" \
+    "prop_dictionary_keysym_t keysym2"
+.\"
+.Ft char *
+.Fn prop_dictionary_externalize "prop_dictionary_t dict"
+.Ft prop_dictionary_t
+.Fn prop_dictionary_internalize "const char *xml"
+.\"
+.Ft bool
+.Fn prop_dictionary_externalize_to_file "prop_dictionary_t dict" \
+    "const char *path"
+.Ft prop_dictionary_t
+.Fn prop_dictionary_internalize_from_file "const char *path"
+.\"
+.Ft bool
+.Fn prop_dictionary_externalize_to_pref "prop_dictionary_t dict" \
+    "struct plistref *pref"
+.Ft bool
+.Fn prop_dictionary_internalize_from_pref "const struct plistref *pref" \
+    "prop_dictionary_t *dictp"
+.\"
+.Sh DESCRIPTION
+The
+.Nm prop_dictionary
+family of functions operate on the dictionary property collection object type.
+A dictionary is an unordered set of objects stored as key-value pairs.
+.Bl -tag -width "xxxxx"
+.It Fn prop_dictionary_create "void"
+Create an empty dictionary.
+The dictionary initially has no capacity.
+Returns
+.Dv NULL
+on failure.
+.It Fn prop_dictionary_create_with_capacity "unsigned int capacity"
+Create a dictionary with the capacity to store
+.Fa capacity
+objects.
+Returns
+.Dv NULL
+on failure.
+.It Fn prop_dictionary_copy "prop_dictionary_t dict"
+Copy a dictionary.
+The new dictionary has an initial capacity equal to the number of objects
+stored in the dictionary being copied.
+The new dictionary contains references to the original dictionary's objects,
+not copies of those objects
+.Pq i.e. a shallow copy is made .
+If the original dictionary is immutable, the resulting dictionary is also
+immutable.
+.It Fn prop_dictionary_copy_mutable "prop_dictionary_t dict"
+Like
+.Fn prop_dictionary_copy ,
+except the resulting dictionary is always mutable.
+.It Fn prop_dictionary_count "prop_dictionary_t dict"
+Returns the number of objects stored in the dictionary.
+.It Fn prop_dictionary_ensure_capacity "prop_dictionary_t dict" \
+    "unsigned int capacity"
+Ensure that the dictionary has a total capacity of
+.Fa capacity ,
+including objects already stored in the dictionary.
+Returns
+.Dv true
+if the capacity of the dictionary is greater or equal to
+.Fa capacity
+or if the expansion of the dictionary's capacity was successful
+and
+.Dv false
+otherwise.
+If the supplied object isn't a dictionary,
+.Dv false
+is returned.
+.It Fn prop_dictionary_iterator "prop_dictionary_t dict"
+Create an iterator for the dictionary.
+The dictionary is retained by the iterator.
+A dictionary iterator returns the key symbols used to look up objects stored
+in the dictionary; to get the object itself, a dictionary lookup using this
+key symbol must be performed.
+Storing to or removing from the dictionary invalidates any active iterators for
+the dictionary.
+Returns
+.Dv NULL
+on failure.
+.It Fn prop_dictionary_all_keys "prop_dictionary_t dict"
+Return an array of all of the dictionary key symbols
+.Pq prop_dictionary_keysym_t
+in the dictionary.
+This provides a way to iterate over the items in the dictionary while
+retaining the ability to mutate the dictionary; instead of iterating
+over the dictionary itself, iterate over the array of keys.
+The caller is responsible for releasing the array.
+Returns
+.Dv NULL
+on failure.
+.It Fn prop_dictionary_make_immutable "prop_dictionary_t dict"
+Make
+.Fa dict
+immutable.
+.It Fn prop_dictionary_mutable "prop_dictionary_t dict"
+Returns
+.Dv true
+if the dictionary is mutable.
+.It Fn prop_dictionary_get "prop_dictionary_t dict" "const char *key"
+Return the object stored in the dictionary with the key
+.Fa key .
+If no object is stored with the specified key,
+.Dv NULL
+is returned.
+.It Fn prop_dictionary_set "prop_dictionary_t dict" "const char *key" \
+       "prop_object_t obj"
+Store a reference to the object
+.Fa obj
+with the key
+.Fa key .
+The object will be retained by the dictionary.
+If the key already exists in the dictionary, the object associated with
+that key will be released and replaced with the new object.
+Returns
+.Dv true
+if storing the object was successful and
+.Dv false
+otherwise.
+.It Fn prop_dictionary_remove "prop_dictionary_t dict" "const char *key"
+Remove the reference to the object stored in the dictionary with the key
+.Fa key .
+The object will be released.
+.It Fn prop_dictionary_get_keysym "prop_dictionary_t dict" \
+    "prop_dictionary_keysym_t sym"
+Like
+.Fn prop_dictionary_get ,
+but the lookup is performed using a key symbol returned by a dictionary
+iterator.
+The results are undefined if the iterator used to obtain the key symbol
+is not associated with
+.Fa dict .
+.It Fn prop_dictionary_set_keysym "prop_dictionary_t dict" \
+    "prop_dictionary_keysym_t sym" "prop_object_t obj"
+Like
+.Fn prop_dictionary_set ,
+but the lookup of the object to replace is performed using a key symbol
+returned by a dictionary iterator.
+The results are undefined if the iterator used to obtain the key symbol
+is not associated with
+.Fa dict .
+.It Fn prop_dictionary_remove_keysym "prop_dictionary_t dict" \
+    "prop_dictionary_keysym_t sym"
+Like
+.Fn prop_dictionary_remove ,
+but the lookup of the object to remove is performed using a key symbol
+returned by a dictionary iterator.
+The results are undefined if the iterator used to obtain the key symbol
+is not associated with
+.Fa dict .
+.It Fn prop_dictionary_equals "prop_dictionary_t dict1" \
+    "prop_dictionary_t dict2"
+Returns
+.Dv true
+if the two dictionaries are equivalent.
+Note: Objects contained in the dictionary are compared by value, not by
+reference.
+.It Fn prop_dictionary_keysym_cstring_nocopy "prop_dictionary_keysym_t keysym"
+Returns an immutable reference to the dictionary key symbol's string value.
+.It Fn prop_dictionary_keysym_equals "prop_dictionary_keysym_t keysym1" \
+    "prop_dictionary_keysym_t keysym2"
+Returns
+.Dv true
+if the two dictionary key symbols are equivalent.
+.It Fn prop_dictionary_externalize "prop_dictionary_t dict"
+Externalizes a dictionary, returning a NUL-terminated buffer containing
+the XML representation of the dictionary.
+The caller is responsible for freeing the returned buffer.
+If converting to the external representation fails for any reason,
+.Dv NULL
+is returned.
+.Pp
+In user space, the buffer is allocated using
+.Xr malloc 3 .
+In the kernel, the buffer is allocated using
+.Xr malloc 9
+using the malloc type
+.Dv M_TEMP .
+.It Fn prop_dictionary_internalize "const char *xml"
+Parse the XML representation of a property list in the NUL-terminated
+buffer
+.Fa xml
+and return the corresponding dictionary.
+Returns
+.Dv NULL
+if parsing fails for any reason.
+.It Fn prop_dictionary_externalize_to_file "prop_dictionary_t dict" \
+    "const char *path"
+Externalizes a dictionary and writes it to the file specified by
+.Fa path .
+The file is saved with the mode
+.Dv 0666
+as modified by the process's file creation mask
+.Pq see Xr umask 2
+and is written atomically.
+Returns
+.Dv false
+if externalizing or writing the dictionary fails for any reason.
+.It Fn prop_dictionary_internalize_from_file "const char *path"
+Reads the XML property list contained in the file specified by
+.Fa path ,
+internalizes it, and returns the corresponding dictionary.
+Returns
+.Dv NULL
+on failure.
+.It Fn prop_dictionary_externalize_to_pref "prop_dictionary_t dict" \
+    "struct plistref *pref"
+Externalizes a dictionary and packs it into the plistref specified by
+.Fa pref .
+Returns
+.Dv false
+if externalizing the dictionary fails for any reason.
+.It Fn prop_dictionary_internalize_from_pref "const struct plistref *pref" \
+    "prop_dictionary_t *dictp"
+Reads the plistref specified by
+.Fa pref ,
+internalizes it, and returns the corresponding dictionary.
+Returns
+.Dv false
+if internalizing or writing the dictionary fails for any reason.
+.El
+.Sh SEE ALSO
+.Xr prop_array 3 ,
+.Xr prop_bool 3 ,
+.Xr prop_data 3 ,
+.Xr prop_dictionary_util 3 ,
+.Xr prop_number 3 ,
+.Xr prop_object 3 ,
+.Xr prop_string 3 ,
+.Xr proplib 3
+.Sh HISTORY
+The
+.Nm proplib
+property container object library first appeared in
+.Nx 4.0 .
diff --git a/common/lib/libprop/prop_dictionary.c b/common/lib/libprop/prop_dictionary.c
new file mode 100644 (file)
index 0000000..8c2dcea
--- /dev/null
@@ -0,0 +1,1420 @@
+/*     $NetBSD: prop_dictionary.c,v 1.37 2011/04/20 19:40:00 martin Exp $      */
+
+/*-
+ * Copyright (c) 2006, 2007 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jason R. Thorpe.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <prop/prop_array.h>
+#include <prop/prop_dictionary.h>
+#include <prop/prop_string.h>
+#include "prop_object_impl.h"
+#include "prop_rb_impl.h"
+
+#if !defined(_KERNEL) && !defined(_STANDALONE)
+#include <errno.h>
+#endif
+
+/*
+ * We implement these like arrays, but we keep them sorted by key.
+ * This allows us to binary-search as well as keep externalized output
+ * sane-looking for human eyes.
+ */
+
+#define        EXPAND_STEP             16
+
+/*
+ * prop_dictionary_keysym_t is allocated with space at the end to hold the
+ * key.  This must be a regular object so that we can maintain sane iterator
+ * semantics -- we don't want to require that the caller release the result
+ * of prop_object_iterator_next().
+ *
+ * We'd like to have some small'ish keysym objects for up-to-16 characters
+ * in a key, some for up-to-32 characters in a key, and then a final bucket
+ * for up-to-128 characters in a key (not including NUL).  Keys longer than
+ * 128 characters are not allowed.
+ */
+struct _prop_dictionary_keysym {
+       struct _prop_object             pdk_obj;
+       size_t                          pdk_size;
+       struct rb_node                  pdk_link;
+       char                            pdk_key[1];
+       /* actually variable length */
+};
+
+       /* pdk_key[1] takes care of the NUL */
+#define        PDK_SIZE_16             (sizeof(struct _prop_dictionary_keysym) + 16)
+#define        PDK_SIZE_32             (sizeof(struct _prop_dictionary_keysym) + 32)
+#define        PDK_SIZE_128            (sizeof(struct _prop_dictionary_keysym) + 128)
+
+#define        PDK_MAXKEY              128
+
+_PROP_POOL_INIT(_prop_dictionary_keysym16_pool, PDK_SIZE_16, "pdict16")
+_PROP_POOL_INIT(_prop_dictionary_keysym32_pool, PDK_SIZE_32, "pdict32")
+_PROP_POOL_INIT(_prop_dictionary_keysym128_pool, PDK_SIZE_128, "pdict128")
+
+struct _prop_dict_entry {
+       prop_dictionary_keysym_t        pde_key;
+       prop_object_t                   pde_objref;
+};
+
+struct _prop_dictionary {
+       struct _prop_object     pd_obj;
+       _PROP_RWLOCK_DECL(pd_rwlock)
+       struct _prop_dict_entry *pd_array;
+       unsigned int            pd_capacity;
+       unsigned int            pd_count;
+       int                     pd_flags;
+
+       uint32_t                pd_version;
+};
+
+#define        PD_F_IMMUTABLE          0x01    /* dictionary is immutable */
+
+_PROP_POOL_INIT(_prop_dictionary_pool, sizeof(struct _prop_dictionary),
+               "propdict")
+_PROP_MALLOC_DEFINE(M_PROP_DICT, "prop dictionary",
+                   "property dictionary container object")
+
+static _prop_object_free_rv_t
+               _prop_dictionary_free(prop_stack_t, prop_object_t *);
+static void    _prop_dictionary_emergency_free(prop_object_t);
+static bool    _prop_dictionary_externalize(
+                               struct _prop_object_externalize_context *,
+                               void *);
+static _prop_object_equals_rv_t
+               _prop_dictionary_equals(prop_object_t, prop_object_t,
+                                       void **, void **,
+                                       prop_object_t *, prop_object_t *);
+static void    _prop_dictionary_equals_finish(prop_object_t, prop_object_t);
+static prop_object_iterator_t
+               _prop_dictionary_iterator_locked(prop_dictionary_t);
+static prop_object_t
+               _prop_dictionary_iterator_next_object_locked(void *);
+static prop_object_t
+               _prop_dictionary_get_keysym(prop_dictionary_t,
+                                           prop_dictionary_keysym_t, bool);
+static prop_object_t
+               _prop_dictionary_get(prop_dictionary_t, const char *, bool);
+
+static void _prop_dictionary_lock(void);
+static void _prop_dictionary_unlock(void);
+
+static const struct _prop_object_type _prop_object_type_dictionary = {
+       .pot_type               =       PROP_TYPE_DICTIONARY,
+       .pot_free               =       _prop_dictionary_free,
+       .pot_emergency_free     =       _prop_dictionary_emergency_free,
+       .pot_extern             =       _prop_dictionary_externalize,
+       .pot_equals             =       _prop_dictionary_equals,
+       .pot_equals_finish      =       _prop_dictionary_equals_finish,
+       .pot_lock               =       _prop_dictionary_lock,
+       .pot_unlock             =       _prop_dictionary_unlock,                
+};
+
+static _prop_object_free_rv_t
+               _prop_dict_keysym_free(prop_stack_t, prop_object_t *);
+static bool    _prop_dict_keysym_externalize(
+                               struct _prop_object_externalize_context *,
+                               void *);
+static _prop_object_equals_rv_t
+               _prop_dict_keysym_equals(prop_object_t, prop_object_t,
+                                        void **, void **,
+                                        prop_object_t *, prop_object_t *);
+
+static const struct _prop_object_type _prop_object_type_dict_keysym = {
+       .pot_type       =       PROP_TYPE_DICT_KEYSYM,
+       .pot_free       =       _prop_dict_keysym_free,
+       .pot_extern     =       _prop_dict_keysym_externalize,
+       .pot_equals     =       _prop_dict_keysym_equals,
+};
+
+#define        prop_object_is_dictionary(x)            \
+       ((x) != NULL && (x)->pd_obj.po_type == &_prop_object_type_dictionary)
+#define        prop_object_is_dictionary_keysym(x)     \
+       ((x) != NULL && (x)->pdk_obj.po_type == &_prop_object_type_dict_keysym)
+
+#define        prop_dictionary_is_immutable(x)         \
+                               (((x)->pd_flags & PD_F_IMMUTABLE) != 0)
+
+struct _prop_dictionary_iterator {
+       struct _prop_object_iterator pdi_base;
+       unsigned int            pdi_index;
+};
+
+/*
+ * Dictionary key symbols are immutable, and we are likely to have many
+ * duplicated key symbols.  So, to save memory, we unique'ify key symbols
+ * so we only have to have one copy of each string.
+ */
+
+static int
+/*ARGSUSED*/
+_prop_dict_keysym_rb_compare_nodes(void *ctx __unused,
+                                  const void *n1, const void *n2)
+{
+       const struct _prop_dictionary_keysym *pdk1 = n1;
+       const struct _prop_dictionary_keysym *pdk2 = n2;
+
+       return strcmp(pdk1->pdk_key, pdk2->pdk_key);
+}
+
+static int
+/*ARGSUSED*/
+_prop_dict_keysym_rb_compare_key(void *ctx __unused,
+                                const void *n, const void *v)
+{
+       const struct _prop_dictionary_keysym *pdk = n;
+       const char *cp = v;
+
+       return strcmp(pdk->pdk_key, cp);
+}
+
+static const rb_tree_ops_t _prop_dict_keysym_rb_tree_ops = {
+       .rbto_compare_nodes = _prop_dict_keysym_rb_compare_nodes,
+       .rbto_compare_key = _prop_dict_keysym_rb_compare_key,
+       .rbto_node_offset = offsetof(struct _prop_dictionary_keysym, pdk_link),
+       .rbto_context = NULL
+};
+
+static struct rb_tree _prop_dict_keysym_tree;
+
+_PROP_ONCE_DECL(_prop_dict_init_once)
+_PROP_MUTEX_DECL_STATIC(_prop_dict_keysym_tree_mutex)
+
+static int
+_prop_dict_init(void)
+{
+
+       _PROP_MUTEX_INIT(_prop_dict_keysym_tree_mutex);
+       _prop_rb_tree_init(&_prop_dict_keysym_tree,
+                          &_prop_dict_keysym_rb_tree_ops);
+       return 0;
+}
+
+static void
+_prop_dict_keysym_put(prop_dictionary_keysym_t pdk)
+{
+
+       if (pdk->pdk_size <= PDK_SIZE_16)
+               _PROP_POOL_PUT(_prop_dictionary_keysym16_pool, pdk);
+       else if (pdk->pdk_size <= PDK_SIZE_32)
+               _PROP_POOL_PUT(_prop_dictionary_keysym32_pool, pdk);
+       else {
+               _PROP_ASSERT(pdk->pdk_size <= PDK_SIZE_128);
+               _PROP_POOL_PUT(_prop_dictionary_keysym128_pool, pdk);
+       }
+}
+
+/* ARGSUSED */
+static _prop_object_free_rv_t
+_prop_dict_keysym_free(prop_stack_t stack, prop_object_t *obj)
+{
+       prop_dictionary_keysym_t pdk = *obj;
+
+       _prop_rb_tree_remove_node(&_prop_dict_keysym_tree, pdk);
+       _prop_dict_keysym_put(pdk);
+
+       return _PROP_OBJECT_FREE_DONE;
+}
+
+static bool
+_prop_dict_keysym_externalize(struct _prop_object_externalize_context *ctx,
+                            void *v)
+{
+       prop_dictionary_keysym_t pdk = v;
+
+       /* We externalize these as strings, and they're never empty. */
+
+       _PROP_ASSERT(pdk->pdk_key[0] != '\0');
+
+       if (_prop_object_externalize_start_tag(ctx, "string") == false ||
+           _prop_object_externalize_append_encoded_cstring(ctx,
+                                               pdk->pdk_key) == false ||
+           _prop_object_externalize_end_tag(ctx, "string") == false)
+               return (false);
+       
+       return (true);
+}
+
+/* ARGSUSED */
+static _prop_object_equals_rv_t
+_prop_dict_keysym_equals(prop_object_t v1, prop_object_t v2,
+    void **stored_pointer1, void **stored_pointer2,
+    prop_object_t *next_obj1, prop_object_t *next_obj2)
+{
+       prop_dictionary_keysym_t pdk1 = v1;
+       prop_dictionary_keysym_t pdk2 = v2;
+
+       /*
+        * There is only ever one copy of a keysym at any given time,
+        * so we can reduce this to a simple pointer equality check.
+        */
+       if (pdk1 == pdk2)
+               return _PROP_OBJECT_EQUALS_TRUE;
+       else
+               return _PROP_OBJECT_EQUALS_FALSE;
+}
+
+static prop_dictionary_keysym_t
+_prop_dict_keysym_alloc(const char *key)
+{
+       prop_dictionary_keysym_t opdk, pdk, rpdk;
+       size_t size;
+
+       _PROP_ONCE_RUN(_prop_dict_init_once, _prop_dict_init);
+
+       /*
+        * Check to see if this already exists in the tree.  If it does,
+        * we just retain it and return it.
+        */
+       _PROP_MUTEX_LOCK(_prop_dict_keysym_tree_mutex);
+       opdk = _prop_rb_tree_find(&_prop_dict_keysym_tree, key);
+       if (opdk != NULL) {
+               prop_object_retain(opdk);
+               _PROP_MUTEX_UNLOCK(_prop_dict_keysym_tree_mutex);
+               return (opdk);
+       }
+       _PROP_MUTEX_UNLOCK(_prop_dict_keysym_tree_mutex);
+
+       /*
+        * Not in the tree.  Create it now.
+        */
+
+       size = sizeof(*pdk) + strlen(key) /* pdk_key[1] covers the NUL */;
+
+       if (size <= PDK_SIZE_16)
+               pdk = _PROP_POOL_GET(_prop_dictionary_keysym16_pool);
+       else if (size <= PDK_SIZE_32)
+               pdk = _PROP_POOL_GET(_prop_dictionary_keysym32_pool);
+       else if (size <= PDK_SIZE_128)
+               pdk = _PROP_POOL_GET(_prop_dictionary_keysym128_pool);
+       else
+               pdk = NULL;     /* key too long */
+
+       if (pdk == NULL)
+               return (NULL);
+
+       _prop_object_init(&pdk->pdk_obj, &_prop_object_type_dict_keysym);
+
+       strcpy(pdk->pdk_key, key);
+       pdk->pdk_size = size;
+
+       /*
+        * We dropped the mutex when we allocated the new object, so
+        * we have to check again if it is in the tree.
+        */
+       _PROP_MUTEX_LOCK(_prop_dict_keysym_tree_mutex);
+       opdk = _prop_rb_tree_find(&_prop_dict_keysym_tree, key);
+       if (opdk != NULL) {
+               prop_object_retain(opdk);
+               _PROP_MUTEX_UNLOCK(_prop_dict_keysym_tree_mutex);
+               _prop_dict_keysym_put(pdk);
+               return (opdk);
+       }
+       rpdk = _prop_rb_tree_insert_node(&_prop_dict_keysym_tree, pdk);
+       _PROP_ASSERT(rpdk == pdk);
+       _PROP_MUTEX_UNLOCK(_prop_dict_keysym_tree_mutex);
+       return (pdk);
+}
+
+static _prop_object_free_rv_t
+_prop_dictionary_free(prop_stack_t stack, prop_object_t *obj)
+{
+       prop_dictionary_t pd = *obj;
+       prop_dictionary_keysym_t pdk;
+       prop_object_t po;
+
+       _PROP_ASSERT(pd->pd_count <= pd->pd_capacity);
+       _PROP_ASSERT((pd->pd_capacity == 0 && pd->pd_array == NULL) ||
+                    (pd->pd_capacity != 0 && pd->pd_array != NULL));
+
+       /* The empty dictorinary is easy, handle that first. */
+       if (pd->pd_count == 0) {
+               if (pd->pd_array != NULL)
+                       _PROP_FREE(pd->pd_array, M_PROP_DICT);
+
+               _PROP_RWLOCK_DESTROY(pd->pd_rwlock);
+
+               _PROP_POOL_PUT(_prop_dictionary_pool, pd);
+
+               return (_PROP_OBJECT_FREE_DONE);
+       }
+
+       po = pd->pd_array[pd->pd_count - 1].pde_objref;
+       _PROP_ASSERT(po != NULL);
+
+       if (stack == NULL) {
+               /*
+                * If we are in emergency release mode,
+                * just let caller recurse down.
+                */
+               *obj = po;
+               return (_PROP_OBJECT_FREE_FAILED);
+       }
+
+       /* Otherwise, try to push the current object on the stack. */
+       if (!_prop_stack_push(stack, pd, NULL, NULL, NULL)) {
+               /* Push failed, entering emergency release mode. */
+               return (_PROP_OBJECT_FREE_FAILED);
+       }
+       /* Object pushed on stack, caller will release it. */
+       --pd->pd_count;
+       pdk = pd->pd_array[pd->pd_count].pde_key;
+       _PROP_ASSERT(pdk != NULL);
+
+       prop_object_release(pdk);
+
+       *obj = po;
+       return (_PROP_OBJECT_FREE_RECURSE);
+}
+
+
+static void
+_prop_dictionary_lock(void)
+{
+
+       /* XXX: once necessary or paranoia? */
+       _PROP_ONCE_RUN(_prop_dict_init_once, _prop_dict_init);
+       _PROP_MUTEX_LOCK(_prop_dict_keysym_tree_mutex);
+}
+
+static void
+_prop_dictionary_unlock(void)
+{
+       _PROP_MUTEX_UNLOCK(_prop_dict_keysym_tree_mutex);
+}
+
+static void
+_prop_dictionary_emergency_free(prop_object_t obj)
+{
+       prop_dictionary_t pd = obj;
+       prop_dictionary_keysym_t pdk;
+
+       _PROP_ASSERT(pd->pd_count != 0);
+       --pd->pd_count;
+
+       pdk = pd->pd_array[pd->pd_count].pde_key;
+       _PROP_ASSERT(pdk != NULL);
+       prop_object_release(pdk);
+}
+
+static bool
+_prop_dictionary_externalize(struct _prop_object_externalize_context *ctx,
+                            void *v)
+{
+       prop_dictionary_t pd = v;
+       prop_dictionary_keysym_t pdk;
+       struct _prop_object *po;
+       prop_object_iterator_t pi;
+       unsigned int i;
+       bool rv = false;
+
+       _PROP_RWLOCK_RDLOCK(pd->pd_rwlock);
+
+       if (pd->pd_count == 0) {
+               _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
+               return (_prop_object_externalize_empty_tag(ctx, "dict"));
+       }
+
+       if (_prop_object_externalize_start_tag(ctx, "dict") == false ||
+           _prop_object_externalize_append_char(ctx, '\n') == false)
+               goto out;
+
+       pi = _prop_dictionary_iterator_locked(pd);
+       if (pi == NULL)
+               goto out;
+       
+       ctx->poec_depth++;
+       _PROP_ASSERT(ctx->poec_depth != 0);
+
+       while ((pdk = _prop_dictionary_iterator_next_object_locked(pi))
+           != NULL) {
+               po = _prop_dictionary_get_keysym(pd, pdk, true);
+               if (po == NULL ||
+                   _prop_object_externalize_start_tag(ctx, "key") == false ||
+                   _prop_object_externalize_append_encoded_cstring(ctx,
+                                                  pdk->pdk_key) == false ||
+                   _prop_object_externalize_end_tag(ctx, "key") == false ||
+                   (*po->po_type->pot_extern)(ctx, po) == false) {
+                       prop_object_iterator_release(pi);
+                       goto out;
+               }
+       }
+
+       prop_object_iterator_release(pi);
+
+       ctx->poec_depth--;
+       for (i = 0; i < ctx->poec_depth; i++) {
+               if (_prop_object_externalize_append_char(ctx, '\t') == false)
+                       goto out;
+       }
+       if (_prop_object_externalize_end_tag(ctx, "dict") == false)
+               goto out;
+       
+       rv = true;
+
+ out:
+       _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
+       return (rv);
+}
+
+/* ARGSUSED */
+static _prop_object_equals_rv_t
+_prop_dictionary_equals(prop_object_t v1, prop_object_t v2,
+    void **stored_pointer1, void **stored_pointer2,
+    prop_object_t *next_obj1, prop_object_t *next_obj2)
+{
+       prop_dictionary_t dict1 = v1;
+       prop_dictionary_t dict2 = v2;
+       uintptr_t idx;
+       _prop_object_equals_rv_t rv = _PROP_OBJECT_EQUALS_FALSE;
+
+       if (dict1 == dict2)
+               return (_PROP_OBJECT_EQUALS_TRUE);
+
+       _PROP_ASSERT(*stored_pointer1 == *stored_pointer2);
+
+       idx = (uintptr_t)*stored_pointer1;
+
+       if (idx == 0) {
+               if ((uintptr_t)dict1 < (uintptr_t)dict2) {
+                       _PROP_RWLOCK_RDLOCK(dict1->pd_rwlock);
+                       _PROP_RWLOCK_RDLOCK(dict2->pd_rwlock);
+               } else {
+                       _PROP_RWLOCK_RDLOCK(dict2->pd_rwlock);
+                       _PROP_RWLOCK_RDLOCK(dict1->pd_rwlock);
+               }
+       }
+
+       if (dict1->pd_count != dict2->pd_count)
+               goto out;
+
+       if (idx == dict1->pd_count) {
+               rv = _PROP_OBJECT_EQUALS_TRUE;
+               goto out;
+       }
+
+       _PROP_ASSERT(idx < dict1->pd_count);
+
+       *stored_pointer1 = (void *)(idx + 1);
+       *stored_pointer2 = (void *)(idx + 1);
+
+       *next_obj1 = dict1->pd_array[idx].pde_objref;
+       *next_obj2 = dict2->pd_array[idx].pde_objref;
+
+       if (!prop_dictionary_keysym_equals(dict1->pd_array[idx].pde_key,
+                                          dict2->pd_array[idx].pde_key))
+               goto out;
+
+       return (_PROP_OBJECT_EQUALS_RECURSE);
+
+ out:
+       _PROP_RWLOCK_UNLOCK(dict1->pd_rwlock);
+       _PROP_RWLOCK_UNLOCK(dict2->pd_rwlock);
+       return (rv);
+}
+
+static void
+_prop_dictionary_equals_finish(prop_object_t v1, prop_object_t v2)
+{
+       _PROP_RWLOCK_UNLOCK(((prop_dictionary_t)v1)->pd_rwlock);
+       _PROP_RWLOCK_UNLOCK(((prop_dictionary_t)v2)->pd_rwlock);
+}
+
+static prop_dictionary_t
+_prop_dictionary_alloc(unsigned int capacity)
+{
+       prop_dictionary_t pd;
+       struct _prop_dict_entry *array;
+
+       if (capacity != 0) {
+               array = _PROP_CALLOC(capacity * sizeof(*array), M_PROP_DICT);
+               if (array == NULL)
+                       return (NULL);
+       } else
+               array = NULL;
+
+       pd = _PROP_POOL_GET(_prop_dictionary_pool);
+       if (pd != NULL) {
+               _prop_object_init(&pd->pd_obj, &_prop_object_type_dictionary);
+
+               _PROP_RWLOCK_INIT(pd->pd_rwlock);
+               pd->pd_array = array;
+               pd->pd_capacity = capacity;
+               pd->pd_count = 0;
+               pd->pd_flags = 0;
+
+               pd->pd_version = 0;
+       } else if (array != NULL)
+               _PROP_FREE(array, M_PROP_DICT);
+
+       return (pd);
+}
+
+static bool
+_prop_dictionary_expand(prop_dictionary_t pd, unsigned int capacity)
+{
+       struct _prop_dict_entry *array, *oarray;
+
+       /*
+        * Dictionary must be WRITE-LOCKED.
+        */
+
+       oarray = pd->pd_array;
+
+       array = _PROP_CALLOC(capacity * sizeof(*array), M_PROP_DICT);
+       if (array == NULL)
+               return (false);
+       if (oarray != NULL)
+               memcpy(array, oarray, pd->pd_capacity * sizeof(*array));
+       pd->pd_array = array;
+       pd->pd_capacity = capacity;
+
+       if (oarray != NULL)
+               _PROP_FREE(oarray, M_PROP_DICT);
+       
+       return (true);
+}
+
+static prop_object_t
+_prop_dictionary_iterator_next_object_locked(void *v)
+{
+       struct _prop_dictionary_iterator *pdi = v;
+       prop_dictionary_t pd = pdi->pdi_base.pi_obj;
+       prop_dictionary_keysym_t pdk = NULL;
+
+       _PROP_ASSERT(prop_object_is_dictionary(pd));
+
+       if (pd->pd_version != pdi->pdi_base.pi_version)
+               goto out;       /* dictionary changed during iteration */
+
+       _PROP_ASSERT(pdi->pdi_index <= pd->pd_count);
+
+       if (pdi->pdi_index == pd->pd_count)
+               goto out;       /* we've iterated all objects */
+
+       pdk = pd->pd_array[pdi->pdi_index].pde_key;
+       pdi->pdi_index++;
+
+ out:
+       return (pdk);
+}
+
+static prop_object_t
+_prop_dictionary_iterator_next_object(void *v)
+{
+       struct _prop_dictionary_iterator *pdi = v;
+       prop_dictionary_t pd __unused = pdi->pdi_base.pi_obj;
+       prop_dictionary_keysym_t pdk;
+
+       _PROP_ASSERT(prop_object_is_dictionary(pd));
+
+       _PROP_RWLOCK_RDLOCK(pd->pd_rwlock);
+       pdk = _prop_dictionary_iterator_next_object_locked(pdi);
+       _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
+       return (pdk);
+}
+
+static void
+_prop_dictionary_iterator_reset_locked(void *v)
+{
+       struct _prop_dictionary_iterator *pdi = v;
+       prop_dictionary_t pd = pdi->pdi_base.pi_obj;
+
+       _PROP_ASSERT(prop_object_is_dictionary(pd));
+
+       pdi->pdi_index = 0;
+       pdi->pdi_base.pi_version = pd->pd_version;
+}
+
+static void
+_prop_dictionary_iterator_reset(void *v)
+{
+       struct _prop_dictionary_iterator *pdi = v;
+       prop_dictionary_t pd __unused = pdi->pdi_base.pi_obj;
+
+       _PROP_RWLOCK_RDLOCK(pd->pd_rwlock);
+       _prop_dictionary_iterator_reset_locked(pdi);
+       _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
+}
+
+/*
+ * prop_dictionary_create --
+ *     Create a dictionary.
+ */
+prop_dictionary_t
+prop_dictionary_create(void)
+{
+
+       return (_prop_dictionary_alloc(0));
+}
+
+/*
+ * prop_dictionary_create_with_capacity --
+ *     Create a dictionary with the capacity to store N objects.
+ */
+prop_dictionary_t
+prop_dictionary_create_with_capacity(unsigned int capacity)
+{
+
+       return (_prop_dictionary_alloc(capacity));
+}
+
+/*
+ * prop_dictionary_copy --
+ *     Copy a dictionary.  The new dictionary has an initial capacity equal
+ *     to the number of objects stored int the original dictionary.  The new
+ *     dictionary contains refrences to the original dictionary's objects,
+ *     not copies of those objects (i.e. a shallow copy).
+ */
+prop_dictionary_t
+prop_dictionary_copy(prop_dictionary_t opd)
+{
+       prop_dictionary_t pd;
+       prop_dictionary_keysym_t pdk;
+       prop_object_t po;
+       unsigned int idx;
+
+       if (! prop_object_is_dictionary(opd))
+               return (NULL);
+
+       _PROP_RWLOCK_RDLOCK(opd->pd_rwlock);
+
+       pd = _prop_dictionary_alloc(opd->pd_count);
+       if (pd != NULL) {
+               for (idx = 0; idx < opd->pd_count; idx++) {
+                       pdk = opd->pd_array[idx].pde_key;
+                       po = opd->pd_array[idx].pde_objref;
+
+                       prop_object_retain(pdk);
+                       prop_object_retain(po);
+
+                       pd->pd_array[idx].pde_key = pdk;
+                       pd->pd_array[idx].pde_objref = po;
+               }
+               pd->pd_count = opd->pd_count;
+               pd->pd_flags = opd->pd_flags;
+       }
+       _PROP_RWLOCK_UNLOCK(opd->pd_rwlock);
+       return (pd);
+}
+
+/*
+ * prop_dictionary_copy_mutable --
+ *     Like prop_dictionary_copy(), but the resulting dictionary is
+ *     mutable.
+ */
+prop_dictionary_t
+prop_dictionary_copy_mutable(prop_dictionary_t opd)
+{
+       prop_dictionary_t pd;
+
+       if (! prop_object_is_dictionary(opd))
+               return (NULL);
+
+       pd = prop_dictionary_copy(opd);
+       if (pd != NULL)
+               pd->pd_flags &= ~PD_F_IMMUTABLE;
+
+       return (pd);
+}
+
+/*
+ * prop_dictionary_make_immutable --
+ *     Set the immutable flag on that dictionary.
+ */
+void
+prop_dictionary_make_immutable(prop_dictionary_t pd)
+{
+
+       _PROP_RWLOCK_WRLOCK(pd->pd_rwlock);
+       if (prop_dictionary_is_immutable(pd) == false)
+               pd->pd_flags |= PD_F_IMMUTABLE;
+       _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
+}
+
+/*
+ * prop_dictionary_count --
+ *     Return the number of objects stored in the dictionary.
+ */
+unsigned int
+prop_dictionary_count(prop_dictionary_t pd)
+{
+       unsigned int rv;
+
+       if (! prop_object_is_dictionary(pd))
+               return (0);
+
+       _PROP_RWLOCK_RDLOCK(pd->pd_rwlock);
+       rv = pd->pd_count;
+       _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
+
+       return (rv);
+}
+
+/*
+ * prop_dictionary_ensure_capacity --
+ *     Ensure that the dictionary has the capacity to store the specified
+ *     total number of objects (including the objects already stored in
+ *     the dictionary).
+ */
+bool
+prop_dictionary_ensure_capacity(prop_dictionary_t pd, unsigned int capacity)
+{
+       bool rv;
+
+       if (! prop_object_is_dictionary(pd))
+               return (false);
+
+       _PROP_RWLOCK_WRLOCK(pd->pd_rwlock);
+       if (capacity > pd->pd_capacity)
+               rv = _prop_dictionary_expand(pd, capacity);
+       else
+               rv = true;
+       _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
+       return (rv);
+}
+
+static prop_object_iterator_t
+_prop_dictionary_iterator_locked(prop_dictionary_t pd)
+{
+       struct _prop_dictionary_iterator *pdi;
+
+       if (! prop_object_is_dictionary(pd))
+               return (NULL);
+
+       pdi = _PROP_CALLOC(sizeof(*pdi), M_TEMP);
+       if (pdi == NULL)
+               return (NULL);
+       pdi->pdi_base.pi_next_object = _prop_dictionary_iterator_next_object;
+       pdi->pdi_base.pi_reset = _prop_dictionary_iterator_reset;
+       prop_object_retain(pd);
+       pdi->pdi_base.pi_obj = pd;
+       _prop_dictionary_iterator_reset_locked(pdi);
+
+       return (&pdi->pdi_base);
+}
+
+/*
+ * prop_dictionary_iterator --
+ *     Return an iterator for the dictionary.  The dictionary is retained by
+ *     the iterator.
+ */
+prop_object_iterator_t
+prop_dictionary_iterator(prop_dictionary_t pd)
+{
+       prop_object_iterator_t pi;
+
+       _PROP_RWLOCK_RDLOCK(pd->pd_rwlock);
+       pi = _prop_dictionary_iterator_locked(pd);
+       _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
+       return (pi);
+}
+
+/*
+ * prop_dictionary_all_keys --
+ *     Return an array containing a snapshot of all of the keys
+ *     in the dictionary.
+ */
+prop_array_t
+prop_dictionary_all_keys(prop_dictionary_t pd)
+{
+       prop_array_t array;
+       unsigned int idx;
+       bool rv = true;
+
+       if (! prop_object_is_dictionary(pd))
+               return (NULL);
+
+       /* There is no pressing need to lock the dictionary for this. */
+       array = prop_array_create_with_capacity(pd->pd_count);
+
+       _PROP_RWLOCK_RDLOCK(pd->pd_rwlock);
+
+       for (idx = 0; idx < pd->pd_count; idx++) {
+               rv = prop_array_add(array, pd->pd_array[idx].pde_key);
+               if (rv == false)
+                       break;
+       }
+
+       _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
+
+       if (rv == false) {
+               prop_object_release(array);
+               array = NULL;
+       }
+       return (array);
+}
+
+static struct _prop_dict_entry *
+_prop_dict_lookup(prop_dictionary_t pd, const char *key,
+                 unsigned int *idxp)
+{
+       struct _prop_dict_entry *pde;
+       unsigned int base, idx, distance;
+       int res;
+
+       /*
+        * Dictionary must be READ-LOCKED or WRITE-LOCKED.
+        */
+
+       for (idx = 0, base = 0, distance = pd->pd_count; distance != 0;
+            distance >>= 1) {
+               idx = base + (distance >> 1);
+               pde = &pd->pd_array[idx];
+               _PROP_ASSERT(pde->pde_key != NULL);
+               res = strcmp(key, pde->pde_key->pdk_key);
+               if (res == 0) {
+                       if (idxp != NULL)
+                               *idxp = idx;
+                       return (pde);
+               }
+               if (res > 0) {  /* key > pdk_key: move right */
+                       base = idx + 1;
+                       distance--;
+               }               /* else move left */
+       }
+
+       /* idx points to the slot we looked at last. */
+       if (idxp != NULL)
+               *idxp = idx;
+       return (NULL);
+}
+
+static prop_object_t
+_prop_dictionary_get(prop_dictionary_t pd, const char *key, bool locked)
+{
+       const struct _prop_dict_entry *pde;
+       prop_object_t po = NULL;
+
+       if (! prop_object_is_dictionary(pd))
+               return (NULL);
+
+       if (!locked)
+               _PROP_RWLOCK_RDLOCK(pd->pd_rwlock);
+       pde = _prop_dict_lookup(pd, key, NULL);
+       if (pde != NULL) {
+               _PROP_ASSERT(pde->pde_objref != NULL);
+               po = pde->pde_objref;
+       }
+       if (!locked)
+               _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
+       return (po);
+}
+/*
+ * prop_dictionary_get --
+ *     Return the object stored with specified key.
+ */
+prop_object_t
+prop_dictionary_get(prop_dictionary_t pd, const char *key)
+{
+       prop_object_t po = NULL;
+
+       if (! prop_object_is_dictionary(pd))
+               return (NULL);
+
+       _PROP_RWLOCK_RDLOCK(pd->pd_rwlock);
+       po = _prop_dictionary_get(pd, key, true);
+       _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
+       return (po);
+}
+
+static prop_object_t
+_prop_dictionary_get_keysym(prop_dictionary_t pd, prop_dictionary_keysym_t pdk,
+    bool locked)
+{
+
+       if (! (prop_object_is_dictionary(pd) &&
+              prop_object_is_dictionary_keysym(pdk)))
+               return (NULL);
+
+       return (_prop_dictionary_get(pd, pdk->pdk_key, locked));
+}
+
+/*
+ * prop_dictionary_get_keysym --
+ *     Return the object stored at the location encoded by the keysym.
+ */
+prop_object_t
+prop_dictionary_get_keysym(prop_dictionary_t pd, prop_dictionary_keysym_t pdk)
+{
+
+       return (_prop_dictionary_get_keysym(pd, pdk, false));
+}
+
+/*
+ * prop_dictionary_set --
+ *     Store a reference to an object at with the specified key.
+ *     If the key already exisit, the original object is released.
+ */
+bool
+prop_dictionary_set(prop_dictionary_t pd, const char *key, prop_object_t po)
+{
+       struct _prop_dict_entry *pde;
+       prop_dictionary_keysym_t pdk;
+       unsigned int idx;
+       bool rv = false;
+
+       if (! prop_object_is_dictionary(pd))
+               return (false);
+
+       _PROP_ASSERT(pd->pd_count <= pd->pd_capacity);
+
+       if (prop_dictionary_is_immutable(pd))
+               return (false);
+
+       _PROP_RWLOCK_WRLOCK(pd->pd_rwlock);
+
+       pde = _prop_dict_lookup(pd, key, &idx);
+       if (pde != NULL) {
+               prop_object_t opo = pde->pde_objref;
+               prop_object_retain(po);
+               pde->pde_objref = po;
+               prop_object_release(opo);
+               rv = true;
+               goto out;
+       }
+
+       pdk = _prop_dict_keysym_alloc(key);
+       if (pdk == NULL)
+               goto out;
+
+       if (pd->pd_count == pd->pd_capacity &&
+           _prop_dictionary_expand(pd,
+                                   pd->pd_capacity + EXPAND_STEP) == false) {
+               prop_object_release(pdk);
+               goto out;
+       }
+
+       /* At this point, the store will succeed. */
+       prop_object_retain(po);
+
+       if (pd->pd_count == 0) {
+               pd->pd_array[0].pde_key = pdk;
+               pd->pd_array[0].pde_objref = po;
+               pd->pd_count++;
+               pd->pd_version++;
+               rv = true;
+               goto out;
+       }
+
+       pde = &pd->pd_array[idx];
+       _PROP_ASSERT(pde->pde_key != NULL);
+
+       if (strcmp(key, pde->pde_key->pdk_key) < 0) {
+               /*
+                * key < pdk_key: insert to the left.  This is the same as
+                * inserting to the right, except we decrement the current
+                * index first.
+                *
+                * Because we're unsigned, we have to special case 0
+                * (grumble).
+                */
+               if (idx == 0) {
+                       memmove(&pd->pd_array[1], &pd->pd_array[0],
+                               pd->pd_count * sizeof(*pde));
+                       pd->pd_array[0].pde_key = pdk;
+                       pd->pd_array[0].pde_objref = po;
+                       pd->pd_count++;
+                       pd->pd_version++;
+                       rv = true;
+                       goto out;
+               }
+               idx--;
+       }
+
+       memmove(&pd->pd_array[idx + 2], &pd->pd_array[idx + 1],
+               (pd->pd_count - (idx + 1)) * sizeof(*pde));
+       pd->pd_array[idx + 1].pde_key = pdk;
+       pd->pd_array[idx + 1].pde_objref = po;
+       pd->pd_count++;
+
+       pd->pd_version++;
+
+       rv = true;
+
+ out:
+       _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
+       return (rv);
+}
+
+/*
+ * prop_dictionary_set_keysym --
+ *     Replace the object in the dictionary at the location encoded by
+ *     the keysym.
+ */
+bool
+prop_dictionary_set_keysym(prop_dictionary_t pd, prop_dictionary_keysym_t pdk,
+                          prop_object_t po)
+{
+
+       if (! (prop_object_is_dictionary(pd) &&
+              prop_object_is_dictionary_keysym(pdk)))
+               return (false);
+
+       return (prop_dictionary_set(pd, pdk->pdk_key, po));
+}
+
+static void
+_prop_dictionary_remove(prop_dictionary_t pd, struct _prop_dict_entry *pde,
+    unsigned int idx)
+{
+       prop_dictionary_keysym_t pdk = pde->pde_key;
+       prop_object_t po = pde->pde_objref;
+
+       /*
+        * Dictionary must be WRITE-LOCKED.
+        */
+
+       _PROP_ASSERT(pd->pd_count != 0);
+       _PROP_ASSERT(idx < pd->pd_count);
+       _PROP_ASSERT(pde == &pd->pd_array[idx]);
+
+       idx++;
+       memmove(&pd->pd_array[idx - 1], &pd->pd_array[idx],
+               (pd->pd_count - idx) * sizeof(*pde));
+       pd->pd_count--;
+       pd->pd_version++;
+
+
+       prop_object_release(pdk);
+
+       prop_object_release(po);
+}
+
+/*
+ * prop_dictionary_remove --
+ *     Remove the reference to an object with the specified key from
+ *     the dictionary.
+ */
+void
+prop_dictionary_remove(prop_dictionary_t pd, const char *key)
+{
+       struct _prop_dict_entry *pde;
+       unsigned int idx;
+
+       if (! prop_object_is_dictionary(pd))
+               return;
+
+       _PROP_RWLOCK_WRLOCK(pd->pd_rwlock);
+
+       /* XXX Should this be a _PROP_ASSERT()? */
+       if (prop_dictionary_is_immutable(pd))
+               goto out;
+
+       pde = _prop_dict_lookup(pd, key, &idx);
+       /* XXX Should this be a _PROP_ASSERT()? */
+       if (pde == NULL)
+               goto out;
+
+       _prop_dictionary_remove(pd, pde, idx);
+ out:
+       _PROP_RWLOCK_UNLOCK(pd->pd_rwlock);
+}
+
+/*
+ * prop_dictionary_remove_keysym --
+ *     Remove a reference to an object stored in the dictionary at the
+ *     location encoded by the keysym.
+ */
+void
+prop_dictionary_remove_keysym(prop_dictionary_t pd,
+                             prop_dictionary_keysym_t pdk)
+{
+
+       if (! (prop_object_is_dictionary(pd) &&
+              prop_object_is_dictionary_keysym(pdk)))
+               return;
+
+       prop_dictionary_remove(pd, pdk->pdk_key);
+}
+
+/*
+ * prop_dictionary_equals --
+ *     Return true if the two dictionaries are equivalent.  Note we do a
+ *     by-value comparison of the objects in the dictionary.
+ */
+bool
+prop_dictionary_equals(prop_dictionary_t dict1, prop_dictionary_t dict2)
+{
+       if (!prop_object_is_dictionary(dict1) ||
+           !prop_object_is_dictionary(dict2))
+               return (false);
+
+       return (prop_object_equals(dict1, dict2));
+}
+
+/*
+ * prop_dictionary_keysym_cstring_nocopy --
+ *     Return an immutable reference to the keysym's value.
+ */
+const char *
+prop_dictionary_keysym_cstring_nocopy(prop_dictionary_keysym_t pdk)
+{
+
+       if (! prop_object_is_dictionary_keysym(pdk))
+               return (NULL);
+
+       return (pdk->pdk_key);
+}
+
+/*
+ * prop_dictionary_keysym_equals --
+ *     Return true if the two dictionary key symbols are equivalent.
+ *     Note: We do not compare the object references.
+ */
+bool
+prop_dictionary_keysym_equals(prop_dictionary_keysym_t pdk1,
+                             prop_dictionary_keysym_t pdk2)
+{
+       if (!prop_object_is_dictionary_keysym(pdk1) ||
+           !prop_object_is_dictionary_keysym(pdk2))
+               return (false);
+
+       return (prop_object_equals(pdk1, pdk2));
+}
+
+/*
+ * prop_dictionary_externalize --
+ *     Externalize a dictionary, returning a NUL-terminated buffer
+ *     containing the XML-style representation.  The buffer is allocated
+ *     with the M_TEMP memory type.
+ */
+char *
+prop_dictionary_externalize(prop_dictionary_t pd)
+{
+       struct _prop_object_externalize_context *ctx;
+       char *cp;
+
+       ctx = _prop_object_externalize_context_alloc();
+       if (ctx == NULL)
+               return (NULL);
+
+       if (_prop_object_externalize_header(ctx) == false ||
+           (*pd->pd_obj.po_type->pot_extern)(ctx, pd) == false ||
+           _prop_object_externalize_footer(ctx) == false) {
+               /* We are responsible for releasing the buffer. */
+               _PROP_FREE(ctx->poec_buf, M_TEMP);
+               _prop_object_externalize_context_free(ctx);
+               return (NULL);
+       }
+
+       cp = ctx->poec_buf;
+       _prop_object_externalize_context_free(ctx);
+
+       return (cp);
+}
+
+/*
+ * _prop_dictionary_internalize --
+ *     Parse a <dict>...</dict> and return the object created from the
+ *     external representation.
+ *
+ * Internal state in via rec_data is the storage area for the last processed
+ * key.
+ * _prop_dictionary_internalize_body is the upper half of the parse loop.
+ * It is responsible for parsing the key directly and storing it in the area
+ * referenced by rec_data.
+ * _prop_dictionary_internalize_cont is the lower half and called with the value
+ * associated with the key.
+ */
+static bool _prop_dictionary_internalize_body(prop_stack_t,
+    prop_object_t *, struct _prop_object_internalize_context *, char *);
+
+bool
+_prop_dictionary_internalize(prop_stack_t stack, prop_object_t *obj,
+    struct _prop_object_internalize_context *ctx)
+{
+       prop_dictionary_t dict;
+       char *tmpkey;
+
+       /* We don't currently understand any attributes. */
+       if (ctx->poic_tagattr != NULL)
+               return (true);
+
+       dict = prop_dictionary_create();
+       if (dict == NULL)
+               return (true);
+
+       if (ctx->poic_is_empty_element) {
+               *obj = dict;
+               return (true);
+       }
+
+       tmpkey = _PROP_MALLOC(PDK_MAXKEY + 1, M_TEMP);
+       if (tmpkey == NULL) {
+               prop_object_release(dict);
+               return (true);
+       }
+
+       *obj = dict;
+       /*
+        * Opening tag is found, storage for key allocated and
+        * now continue to the first element.
+        */
+       return _prop_dictionary_internalize_body(stack, obj, ctx, tmpkey);
+}
+
+static bool
+_prop_dictionary_internalize_continue(prop_stack_t stack, prop_object_t *obj,
+    struct _prop_object_internalize_context *ctx, void *data, prop_object_t child)
+{
+       prop_dictionary_t dict = *obj;
+       char *tmpkey = data;
+
+       _PROP_ASSERT(tmpkey != NULL);
+
+       if (child == NULL ||
+           prop_dictionary_set(dict, tmpkey, child) == false) {
+               _PROP_FREE(tmpkey, M_TEMP);
+               if (child != NULL)
+                       prop_object_release(child);
+               prop_object_release(dict);
+               *obj = NULL;
+               return (true);
+       }
+
+       prop_object_release(child);
+
+       /*
+        * key, value was added, now continue looking for the next key
+        * or the closing tag.
+        */
+       return _prop_dictionary_internalize_body(stack, obj, ctx, tmpkey);
+}
+
+static bool
+_prop_dictionary_internalize_body(prop_stack_t stack, prop_object_t *obj,
+    struct _prop_object_internalize_context *ctx, char *tmpkey)
+{
+       prop_dictionary_t dict = *obj;
+       size_t keylen;
+
+       /* Fetch the next tag. */
+       if (_prop_object_internalize_find_tag(ctx, NULL, _PROP_TAG_TYPE_EITHER) == false)
+               goto bad;
+
+       /* Check to see if this is the end of the dictionary. */
+       if (_PROP_TAG_MATCH(ctx, "dict") &&
+           ctx->poic_tag_type == _PROP_TAG_TYPE_END) {
+               _PROP_FREE(tmpkey, M_TEMP);
+               return (true);
+       }
+
+       /* Ok, it must be a non-empty key start tag. */
+       if (!_PROP_TAG_MATCH(ctx, "key") ||
+           ctx->poic_tag_type != _PROP_TAG_TYPE_START ||
+           ctx->poic_is_empty_element)
+               goto bad;
+
+       if (_prop_object_internalize_decode_string(ctx,
+                                       tmpkey, PDK_MAXKEY, &keylen,
+                                       &ctx->poic_cp) == false)
+               goto bad;
+
+       _PROP_ASSERT(keylen <= PDK_MAXKEY);
+       tmpkey[keylen] = '\0';
+
+       if (_prop_object_internalize_find_tag(ctx, "key",
+                               _PROP_TAG_TYPE_END) == false)
+               goto bad;
+   
+       /* ..and now the beginning of the value. */
+       if (_prop_object_internalize_find_tag(ctx, NULL,
+                               _PROP_TAG_TYPE_START) == false)
+               goto bad;
+
+       /*
+        * Key is found, now wait for value to be parsed.
+        */
+       if (_prop_stack_push(stack, *obj,
+                            _prop_dictionary_internalize_continue,
+                            tmpkey, NULL))
+               return (false);
+
+ bad:
+       _PROP_FREE(tmpkey, M_TEMP);
+       prop_object_release(dict);
+       *obj = NULL;
+       return (true);
+}
+
+/*
+ * prop_dictionary_internalize --
+ *     Create a dictionary by parsing the NUL-terminated XML-style
+ *     representation.
+ */
+prop_dictionary_t
+prop_dictionary_internalize(const char *xml)
+{
+       return _prop_generic_internalize(xml, "dict");
+}
+
+#if !defined(_KERNEL) && !defined(_STANDALONE)
+/*
+ * prop_dictionary_externalize_to_file --
+ *     Externalize a dictionary to the specified file.
+ */
+bool
+prop_dictionary_externalize_to_file(prop_dictionary_t dict, const char *fname)
+{
+       char *xml;
+       bool rv;
+       int save_errno = 0;     /* XXXGCC -Wuninitialized [mips, ...] */
+
+       xml = prop_dictionary_externalize(dict);
+       if (xml == NULL)
+               return (false);
+       rv = _prop_object_externalize_write_file(fname, xml, strlen(xml));
+       if (rv == false)
+               save_errno = errno;
+       _PROP_FREE(xml, M_TEMP);
+       if (rv == false)
+               errno = save_errno;
+
+       return (rv);
+}
+
+/*
+ * prop_dictionary_internalize_from_file --
+ *     Internalize a dictionary from a file.
+ */
+prop_dictionary_t
+prop_dictionary_internalize_from_file(const char *fname)
+{
+       struct _prop_object_internalize_mapped_file *mf;
+       prop_dictionary_t dict;
+
+       mf = _prop_object_internalize_map_file(fname);
+       if (mf == NULL)
+               return (NULL);
+       dict = prop_dictionary_internalize(mf->poimf_xml);
+       _prop_object_internalize_unmap_file(mf);
+
+       return (dict);
+}
+#endif /* !_KERNEL && !_STANDALONE */
diff --git a/common/lib/libprop/prop_dictionary_util.3 b/common/lib/libprop/prop_dictionary_util.3
new file mode 100644 (file)
index 0000000..b262357
--- /dev/null
@@ -0,0 +1,200 @@
+.\"    $NetBSD: prop_dictionary_util.3,v 1.8 2011/10/17 09:24:54 wiz Exp $
+.\"
+.\" Copyright (c) 2006 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by Jason R. Thorpe.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd August 7, 2011
+.Dt PROP_DICTIONARY_UTIL 3
+.Os
+.Sh NAME
+.Nm prop_dictionary_util ,
+.Nm prop_dictionary_get_dict ,
+.Nm prop_dictionary_get_bool ,
+.Nm prop_dictionary_set_bool ,
+.Nm prop_dictionary_get_int8 ,
+.Nm prop_dictionary_get_uint8 ,
+.Nm prop_dictionary_set_int8 ,
+.Nm prop_dictionary_set_uint8 ,
+.Nm prop_dictionary_get_int16 ,
+.Nm prop_dictionary_get_uint16 ,
+.Nm prop_dictionary_set_int16 ,
+.Nm prop_dictionary_set_uint16 ,
+.Nm prop_dictionary_get_int32 ,
+.Nm prop_dictionary_get_uint32 ,
+.Nm prop_dictionary_set_int32 ,
+.Nm prop_dictionary_set_uint32 ,
+.Nm prop_dictionary_get_int64 ,
+.Nm prop_dictionary_get_uint64 ,
+.Nm prop_dictionary_set_int64 ,
+.Nm prop_dictionary_set_uint64 ,
+.Nm prop_dictionary_get_cstring ,
+.Nm prop_dictionary_set_cstring ,
+.Nm prop_dictionary_get_cstring_nocopy ,
+.Nm prop_dictionary_set_cstring_nocopy ,
+.Nm prop_dictionary_set_and_rel
+.Nd dictionary property collection object utility functions
+.Sh LIBRARY
+.Lb libprop
+.Sh SYNOPSIS
+.In prop/proplib.h
+.\"
+.Ft bool
+.Fn prop_dictionary_get_dict "prop_dictionary_t dict" "const char *key" \
+    "prop_dictionary_t *dictp"
+.Ft bool
+.Fn prop_dictionary_get_bool "prop_dictionary_t dict" "const char *key" \
+    "bool *valp"
+.Ft bool
+.Fn prop_dictionary_set_bool "prop_dictionary_t dict" "const char *key" \
+    "bool val"
+.\"
+.Ft bool
+.Fn prop_dictionary_get_int8 "prop_dictionary_t dict" "const char *key" \
+    "int8_t *valp"
+.Ft bool
+.Fn prop_dictionary_get_uint8 "prop_dictionary_t dict" "const char *key" \
+    "uint8_t *valp"
+.Ft bool
+.Fn prop_dictionary_set_int8 "prop_dictionary_t dict" "const char *key" \
+    "int8_t val"
+.Ft bool
+.Fn prop_dictionary_set_uint8 "prop_dictionary_t dict" "const char *key" \
+    "uint8_t val"
+.\"
+.Ft bool
+.Fn prop_dictionary_get_int16 "prop_dictionary_t dict" "const char *key" \
+    "int16_t *valp"
+.Ft bool
+.Fn prop_dictionary_get_uint16 "prop_dictionary_t dict" "const char *key" \
+    "uint16_t *valp"
+.Ft bool
+.Fn prop_dictionary_set_int16 "prop_dictionary_t dict" "const char *key" \
+    "int16_t val"
+.Ft bool
+.Fn prop_dictionary_set_uint16 "prop_dictionary_t dict" "const char *key" \
+    "uint16_t val"
+.\"
+.Ft bool
+.Fn prop_dictionary_get_int32 "prop_dictionary_t dict" "const char *key" \
+    "int32_t *valp"
+.Ft bool
+.Fn prop_dictionary_get_uint32 "prop_dictionary_t dict" "const char *key" \
+    "uint32_t *valp"
+.Ft bool
+.Fn prop_dictionary_set_int32 "prop_dictionary_t dict" "const char *key" \
+    "int32_t val"
+.Ft bool
+.Fn prop_dictionary_set_uint32 "prop_dictionary_t dict" "const char *key" \
+    "uint32_t val"
+.\"
+.Ft bool
+.Fn prop_dictionary_get_int64 "prop_dictionary_t dict" "const char *key" \
+    "int64_t *valp"
+.Ft bool
+.Fn prop_dictionary_get_uint64 "prop_dictionary_t dict" "const char *key" \
+    "uint64_t *valp"
+.Ft bool
+.Fn prop_dictionary_set_int64 "prop_dictionary_t dict" "const char *key" \
+    "int64_t val"
+.Ft bool
+.Fn prop_dictionary_set_uint64 "prop_dictionary_t dict" "const char *key" \
+    "uint64_t val"
+.\"
+.Ft bool
+.Fn prop_dictionary_get_cstring "prop_dictionary_t dict" "const char *key" \
+    "char **strp"
+.Ft bool
+.Fn prop_dictionary_set_cstring "prop_dictionary_t dict" "const char *key" \
+    "const char *str"
+.\"
+.Ft bool
+.Fn prop_dictionary_get_cstring_nocopy "prop_dictionary_t dict" \
+    "const char *key" "const char **strp"
+.Ft bool
+.Fn prop_dictionary_set_cstring_nocopy "prop_dictionary_t dict" \
+    "const char *key" "const char *strp"
+.Ft bool
+.Fn prop_dictionary_set_and_rel "prop_dictionary_t dict" \
+    "const char *key" "prop_object_t obj"
+.Sh DESCRIPTION
+The
+.Nm prop_dictionary_util
+family of functions are provided to make getting and setting values in
+dictionaries more convenient in some applications.
+.Pp
+The getters check the type of the returned object and, in some cases, also
+ensure that the returned value is within the range implied by the getter's
+value type.
+.Pp
+The setters handle object creation and release for the caller.
+.Pp
+The
+.Fn prop_dictionary_get_cstring
+function returns dynamically allocated memory.
+See
+.Xr prop_string 3
+for more information.
+.Pp
+The
+.Fn prop_dictionary_get_cstring_nocopy
+and
+.Fn prop_dictionary_set_cstring_nocopy
+functions do not copy the string that is set or returned.
+See
+.Xr prop_string 3
+for more information.
+.Pp
+The
+.Fn prop_dictionary_set_and_rel
+function adds the object to the dictionary and releases it.
+The object is also released on failure.
+.Sh RETURN VALUES
+The
+.Nm prop_dictionary_util
+getter functions return
+.Dv true
+if the object exists in the dictionary and the value is in-range, or
+.Dv false
+otherwise.
+.Pp
+The
+.Nm prop_dictionary_util
+setter functions return
+.Dv true
+if creating the object and storing it in the dictionary is successful, or
+.Dv false
+otherwise.
+.Sh SEE ALSO
+.Xr prop_bool 3 ,
+.Xr prop_dictionary 3 ,
+.Xr prop_number 3 ,
+.Xr proplib 3
+.Sh HISTORY
+The
+.Nm proplib
+property container object library first appeared in
+.Nx 4.0 .
diff --git a/common/lib/libprop/prop_dictionary_util.c b/common/lib/libprop/prop_dictionary_util.c
new file mode 100644 (file)
index 0000000..65baf73
--- /dev/null
@@ -0,0 +1,232 @@
+/*     $NetBSD: prop_dictionary_util.c,v 1.4 2011/03/24 17:05:39 bouyer Exp $  */
+
+/*-
+ * Copyright (c) 2006 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jason R. Thorpe.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Utility routines to make it more convenient to work with values
+ * stored in dictionaries.
+ *
+ * Note: There is no special magic going on here.  We use the standard
+ * proplib(3) APIs to do all of this work.  Any application could do
+ * exactly what we're doing here.
+ */
+
+#include <prop/proplib.h>
+#include "prop_object_impl.h"  /* only to hide kernel vs. not-kernel */
+
+bool
+prop_dictionary_get_dict(prop_dictionary_t dict, const char *key, prop_dictionary_t *dp)
+{
+       prop_object_t o;
+       o = prop_dictionary_get(dict, key);
+       if (o == NULL || prop_object_type(o) != PROP_TYPE_DICTIONARY)
+               return false;
+       *dp = o;
+       return true;
+
+}
+
+bool
+prop_dictionary_get_bool(prop_dictionary_t dict,
+                        const char *key,
+                        bool *valp)
+{
+       prop_bool_t b;
+
+       b = prop_dictionary_get(dict, key);
+       if (prop_object_type(b) != PROP_TYPE_BOOL)
+               return (false);
+       
+       *valp = prop_bool_true(b);
+
+       return (true);
+}
+
+bool
+prop_dictionary_set_bool(prop_dictionary_t dict,
+                        const char *key,
+                        bool val)
+{
+       prop_bool_t b;
+       int rv;
+
+       b = prop_bool_create(val);
+       if (b == NULL)
+               return (false);
+       rv = prop_dictionary_set(dict, key, b);
+       prop_object_release(b);
+
+       return (rv);
+}
+
+#define        TEMPLATE(size)                                                  \
+bool                                                           \
+prop_dictionary_get_int ## size (prop_dictionary_t dict,               \
+                                const char *key,                       \
+                                int ## size ## _t *valp)               \
+{                                                                      \
+       prop_number_t num;                                              \
+                                                                       \
+       num = prop_dictionary_get(dict, key);                           \
+       if (prop_object_type(num) != PROP_TYPE_NUMBER)                  \
+               return (false);                                         \
+                                                                       \
+       if (prop_number_unsigned(num) &&                                \
+           prop_number_unsigned_integer_value(num) >                   \
+          /*CONSTCOND*/((size) ==  8 ?  INT8_MAX :                     \
+                        (size) == 16 ? INT16_MAX :                     \
+                        (size) == 32 ? INT32_MAX : INT64_MAX)) {       \
+               return (false);                                         \
+       }                                                               \
+                                                                       \
+       if (prop_number_size(num) > (size))                             \
+               return (false);                                         \
+                                                                       \
+       *valp = (int ## size ## _t) prop_number_integer_value(num);     \
+                                                                       \
+       return (true);                                                  \
+}                                                                      \
+                                                                       \
+bool                                                           \
+prop_dictionary_get_uint ## size (prop_dictionary_t dict,              \
+                                 const char *key,                      \
+                                 uint ## size ## _t *valp)             \
+{                                                                      \
+       prop_number_t num;                                              \
+                                                                       \
+       num = prop_dictionary_get(dict, key);                           \
+       if (prop_object_type(num) != PROP_TYPE_NUMBER)                  \
+               return (false);                                         \
+                                                                       \
+       if (prop_number_unsigned(num) == false &&                       \
+           prop_number_integer_value(num) < 0) {                       \
+               return (false);                                         \
+       }                                                               \
+                                                                       \
+       if (prop_number_size(num) > (size))                             \
+               return (false);                                         \
+                                                                       \
+       *valp = (uint ## size ## _t)                                    \
+           prop_number_unsigned_integer_value(num);                    \
+                                                                       \
+       return (true);                                                  \
+}                                                                      \
+                                                                       \
+bool                                                           \
+prop_dictionary_set_int ## size (prop_dictionary_t dict,               \
+                                const char *key,                       \
+                                int ## size ## _t val)                 \
+{                                                                      \
+       prop_number_t num;                                              \
+       int rv;                                                         \
+                                                                       \
+       num = prop_number_create_integer((int64_t) val);                \
+       if (num == NULL)                                                \
+               return (false);                                         \
+       rv = prop_dictionary_set(dict, key, num);                       \
+       prop_object_release(num);                                       \
+                                                                       \
+       return (rv);                                                    \
+}                                                                      \
+                                                                       \
+bool                                                           \
+prop_dictionary_set_uint ## size (prop_dictionary_t dict,              \
+                                 const char *key,                      \
+                                 uint ## size ## _t val)               \
+{                                                                      \
+       prop_number_t num;                                              \
+       int rv;                                                         \
+                                                                       \
+       num = prop_number_create_unsigned_integer((uint64_t) val);      \
+       if (num == NULL)                                                \
+               return (false);                                         \
+       rv = prop_dictionary_set(dict, key, num);                       \
+       prop_object_release(num);                                       \
+                                                                       \
+       return (rv);                                                    \
+}
+
+TEMPLATE(8)
+TEMPLATE(16)
+TEMPLATE(32)
+TEMPLATE(64)
+
+#undef TEMPLATE
+
+#define        TEMPLATE(variant, qualifier)                                    \
+bool                                                           \
+prop_dictionary_get_cstring ## variant (prop_dictionary_t dict,                \
+                                       const char *key,                \
+                                       qualifier char **cpp)           \
+{                                                                      \
+       prop_string_t str;                                              \
+                                                                       \
+       str = prop_dictionary_get(dict, key);                           \
+       if (prop_object_type(str) != PROP_TYPE_STRING)                  \
+               return (false);                                         \
+                                                                       \
+       *cpp = prop_string_cstring ## variant (str);                    \
+                                                                       \
+       return (*cpp == NULL ? false : true);                           \
+}                                                                      \
+                                                                       \
+bool                                                           \
+prop_dictionary_set_cstring ## variant (prop_dictionary_t dict,                \
+                                       const char *key,                \
+                                       const char *cp)                 \
+{                                                                      \
+       prop_string_t str;                                              \
+       int rv;                                                         \
+                                                                       \
+       str = prop_string_create_cstring ## variant (cp);               \
+       if (str == NULL)                                                \
+               return (false);                                         \
+       rv = prop_dictionary_set(dict, key, str);                       \
+       prop_object_release(str);                                       \
+                                                                       \
+       return (rv);                                                    \
+}
+
+TEMPLATE(,)
+TEMPLATE(_nocopy,const)
+
+#undef TEMPLATE
+
+bool
+prop_dictionary_set_and_rel(prop_dictionary_t dict, const char *key,
+    prop_object_t po)
+{
+       bool ret;
+       if (po == NULL)
+               return false;
+       ret = prop_dictionary_set(dict, key, po);
+       prop_object_release(po);
+       return ret;
+}
diff --git a/common/lib/libprop/prop_ingest.3 b/common/lib/libprop/prop_ingest.3
new file mode 100644 (file)
index 0000000..ae29f22
--- /dev/null
@@ -0,0 +1,181 @@
+.\"    $NetBSD: prop_ingest.3,v 1.6 2010/02/18 14:00:39 wiz Exp $
+.\"
+.\" Copyright (c) 2006 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by Jason R. Thorpe.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd January 21, 2008
+.Dt PROP_INGEST 3
+.Os
+.Sh NAME
+.Nm prop_ingest_context_alloc ,
+.Nm prop_ingest_context_free ,
+.Nm prop_ingest_context_error ,
+.Nm prop_ingest_context_type ,
+.Nm prop_ingest_context_key ,
+.Nm prop_ingest_context_private ,
+.Nm prop_dictionary_ingest
+.Nd Ingest a dictionary into an arbitrary binary format
+.Sh SYNOPSIS
+.In prop/proplib.h
+.Ft prop_ingest_context_t
+.Fn prop_ingest_context_alloc "void *private"
+.Ft void
+.Fn prop_ingest_context_free "prop_ingest_context_t ctx"
+.Ft prop_ingest_error_t
+.Fn prop_ingest_context_error "prop_ingest_context_t ctx"
+.Ft prop_type_t
+.Fn prop_ingest_context_type "prop_ingest_context_t ctx"
+.Ft const char *
+.Fn prop_ingest_context_key "prop_ingest_context_t ctx"
+.Ft void *
+.Fn prop_ingest_context_private "prop_ingest_context_t ctx"
+.Ft bool
+.Fn prop_dictionary_ingest "prop_dictionary_t dict" \
+    "const prop_ingest_table_entry rules[]" \
+    "prop_ingest_context_t ctx"
+.Pp
+.Ft typedef bool
+.Fn (*prop_ingest_handler_t) "prop_ingest_context_t" "prop_object_t"
+.Sh DESCRIPTION
+The
+.Nm prop_dictionary_ingest
+function provides a convenient way to convert a property list into
+an arbitrary binary format or to extract values from dictionaries in a
+way that is convenient to an application
+.Pq for configuration files, for example .
+.Pp
+.Nm prop_dictionary_ingest
+is driven by a table of rules provided by the application.
+Each rule consists of three items:
+.Bl -bullet
+.It
+A C string containing a key to be looked up in the dictionary.
+.It
+The expected property type of the object associated with the key
+(or
+.Dv PROP_TYPE_UNKNOWN
+to specify that any type is allowed).
+.It
+A callback function of type
+.Dv prop_ingest_handler_t
+that will perform the translation for the application.
+.El
+.Pp
+The table is constructed using a series of macros as follows:
+.Bd -literal
+static const prop_ingest_table_entry ingest_rules[] = {
+       PROP_INGEST("file-name", PROP_TYPE_STRING, ingest_filename),
+       PROP_INGEST("count", PROP_TYPE_NUMBER, ingest_count),
+       PROP_INGEST_OPTIONAL("required", PROP_TYPE_BOOL, ingest_required),
+       PROP_INGEST_OPTIONAL("extra", PROP_TYPE_UNKNOWN, ingest_extra),
+       PROP_INGEST_END
+};
+.Ed
+.Pp
+The
+.Dv PROP_INGEST
+macro specifies that the key is required to be present in the dictionary.
+The
+.Dv PROP_INGEST_OPTIONAL
+macro specifies that the presence of the key in the dictionary is optional.
+The
+.Dv PROP_INGEST_END
+macro marks the end of the rules table.
+.Pp
+In each case,
+.Nm prop_dictionary_ingest
+looks up the rule's key in the dictionary.
+If an object is present in the dictionary at that key, its type is checked
+against the type specified in the rule.
+A type specification of
+.Dv PROP_TYPE_UNKNOWN
+allows the object to be of any type.
+If the object does not exist and the rule is not marked as optional, then
+an error is returned.
+Otherwise, the handler specified in the rule is invoked with the ingest
+context and the object
+(or
+.Dv NULL
+if the key does not exist in the dictionary).
+The handler should return
+.Dv false
+if the value of the object is invalid to indicate failure and
+.Dv true
+otherwise.
+.Pp
+The ingest context contains several pieces of information that are
+useful during the ingest process.
+The context also provides specific error information should the ingest
+fail.
+.Bl -tag -width "xxxxx"
+.It Fn prop_ingest_context_alloc "void *private"
+Allocate an ingest context.
+The argument
+.Fa private
+may be used to pass application-specific context to the ingest handlers.
+Note that an ingest context can be re-used to perform multiple ingests.
+Returns
+.Dv NULL
+on failure.
+.It Fn prop_ingest_context_free "prop_ingest_context_t ctx"
+Free an ingest context.
+.It Fn prop_ingest_context_error "prop_ingest_context_t ctx"
+Returns the code indicating the error encountered during ingest.
+The following error codes are defined:
+.Pp
+.Bl -tag -width "PROP_INGEST_ERROR_HANDLER_FAILED" -compact
+.It Dv PROP_INGEST_ERROR_NO_ERROR
+No error was encountered during ingest.
+.It Dv PROP_INGEST_ERROR_NO_KEY
+A non-optional key was not found in the dictionary.
+.It Dv PROP_INGEST_ERROR_WRONG_TYPE
+An object in the dictionary was not the same type specified in the rules.
+.It Dv PROP_INGEST_ERROR_HANDLER_FAILED
+An object's handler returned
+.Dv false .
+.El
+.Pp
+.It Fn prop_ingest_context_type "prop_ingest_context_t ctx"
+Returns the type of the last object visited during an ingest.
+When called by an ingest handler, it returns the type of the object
+currently being processed.
+.It Fn prop_ingest_context_key "prop_ingest_context_t ctx"
+Returns the last dictionary key looked up during an ingest.
+When called by an ingest handler, it returns the dictionary key corresponding
+to the object currently being processed.
+.It Fn prop_ingest_context_private "prop_ingest_context_t ctx"
+Returns the private data set when the context was allocated with
+.Fn prop_ingest_context_alloc .
+.El
+.Sh SEE ALSO
+.Xr prop_dictionary 3 ,
+.Xr proplib 3
+.Sh HISTORY
+The
+.Nm proplib
+property container object library first appeared in
+.Nx 4.0 .
diff --git a/common/lib/libprop/prop_ingest.c b/common/lib/libprop/prop_ingest.c
new file mode 100644 (file)
index 0000000..823d2c6
--- /dev/null
@@ -0,0 +1,159 @@
+/*     $NetBSD: prop_ingest.c,v 1.3 2008/04/28 20:22:53 martin Exp $   */
+
+/*-
+ * Copyright (c) 2006 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jason R. Thorpe.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <prop/proplib.h>
+#include "prop_object_impl.h"
+
+struct _prop_ingest_context {
+       prop_ingest_error_t     pic_error;
+       prop_type_t             pic_type;
+       const char *            pic_key;
+       void *                  pic_private;
+};
+
+/*
+ * prop_ingest_context_alloc --
+ *     Allocate and initialize an ingest context.
+ */
+prop_ingest_context_t
+prop_ingest_context_alloc(void *private)
+{
+       prop_ingest_context_t ctx;
+
+       ctx = _PROP_MALLOC(sizeof(*ctx), M_TEMP);
+       if (ctx != NULL) {
+               ctx->pic_error = PROP_INGEST_ERROR_NO_ERROR;
+               ctx->pic_type = PROP_TYPE_UNKNOWN;
+               ctx->pic_key = NULL;
+               ctx->pic_private = private;
+       }
+       return (ctx);
+}
+
+/*
+ * prop_ingest_context_free --
+ *     Free an ingest context.
+ */
+void
+prop_ingest_context_free(prop_ingest_context_t ctx)
+{
+
+       _PROP_FREE(ctx, M_TEMP);
+}
+
+/*
+ * prop_ingest_context_error --
+ *     Get the error code from an ingest context.
+ */
+prop_ingest_error_t
+prop_ingest_context_error(prop_ingest_context_t ctx)
+{
+
+       return (ctx->pic_error);
+}
+
+/*
+ * prop_ingest_context_type --
+ *     Return the type of last object visisted by an ingest context.
+ */
+prop_type_t
+prop_ingest_context_type(prop_ingest_context_t ctx)
+{
+
+       return (ctx->pic_type);
+}
+
+/*
+ * prop_ingest_context_key --
+ *     Return the last key looked up by an ingest context.
+ */
+const char *
+prop_ingest_context_key(prop_ingest_context_t ctx)
+{
+
+       return (ctx->pic_key);
+}
+
+/*
+ * prop_ingest_context_private --
+ *     Return the caller-private data associated with an ingest context.
+ */
+void *
+prop_ingest_context_private(prop_ingest_context_t ctx)
+{
+
+       return (ctx->pic_private);
+}
+
+/*
+ * prop_dictionary_ingest --
+ *     Ingest a dictionary using handlers for each object to translate
+ *     into an arbitrary binary format.
+ */
+bool
+prop_dictionary_ingest(prop_dictionary_t dict,
+                      const prop_ingest_table_entry rules[],
+                      prop_ingest_context_t ctx)
+{
+       const prop_ingest_table_entry *pite;
+       prop_object_t obj;
+
+       ctx->pic_error = PROP_INGEST_ERROR_NO_ERROR;
+
+       for (pite = rules; pite->pite_key != NULL; pite++) {
+               ctx->pic_key = pite->pite_key;
+               obj = prop_dictionary_get(dict, pite->pite_key);
+               ctx->pic_type = prop_object_type(obj);
+               if (obj == NULL) {
+                       if (pite->pite_flags & PROP_INGEST_FLAG_OPTIONAL) {
+                               if ((*pite->pite_handler)(ctx, NULL) == false) {
+                                       ctx->pic_error =
+                                           PROP_INGEST_ERROR_HANDLER_FAILED;
+                                       return (false);
+                               }
+                               continue;
+                       }
+                       ctx->pic_error = PROP_INGEST_ERROR_NO_KEY;
+                       return (false);
+               }
+               if (ctx->pic_type != pite->pite_type &&
+                   pite->pite_type != PROP_TYPE_UNKNOWN) {
+                       ctx->pic_error = PROP_INGEST_ERROR_WRONG_TYPE;
+                       return (false);
+               }
+               if ((*pite->pite_handler)(ctx, obj) == false) {
+                       ctx->pic_error = PROP_INGEST_ERROR_HANDLER_FAILED;
+                       return (false);
+               }
+       }
+
+       return (true);
+}
diff --git a/common/lib/libprop/prop_kern.c b/common/lib/libprop/prop_kern.c
new file mode 100644 (file)
index 0000000..eeda733
--- /dev/null
@@ -0,0 +1,612 @@
+/*     $NetBSD: prop_kern.c,v 1.17 2011/09/30 22:08:18 jym Exp $       */
+
+/*-
+ * Copyright (c) 2006, 2009 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jason R. Thorpe.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if defined(__NetBSD__)
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include <prop/proplib.h>
+
+#if !defined(_KERNEL) && !defined(_STANDALONE)
+#include <sys/mman.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#ifdef RUMP_ACTION
+#include <rump/rump_syscalls.h>
+#define ioctl(a,b,c) rump_sys_ioctl(a,b,c)
+#endif
+
+static int
+_prop_object_externalize_to_pref(prop_object_t obj, struct plistref *pref,
+                                char **bufp)
+{
+       char *buf;
+
+       switch (prop_object_type(obj)) {
+       case PROP_TYPE_DICTIONARY:
+               buf = prop_dictionary_externalize(obj);
+               break;
+       case PROP_TYPE_ARRAY:
+               buf = prop_array_externalize(obj);
+               break;
+       default:
+               return (ENOTSUP);
+       }
+       if (buf == NULL) {
+               /* Assume we ran out of memory. */
+               return (ENOMEM);
+       }
+       pref->pref_plist = buf;
+       pref->pref_len = strlen(buf) + 1;
+
+       *bufp = buf;
+
+       return (0);
+}
+
+bool
+prop_array_externalize_to_pref(prop_array_t array, struct plistref *prefp)
+{
+       char *buf;
+       int rv;
+
+       rv = _prop_object_externalize_to_pref(array, prefp, &buf);
+       if (rv != 0)
+               errno = rv;     /* pass up error value in errno */
+       return (rv == 0);
+}
+
+/*
+ * prop_array_externalize_to_pref --
+ *     Externalize an array into a plistref for sending to the kernel.
+ */
+int
+prop_array_send_syscall(prop_array_t array, struct plistref *prefp)
+{
+       if (prop_array_externalize_to_pref(array, prefp))
+               return 0;
+       else
+               return errno;
+}
+
+bool
+prop_dictionary_externalize_to_pref(prop_dictionary_t dict,
+                                   struct plistref *prefp)
+{
+       char *buf;
+       int rv;
+
+       rv = _prop_object_externalize_to_pref(dict, prefp, &buf);
+       if (rv != 0)
+               errno = rv;     /* pass up error value in errno */
+       return (rv == 0);
+}
+
+/*
+ * prop_dictionary_externalize_to_pref --
+ *     Externalize an dictionary into a plistref for sending to the kernel.
+ */
+int
+prop_dictionary_send_syscall(prop_dictionary_t dict,
+                            struct plistref *prefp)
+{
+       if (prop_dictionary_externalize_to_pref(dict, prefp))
+               return 0;
+       else
+               return errno;
+}
+
+static int
+_prop_object_send_ioctl(prop_object_t obj, int fd, unsigned long cmd)
+{
+       struct plistref pref;
+       char *buf;
+       int error;
+
+       error = _prop_object_externalize_to_pref(obj, &pref, &buf);
+       if (error)
+               return (error);
+
+       if (ioctl(fd, cmd, &pref) == -1)
+               error = errno;
+       else
+               error = 0;
+       
+       free(buf);
+
+       return (error);
+}
+
+/*
+ * prop_array_send_ioctl --
+ *     Send an array to the kernel using the specified ioctl.
+ */
+int
+prop_array_send_ioctl(prop_array_t array, int fd, unsigned long cmd)
+{
+       int rv;
+
+       rv = _prop_object_send_ioctl(array, fd, cmd);
+       if (rv != 0) {
+               errno = rv;     /* pass up error value in errno */
+               return rv;
+       } else 
+               return 0;
+}
+
+/*
+ * prop_dictionary_send_ioctl --
+ *     Send a dictionary to the kernel using the specified ioctl.
+ */
+int
+prop_dictionary_send_ioctl(prop_dictionary_t dict, int fd, unsigned long cmd)
+{
+       int rv;
+
+       rv = _prop_object_send_ioctl(dict, fd, cmd);
+       if (rv != 0) {
+               errno = rv;     /* pass up error value in errno */
+               return rv;
+       } else 
+               return 0;
+}
+
+static int
+_prop_object_internalize_from_pref(const struct plistref *pref,
+                                  prop_type_t type, prop_object_t *objp)
+{
+       prop_object_t obj = NULL;
+       char *buf;
+       int error = 0;
+
+       if (pref->pref_len == 0) {
+               /*
+                * This should never happen; we should always get the XML
+                * for an empty dictionary if it's really empty.
+                */
+               error = EIO;
+               goto out;
+       } else {
+               buf = pref->pref_plist;
+               buf[pref->pref_len - 1] = '\0'; /* extra insurance */
+               switch (type) {
+               case PROP_TYPE_DICTIONARY:
+                       obj = prop_dictionary_internalize(buf);
+                       break;
+               case PROP_TYPE_ARRAY:
+                       obj = prop_array_internalize(buf);
+                       break;
+               default:
+                       error = ENOTSUP;
+               }
+               (void) munmap(buf, pref->pref_len);
+               if (obj == NULL && error == 0)
+                       error = EIO;
+       }
+
+ out:
+       if (error == 0)
+               *objp = obj;
+       return (error);
+}
+
+/*
+ * prop_array_internalize_from_pref --
+ *     Internalize a pref into a prop_array_t object.
+ */
+bool
+prop_array_internalize_from_pref(const struct plistref *prefp,
+                                prop_array_t *arrayp)
+{
+       int rv;
+
+       rv = _prop_object_internalize_from_pref(prefp, PROP_TYPE_ARRAY,
+           (prop_object_t *)arrayp);
+       if (rv != 0)
+               errno = rv;     /* pass up error value in errno */
+       return (rv == 0);
+}
+
+/*
+ * prop_array_recv_syscall --
+ *     Internalize an array received from the kernel as pref.
+ */
+int
+prop_array_recv_syscall(const struct plistref *prefp,
+                       prop_array_t *arrayp)
+{
+       if (prop_array_internalize_from_pref(prefp, arrayp))
+               return 0;
+       else
+               return errno;
+}
+
+/*
+ * prop_dictionary_internalize_from_pref --
+ *     Internalize a pref into a prop_dictionary_t object.
+ */
+bool
+prop_dictionary_internalize_from_pref(const struct plistref *prefp,
+                                     prop_dictionary_t *dictp)
+{
+       int rv;
+
+       rv = _prop_object_internalize_from_pref(prefp, PROP_TYPE_DICTIONARY,
+           (prop_object_t *)dictp);
+       if (rv != 0)
+               errno = rv;     /* pass up error value in errno */
+       return (rv == 0);
+}
+
+/*
+ * prop_dictionary_recv_syscall --
+ *     Internalize a dictionary received from the kernel as pref.
+ */
+int
+prop_dictionary_recv_syscall(const struct plistref *prefp,
+                            prop_dictionary_t *dictp)
+{
+       if (prop_dictionary_internalize_from_pref(prefp, dictp))
+               return 0;
+       else
+               return errno;
+}
+
+
+/*
+ * prop_array_recv_ioctl --
+ *     Receive an array from the kernel using the specified ioctl.
+ */
+int
+prop_array_recv_ioctl(int fd, unsigned long cmd, prop_array_t *arrayp)
+{
+       int rv;
+       struct plistref pref;
+
+       rv = ioctl(fd, cmd, &pref);
+       if (rv == -1)
+               return errno;
+
+       rv = _prop_object_internalize_from_pref(&pref, PROP_TYPE_ARRAY,
+                           (prop_object_t *)arrayp);
+       if (rv != 0) {
+               errno = rv;     /* pass up error value in errno */
+               return rv;
+       } else
+               return 0;
+}
+
+/*
+ * prop_dictionary_recv_ioctl --
+ *     Receive a dictionary from the kernel using the specified ioctl.
+ */
+int
+prop_dictionary_recv_ioctl(int fd, unsigned long cmd, prop_dictionary_t *dictp)
+{
+       int rv;
+       struct plistref pref;
+
+       rv = ioctl(fd, cmd, &pref);
+       if (rv == -1)
+               return errno;
+
+       rv = _prop_object_internalize_from_pref(&pref, PROP_TYPE_DICTIONARY,
+                           (prop_object_t *)dictp);
+       if (rv != 0) {
+               errno = rv;     /* pass up error value in errno */
+               return rv;
+       } else
+               return 0;
+}
+
+/*
+ * prop_dictionary_sendrecv_ioctl --
+ *     Combination send/receive a dictionary to/from the kernel using
+ *     the specified ioctl.
+ */
+int
+prop_dictionary_sendrecv_ioctl(prop_dictionary_t dict, int fd,
+                              unsigned long cmd, prop_dictionary_t *dictp)
+{
+       struct plistref pref;
+       char *buf;
+       int error;
+
+       error = _prop_object_externalize_to_pref(dict, &pref, &buf);
+       if (error != 0) {
+               errno = error;
+               return error;
+       }
+
+       if (ioctl(fd, cmd, &pref) == -1)
+               error = errno;
+       else
+               error = 0;
+       
+       free(buf);
+
+       if (error != 0)
+               return error;
+
+       error = _prop_object_internalize_from_pref(&pref, PROP_TYPE_DICTIONARY,
+                           (prop_object_t *)dictp);
+       if (error != 0) {
+               errno = error;     /* pass up error value in errno */
+               return error;
+       } else
+               return 0;
+}
+#endif /* !_KERNEL && !_STANDALONE */
+
+#if defined(_KERNEL)
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <sys/errno.h>
+#include <sys/malloc.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/resource.h>
+#include <sys/pool.h>
+
+#include <uvm/uvm.h>
+
+#include "prop_object_impl.h"
+
+/* Arbitrary limit ioctl input to 64KB */
+unsigned int prop_object_copyin_limit = 65536;
+
+/* initialize proplib for use in the kernel */
+void
+prop_kern_init(void)
+{
+       __link_set_decl(prop_linkpools, struct prop_pool_init);
+       struct prop_pool_init * const *pi;
+
+       __link_set_foreach(pi, prop_linkpools)
+               pool_init((*pi)->pp, (*pi)->size, 0, 0, 0, (*pi)->wchan,
+                   &pool_allocator_nointr, IPL_NONE);
+}
+
+static int
+_prop_object_copyin(const struct plistref *pref, const prop_type_t type,
+                         prop_object_t *objp)
+{
+       prop_object_t obj = NULL;
+       char *buf;
+       int error;
+
+       /*
+        * Allocate an extra byte so we can guarantee NUL-termination.
+        *
+        * Allow malloc to fail in case pmap would be exhausted.
+        */
+       buf = malloc(pref->pref_len + 1, M_TEMP, M_WAITOK | M_CANFAIL);
+       if (buf == NULL)
+               return (ENOMEM);
+       error = copyin(pref->pref_plist, buf, pref->pref_len);
+       if (error) {
+               free(buf, M_TEMP);
+               return (error);
+       }
+       buf[pref->pref_len] = '\0';
+
+       switch (type) {
+       case PROP_TYPE_ARRAY:
+               obj = prop_array_internalize(buf);
+               break;
+       case PROP_TYPE_DICTIONARY:
+               obj = prop_dictionary_internalize(buf);
+               break;
+       default:
+               error = ENOTSUP;
+       }
+
+       free(buf, M_TEMP);
+       if (obj == NULL) {
+               if (error == 0)
+                       error = EIO;
+       } else {
+               *objp = obj;
+       }
+       return (error);
+}
+
+
+static int
+_prop_object_copyin_ioctl(const struct plistref *pref, const prop_type_t type,
+                         const u_long cmd, prop_object_t *objp)
+{
+       if ((cmd & IOC_IN) == 0)
+               return (EFAULT);
+
+       return _prop_object_copyin(pref, type, objp);
+}
+
+/*
+ * prop_array_copyin --
+ *     Copy in an array passed as a syscall arg.
+ */
+int
+prop_array_copyin(const struct plistref *pref, prop_array_t *arrayp)
+{
+       return (_prop_object_copyin(pref, PROP_TYPE_ARRAY,
+                                         (prop_object_t *)arrayp));
+}
+
+/*
+ * prop_dictionary_copyin --
+ *     Copy in a dictionary passed as a syscall arg.
+ */
+int
+prop_dictionary_copyin(const struct plistref *pref, prop_dictionary_t *dictp)
+{
+       return (_prop_object_copyin(pref, PROP_TYPE_DICTIONARY,
+                                         (prop_object_t *)dictp));
+}
+
+
+/*
+ * prop_array_copyin_ioctl --
+ *     Copy in an array send with an ioctl.
+ */
+int
+prop_array_copyin_ioctl(const struct plistref *pref, const u_long cmd,
+                       prop_array_t *arrayp)
+{
+       return (_prop_object_copyin_ioctl(pref, PROP_TYPE_ARRAY,
+                                         cmd, (prop_object_t *)arrayp));
+}
+
+/*
+ * prop_dictionary_copyin_ioctl --
+ *     Copy in a dictionary sent with an ioctl.
+ */
+int
+prop_dictionary_copyin_ioctl(const struct plistref *pref, const u_long cmd,
+                            prop_dictionary_t *dictp)
+{
+       return (_prop_object_copyin_ioctl(pref, PROP_TYPE_DICTIONARY,
+                                         cmd, (prop_object_t *)dictp));
+}
+
+static int
+_prop_object_copyout(struct plistref *pref, prop_object_t obj)
+{
+       struct lwp *l = curlwp;         /* XXX */
+       struct proc *p = l->l_proc;
+       char *buf;
+       size_t len, rlen;
+       int error = 0;
+       vaddr_t uaddr;
+
+       switch (prop_object_type(obj)) {
+       case PROP_TYPE_ARRAY:
+               buf = prop_array_externalize(obj);
+               break;
+       case PROP_TYPE_DICTIONARY:
+               buf = prop_dictionary_externalize(obj);
+               break;
+       default:
+               return (ENOTSUP);
+       }
+       if (buf == NULL)
+               return (ENOMEM);
+
+       len = strlen(buf) + 1;
+       rlen = round_page(len);
+
+       /*
+        * See sys_mmap() in sys/uvm/uvm_mmap.c.
+        * Let's act as if we were calling mmap(0, ...)
+        */
+       uaddr = p->p_emul->e_vm_default_addr(p,
+           (vaddr_t)p->p_vmspace->vm_daddr, rlen);
+
+       error = uvm_mmap(&p->p_vmspace->vm_map,
+                        &uaddr, rlen,
+                        VM_PROT_READ|VM_PROT_WRITE,
+                        VM_PROT_READ|VM_PROT_WRITE,
+                        MAP_PRIVATE|MAP_ANON,
+                        NULL, 0,
+                        p->p_rlimit[RLIMIT_MEMLOCK].rlim_cur);
+       
+       if (error == 0) {
+               error = copyout(buf, (char *)uaddr, len);
+               if (error == 0) {
+                       pref->pref_plist = (char *)uaddr;
+                       pref->pref_len   = len;
+               }
+       }
+
+       free(buf, M_TEMP);
+
+       return (error);
+}
+
+/*
+ * prop_array_copyout --
+ *     Copy out an array to a syscall arg.
+ */
+int
+prop_array_copyout(struct plistref *pref, prop_array_t array)
+{
+       return (_prop_object_copyout(pref, array));
+}
+
+/*
+ * prop_dictionary_copyout --
+ *     Copy out a dictionary to a syscall arg.
+ */
+int
+prop_dictionary_copyout(struct plistref *pref, prop_dictionary_t dict)
+{
+       return (_prop_object_copyout(pref, dict));
+}
+
+static int
+_prop_object_copyout_ioctl(struct plistref *pref, const u_long cmd,
+                          prop_object_t obj)
+{
+       if ((cmd & IOC_OUT) == 0)
+               return (EFAULT);
+       return _prop_object_copyout(pref, obj);
+}
+
+
+/*
+ * prop_array_copyout_ioctl --
+ *     Copy out an array being received with an ioctl.
+ */
+int
+prop_array_copyout_ioctl(struct plistref *pref, const u_long cmd,
+                        prop_array_t array)
+{
+       return (_prop_object_copyout_ioctl(pref, cmd, array));
+}
+
+/*
+ * prop_dictionary_copyout_ioctl --
+ *     Copy out a dictionary being received with an ioctl.
+ */
+int
+prop_dictionary_copyout_ioctl(struct plistref *pref, const u_long cmd,
+                             prop_dictionary_t dict)
+{
+       return (
+           _prop_object_copyout_ioctl(pref, cmd, dict));
+}
+#endif /* _KERNEL */
+
+#endif /* __NetBSD__ */
diff --git a/common/lib/libprop/prop_number.3 b/common/lib/libprop/prop_number.3
new file mode 100644 (file)
index 0000000..0b5e9ca
--- /dev/null
@@ -0,0 +1,210 @@
+.\"    $NetBSD: prop_number.3,v 1.10 2011/01/20 10:44:42 wiz Exp $
+.\"
+.\" Copyright (c) 2006 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by Jason R. Thorpe.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd January 21, 2008
+.Dt PROP_NUMBER 3
+.Os
+.Sh NAME
+.Nm prop_number ,
+.Nm prop_number_create_integer ,
+.Nm prop_number_create_unsigned_integer ,
+.Nm prop_number_copy ,
+.Nm prop_number_size ,
+.Nm prop_number_unsigned ,
+.Nm prop_number_integer_value ,
+.Nm prop_number_unsigned_integer_value ,
+.Nm prop_number_equals ,
+.Nm prop_number_equals_integer ,
+.Nm prop_number_equals_unsigned_integer
+.Nd numeric value property object
+.Sh LIBRARY
+.Lb libprop
+.Sh SYNOPSIS
+.In prop/proplib.h
+.\"
+.Ft prop_number_t
+.Fn prop_number_create_integer "int64_t val"
+.Ft prop_number_t
+.Fn prop_number_create_unsigned_integer "uint64_t val"
+.Ft prop_number_t
+.Fn prop_number_copy "prop_number_t number"
+.\"
+.Ft int
+.Fn prop_number_size "prop_number_t number"
+.Ft bool
+.Fn prop_number_unsigned "prop_number_t number"
+.Ft int64_t
+.Fn prop_number_integer_value "prop_number_t number"
+.Ft uint64_t
+.Fn prop_number_unsigned_integer_value "prop_number_t number"
+.\"
+.Ft bool
+.Fn prop_number_equals "prop_number_t num1" "prop_number_t num2"
+.Ft bool
+.Fn prop_number_equals_integer "prop_number_t number" "int64_t val"
+.Ft bool
+.Fn prop_number_equals_unsigned_integer "prop_number_t number" "uint64_t val"
+.Sh DESCRIPTION
+The
+.Nm prop_number
+family of functions operate on a numeric value property object type.
+Values are either signed or unsigned, and promoted to a 64-bit type
+.Pq int64_t or uint64_t , respectively .
+.Pp
+It is possible to compare number objects that differ in sign.
+Such comparisons first test to see if each object is within the valid
+number range of the other:
+.Bl -bullet
+.It
+Signed numbers that are greater than or equal to 0 can be compared to
+unsigned numbers.
+.It
+Unsigned numbers that are less than or equal to the largest signed 64-bit
+value
+.Pq Dv INT64_MAX
+can be compared to signed numbers.
+.El
+.Pp
+Number objects have a different externalized representation depending
+on their sign:
+.Bl -bullet
+.It
+Signed numbers are externalized in base-10
+.Pq decimal .
+.It
+Unsigned numbers are externalized in base-16
+.Pq hexadecimal .
+.El
+.Pp
+When numbers are internalized, the sign of the resulting number object
+.Pq and thus its valid range
+is determined by a set of rules evaluated in the following order:
+.Bl -bullet
+.It
+If the first character of the number is a
+.Sq -
+then the number is signed.
+.It
+If the first two characters of the number are
+.Sq 0x
+then the number is unsigned.
+.It
+If the number value fits into the range of a signed number then the
+number is signed.
+.It
+In all other cases, the number is unsigned.
+.El
+.Bl -tag -width "xxxxx"
+.It Fn prop_number_create_integer "int64_t val"
+Create a numeric value object with the signed value
+.Fa val .
+Returns
+.Dv NULL
+on failure.
+.It Fn prop_number_create_unsigned_integer "uint64_t val"
+Create a numeric value object with the unsigned value
+.Fa val .
+Returns
+.Dv NULL
+on failure.
+.It Fn prop_number_copy "prop_number_t number"
+Copy a numeric value object.
+If the supplied object isn't a numeric value,
+.Dv NULL
+is returned.
+.It Fn prop_number_size "prop_number_t number"
+Returns 8, 16, 32, or 64, representing the number of bits required to
+hold the value of the object.
+If the supplied object isn't a numeric value,
+.Dv NULL
+is returned.
+.It Fn prop_number_unsigned "prop_number_t number"
+Returns
+.Dv true
+if the numeric value object has an unsigned value.
+.It Fn prop_number_integer_value "prop_number_t number"
+Returns the signed integer value of the numeric value object.
+If the supplied object isn't a numeric value, zero is returned.
+Thus,
+it is not possible to distinguish between
+.Dq not a prop_number_t
+and
+.Dq prop_number_t has a value of 0 .
+.It Fn prop_number_unsigned_integer_value "prop_number_t number"
+Returns the unsigned integer value of the numeric value object.
+If the supplied object isn't a numeric value, zero is returned.
+Thus,
+it is not possible to distinguish between
+.Dq not a prop_number_t
+and
+.Dq prop_number_t has a value of 0 .
+.It Fn prop_number_equals "prop_number_t num1" "prop_number_t num2"
+Returns
+.Dv true
+if the two numeric value objects are equivalent.
+If at least one of the supplied objects isn't a numeric value,
+.Dv false
+is returned.
+.It Fn prop_number_equals_integer "prop_number_t number" "int64_t val"
+Returns
+.Dv true
+if the object's value is equivalent to the signed value
+.Fa val .
+If the supplied object isn't a numerical value or if
+.Fa val
+exceeds
+.Dv INT64_MAX ,
+.Dv false
+is returned.
+.It Fn prop_number_equals_unsigned_integer "prop_number_t number" \
+    "uint64_t val"
+Returns
+.Dv true
+if the object's value is equivalent to the unsigned value
+.Fa val .
+If the supplied object isn't a numerical value or if
+.Fa val
+exceeds
+.Dv INT64_MAX ,
+.Dv false
+is returned.
+.El
+.Sh SEE ALSO
+.Xr prop_array 3 ,
+.Xr prop_bool 3 ,
+.Xr prop_data 3 ,
+.Xr prop_dictionary 3 ,
+.Xr prop_object 3 ,
+.Xr prop_string 3 ,
+.Xr proplib 3
+.Sh HISTORY
+The
+.Nm proplib
+property container object library first appeared in
+.Nx 4.0 .
diff --git a/common/lib/libprop/prop_number.c b/common/lib/libprop/prop_number.c
new file mode 100644 (file)
index 0000000..ba61b58
--- /dev/null
@@ -0,0 +1,592 @@
+/*     $NetBSD: prop_number.c,v 1.23 2010/09/24 22:51:52 rmind Exp $   */
+
+/*-
+ * Copyright (c) 2006 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jason R. Thorpe.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <prop/prop_number.h>
+#include "prop_object_impl.h"
+#include "prop_rb_impl.h"
+
+#if defined(_KERNEL)
+#include <sys/systm.h>
+#elif defined(_STANDALONE)
+#include <sys/param.h>
+#include <lib/libkern/libkern.h>
+#else
+#include <errno.h>
+#include <stdlib.h>
+#endif
+
+struct _prop_number {
+       struct _prop_object     pn_obj;
+       struct rb_node          pn_link;
+       struct _prop_number_value {
+               union {
+                       int64_t  pnu_signed;
+                       uint64_t pnu_unsigned;
+               } pnv_un;
+#define        pnv_signed      pnv_un.pnu_signed
+#define        pnv_unsigned    pnv_un.pnu_unsigned
+               unsigned int    pnv_is_unsigned :1,
+                                               :31;
+       } pn_value;
+};
+
+_PROP_POOL_INIT(_prop_number_pool, sizeof(struct _prop_number), "propnmbr")
+
+static _prop_object_free_rv_t
+               _prop_number_free(prop_stack_t, prop_object_t *);
+static bool    _prop_number_externalize(
+                               struct _prop_object_externalize_context *,
+                               void *);
+static _prop_object_equals_rv_t
+               _prop_number_equals(prop_object_t, prop_object_t,
+                                   void **, void **,
+                                   prop_object_t *, prop_object_t *);
+
+static void _prop_number_lock(void);
+static void _prop_number_unlock(void);
+
+static const struct _prop_object_type _prop_object_type_number = {
+       .pot_type       =       PROP_TYPE_NUMBER,
+       .pot_free       =       _prop_number_free,
+       .pot_extern     =       _prop_number_externalize,
+       .pot_equals     =       _prop_number_equals,
+       .pot_lock       =       _prop_number_lock,
+       .pot_unlock     =       _prop_number_unlock,
+};
+
+#define        prop_object_is_number(x)        \
+       ((x) != NULL && (x)->pn_obj.po_type == &_prop_object_type_number)
+
+/*
+ * Number objects are immutable, and we are likely to have many number
+ * objects that have the same value.  So, to save memory, we unique'ify
+ * numbers so we only have one copy of each.
+ */
+
+static int
+_prop_number_compare_values(const struct _prop_number_value *pnv1,
+                           const struct _prop_number_value *pnv2)
+{
+
+       /* Signed numbers are sorted before unsigned numbers. */
+
+       if (pnv1->pnv_is_unsigned) {
+               if (! pnv2->pnv_is_unsigned)
+                       return (1);
+               if (pnv1->pnv_unsigned < pnv2->pnv_unsigned)
+                       return (-1);
+               if (pnv1->pnv_unsigned > pnv2->pnv_unsigned)
+                       return (1);
+               return (0);
+       }
+
+       if (pnv2->pnv_is_unsigned)
+               return (-1);
+       if (pnv1->pnv_signed < pnv2->pnv_signed)
+               return (-1);
+       if (pnv1->pnv_signed > pnv2->pnv_signed)
+               return (1);
+       return (0);
+}
+
+static int
+/*ARGSUSED*/
+_prop_number_rb_compare_nodes(void *ctx __unused,
+                             const void *n1, const void *n2)
+{
+       const struct _prop_number *pn1 = n1;
+       const struct _prop_number *pn2 = n2;
+
+       return _prop_number_compare_values(&pn1->pn_value, &pn2->pn_value);
+}
+
+static int
+/*ARGSUSED*/
+_prop_number_rb_compare_key(void *ctx __unused, const void *n, const void *v)
+{
+       const struct _prop_number *pn = n;
+       const struct _prop_number_value *pnv = v;
+
+       return _prop_number_compare_values(&pn->pn_value, pnv);
+}
+
+static const rb_tree_ops_t _prop_number_rb_tree_ops = {
+       .rbto_compare_nodes = _prop_number_rb_compare_nodes,
+       .rbto_compare_key = _prop_number_rb_compare_key,
+       .rbto_node_offset = offsetof(struct _prop_number, pn_link),
+       .rbto_context = NULL
+};
+
+static struct rb_tree _prop_number_tree;
+_PROP_MUTEX_DECL_STATIC(_prop_number_tree_mutex)
+
+/* ARGSUSED */
+static _prop_object_free_rv_t
+_prop_number_free(prop_stack_t stack, prop_object_t *obj)
+{
+       prop_number_t pn = *obj;
+
+       _prop_rb_tree_remove_node(&_prop_number_tree, pn);
+
+       _PROP_POOL_PUT(_prop_number_pool, pn);
+
+       return (_PROP_OBJECT_FREE_DONE);
+}
+
+_PROP_ONCE_DECL(_prop_number_init_once)
+
+static int
+_prop_number_init(void)
+{
+
+       _PROP_MUTEX_INIT(_prop_number_tree_mutex);
+       _prop_rb_tree_init(&_prop_number_tree, &_prop_number_rb_tree_ops);
+       return 0;
+}
+
+static void 
+_prop_number_lock(void)
+{
+       /* XXX: init necessary? */
+       _PROP_ONCE_RUN(_prop_number_init_once, _prop_number_init);
+       _PROP_MUTEX_LOCK(_prop_number_tree_mutex);
+}
+
+static void
+_prop_number_unlock(void)
+{
+       _PROP_MUTEX_UNLOCK(_prop_number_tree_mutex);
+}
+       
+static bool
+_prop_number_externalize(struct _prop_object_externalize_context *ctx,
+                        void *v)
+{
+       prop_number_t pn = v;
+       char tmpstr[32];
+
+       /*
+        * For unsigned numbers, we output in hex.  For signed numbers,
+        * we output in decimal.
+        */
+       if (pn->pn_value.pnv_is_unsigned)
+               sprintf(tmpstr, "0x%" PRIx64, pn->pn_value.pnv_unsigned);
+       else
+               sprintf(tmpstr, "%" PRIi64, pn->pn_value.pnv_signed);
+
+       if (_prop_object_externalize_start_tag(ctx, "integer") == false ||
+           _prop_object_externalize_append_cstring(ctx, tmpstr) == false ||
+           _prop_object_externalize_end_tag(ctx, "integer") == false)
+               return (false);
+       
+       return (true);
+}
+
+/* ARGSUSED */
+static _prop_object_equals_rv_t
+_prop_number_equals(prop_object_t v1, prop_object_t v2,
+    void **stored_pointer1, void **stored_pointer2,
+    prop_object_t *next_obj1, prop_object_t *next_obj2)
+{
+       prop_number_t num1 = v1;
+       prop_number_t num2 = v2;
+
+       /*
+        * There is only ever one copy of a number object at any given
+        * time, so we can reduce this to a simple pointer equality check
+        * in the common case.
+        */
+       if (num1 == num2)
+               return (_PROP_OBJECT_EQUALS_TRUE);
+
+       /*
+        * If the numbers are the same signed-ness, then we know they
+        * cannot be equal because they would have had pointer equality.
+        */
+       if (num1->pn_value.pnv_is_unsigned == num2->pn_value.pnv_is_unsigned)
+               return (_PROP_OBJECT_EQUALS_FALSE);
+
+       /*
+        * We now have one signed value and one unsigned value.  We can
+        * compare them iff:
+        *      - The unsigned value is not larger than the signed value
+        *        can represent.
+        *      - The signed value is not smaller than the unsigned value
+        *        can represent.
+        */
+       if (num1->pn_value.pnv_is_unsigned) {
+               /*
+                * num1 is unsigned and num2 is signed.
+                */
+               if (num1->pn_value.pnv_unsigned > INT64_MAX)
+                       return (_PROP_OBJECT_EQUALS_FALSE);
+               if (num2->pn_value.pnv_signed < 0)
+                       return (_PROP_OBJECT_EQUALS_FALSE);
+       } else {
+               /*
+                * num1 is signed and num2 is unsigned.
+                */
+               if (num1->pn_value.pnv_signed < 0)
+                       return (_PROP_OBJECT_EQUALS_FALSE);
+               if (num2->pn_value.pnv_unsigned > INT64_MAX)
+                       return (_PROP_OBJECT_EQUALS_FALSE);
+       }
+
+       if (num1->pn_value.pnv_signed == num2->pn_value.pnv_signed)
+               return _PROP_OBJECT_EQUALS_TRUE;
+       else
+               return _PROP_OBJECT_EQUALS_FALSE;
+}
+
+static prop_number_t
+_prop_number_alloc(const struct _prop_number_value *pnv)
+{
+       prop_number_t opn, pn, rpn;
+
+       _PROP_ONCE_RUN(_prop_number_init_once, _prop_number_init);
+
+       /*
+        * Check to see if this already exists in the tree.  If it does,
+        * we just retain it and return it.
+        */
+       _PROP_MUTEX_LOCK(_prop_number_tree_mutex);
+       opn = _prop_rb_tree_find(&_prop_number_tree, pnv);
+       if (opn != NULL) {
+               prop_object_retain(opn);
+               _PROP_MUTEX_UNLOCK(_prop_number_tree_mutex);
+               return (opn);
+       }
+       _PROP_MUTEX_UNLOCK(_prop_number_tree_mutex);
+
+       /*
+        * Not in the tree.  Create it now.
+        */
+
+       pn = _PROP_POOL_GET(_prop_number_pool);
+       if (pn == NULL)
+               return (NULL);
+
+       _prop_object_init(&pn->pn_obj, &_prop_object_type_number);
+
+       pn->pn_value = *pnv;
+
+       /*
+        * We dropped the mutex when we allocated the new object, so
+        * we have to check again if it is in the tree.
+        */
+       _PROP_MUTEX_LOCK(_prop_number_tree_mutex);
+       opn = _prop_rb_tree_find(&_prop_number_tree, pnv);
+       if (opn != NULL) {
+               prop_object_retain(opn);
+               _PROP_MUTEX_UNLOCK(_prop_number_tree_mutex);
+               _PROP_POOL_PUT(_prop_number_pool, pn);
+               return (opn);
+       }
+       rpn = _prop_rb_tree_insert_node(&_prop_number_tree, pn);
+       _PROP_ASSERT(rpn == pn);
+       _PROP_MUTEX_UNLOCK(_prop_number_tree_mutex);
+       return (pn);
+}
+
+/*
+ * prop_number_create_integer --
+ *     Create a prop_number_t and initialize it with the
+ *     provided integer value.
+ */
+prop_number_t
+prop_number_create_integer(int64_t val)
+{
+       struct _prop_number_value pnv;
+
+       memset(&pnv, 0, sizeof(pnv));
+       pnv.pnv_signed = val;
+       pnv.pnv_is_unsigned = false;
+
+       return (_prop_number_alloc(&pnv));
+}
+
+/*
+ * prop_number_create_unsigned_integer --
+ *     Create a prop_number_t and initialize it with the
+ *     provided unsigned integer value.
+ */
+prop_number_t
+prop_number_create_unsigned_integer(uint64_t val)
+{
+       struct _prop_number_value pnv;
+
+       memset(&pnv, 0, sizeof(pnv));
+       pnv.pnv_unsigned = val;
+       pnv.pnv_is_unsigned = true;
+
+       return (_prop_number_alloc(&pnv));
+}
+
+/*
+ * prop_number_copy --
+ *     Copy a prop_number_t.
+ */
+prop_number_t
+prop_number_copy(prop_number_t opn)
+{
+
+       if (! prop_object_is_number(opn))
+               return (NULL);
+
+       /*
+        * Because we only ever allocate one object for any given
+        * value, this can be reduced to a simple retain operation.
+        */
+       prop_object_retain(opn);
+       return (opn);
+}
+
+/*
+ * prop_number_unsigned --
+ *     Returns true if the prop_number_t has an unsigned value.
+ */
+bool
+prop_number_unsigned(prop_number_t pn)
+{
+
+       return (pn->pn_value.pnv_is_unsigned);
+}
+
+/*
+ * prop_number_size --
+ *     Return the size, in bits, required to hold the value of
+ *     the specified number.
+ */
+int
+prop_number_size(prop_number_t pn)
+{
+       struct _prop_number_value *pnv;
+
+       if (! prop_object_is_number(pn))
+               return (0);
+
+       pnv = &pn->pn_value;
+
+       if (pnv->pnv_is_unsigned) {
+               if (pnv->pnv_unsigned > UINT32_MAX)
+                       return (64);
+               if (pnv->pnv_unsigned > UINT16_MAX)
+                       return (32);
+               if (pnv->pnv_unsigned > UINT8_MAX)
+                       return (16);
+               return (8);
+       }
+
+       if (pnv->pnv_signed > INT32_MAX || pnv->pnv_signed < INT32_MIN)
+               return (64);
+       if (pnv->pnv_signed > INT16_MAX || pnv->pnv_signed < INT16_MIN)
+               return (32);
+       if (pnv->pnv_signed > INT8_MAX  || pnv->pnv_signed < INT8_MIN)
+               return (16);
+       return (8);
+}
+
+/*
+ * prop_number_integer_value --
+ *     Get the integer value of a prop_number_t.
+ */
+int64_t
+prop_number_integer_value(prop_number_t pn)
+{
+
+       /*
+        * XXX Impossible to distinguish between "not a prop_number_t"
+        * XXX and "prop_number_t has a value of 0".
+        */
+       if (! prop_object_is_number(pn))
+               return (0);
+
+       return (pn->pn_value.pnv_signed);
+}
+
+/*
+ * prop_number_unsigned_integer_value --
+ *     Get the unsigned integer value of a prop_number_t.
+ */
+uint64_t
+prop_number_unsigned_integer_value(prop_number_t pn)
+{
+
+       /*
+        * XXX Impossible to distinguish between "not a prop_number_t"
+        * XXX and "prop_number_t has a value of 0".
+        */
+       if (! prop_object_is_number(pn))
+               return (0);
+
+       return (pn->pn_value.pnv_unsigned);
+}
+
+/*
+ * prop_number_equals --
+ *     Return true if two numbers are equivalent.
+ */
+bool
+prop_number_equals(prop_number_t num1, prop_number_t num2)
+{
+       if (!prop_object_is_number(num1) || !prop_object_is_number(num2))
+               return (false);
+
+       return (prop_object_equals(num1, num2));
+}
+
+/*
+ * prop_number_equals_integer --
+ *     Return true if the number is equivalent to the specified integer.
+ */
+bool
+prop_number_equals_integer(prop_number_t pn, int64_t val)
+{
+
+       if (! prop_object_is_number(pn))
+               return (false);
+
+       if (pn->pn_value.pnv_is_unsigned &&
+           (pn->pn_value.pnv_unsigned > INT64_MAX || val < 0))
+               return (false);
+       
+       return (pn->pn_value.pnv_signed == val);
+}
+
+/*
+ * prop_number_equals_unsigned_integer --
+ *     Return true if the number is equivalent to the specified
+ *     unsigned integer.
+ */
+bool
+prop_number_equals_unsigned_integer(prop_number_t pn, uint64_t val)
+{
+
+       if (! prop_object_is_number(pn))
+               return (false);
+       
+       if (! pn->pn_value.pnv_is_unsigned &&
+           (pn->pn_value.pnv_signed < 0 || val > INT64_MAX))
+               return (false);
+       
+       return (pn->pn_value.pnv_unsigned == val);
+}
+
+static bool
+_prop_number_internalize_unsigned(struct _prop_object_internalize_context *ctx,
+                                 struct _prop_number_value *pnv)
+{
+       char *cp;
+
+       _PROP_ASSERT(/*CONSTCOND*/sizeof(unsigned long long) ==
+                    sizeof(uint64_t));
+
+#ifndef _KERNEL
+       errno = 0;
+#endif
+       pnv->pnv_unsigned = (uint64_t) strtoull(ctx->poic_cp, &cp, 0);
+#ifndef _KERNEL                /* XXX can't check for ERANGE in the kernel */
+       if (pnv->pnv_unsigned == UINT64_MAX && errno == ERANGE)
+               return (false);
+#endif
+       pnv->pnv_is_unsigned = true;
+       ctx->poic_cp = cp;
+
+       return (true);
+}
+
+static bool
+_prop_number_internalize_signed(struct _prop_object_internalize_context *ctx,
+                               struct _prop_number_value *pnv)
+{
+       char *cp;
+
+       _PROP_ASSERT(/*CONSTCOND*/sizeof(long long) == sizeof(int64_t));
+
+#ifndef _KERNEL
+       errno = 0;
+#endif
+       pnv->pnv_signed = (int64_t) strtoll(ctx->poic_cp, &cp, 0);
+#ifndef _KERNEL                /* XXX can't check for ERANGE in the kernel */
+       if ((pnv->pnv_signed == INT64_MAX || pnv->pnv_signed == INT64_MIN) &&
+           errno == ERANGE)
+               return (false);
+#endif
+       pnv->pnv_is_unsigned = false;
+       ctx->poic_cp = cp;
+
+       return (true);
+}
+
+/*
+ * _prop_number_internalize --
+ *     Parse a <number>...</number> and return the object created from
+ *     the external representation.
+ */
+/* ARGSUSED */
+bool
+_prop_number_internalize(prop_stack_t stack, prop_object_t *obj,
+    struct _prop_object_internalize_context *ctx)
+{
+       struct _prop_number_value pnv;
+
+       memset(&pnv, 0, sizeof(pnv));
+
+       /* No attributes, no empty elements. */
+       if (ctx->poic_tagattr != NULL || ctx->poic_is_empty_element)
+               return (true);
+
+       /*
+        * If the first character is '-', then we treat as signed.
+        * If the first two characters are "0x" (i.e. the number is
+        * in hex), then we treat as unsigned.  Otherwise, we try
+        * signed first, and if that fails (presumably due to ERANGE),
+        * then we switch to unsigned.
+        */
+       if (ctx->poic_cp[0] == '-') {
+               if (_prop_number_internalize_signed(ctx, &pnv) == false)
+                       return (true);
+       } else if (ctx->poic_cp[0] == '0' && ctx->poic_cp[1] == 'x') {
+               if (_prop_number_internalize_unsigned(ctx, &pnv) == false)
+                       return (true);
+       } else {
+               if (_prop_number_internalize_signed(ctx, &pnv) == false &&
+                   _prop_number_internalize_unsigned(ctx, &pnv) == false)
+                       return (true);
+       }
+
+       if (_prop_object_internalize_find_tag(ctx, "integer",
+                                             _PROP_TAG_TYPE_END) == false)
+               return (true);
+
+       *obj = _prop_number_alloc(&pnv);
+       return (true);
+}
diff --git a/common/lib/libprop/prop_object.3 b/common/lib/libprop/prop_object.3
new file mode 100644 (file)
index 0000000..7df09ea
--- /dev/null
@@ -0,0 +1,141 @@
+.\"    $NetBSD: prop_object.3,v 1.8 2011/01/20 10:44:42 wiz Exp $
+.\"
+.\" Copyright (c) 2006 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by Jason R. Thorpe.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd August 21, 2006
+.Dt PROP_OBJECT 3
+.Os
+.Sh NAME
+.Nm prop_object ,
+.Nm prop_object_retain ,
+.Nm prop_object_release ,
+.Nm prop_object_type ,
+.Nm prop_object_equals ,
+.Nm prop_object_iterator_next ,
+.Nm prop_object_iterator_reset ,
+.Nm prop_object_iterator_release
+.Nd general property container object functions
+.Sh LIBRARY
+.Lb libprop
+.Sh SYNOPSIS
+.In prop/proplib.h
+.\"
+.Ft void
+.Fn prop_object_retain "prop_object_t obj"
+.Ft void
+.Fn prop_object_release "prop_object_t obj"
+.\"
+.Ft prop_type_t
+.Fn prop_object_type "prop_object_t obj"
+.Ft bool
+.Fn prop_object_equals "prop_object_t obj1" "prop_object_t obj2"
+.\"
+.Ft prop_object_t
+.Fn prop_object_iterator_next "prop_object_iterator_t iter"
+.Ft void
+.Fn prop_object_iterator_reset "prop_object_iterator_t iter"
+.Ft void
+.Fn prop_object_iterator_release "prop_object_iterator_t iter"
+.Sh DESCRIPTION
+The
+.Nm prop_object
+family of functions operate on all property container object types.
+.Bl -tag -width ""
+.It Fn prop_object_retain "prop_object_t obj"
+Increment the reference count on an object.
+.It Fn prop_object_release "prop_object_t obj"
+Decrement the reference count on an object.
+If the last reference is dropped, the object is freed.
+.It Fn prop_object_type "prop_object_t obj"
+Determine the type of the object.
+Objects are one of the following types:
+.Pp
+.Bl -tag -width "PROP_TYPE_DICT_KEYSYM" -compact
+.It Dv PROP_TYPE_BOOL
+Boolean value
+.Pq prop_bool_t
+.It Dv PROP_TYPE_NUMBER
+Number
+.Pq prop_number_t
+.It Dv PROP_TYPE_STRING
+String
+.Pq prop_string_t
+.It Dv PROP_TYPE_DATA
+Opaque data
+.Pq prop_data_t
+.It Dv PROP_TYPE_ARRAY
+Array
+.Pq prop_array_t
+.It Dv PROP_TYPE_DICTIONARY
+Dictionary
+.Pq prop_dictionary_t
+.It Dv PROP_TYPE_DICT_KEYSYM
+Dictionary key symbol
+.Pq prop_dictionary_keysym_t
+.El
+.Pp
+If
+.Fa obj
+is
+.Dv NULL ,
+then
+.Dv PROP_TYPE_UNKNOWN
+is returned.
+.It Fn prop_object_equals "prop_object_t obj1" "prop_object_t obj2"
+Returns
+.Dv true
+if the two objects are of the same type and are equivalent.
+.It Fn prop_object_iterator_next "prop_object_iterator_t iter"
+Return the next object in the collection
+.Pq array or dictionary
+being iterated by the iterator
+.Fa iter .
+If there are no more objects in the collection,
+.Dv NULL
+is returned.
+.It Fn prop_object_iterator_reset "prop_object_iterator_t iter"
+Reset the iterator to the first object in the collection being iterated
+by the iterator
+.Fa iter .
+.It Fn prop_object_iterator_release "prop_object_iterator_t iter"
+Release the iterator
+.Fa iter .
+.El
+.Sh SEE ALSO
+.Xr prop_array 3 ,
+.Xr prop_bool 3 ,
+.Xr prop_data 3 ,
+.Xr prop_dictionary 3 ,
+.Xr prop_number 3 ,
+.Xr prop_string 3 ,
+.Xr proplib 3
+.Sh HISTORY
+The
+.Nm proplib
+property container object library first appeared in
+.Nx 4.0 .
diff --git a/common/lib/libprop/prop_object.c b/common/lib/libprop/prop_object.c
new file mode 100644 (file)
index 0000000..fd0f14d
--- /dev/null
@@ -0,0 +1,1230 @@
+/*     $NetBSD: prop_object.c,v 1.27 2011/04/20 20:00:07 martin Exp $  */
+
+/*-
+ * Copyright (c) 2006, 2007 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jason R. Thorpe.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <prop/prop_object.h>
+#include "prop_object_impl.h"
+
+#if !defined(_KERNEL) && !defined(_STANDALONE)
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <unistd.h>
+#include <assert.h>
+#endif
+#include <sys/atomic.h>
+
+#ifdef _STANDALONE
+void *
+_prop_standalone_calloc(size_t size)
+{
+       void *rv;
+
+       rv = alloc(size);
+       if (rv != NULL)
+               memset(rv, 0, size);
+
+       return (rv);
+}
+
+void *
+_prop_standalone_realloc(void *v, size_t size)
+{
+       void *rv;
+
+       rv = alloc(size);
+       if (rv != NULL) {
+               memcpy(rv, v, size);    /* XXX */
+               dealloc(v, 0);          /* XXX */
+       }
+       
+       return (rv);
+}
+#endif /* _STANDALONE */
+
+/*
+ * _prop_object_init --
+ *     Initialize an object.  Called when sub-classes create
+ *     an instance.
+ */
+void
+_prop_object_init(struct _prop_object *po, const struct _prop_object_type *pot)
+{
+
+       po->po_type = pot;
+       po->po_refcnt = 1;
+}
+
+/*
+ * _prop_object_fini --
+ *     Finalize an object.  Called when sub-classes destroy
+ *     an instance.
+ */
+/*ARGSUSED*/
+void
+_prop_object_fini(struct _prop_object *po _PROP_ARG_UNUSED)
+{
+       /* Nothing to do, currently. */
+}
+
+/*
+ * _prop_object_externalize_start_tag --
+ *     Append an XML-style start tag to the externalize buffer.
+ */
+bool
+_prop_object_externalize_start_tag(
+    struct _prop_object_externalize_context *ctx, const char *tag)
+{
+       unsigned int i;
+
+       for (i = 0; i < ctx->poec_depth; i++) {
+               if (_prop_object_externalize_append_char(ctx, '\t') == false)
+                       return (false);
+       }
+       if (_prop_object_externalize_append_char(ctx, '<') == false ||
+           _prop_object_externalize_append_cstring(ctx, tag) == false ||
+           _prop_object_externalize_append_char(ctx, '>') == false)
+               return (false);
+       
+       return (true);
+}
+
+/*
+ * _prop_object_externalize_end_tag --
+ *     Append an XML-style end tag to the externalize buffer.
+ */
+bool
+_prop_object_externalize_end_tag(
+    struct _prop_object_externalize_context *ctx, const char *tag)
+{
+
+       if (_prop_object_externalize_append_char(ctx, '<') == false ||
+           _prop_object_externalize_append_char(ctx, '/') == false ||
+           _prop_object_externalize_append_cstring(ctx, tag) == false ||
+           _prop_object_externalize_append_char(ctx, '>') == false ||
+           _prop_object_externalize_append_char(ctx, '\n') == false)
+               return (false);
+
+       return (true);
+}
+
+/*
+ * _prop_object_externalize_empty_tag --
+ *     Append an XML-style empty tag to the externalize buffer.
+ */
+bool
+_prop_object_externalize_empty_tag(
+    struct _prop_object_externalize_context *ctx, const char *tag)
+{
+       unsigned int i;
+
+       for (i = 0; i < ctx->poec_depth; i++) {
+               if (_prop_object_externalize_append_char(ctx, '\t') == false)
+                       return (false);
+       }
+
+       if (_prop_object_externalize_append_char(ctx, '<') == false ||
+           _prop_object_externalize_append_cstring(ctx, tag) == false ||
+           _prop_object_externalize_append_char(ctx, '/') == false ||
+           _prop_object_externalize_append_char(ctx, '>') == false ||
+           _prop_object_externalize_append_char(ctx, '\n') == false)
+               return (false);
+       
+       return (true);
+}
+
+/*
+ * _prop_object_externalize_append_cstring --
+ *     Append a C string to the externalize buffer.
+ */
+bool
+_prop_object_externalize_append_cstring(
+    struct _prop_object_externalize_context *ctx, const char *cp)
+{
+
+       while (*cp != '\0') {
+               if (_prop_object_externalize_append_char(ctx,
+                                               (unsigned char) *cp) == false)
+                       return (false);
+               cp++;
+       }
+
+       return (true);
+}
+
+/*
+ * _prop_object_externalize_append_encoded_cstring --
+ *     Append an encoded C string to the externalize buffer.
+ */
+bool
+_prop_object_externalize_append_encoded_cstring(
+    struct _prop_object_externalize_context *ctx, const char *cp)
+{
+
+       while (*cp != '\0') {
+               switch (*cp) {
+               case '<':
+                       if (_prop_object_externalize_append_cstring(ctx,
+                                       "&lt;") == false)
+                               return (false);
+                       break;
+               case '>':
+                       if (_prop_object_externalize_append_cstring(ctx,
+                                       "&gt;") == false)
+                               return (false);
+                       break;
+               case '&':
+                       if (_prop_object_externalize_append_cstring(ctx,
+                                       "&amp;") == false)
+                               return (false);
+                       break;
+               default:
+                       if (_prop_object_externalize_append_char(ctx,
+                                       (unsigned char) *cp) == false)
+                               return (false);
+                       break;
+               }
+               cp++;
+       }
+
+       return (true);
+}
+
+#define        BUF_EXPAND              256
+
+/*
+ * _prop_object_externalize_append_char --
+ *     Append a single character to the externalize buffer.
+ */
+bool
+_prop_object_externalize_append_char(
+    struct _prop_object_externalize_context *ctx, unsigned char c)
+{
+
+       _PROP_ASSERT(ctx->poec_capacity != 0);
+       _PROP_ASSERT(ctx->poec_buf != NULL);
+       _PROP_ASSERT(ctx->poec_len <= ctx->poec_capacity);
+
+       if (ctx->poec_len == ctx->poec_capacity) {
+               char *cp = _PROP_REALLOC(ctx->poec_buf,
+                                        ctx->poec_capacity + BUF_EXPAND,
+                                        M_TEMP);
+               if (cp == NULL)
+                       return (false);
+               ctx->poec_capacity = ctx->poec_capacity + BUF_EXPAND;
+               ctx->poec_buf = cp;
+       }
+
+       ctx->poec_buf[ctx->poec_len++] = c;
+
+       return (true);
+}
+
+/*
+ * _prop_object_externalize_header --
+ *     Append the standard XML header to the externalize buffer.
+ */
+bool
+_prop_object_externalize_header(struct _prop_object_externalize_context *ctx)
+{
+       static const char _plist_xml_header[] =
+"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+"<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n";
+
+       if (_prop_object_externalize_append_cstring(ctx,
+                                                _plist_xml_header) == false ||
+           _prop_object_externalize_start_tag(ctx,
+                                      "plist version=\"1.0\"") == false ||
+           _prop_object_externalize_append_char(ctx, '\n') == false)
+               return (false);
+
+       return (true);
+}
+
+/*
+ * _prop_object_externalize_footer --
+ *     Append the standard XML footer to the externalize buffer.  This
+ *     also NUL-terminates the buffer.
+ */
+bool
+_prop_object_externalize_footer(struct _prop_object_externalize_context *ctx)
+{
+
+       if (_prop_object_externalize_end_tag(ctx, "plist") == false ||
+           _prop_object_externalize_append_char(ctx, '\0') == false)
+               return (false);
+
+       return (true);
+}
+
+/*
+ * _prop_object_externalize_context_alloc --
+ *     Allocate an externalize context.
+ */
+struct _prop_object_externalize_context *
+_prop_object_externalize_context_alloc(void)
+{
+       struct _prop_object_externalize_context *ctx;
+
+       ctx = _PROP_MALLOC(sizeof(*ctx), M_TEMP);
+       if (ctx != NULL) {
+               ctx->poec_buf = _PROP_MALLOC(BUF_EXPAND, M_TEMP);
+               if (ctx->poec_buf == NULL) {
+                       _PROP_FREE(ctx, M_TEMP);
+                       return (NULL);
+               }
+               ctx->poec_len = 0;
+               ctx->poec_capacity = BUF_EXPAND;
+               ctx->poec_depth = 0;
+       }
+       return (ctx);
+}
+
+/*
+ * _prop_object_externalize_context_free --
+ *     Free an externalize context.
+ */
+void
+_prop_object_externalize_context_free(
+               struct _prop_object_externalize_context *ctx)
+{
+
+       /* Buffer is always freed by the caller. */
+       _PROP_FREE(ctx, M_TEMP);
+}
+
+/*
+ * _prop_object_internalize_skip_comment --
+ *     Skip the body and end tag of a comment.
+ */
+static bool
+_prop_object_internalize_skip_comment(
+                               struct _prop_object_internalize_context *ctx)
+{
+       const char *cp = ctx->poic_cp;
+
+       while (!_PROP_EOF(*cp)) {
+               if (cp[0] == '-' &&
+                   cp[1] == '-' &&
+                   cp[2] == '>') {
+                       ctx->poic_cp = cp + 3;
+                       return (true);
+               }
+               cp++;
+       }
+
+       return (false);         /* ran out of buffer */
+}
+
+/*
+ * _prop_object_internalize_find_tag --
+ *     Find the next tag in an XML stream.  Optionally compare the found
+ *     tag to an expected tag name.  State of the context is undefined
+ *     if this routine returns false.  Upon success, the context points
+ *     to the first octet after the tag.
+ */
+bool
+_prop_object_internalize_find_tag(struct _prop_object_internalize_context *ctx,
+                     const char *tag, _prop_tag_type_t type)
+{
+       const char *cp;
+       size_t taglen;
+
+       if (tag != NULL)
+               taglen = strlen(tag);
+       else
+               taglen = 0;
+
+ start_over:
+       cp = ctx->poic_cp;
+
+       /*
+        * Find the start of the tag.
+        */
+       while (_PROP_ISSPACE(*cp))
+               cp++;
+       if (_PROP_EOF(*cp))
+               return (false);
+
+       if (*cp != '<')
+               return (false);
+
+       ctx->poic_tag_start = cp++;
+       if (_PROP_EOF(*cp))
+               return (false);
+
+       if (*cp == '!') {
+               if (cp[1] != '-' || cp[2] != '-')
+                       return (false);
+               /*
+                * Comment block -- only allowed if we are allowed to
+                * return a start tag.
+                */
+               if (type == _PROP_TAG_TYPE_END)
+                       return (false);
+               ctx->poic_cp = cp + 3;
+               if (_prop_object_internalize_skip_comment(ctx) == false)
+                       return (false);
+               goto start_over;
+       }
+
+       if (*cp == '/') {
+               if (type != _PROP_TAG_TYPE_END &&
+                   type != _PROP_TAG_TYPE_EITHER)
+                       return (false);
+               cp++;
+               if (_PROP_EOF(*cp))
+                       return (false);
+               ctx->poic_tag_type = _PROP_TAG_TYPE_END;
+       } else {
+               if (type != _PROP_TAG_TYPE_START &&
+                   type != _PROP_TAG_TYPE_EITHER)
+                       return (false);
+               ctx->poic_tag_type = _PROP_TAG_TYPE_START;
+       }
+
+       ctx->poic_tagname = cp;
+
+       while (!_PROP_ISSPACE(*cp) && *cp != '/' && *cp != '>')
+               cp++;
+       if (_PROP_EOF(*cp))
+               return (false);
+
+       ctx->poic_tagname_len = cp - ctx->poic_tagname;
+
+       /* Make sure this is the tag we're looking for. */
+       if (tag != NULL &&
+           (taglen != ctx->poic_tagname_len ||
+            memcmp(tag, ctx->poic_tagname, taglen) != 0))
+               return (false);
+       
+       /* Check for empty tag. */
+       if (*cp == '/') {
+               if (ctx->poic_tag_type != _PROP_TAG_TYPE_START)
+                       return(false);          /* only valid on start tags */
+               ctx->poic_is_empty_element = true;
+               cp++;
+               if (_PROP_EOF(*cp) || *cp != '>')
+                       return (false);
+       } else
+               ctx->poic_is_empty_element = false;
+
+       /* Easy case of no arguments. */
+       if (*cp == '>') {
+               ctx->poic_tagattr = NULL;
+               ctx->poic_tagattr_len = 0;
+               ctx->poic_tagattrval = NULL;
+               ctx->poic_tagattrval_len = 0;
+               ctx->poic_cp = cp + 1;
+               return (true);
+       }
+
+       _PROP_ASSERT(!_PROP_EOF(*cp));
+       cp++;
+       if (_PROP_EOF(*cp))
+               return (false);
+
+       while (_PROP_ISSPACE(*cp))
+               cp++;
+       if (_PROP_EOF(*cp))
+               return (false);
+
+       ctx->poic_tagattr = cp;
+
+       while (!_PROP_ISSPACE(*cp) && *cp != '=')
+               cp++;
+       if (_PROP_EOF(*cp))
+               return (false);
+
+       ctx->poic_tagattr_len = cp - ctx->poic_tagattr;
+       
+       cp++;
+       if (*cp != '\"')
+               return (false);
+       cp++;
+       if (_PROP_EOF(*cp))
+               return (false);
+       
+       ctx->poic_tagattrval = cp;
+       while (*cp != '\"')
+               cp++;
+       if (_PROP_EOF(*cp))
+               return (false);
+       ctx->poic_tagattrval_len = cp - ctx->poic_tagattrval;
+       
+       cp++;
+       if (*cp != '>')
+               return (false);
+
+       ctx->poic_cp = cp + 1;
+       return (true);
+}
+
+/*
+ * _prop_object_internalize_decode_string --
+ *     Decode an encoded string.
+ */
+bool
+_prop_object_internalize_decode_string(
+                               struct _prop_object_internalize_context *ctx,
+                               char *target, size_t targsize, size_t *sizep,
+                               const char **cpp)
+{
+       const char *src;
+       size_t tarindex;
+       char c;
+       
+       tarindex = 0;
+       src = ctx->poic_cp;
+
+       for (;;) {
+               if (_PROP_EOF(*src))
+                       return (false);
+               if (*src == '<') {
+                       break;
+               }
+
+               if ((c = *src) == '&') {
+                       if (src[1] == 'a' &&
+                           src[2] == 'm' &&
+                           src[3] == 'p' &&
+                           src[4] == ';') {
+                               c = '&';
+                               src += 5;
+                       } else if (src[1] == 'l' &&
+                                  src[2] == 't' &&
+                                  src[3] == ';') {
+                               c = '<';
+                               src += 4;
+                       } else if (src[1] == 'g' &&
+                                  src[2] == 't' &&
+                                  src[3] == ';') {
+                               c = '>';
+                               src += 4;
+                       } else if (src[1] == 'a' &&
+                                  src[2] == 'p' &&
+                                  src[3] == 'o' &&
+                                  src[4] == 's' &&
+                                  src[5] == ';') {
+                               c = '\'';
+                               src += 6;
+                       } else if (src[1] == 'q' &&
+                                  src[2] == 'u' &&
+                                  src[3] == 'o' &&
+                                  src[4] == 't' &&
+                                  src[5] == ';') {
+                               c = '\"';
+                               src += 6;
+                       } else
+                               return (false);
+               } else
+                       src++;
+               if (target) {
+                       if (tarindex >= targsize)
+                               return (false);
+                       target[tarindex] = c;
+               }
+               tarindex++;
+       }
+
+       _PROP_ASSERT(*src == '<');
+       if (sizep != NULL)
+               *sizep = tarindex;
+       if (cpp != NULL)
+               *cpp = src;
+       
+       return (true);
+}
+
+/*
+ * _prop_object_internalize_match --
+ *     Returns true if the two character streams match.
+ */
+bool
+_prop_object_internalize_match(const char *str1, size_t len1,
+                              const char *str2, size_t len2)
+{
+
+       return (len1 == len2 && memcmp(str1, str2, len1) == 0);
+}
+
+#define        INTERNALIZER(t, f)                      \
+{      t,      sizeof(t) - 1,          f       }
+
+static const struct _prop_object_internalizer {
+       const char                      *poi_tag;
+       size_t                          poi_taglen;
+       prop_object_internalizer_t      poi_intern;
+} _prop_object_internalizer_table[] = {
+       INTERNALIZER("array", _prop_array_internalize),
+
+       INTERNALIZER("true", _prop_bool_internalize),
+       INTERNALIZER("false", _prop_bool_internalize),
+
+       INTERNALIZER("data", _prop_data_internalize),
+
+       INTERNALIZER("dict", _prop_dictionary_internalize),
+
+       INTERNALIZER("integer", _prop_number_internalize),
+
+       INTERNALIZER("string", _prop_string_internalize),
+
+       { 0, 0, NULL }
+};
+
+#undef INTERNALIZER
+
+/*
+ * _prop_object_internalize_by_tag --
+ *     Determine the object type from the tag in the context and
+ *     internalize it.
+ */
+prop_object_t
+_prop_object_internalize_by_tag(struct _prop_object_internalize_context *ctx)
+{
+       const struct _prop_object_internalizer *poi;
+       prop_object_t obj, parent_obj;
+       void *data, *iter;
+       prop_object_internalizer_continue_t iter_func;
+       struct _prop_stack stack;
+
+       _prop_stack_init(&stack);
+
+match_start:
+       for (poi = _prop_object_internalizer_table;
+            poi->poi_tag != NULL; poi++) {
+               if (_prop_object_internalize_match(ctx->poic_tagname,
+                                                  ctx->poic_tagname_len,
+                                                  poi->poi_tag,
+                                                  poi->poi_taglen))
+                       break;
+       }
+       if ((poi == NULL) || (poi->poi_tag == NULL)) {
+               while (_prop_stack_pop(&stack, &obj, &iter, &data, NULL)) {
+                       iter_func = (prop_object_internalizer_continue_t)iter;
+                       (*iter_func)(&stack, &obj, ctx, data, NULL);
+               }
+
+               return (NULL);
+       }
+
+       obj = NULL;
+       if (!(*poi->poi_intern)(&stack, &obj, ctx))
+               goto match_start;
+
+       parent_obj = obj;
+       while (_prop_stack_pop(&stack, &parent_obj, &iter, &data, NULL)) {
+               iter_func = (prop_object_internalizer_continue_t)iter;
+               if (!(*iter_func)(&stack, &parent_obj, ctx, data, obj))
+                       goto match_start;
+               obj = parent_obj;
+       }
+
+       return (parent_obj);
+}
+
+prop_object_t
+_prop_generic_internalize(const char *xml, const char *master_tag)
+{
+       prop_object_t obj = NULL;
+       struct _prop_object_internalize_context *ctx;
+
+       ctx = _prop_object_internalize_context_alloc(xml);
+       if (ctx == NULL)
+               return (NULL);
+
+       /* We start with a <plist> tag. */
+       if (_prop_object_internalize_find_tag(ctx, "plist",
+                                             _PROP_TAG_TYPE_START) == false)
+               goto out;
+
+       /* Plist elements cannot be empty. */
+       if (ctx->poic_is_empty_element)
+               goto out;
+
+       /*
+        * We don't understand any plist attributes, but Apple XML
+        * property lists often have a "version" attribute.  If we
+        * see that one, we simply ignore it.
+        */
+       if (ctx->poic_tagattr != NULL &&
+           !_PROP_TAGATTR_MATCH(ctx, "version"))
+               goto out;
+
+       /* Next we expect to see opening master_tag. */
+       if (_prop_object_internalize_find_tag(ctx, master_tag,
+                                             _PROP_TAG_TYPE_START) == false)
+               goto out;
+
+       obj = _prop_object_internalize_by_tag(ctx);
+       if (obj == NULL)
+               goto out;
+
+       /*
+        * We've advanced past the closing master_tag.
+        * Now we want </plist>.
+        */
+       if (_prop_object_internalize_find_tag(ctx, "plist",
+                                             _PROP_TAG_TYPE_END) == false) {
+               prop_object_release(obj);
+               obj = NULL;
+       }
+
+ out:
+       _prop_object_internalize_context_free(ctx);
+       return (obj);
+}
+
+/*
+ * _prop_object_internalize_context_alloc --
+ *     Allocate an internalize context.
+ */
+struct _prop_object_internalize_context *
+_prop_object_internalize_context_alloc(const char *xml)
+{
+       struct _prop_object_internalize_context *ctx;
+
+       ctx = _PROP_MALLOC(sizeof(struct _prop_object_internalize_context),
+                          M_TEMP);
+       if (ctx == NULL)
+               return (NULL);
+       
+       ctx->poic_xml = ctx->poic_cp = xml;
+
+       /*
+        * Skip any whitespace and XML preamble stuff that we don't
+        * know about / care about.
+        */
+       for (;;) {
+               while (_PROP_ISSPACE(*xml))
+                       xml++;
+               if (_PROP_EOF(*xml) || *xml != '<')
+                       goto bad;
+
+#define        MATCH(str)      (memcmp(&xml[1], str, sizeof(str) - 1) == 0)
+
+               /*
+                * Skip over the XML preamble that Apple XML property
+                * lists usually include at the top of the file.
+                */
+               if (MATCH("?xml ") ||
+                   MATCH("!DOCTYPE plist")) {
+                       while (*xml != '>' && !_PROP_EOF(*xml))
+                               xml++;
+                       if (_PROP_EOF(*xml))
+                               goto bad;
+                       xml++;  /* advance past the '>' */
+                       continue;
+               }
+
+               if (MATCH("<!--")) {
+                       ctx->poic_cp = xml + 4;
+                       if (_prop_object_internalize_skip_comment(ctx) == false)
+                               goto bad;
+                       xml = ctx->poic_cp;
+                       continue;
+               }
+
+#undef MATCH
+
+               /*
+                * We don't think we should skip it, so let's hope we can
+                * parse it.
+                */
+               break;
+       }
+
+       ctx->poic_cp = xml;
+       return (ctx);
+ bad:
+       _PROP_FREE(ctx, M_TEMP);
+       return (NULL);
+}
+
+/*
+ * _prop_object_internalize_context_free --
+ *     Free an internalize context.
+ */
+void
+_prop_object_internalize_context_free(
+               struct _prop_object_internalize_context *ctx)
+{
+
+       _PROP_FREE(ctx, M_TEMP);
+}
+
+#if !defined(_KERNEL) && !defined(_STANDALONE)
+/*
+ * _prop_object_externalize_file_dirname --
+ *     dirname(3), basically.  We have to roll our own because the
+ *     system dirname(3) isn't reentrant.
+ */
+static void
+_prop_object_externalize_file_dirname(const char *path, char *result)
+{
+       const char *lastp;
+       size_t len;
+
+       /*
+        * If `path' is a NULL pointer or points to an empty string,
+        * return ".".
+        */
+       if (path == NULL || *path == '\0')
+               goto singledot;
+       
+       /* String trailing slashes, if any. */
+       lastp = path + strlen(path) - 1;
+       while (lastp != path && *lastp == '/')
+               lastp--;
+       
+       /* Terminate path at the last occurrence of '/'. */
+       do {
+               if (*lastp == '/') {
+                       /* Strip trailing slashes, if any. */
+                       while (lastp != path && *lastp == '/')
+                               lastp--;
+
+                       /* ...and copy the result into the result buffer. */
+                       len = (lastp - path) + 1 /* last char */;
+                       if (len > (PATH_MAX - 1))
+                               len = PATH_MAX - 1;
+
+                       memcpy(result, path, len);
+                       result[len] = '\0';
+                       return;
+               }
+       } while (--lastp >= path);
+
+       /* No /'s found, return ".". */
+ singledot:
+       strcpy(result, ".");
+}
+
+/*
+ * _prop_object_externalize_write_file --
+ *     Write an externalized dictionary to the specified file.
+ *     The file is written atomically from the caller's perspective,
+ *     and the mode set to 0666 modified by the caller's umask.
+ */
+bool
+_prop_object_externalize_write_file(const char *fname, const char *xml,
+    size_t len)
+{
+       char tname[PATH_MAX];
+       int fd;
+       int save_errno;
+       mode_t myumask;
+
+       if (len > SSIZE_MAX) {
+               errno = EFBIG;
+               return (false);
+       }
+
+       /*
+        * Get the directory name where the file is to be written
+        * and create the temporary file.
+        */
+       _prop_object_externalize_file_dirname(fname, tname);
+       if (strlcat(tname, "/.plistXXXXXX", sizeof(tname)) >= sizeof(tname)) {
+               errno = ENAMETOOLONG;
+               return (false);
+       }
+       if ((fd = mkstemp(tname)) == -1)
+               return (false);
+
+       if (write(fd, xml, len) != (ssize_t)len)
+               goto bad;
+
+       if (fsync(fd) == -1)
+               goto bad;
+
+       myumask = umask(0);
+       (void)umask(myumask);
+       if (fchmod(fd, 0666 & ~myumask) == -1)
+               goto bad;
+
+       (void) close(fd);
+       fd = -1;
+
+       if (rename(tname, fname) == -1)
+               goto bad;
+
+       return (true);
+
+ bad:
+       save_errno = errno;
+       if (fd != -1)
+               (void) close(fd);
+       (void) unlink(tname);
+       errno = save_errno;
+       return (false);
+}
+
+/*
+ * _prop_object_internalize_map_file --
+ *     Map a file for the purpose of internalizing it.
+ */
+struct _prop_object_internalize_mapped_file *
+_prop_object_internalize_map_file(const char *fname)
+{
+       struct stat sb;
+       struct _prop_object_internalize_mapped_file *mf;
+       size_t pgsize = (size_t)sysconf(_SC_PAGESIZE);
+       size_t pgmask = pgsize - 1;
+       bool need_guard = false;
+       int fd;
+
+       mf = _PROP_MALLOC(sizeof(*mf), M_TEMP);
+       if (mf == NULL)
+               return (NULL);
+       
+       fd = open(fname, O_RDONLY, 0400);
+       if (fd == -1) {
+               _PROP_FREE(mf, M_TEMP);
+               return (NULL);
+       }
+
+       if (fstat(fd, &sb) == -1) {
+               (void) close(fd);
+               _PROP_FREE(mf, M_TEMP);
+               return (NULL);
+       }
+       mf->poimf_mapsize = ((size_t)sb.st_size + pgmask) & ~pgmask;
+       if (mf->poimf_mapsize < (size_t)sb.st_size) {
+               (void) close(fd);
+               _PROP_FREE(mf, M_TEMP);
+               return (NULL);
+       }
+
+       /*
+        * If the file length is an integral number of pages, then we
+        * need to map a guard page at the end in order to provide the
+        * necessary NUL-termination of the buffer.
+        */
+       if ((sb.st_size & pgmask) == 0)
+               need_guard = true;
+
+#ifndef __minix
+       mf->poimf_xml = mmap(NULL, need_guard ? mf->poimf_mapsize + pgsize
+                                             : mf->poimf_mapsize,
+                           PROT_READ, MAP_FILE|MAP_SHARED, fd, (off_t)0);
+#else
+       mf->poimf_xml = MAP_FAILED;
+#endif
+       (void) close(fd);
+       if (mf->poimf_xml == MAP_FAILED) {
+               _PROP_FREE(mf, M_TEMP);
+               return (NULL);
+       }
+
+#ifndef __minix
+       (void) madvise(mf->poimf_xml, mf->poimf_mapsize, MADV_SEQUENTIAL);
+
+       if (need_guard) {
+               if (mmap(mf->poimf_xml + mf->poimf_mapsize,
+                        pgsize, PROT_READ,
+                        MAP_ANON|MAP_PRIVATE|MAP_FIXED, -1,
+                        (off_t)0) == MAP_FAILED) {
+                       (void) munmap(mf->poimf_xml, mf->poimf_mapsize);
+                       _PROP_FREE(mf, M_TEMP);
+                       return (NULL);
+               }
+               mf->poimf_mapsize += pgsize;
+       }
+#endif
+
+       return (mf);
+}
+
+/*
+ * _prop_object_internalize_unmap_file --
+ *     Unmap a file previously mapped for internalizing.
+ */
+void
+_prop_object_internalize_unmap_file(
+    struct _prop_object_internalize_mapped_file *mf)
+{
+
+#ifndef __minix
+       (void) madvise(mf->poimf_xml, mf->poimf_mapsize, MADV_DONTNEED);
+       (void) munmap(mf->poimf_xml, mf->poimf_mapsize);
+       _PROP_FREE(mf, M_TEMP);
+#else
+       assert(0);
+#endif
+}
+#endif /* !_KERNEL && !_STANDALONE */
+
+/*
+ * prop_object_retain --
+ *     Increment the reference count on an object.
+ */
+void
+prop_object_retain(prop_object_t obj)
+{
+       struct _prop_object *po = obj;
+       uint32_t ncnt;
+
+       ncnt = atomic_inc_32_nv(&po->po_refcnt);
+       _PROP_ASSERT(ncnt != 0);
+}
+
+/*
+ * prop_object_release_emergency
+ *     A direct free with prop_object_release failed.
+ *     Walk down the tree until a leaf is found and
+ *     free that. Do not recurse to avoid stack overflows.
+ *
+ *     This is a slow edge condition, but necessary to
+ *     guarantee that an object can always be freed.
+ */
+static void
+prop_object_release_emergency(prop_object_t obj)
+{
+       struct _prop_object *po;
+       void (*unlock)(void);
+       prop_object_t parent = NULL;
+       uint32_t ocnt;
+
+       for (;;) {
+               po = obj;
+               _PROP_ASSERT(obj);
+
+               if (po->po_type->pot_lock != NULL)
+               po->po_type->pot_lock();
+
+               /* Save pointerto unlock function */
+               unlock = po->po_type->pot_unlock;
+               
+               /* Dance a bit to make sure we always get the non-racy ocnt */
+               ocnt = atomic_dec_32_nv(&po->po_refcnt);
+               ocnt++;
+               _PROP_ASSERT(ocnt != 0);
+
+               if (ocnt != 1) {
+                       if (unlock != NULL)
+                               unlock();
+                       break;
+               }
+               
+               _PROP_ASSERT(po->po_type);              
+               if ((po->po_type->pot_free)(NULL, &obj) ==
+                   _PROP_OBJECT_FREE_DONE) {
+                       if (unlock != NULL)
+                               unlock();
+                       break;
+               }
+
+               if (unlock != NULL)
+                       unlock();
+               
+               parent = po;
+               atomic_inc_32(&po->po_refcnt);
+       }
+       _PROP_ASSERT(parent);
+       /* One object was just freed. */
+       po = parent;
+       (*po->po_type->pot_emergency_free)(parent);
+}
+
+/*
+ * prop_object_release --
+ *     Decrement the reference count on an object.
+ *
+ *     Free the object if we are releasing the final
+ *     reference.
+ */
+void
+prop_object_release(prop_object_t obj)
+{
+       struct _prop_object *po;
+       struct _prop_stack stack;
+       void (*unlock)(void); 
+       int ret;
+       uint32_t ocnt;
+
+       _prop_stack_init(&stack);
+
+       do {
+               do {
+                       po = obj;
+                       _PROP_ASSERT(obj);
+
+                       if (po->po_type->pot_lock != NULL)
+                               po->po_type->pot_lock();
+
+                       /* Save pointer to object unlock function */
+                       unlock = po->po_type->pot_unlock;
+                       
+                       ocnt = atomic_dec_32_nv(&po->po_refcnt);
+                       ocnt++;
+                       _PROP_ASSERT(ocnt != 0);
+
+                       if (ocnt != 1) {
+                               ret = 0;
+                               if (unlock != NULL)
+                                       unlock();
+                               break;
+                       }
+                       
+                       ret = (po->po_type->pot_free)(&stack, &obj);
+
+                       if (unlock != NULL)
+                               unlock();
+
+                       if (ret == _PROP_OBJECT_FREE_DONE)
+                               break;
+                       
+                       atomic_inc_32(&po->po_refcnt);
+               } while (ret == _PROP_OBJECT_FREE_RECURSE);
+               if (ret == _PROP_OBJECT_FREE_FAILED)
+                       prop_object_release_emergency(obj);
+       } while (_prop_stack_pop(&stack, &obj, NULL, NULL, NULL));
+}
+
+/*
+ * prop_object_type --
+ *     Return the type of an object.
+ */
+prop_type_t
+prop_object_type(prop_object_t obj)
+{
+       struct _prop_object *po = obj;
+
+       if (obj == NULL)
+               return (PROP_TYPE_UNKNOWN);
+
+       return (po->po_type->pot_type);
+}
+
+/*
+ * prop_object_equals --
+ *     Returns true if thw two objects are equivalent.
+ */
+bool
+prop_object_equals(prop_object_t obj1, prop_object_t obj2)
+{
+       return (prop_object_equals_with_error(obj1, obj2, NULL));
+}
+
+bool
+prop_object_equals_with_error(prop_object_t obj1, prop_object_t obj2,
+    bool *error_flag)
+{
+       struct _prop_object *po1;
+       struct _prop_object *po2;
+       void *stored_pointer1, *stored_pointer2;
+       prop_object_t next_obj1, next_obj2;
+       struct _prop_stack stack;
+       _prop_object_equals_rv_t ret;
+
+       _prop_stack_init(&stack);
+       if (error_flag)
+               *error_flag = false;
+
+ start_subtree:
+       stored_pointer1 = NULL;
+       stored_pointer2 = NULL;
+       po1 = obj1;
+       po2 = obj2;
+
+       if (po1->po_type != po2->po_type)
+               return (false);
+    
+ continue_subtree:
+       ret = (*po1->po_type->pot_equals)(obj1, obj2,
+                                         &stored_pointer1, &stored_pointer2,
+                                         &next_obj1, &next_obj2);
+       if (ret == _PROP_OBJECT_EQUALS_FALSE)
+               goto finish;
+       if (ret == _PROP_OBJECT_EQUALS_TRUE) {
+               if (!_prop_stack_pop(&stack, &obj1, &obj2,
+                                    &stored_pointer1, &stored_pointer2))
+                       return true;
+               po1 = obj1;
+               po2 = obj2;
+               goto continue_subtree;
+       }
+       _PROP_ASSERT(ret == _PROP_OBJECT_EQUALS_RECURSE);
+
+       if (!_prop_stack_push(&stack, obj1, obj2,
+                             stored_pointer1, stored_pointer2)) {
+               if (error_flag)
+                       *error_flag = true;
+               goto finish;
+       }
+       obj1 = next_obj1;
+       obj2 = next_obj2;
+       goto start_subtree;
+
+finish:
+       while (_prop_stack_pop(&stack, &obj1, &obj2, NULL, NULL)) {
+               po1 = obj1;
+               (*po1->po_type->pot_equals_finish)(obj1, obj2);
+       }
+       return (false);         
+}
+
+/*
+ * prop_object_iterator_next --
+ *     Return the next item during an iteration.
+ */
+prop_object_t
+prop_object_iterator_next(prop_object_iterator_t pi)
+{
+
+       return ((*pi->pi_next_object)(pi));
+}
+
+/*
+ * prop_object_iterator_reset --
+ *     Reset the iterator to the first object so as to restart
+ *     iteration.
+ */
+void
+prop_object_iterator_reset(prop_object_iterator_t pi)
+{
+
+       (*pi->pi_reset)(pi);
+}
+
+/*
+ * prop_object_iterator_release --
+ *     Release the object iterator.
+ */
+void
+prop_object_iterator_release(prop_object_iterator_t pi)
+{
+
+       prop_object_release(pi->pi_obj);
+       _PROP_FREE(pi, M_TEMP);
+}
diff --git a/common/lib/libprop/prop_object_impl.h b/common/lib/libprop/prop_object_impl.h
new file mode 100644 (file)
index 0000000..dd438d0
--- /dev/null
@@ -0,0 +1,436 @@
+/*     $NetBSD: prop_object_impl.h,v 1.30 2009/09/13 18:45:10 pooka Exp $      */
+
+/*-
+ * Copyright (c) 2006 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jason R. Thorpe.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _PROPLIB_PROP_OBJECT_IMPL_H_
+#define        _PROPLIB_PROP_OBJECT_IMPL_H_
+
+#if defined(_KERNEL) || defined(_STANDALONE)
+#include <lib/libkern/libkern.h>
+#else
+#include <inttypes.h>
+#endif
+
+#include "prop_stack.h"
+
+struct _prop_object_externalize_context {
+       char *          poec_buf;               /* string buffer */
+       size_t          poec_capacity;          /* capacity of buffer */
+       size_t          poec_len;               /* current length of string */
+       unsigned int    poec_depth;             /* nesting depth */
+};
+
+bool           _prop_object_externalize_start_tag(
+                               struct _prop_object_externalize_context *,
+                               const char *);
+bool           _prop_object_externalize_end_tag(
+                               struct _prop_object_externalize_context *,
+                               const char *);
+bool           _prop_object_externalize_empty_tag(
+                               struct _prop_object_externalize_context *,
+                               const char *);
+bool           _prop_object_externalize_append_cstring(
+                               struct _prop_object_externalize_context *,
+                               const char *);
+bool           _prop_object_externalize_append_encoded_cstring(
+                               struct _prop_object_externalize_context *,
+                               const char *);
+bool           _prop_object_externalize_append_char(
+                               struct _prop_object_externalize_context *,
+                               unsigned char);
+bool           _prop_object_externalize_header(
+                               struct _prop_object_externalize_context *);
+bool           _prop_object_externalize_footer(
+                               struct _prop_object_externalize_context *);
+
+struct _prop_object_externalize_context *
+       _prop_object_externalize_context_alloc(void);
+void   _prop_object_externalize_context_free(
+                               struct _prop_object_externalize_context *);
+
+typedef enum {
+       _PROP_TAG_TYPE_START,                   /* e.g. <dict> */
+       _PROP_TAG_TYPE_END,                     /* e.g. </dict> */
+       _PROP_TAG_TYPE_EITHER
+} _prop_tag_type_t;
+
+struct _prop_object_internalize_context {
+       const char *poic_xml;
+       const char *poic_cp;
+
+       const char *poic_tag_start;
+
+       const char *poic_tagname;
+       size_t      poic_tagname_len;
+       const char *poic_tagattr;
+       size_t      poic_tagattr_len;
+       const char *poic_tagattrval;
+       size_t      poic_tagattrval_len;
+
+       bool   poic_is_empty_element;
+       _prop_tag_type_t poic_tag_type;
+};
+
+typedef enum {
+       _PROP_OBJECT_FREE_DONE,
+       _PROP_OBJECT_FREE_RECURSE,
+       _PROP_OBJECT_FREE_FAILED
+} _prop_object_free_rv_t;
+
+typedef enum {
+       _PROP_OBJECT_EQUALS_FALSE,
+       _PROP_OBJECT_EQUALS_TRUE,
+       _PROP_OBJECT_EQUALS_RECURSE
+} _prop_object_equals_rv_t;
+
+#define        _PROP_EOF(c)            ((c) == '\0')
+#define        _PROP_ISSPACE(c)        \
+       ((c) == ' ' || (c) == '\t' || (c) == '\n' || (c) == '\r' || \
+        _PROP_EOF(c))
+
+#define        _PROP_TAG_MATCH(ctx, t)                                 \
+       _prop_object_internalize_match((ctx)->poic_tagname,     \
+                                      (ctx)->poic_tagname_len, \
+                                      (t), strlen(t))
+
+#define        _PROP_TAGATTR_MATCH(ctx, a)                             \
+       _prop_object_internalize_match((ctx)->poic_tagattr,     \
+                                      (ctx)->poic_tagattr_len, \
+                                      (a), strlen(a))
+
+#define        _PROP_TAGATTRVAL_MATCH(ctx, a)                            \
+       _prop_object_internalize_match((ctx)->poic_tagattrval,    \
+                                      (ctx)->poic_tagattrval_len,\
+                                      (a), strlen(a))
+
+bool           _prop_object_internalize_find_tag(
+                               struct _prop_object_internalize_context *,
+                               const char *, _prop_tag_type_t);
+bool           _prop_object_internalize_match(const char *, size_t,
+                                              const char *, size_t);
+prop_object_t  _prop_object_internalize_by_tag(
+                               struct _prop_object_internalize_context *);
+bool           _prop_object_internalize_decode_string(
+                               struct _prop_object_internalize_context *,
+                               char *, size_t, size_t *, const char **);
+prop_object_t  _prop_generic_internalize(const char *, const char *);
+
+struct _prop_object_internalize_context *
+               _prop_object_internalize_context_alloc(const char *);
+void           _prop_object_internalize_context_free(
+                               struct _prop_object_internalize_context *);
+
+#if !defined(_KERNEL) && !defined(_STANDALONE)
+bool           _prop_object_externalize_write_file(const char *,
+                                                   const char *, size_t);
+
+struct _prop_object_internalize_mapped_file {
+       char *  poimf_xml;
+       size_t  poimf_mapsize;
+};
+
+struct _prop_object_internalize_mapped_file *
+               _prop_object_internalize_map_file(const char *);
+void           _prop_object_internalize_unmap_file(
+                               struct _prop_object_internalize_mapped_file *);
+#endif /* !_KERNEL && !_STANDALONE */
+
+typedef bool (*prop_object_internalizer_t)(prop_stack_t, prop_object_t *,
+                               struct _prop_object_internalize_context *);
+typedef bool (*prop_object_internalizer_continue_t)(prop_stack_t,
+                               prop_object_t *,
+                               struct _prop_object_internalize_context *,
+                               void *, prop_object_t);
+
+       /* These are here because they're required by shared code. */
+bool           _prop_array_internalize(prop_stack_t, prop_object_t *,
+                               struct _prop_object_internalize_context *);
+bool           _prop_bool_internalize(prop_stack_t, prop_object_t *,
+                               struct _prop_object_internalize_context *);
+bool           _prop_data_internalize(prop_stack_t, prop_object_t *,
+                               struct _prop_object_internalize_context *);
+bool           _prop_dictionary_internalize(prop_stack_t, prop_object_t *,
+                               struct _prop_object_internalize_context *);
+bool           _prop_number_internalize(prop_stack_t, prop_object_t *,
+                               struct _prop_object_internalize_context *);
+bool           _prop_string_internalize(prop_stack_t, prop_object_t *,
+                               struct _prop_object_internalize_context *);
+
+struct _prop_object_type {
+       /* type indicator */
+       uint32_t        pot_type;
+       /* func to free object */
+       _prop_object_free_rv_t
+                       (*pot_free)(prop_stack_t, prop_object_t *);
+       /*
+        * func to free the child returned by pot_free with stack == NULL.
+        *
+        * Must be implemented if pot_free can return anything other than
+        * _PROP_OBJECT_FREE_DONE.
+        */
+       void    (*pot_emergency_free)(prop_object_t);
+       /* func to externalize object */
+       bool    (*pot_extern)(struct _prop_object_externalize_context *,
+                             void *);
+       /* func to test quality */
+       _prop_object_equals_rv_t
+               (*pot_equals)(prop_object_t, prop_object_t,
+                             void **, void **,
+                             prop_object_t *, prop_object_t *);
+       /*
+        * func to finish equality iteration.
+        *
+        * Must be implemented if pot_equals can return
+        * _PROP_OBJECT_EQUALS_RECURSE
+        */
+       void    (*pot_equals_finish)(prop_object_t, prop_object_t);
+       void    (*pot_lock)(void);
+       void    (*pot_unlock)(void);
+};
+
+struct _prop_object {
+       const struct _prop_object_type *po_type;/* type descriptor */
+       uint32_t        po_refcnt;              /* reference count */
+};
+
+void           _prop_object_init(struct _prop_object *,
+                                 const struct _prop_object_type *);
+void           _prop_object_fini(struct _prop_object *);
+
+struct _prop_object_iterator {
+       prop_object_t   (*pi_next_object)(void *);
+       void            (*pi_reset)(void *);
+       prop_object_t   pi_obj;
+       uint32_t        pi_version;
+};
+
+#define _PROP_NOTHREAD_ONCE_DECL(x)    static bool x = false;
+#define _PROP_NOTHREAD_ONCE_RUN(x,f)                                   \
+       do {                                                            \
+               if ((x) == false) {                                     \
+                       f();                                            \
+                       x = true;                                       \
+               }                                                       \
+       } while (/*CONSTCOND*/0)
+
+#if defined(_KERNEL)
+
+/*
+ * proplib in the kernel...
+ */
+
+#include <sys/param.h>
+#include <sys/malloc.h>
+#include <sys/pool.h>
+#include <sys/systm.h>
+#include <sys/rwlock.h>
+#include <sys/once.h>
+
+#define        _PROP_ASSERT(x)                 KASSERT(x)
+
+#define        _PROP_MALLOC(s, t)              malloc((s), (t), M_WAITOK)
+#define        _PROP_CALLOC(s, t)              malloc((s), (t), M_WAITOK | M_ZERO)
+#define        _PROP_REALLOC(v, s, t)          realloc((v), (s), (t), M_WAITOK)
+#define        _PROP_FREE(v, t)                free((v), (t))
+
+#define        _PROP_POOL_GET(p)               pool_get(&(p), PR_WAITOK)
+#define        _PROP_POOL_PUT(p, v)            pool_put(&(p), (v))
+
+struct prop_pool_init {
+       struct pool *pp;
+       size_t size;
+       const char *wchan;
+};
+#define        _PROP_POOL_INIT(pp, size, wchan)                                \
+struct pool pp;                                                                \
+static const struct prop_pool_init _link_ ## pp[1] = {                 \
+       { &pp, size, wchan }                                            \
+};                                                                     \
+__link_set_add_rodata(prop_linkpools, _link_ ## pp);
+
+#define        _PROP_MALLOC_DEFINE(t, s, l)                                    \
+               MALLOC_DEFINE(t, s, l);
+
+#define        _PROP_MUTEX_DECL_STATIC(x)      static kmutex_t x;
+#define        _PROP_MUTEX_INIT(x)             mutex_init(&(x),MUTEX_DEFAULT,IPL_NONE)
+#define        _PROP_MUTEX_LOCK(x)             mutex_enter(&(x))
+#define        _PROP_MUTEX_UNLOCK(x)           mutex_exit(&(x))
+
+#define        _PROP_RWLOCK_DECL(x)            krwlock_t x ;
+#define        _PROP_RWLOCK_INIT(x)            rw_init(&(x))
+#define        _PROP_RWLOCK_RDLOCK(x)          rw_enter(&(x), RW_READER)
+#define        _PROP_RWLOCK_WRLOCK(x)          rw_enter(&(x), RW_WRITER)
+#define        _PROP_RWLOCK_UNLOCK(x)          rw_exit(&(x))
+#define        _PROP_RWLOCK_DESTROY(x)         rw_destroy(&(x))
+
+#define _PROP_ONCE_DECL(x)             static ONCE_DECL(x);
+#define _PROP_ONCE_RUN(x,f)            RUN_ONCE(&(x), f)
+
+#elif defined(_STANDALONE)
+
+/*
+ * proplib in a standalone environment...
+ */
+
+#include <lib/libsa/stand.h>
+
+void *         _prop_standalone_calloc(size_t);
+void *         _prop_standalone_realloc(void *, size_t);
+
+#define        _PROP_ASSERT(x)                 /* nothing */
+
+#define        _PROP_MALLOC(s, t)              alloc((s))
+#define        _PROP_CALLOC(s, t)              _prop_standalone_calloc((s))
+#define        _PROP_REALLOC(v, s, t)          _prop_standalone_realloc((v), (s))
+#define        _PROP_FREE(v, t)                dealloc((v), 0)         /* XXX */
+
+#define        _PROP_POOL_GET(p)               alloc((p))
+#define        _PROP_POOL_PUT(p, v)            dealloc((v), (p))
+
+#define        _PROP_POOL_INIT(p, s, d)        static const size_t p = s;
+
+#define        _PROP_MALLOC_DEFINE(t, s, l)    /* nothing */
+
+#define        _PROP_MUTEX_DECL_STATIC(x)      /* nothing */
+#define        _PROP_MUTEX_INIT(x)             /* nothing */
+#define        _PROP_MUTEX_LOCK(x)             /* nothing */
+#define        _PROP_MUTEX_UNLOCK(x)           /* nothing */
+
+#define        _PROP_RWLOCK_DECL(x)            /* nothing */
+#define        _PROP_RWLOCK_INIT(x)            /* nothing */
+#define        _PROP_RWLOCK_RDLOCK(x)          /* nothing */
+#define        _PROP_RWLOCK_WRLOCK(x)          /* nothing */
+#define        _PROP_RWLOCK_UNLOCK(x)          /* nothing */
+#define        _PROP_RWLOCK_DESTROY(x)         /* nothing */
+
+#define _PROP_ONCE_DECL(x)             _PROP_NOTHREAD_ONCE_DECL(x)
+#define _PROP_ONCE_RUN(x,f)            _PROP_NOTHREAD_ONCE_RUN(x,f)
+
+#else
+
+/*
+ * proplib in user space...
+ */
+
+#include <assert.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+
+#define        _PROP_ASSERT(x)                 /*LINTED*/assert(x)
+
+#define        _PROP_MALLOC(s, t)              malloc((s))
+#define        _PROP_CALLOC(s, t)              calloc(1, (s))
+#define        _PROP_REALLOC(v, s, t)          realloc((v), (s))
+#define        _PROP_FREE(v, t)                free((v))
+
+#define        _PROP_POOL_GET(p)               malloc((p))
+#define        _PROP_POOL_PUT(p, v)            free((v))
+
+#define        _PROP_POOL_INIT(p, s, d)        static const size_t p = s;
+
+#define        _PROP_MALLOC_DEFINE(t, s, l)    /* nothing */
+
+#if (defined(__NetBSD__) && defined(_LIBPROP)) 
+/*
+ * Use the same mechanism as libc; we get pthread mutexes for threaded
+ * programs and do-nothing stubs for non-threaded programs.
+ */
+#include "reentrant.h"
+#define        _PROP_MUTEX_DECL_STATIC(x)      static mutex_t x;
+#define        _PROP_MUTEX_INIT(x)             mutex_init(&(x), NULL)
+#define        _PROP_MUTEX_LOCK(x)             mutex_lock(&(x))
+#define        _PROP_MUTEX_UNLOCK(x)           mutex_unlock(&(x))
+
+#define        _PROP_RWLOCK_DECL(x)            rwlock_t x ;
+#define        _PROP_RWLOCK_INIT(x)            rwlock_init(&(x), NULL)
+#define        _PROP_RWLOCK_RDLOCK(x)          rwlock_rdlock(&(x))
+#define        _PROP_RWLOCK_WRLOCK(x)          rwlock_wrlock(&(x))
+#define        _PROP_RWLOCK_UNLOCK(x)          rwlock_unlock(&(x))
+#define        _PROP_RWLOCK_DESTROY(x)         rwlock_destroy(&(x))
+
+#define _PROP_ONCE_DECL(x)                                             \
+       static pthread_once_t x = PTHREAD_ONCE_INIT;
+#define _PROP_ONCE_RUN(x,f)            thr_once(&(x), (void(*)(void))f);
+
+#elif defined(HAVE_NBTOOL_CONFIG_H) || defined(__minix)
+/*
+ * None of NetBSD's build tools are multi-threaded.
+ */
+#define        _PROP_MUTEX_DECL_STATIC(x)      /* nothing */
+#define        _PROP_MUTEX_INIT(x)             /* nothing */
+#define        _PROP_MUTEX_LOCK(x)             /* nothing */
+#define        _PROP_MUTEX_UNLOCK(x)           /* nothing */
+
+#define        _PROP_RWLOCK_DECL(x)            /* nothing */
+#define        _PROP_RWLOCK_INIT(x)            /* nothing */
+#define        _PROP_RWLOCK_RDLOCK(x)          /* nothing */
+#define        _PROP_RWLOCK_WRLOCK(x)          /* nothing */
+#define        _PROP_RWLOCK_UNLOCK(x)          /* nothing */
+#define        _PROP_RWLOCK_DESTROY(x)         /* nothing */
+
+#define _PROP_ONCE_DECL(x)             _PROP_NOTHREAD_ONCE_DECL(x)
+#define _PROP_ONCE_RUN(x,f)            _PROP_NOTHREAD_ONCE_RUN(x,f)
+#else
+/*
+ * Use pthread mutexes everywhere else.
+ */
+#include <pthread.h>
+#define        _PROP_MUTEX_DECL_STATIC(x)      static pthread_mutex_t x;
+#define        _PROP_MUTEX_INIT(x)             pthread_mutex_init(&(x), NULL)
+#define        _PROP_MUTEX_LOCK(x)             pthread_mutex_lock(&(x))
+#define        _PROP_MUTEX_UNLOCK(x)           pthread_mutex_unlock(&(x))
+
+#define        _PROP_RWLOCK_DECL(x)            pthread_rwlock_t x ;
+#define        _PROP_RWLOCK_INIT(x)            pthread_rwlock_init(&(x), NULL)
+#define        _PROP_RWLOCK_RDLOCK(x)          pthread_rwlock_rdlock(&(x))
+#define        _PROP_RWLOCK_WRLOCK(x)          pthread_rwlock_wrlock(&(x))
+#define        _PROP_RWLOCK_UNLOCK(x)          pthread_rwlock_unlock(&(x))
+#define        _PROP_RWLOCK_DESTROY(x)         pthread_rwlock_destroy(&(x))
+
+#define _PROP_ONCE_DECL(x)                                             \
+       static pthread_once_t x = PTHREAD_ONCE_INIT;
+#define _PROP_ONCE_RUN(x,f)            pthread_once(&(x),(void(*)(void))f)
+#endif
+
+#endif /* _KERNEL */
+
+/*
+ * Language features.
+ */
+#if defined(__NetBSD__)
+#include <sys/cdefs.h>
+#define        _PROP_ARG_UNUSED                __unused
+#else
+#define        _PROP_ARG_UNUSED                /* delete */
+#endif /* __NetBSD__ */
+
+#endif /* _PROPLIB_PROP_OBJECT_IMPL_H_ */
diff --git a/common/lib/libprop/prop_rb.c b/common/lib/libprop/prop_rb.c
new file mode 100644 (file)
index 0000000..83657bc
--- /dev/null
@@ -0,0 +1,1057 @@
+/*     $NetBSD: prop_rb.c,v 1.9 2008/06/17 21:29:47 thorpej Exp $      */
+
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Matt Thomas <matt@3am-software.com>.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <prop/proplib.h>
+
+#include "prop_object_impl.h"
+#include "prop_rb_impl.h"
+
+#undef KASSERT
+#ifdef RBDEBUG
+#define        KASSERT(x)      _PROP_ASSERT(x)
+#else
+#define        KASSERT(x)      /* nothing */
+#endif
+
+#ifndef __predict_false
+#define        __predict_false(x)      (x)
+#endif
+
+static void rb_tree_reparent_nodes(struct rb_tree *, struct rb_node *,
+                                  unsigned int);
+static void rb_tree_insert_rebalance(struct rb_tree *, struct rb_node *);
+static void rb_tree_removal_rebalance(struct rb_tree *, struct rb_node *,
+       unsigned int);
+#ifdef RBDEBUG
+static const struct rb_node *rb_tree_iterate_const(const struct rb_tree *,
+       const struct rb_node *, unsigned int);
+static bool rb_tree_check_node(const struct rb_tree *, const struct rb_node *,
+       const struct rb_node *, bool);
+#endif
+
+#ifdef RBDEBUG
+#define        RBT_COUNT_INCR(rbt)     (rbt)->rbt_count++
+#define        RBT_COUNT_DECR(rbt)     (rbt)->rbt_count--
+#else
+#define        RBT_COUNT_INCR(rbt)     /* nothing */
+#define        RBT_COUNT_DECR(rbt)     /* nothing */
+#endif
+
+#define        RBUNCONST(a)    ((void *)(unsigned long)(const void *)(a))
+
+/*
+ * Rather than testing for the NULL everywhere, all terminal leaves are
+ * pointed to this node (and that includes itself).  Note that by setting
+ * it to be const, that on some architectures trying to write to it will
+ * cause a fault.
+ */
+static const struct rb_node sentinel_node = {
+       .rb_nodes = { RBUNCONST(&sentinel_node),
+                     RBUNCONST(&sentinel_node),
+                     NULL },
+       .rb_u = { .u_s = { .s_sentinel = 1 } },
+};
+
+void
+_prop_rb_tree_init(struct rb_tree *rbt, const struct rb_tree_ops *ops)
+{
+       RB_TAILQ_INIT(&rbt->rbt_nodes);
+#ifdef RBDEBUG
+       rbt->rbt_count = 0;
+#endif
+       rbt->rbt_ops = ops;
+       *((const struct rb_node **)&rbt->rbt_root) = &sentinel_node;
+}
+
+/*
+ * Swap the location and colors of 'self' and its child @ which.  The child
+ * can not be a sentinel node.
+ */
+/*ARGSUSED*/
+static void
+rb_tree_reparent_nodes(struct rb_tree *rbt _PROP_ARG_UNUSED,
+    struct rb_node *old_father, unsigned int which)
+{
+       const unsigned int other = which ^ RB_NODE_OTHER;
+       struct rb_node * const grandpa = old_father->rb_parent;
+       struct rb_node * const old_child = old_father->rb_nodes[which];
+       struct rb_node * const new_father = old_child;
+       struct rb_node * const new_child = old_father;
+       unsigned int properties;
+
+       KASSERT(which == RB_NODE_LEFT || which == RB_NODE_RIGHT);
+
+       KASSERT(!RB_SENTINEL_P(old_child));
+       KASSERT(old_child->rb_parent == old_father);
+
+       KASSERT(rb_tree_check_node(rbt, old_father, NULL, false));
+       KASSERT(rb_tree_check_node(rbt, old_child, NULL, false));
+       KASSERT(RB_ROOT_P(old_father) || rb_tree_check_node(rbt, grandpa, NULL, false));
+
+       /*
+        * Exchange descendant linkages.
+        */
+       grandpa->rb_nodes[old_father->rb_position] = new_father;
+       new_child->rb_nodes[which] = old_child->rb_nodes[other];
+       new_father->rb_nodes[other] = new_child;
+
+       /*
+        * Update ancestor linkages
+        */
+       new_father->rb_parent = grandpa;
+       new_child->rb_parent = new_father;
+
+       /*
+        * Exchange properties between new_father and new_child.  The only
+        * change is that new_child's position is now on the other side.
+        */
+       properties = old_child->rb_properties;
+       new_father->rb_properties = old_father->rb_properties;
+       new_child->rb_properties = properties;
+       new_child->rb_position = other;
+
+       /*
+        * Make sure to reparent the new child to ourself.
+        */
+       if (!RB_SENTINEL_P(new_child->rb_nodes[which])) {
+               new_child->rb_nodes[which]->rb_parent = new_child;
+               new_child->rb_nodes[which]->rb_position = which;
+       }
+
+       KASSERT(rb_tree_check_node(rbt, new_father, NULL, false));
+       KASSERT(rb_tree_check_node(rbt, new_child, NULL, false));
+       KASSERT(RB_ROOT_P(new_father) || rb_tree_check_node(rbt, grandpa, NULL, false));
+}
+
+bool
+_prop_rb_tree_insert_node(struct rb_tree *rbt, struct rb_node *self)
+{
+       struct rb_node *parent, *tmp;
+       rb_compare_nodes_fn compare_nodes = rbt->rbt_ops->rbto_compare_nodes;
+       unsigned int position;
+
+       self->rb_properties = 0;
+       tmp = rbt->rbt_root;
+       /*
+        * This is a hack.  Because rbt->rbt_root is just a struct rb_node *,
+        * just like rb_node->rb_nodes[RB_NODE_LEFT], we can use this fact to
+        * avoid a lot of tests for root and know that even at root,
+        * updating rb_node->rb_parent->rb_nodes[rb_node->rb_position] will
+        * rbt->rbt_root.
+        */
+       /* LINTED: see above */
+       parent = (struct rb_node *)&rbt->rbt_root;
+       position = RB_NODE_LEFT;
+
+       /*
+        * Find out where to place this new leaf.
+        */
+       while (!RB_SENTINEL_P(tmp)) {
+               const int diff = (*compare_nodes)(tmp, self);
+               if (__predict_false(diff == 0)) {
+                       /*
+                        * Node already exists; don't insert.
+                        */
+                       return false;
+               }
+               parent = tmp;
+               KASSERT(diff != 0);
+               if (diff < 0) {
+                       position = RB_NODE_LEFT;
+               } else {
+                       position = RB_NODE_RIGHT;
+               }
+               tmp = parent->rb_nodes[position];
+       }
+
+#ifdef RBDEBUG
+       {
+               struct rb_node *prev = NULL, *next = NULL;
+
+               if (position == RB_NODE_RIGHT)
+                       prev = parent;
+               else if (tmp != rbt->rbt_root)
+                       next = parent;
+
+               /*
+                * Verify our sequential position
+                */
+               KASSERT(prev == NULL || !RB_SENTINEL_P(prev));
+               KASSERT(next == NULL || !RB_SENTINEL_P(next));
+               if (prev != NULL && next == NULL)
+                       next = TAILQ_NEXT(prev, rb_link);
+               if (prev == NULL && next != NULL)
+                       prev = TAILQ_PREV(next, rb_node_qh, rb_link);
+               KASSERT(prev == NULL || !RB_SENTINEL_P(prev));
+               KASSERT(next == NULL || !RB_SENTINEL_P(next));
+               KASSERT(prev == NULL
+                       || (*compare_nodes)(prev, self) > 0);
+               KASSERT(next == NULL
+                       || (*compare_nodes)(self, next) > 0);
+       }
+#endif
+
+       /*
+        * Initialize the node and insert as a leaf into the tree.
+        */
+       self->rb_parent = parent;
+       self->rb_position = position;
+       /* LINTED: rbt_root hack */
+       if (__predict_false(parent == (struct rb_node *) &rbt->rbt_root)) {
+               RB_MARK_ROOT(self);
+       } else {
+               KASSERT(position == RB_NODE_LEFT || position == RB_NODE_RIGHT);
+               KASSERT(!RB_ROOT_P(self));      /* Already done */
+       }
+       KASSERT(RB_SENTINEL_P(parent->rb_nodes[position]));
+       self->rb_left = parent->rb_nodes[position];
+       self->rb_right = parent->rb_nodes[position];
+       parent->rb_nodes[position] = self;
+       KASSERT(self->rb_left == &sentinel_node &&
+           self->rb_right == &sentinel_node);
+
+       /*
+        * Insert the new node into a sorted list for easy sequential access
+        */
+       RBT_COUNT_INCR(rbt);
+#ifdef RBDEBUG
+       if (RB_ROOT_P(self)) {
+               RB_TAILQ_INSERT_HEAD(&rbt->rbt_nodes, self, rb_link);
+       } else if (position == RB_NODE_LEFT) {
+               KASSERT((*compare_nodes)(self, self->rb_parent) > 0);
+               RB_TAILQ_INSERT_BEFORE(self->rb_parent, self, rb_link);
+       } else {
+               KASSERT((*compare_nodes)(self->rb_parent, self) > 0);
+               RB_TAILQ_INSERT_AFTER(&rbt->rbt_nodes, self->rb_parent,
+                   self, rb_link);
+       }
+#endif
+
+#if 0
+       /*
+        * Validate the tree before we rebalance
+        */
+       _prop_rb_tree_check(rbt, false);
+#endif
+
+       /*
+        * Rebalance tree after insertion
+        */
+       rb_tree_insert_rebalance(rbt, self);
+
+#if 0
+       /*
+        * Validate the tree after we rebalanced
+        */
+       _prop_rb_tree_check(rbt, true);
+#endif
+
+       return true;
+}
+\f
+static void
+rb_tree_insert_rebalance(struct rb_tree *rbt, struct rb_node *self)
+{
+       RB_MARK_RED(self);
+
+       while (!RB_ROOT_P(self) && RB_RED_P(self->rb_parent)) {
+               const unsigned int which =
+                    (self->rb_parent == self->rb_parent->rb_parent->rb_left
+                       ? RB_NODE_LEFT
+                       : RB_NODE_RIGHT);
+               const unsigned int other = which ^ RB_NODE_OTHER;
+               struct rb_node * father = self->rb_parent;
+               struct rb_node * grandpa = father->rb_parent;
+               struct rb_node * const uncle = grandpa->rb_nodes[other];
+
+               KASSERT(!RB_SENTINEL_P(self));
+               /*
+                * We are red and our parent is red, therefore we must have a
+                * grandfather and he must be black.
+                */
+               KASSERT(RB_RED_P(self)
+                       && RB_RED_P(father)
+                       && RB_BLACK_P(grandpa));
+
+               if (RB_RED_P(uncle)) {
+                       /*
+                        * Case 1: our uncle is red
+                        *   Simply invert the colors of our parent and
+                        *   uncle and make our grandparent red.  And
+                        *   then solve the problem up at his level.
+                        */
+                       RB_MARK_BLACK(uncle);
+                       RB_MARK_BLACK(father);
+                       RB_MARK_RED(grandpa);
+                       self = grandpa;
+                       continue;
+               }
+               /*
+                * Case 2&3: our uncle is black.
+                */
+               if (self == father->rb_nodes[other]) {
+                       /*
+                        * Case 2: we are on the same side as our uncle
+                        *   Swap ourselves with our parent so this case
+                        *   becomes case 3.  Basically our parent becomes our
+                        *   child.
+                        */
+                       rb_tree_reparent_nodes(rbt, father, other);
+                       KASSERT(father->rb_parent == self);
+                       KASSERT(self->rb_nodes[which] == father);
+                       KASSERT(self->rb_parent == grandpa);
+                       self = father;
+                       father = self->rb_parent;
+               }
+               KASSERT(RB_RED_P(self) && RB_RED_P(father));
+               KASSERT(grandpa->rb_nodes[which] == father);
+               /*
+                * Case 3: we are opposite a child of a black uncle.
+                *   Swap our parent and grandparent.  Since our grandfather
+                *   is black, our father will become black and our new sibling
+                *   (former grandparent) will become red.
+                */
+               rb_tree_reparent_nodes(rbt, grandpa, which);
+               KASSERT(self->rb_parent == father);
+               KASSERT(self->rb_parent->rb_nodes[self->rb_position ^ RB_NODE_OTHER] == grandpa);
+               KASSERT(RB_RED_P(self));
+               KASSERT(RB_BLACK_P(father));
+               KASSERT(RB_RED_P(grandpa));
+               break;
+       }
+
+       /*
+        * Final step: Set the root to black.
+        */
+       RB_MARK_BLACK(rbt->rbt_root);
+}
+\f
+struct rb_node *
+_prop_rb_tree_find(struct rb_tree *rbt, const void *key)
+{
+       struct rb_node *parent = rbt->rbt_root;
+       rb_compare_key_fn compare_key = rbt->rbt_ops->rbto_compare_key;
+
+       while (!RB_SENTINEL_P(parent)) {
+               const int diff = (*compare_key)(parent, key);
+               if (diff == 0)
+                       return parent;
+               parent = parent->rb_nodes[diff > 0];
+       }
+
+       return NULL;
+}
+\f
+static void
+rb_tree_prune_node(struct rb_tree *rbt, struct rb_node *self, int rebalance)
+{
+       const unsigned int which = self->rb_position;
+       struct rb_node *father = self->rb_parent;
+
+       KASSERT(rebalance || (RB_ROOT_P(self) || RB_RED_P(self)));
+       KASSERT(!rebalance || RB_BLACK_P(self));
+       KASSERT(RB_CHILDLESS_P(self));
+       KASSERT(rb_tree_check_node(rbt, self, NULL, false));
+
+       father->rb_nodes[which] = self->rb_left;
+
+       /*
+        * Remove ourselves from the node list and decrement the count.
+        */
+       RB_TAILQ_REMOVE(&rbt->rbt_nodes, self, rb_link);
+       RBT_COUNT_DECR(rbt);
+
+       if (rebalance)
+               rb_tree_removal_rebalance(rbt, father, which);
+       KASSERT(RB_ROOT_P(self) || rb_tree_check_node(rbt, father, NULL, true));
+}
+
+static void
+rb_tree_swap_prune_and_rebalance(struct rb_tree *rbt, struct rb_node *self,
+       struct rb_node *standin)
+{
+       unsigned int standin_which = standin->rb_position;
+       unsigned int standin_other = standin_which ^ RB_NODE_OTHER;
+       struct rb_node *standin_child;
+       struct rb_node *standin_father;
+       bool rebalance = RB_BLACK_P(standin);
+
+       if (standin->rb_parent == self) {
+               /*
+                * As a child of self, any childen would be opposite of
+                * our parent (self).
+                */
+               KASSERT(RB_SENTINEL_P(standin->rb_nodes[standin_other]));
+               standin_child = standin->rb_nodes[standin_which];
+       } else {
+               /*
+                * Since we aren't a child of self, any childen would be
+                * on the same side as our parent (self).
+                */
+               KASSERT(RB_SENTINEL_P(standin->rb_nodes[standin_which]));
+               standin_child = standin->rb_nodes[standin_other];
+       }
+
+       /*
+        * the node we are removing must have two children.
+        */
+       KASSERT(RB_TWOCHILDREN_P(self));
+       /*
+        * If standin has a child, it must be red.
+        */
+       KASSERT(RB_SENTINEL_P(standin_child) || RB_RED_P(standin_child));
+
+       /*
+        * Verify things are sane.
+        */
+       KASSERT(rb_tree_check_node(rbt, self, NULL, false));
+       KASSERT(rb_tree_check_node(rbt, standin, NULL, false));
+
+       if (!RB_SENTINEL_P(standin_child)) {
+               /*
+                * We know we have a red child so if we swap them we can
+                * void flipping standin's child to black afterwards.
+                */
+               KASSERT(rb_tree_check_node(rbt, standin_child, NULL, true));
+               rb_tree_reparent_nodes(rbt, standin,
+                   standin_child->rb_position);
+               KASSERT(rb_tree_check_node(rbt, standin, NULL, true));
+               KASSERT(rb_tree_check_node(rbt, standin_child, NULL, true));
+               /*
+                * Since we are removing a red leaf, no need to rebalance.
+                */
+               rebalance = false;
+               /*
+                * We know that standin can not be a child of self, so
+                * update before of that.
+                */
+               KASSERT(standin->rb_parent != self);
+               standin_which = standin->rb_position;
+               standin_other = standin_which ^ RB_NODE_OTHER;
+       }
+       KASSERT(RB_CHILDLESS_P(standin));
+
+       /*
+        * If we are about to delete the standin's father, then when we call
+        * rebalance, we need to use ourselves as our father.  Otherwise
+        * remember our original father.  Also, if we are our standin's father
+        * we only need to reparent the standin's brother.
+        */
+       if (standin->rb_parent == self) {
+               /*
+                * |   R   -->   S   |
+                * | Q   S --> Q   * |
+                * |       -->       |
+                */
+               standin_father = standin;
+               KASSERT(RB_SENTINEL_P(standin->rb_nodes[standin_other]));
+               KASSERT(!RB_SENTINEL_P(self->rb_nodes[standin_other]));
+               KASSERT(self->rb_nodes[standin_which] == standin);
+               /*
+                * Make our brother our son.
+                */
+               standin->rb_nodes[standin_other] = self->rb_nodes[standin_other];
+               standin->rb_nodes[standin_other]->rb_parent = standin;
+               KASSERT(standin->rb_nodes[standin_other]->rb_position == standin_other);
+       } else {
+               /*
+                * |  P      -->  P    |
+                * |      S  -->    Q  |
+                * |    Q    -->       |
+                */
+               standin_father = standin->rb_parent;
+               standin_father->rb_nodes[standin_which] =
+                   standin->rb_nodes[standin_which];
+               standin->rb_left = self->rb_left;
+               standin->rb_right = self->rb_right;
+               standin->rb_left->rb_parent = standin;
+               standin->rb_right->rb_parent = standin;
+       }
+
+       /*
+        * Now copy the result of self to standin and then replace
+        * self with standin in the tree.
+        */
+       standin->rb_parent = self->rb_parent;
+       standin->rb_properties = self->rb_properties;
+       standin->rb_parent->rb_nodes[standin->rb_position] = standin;
+
+       /*
+        * Remove ourselves from the node list and decrement the count.
+        */
+       RB_TAILQ_REMOVE(&rbt->rbt_nodes, self, rb_link);
+       RBT_COUNT_DECR(rbt);
+
+       KASSERT(rb_tree_check_node(rbt, standin, NULL, false));
+       KASSERT(rb_tree_check_node(rbt, standin_father, NULL, false));
+
+       if (!rebalance)
+               return;
+
+       rb_tree_removal_rebalance(rbt, standin_father, standin_which);
+       KASSERT(rb_tree_check_node(rbt, standin, NULL, true));
+}
+
+/*
+ * We could do this by doing
+ *     rb_tree_node_swap(rbt, self, which);
+ *     rb_tree_prune_node(rbt, self, false);
+ *
+ * But it's more efficient to just evalate and recolor the child.
+ */
+/*ARGSUSED*/
+static void
+rb_tree_prune_blackred_branch(struct rb_tree *rbt _PROP_ARG_UNUSED,
+    struct rb_node *self, unsigned int which)
+{
+       struct rb_node *parent = self->rb_parent;
+       struct rb_node *child = self->rb_nodes[which];
+
+       KASSERT(which == RB_NODE_LEFT || which == RB_NODE_RIGHT);
+       KASSERT(RB_BLACK_P(self) && RB_RED_P(child));
+       KASSERT(!RB_TWOCHILDREN_P(child));
+       KASSERT(RB_CHILDLESS_P(child));
+       KASSERT(rb_tree_check_node(rbt, self, NULL, false));
+       KASSERT(rb_tree_check_node(rbt, child, NULL, false));
+
+       /*
+        * Remove ourselves from the tree and give our former child our
+        * properties (position, color, root).
+        */
+       parent->rb_nodes[self->rb_position] = child;
+       child->rb_parent = parent;
+       child->rb_properties = self->rb_properties;
+
+       /*
+        * Remove ourselves from the node list and decrement the count.
+        */
+       RB_TAILQ_REMOVE(&rbt->rbt_nodes, self, rb_link);
+       RBT_COUNT_DECR(rbt);
+
+       KASSERT(RB_ROOT_P(self) || rb_tree_check_node(rbt, parent, NULL, true));
+       KASSERT(rb_tree_check_node(rbt, child, NULL, true));
+}
+/*
+ *
+ */
+void
+_prop_rb_tree_remove_node(struct rb_tree *rbt, struct rb_node *self)
+{
+       struct rb_node *standin;
+       unsigned int which;
+       /*
+        * In the following diagrams, we (the node to be removed) are S.  Red
+        * nodes are lowercase.  T could be either red or black.
+        *
+        * Remember the major axiom of the red-black tree: the number of
+        * black nodes from the root to each leaf is constant across all
+        * leaves, only the number of red nodes varies.
+        *
+        * Thus removing a red leaf doesn't require any other changes to a
+        * red-black tree.  So if we must remove a node, attempt to rearrange
+        * the tree so we can remove a red node.
+        *
+        * The simpliest case is a childless red node or a childless root node:
+        *
+        * |    T  -->    T  |    or    |  R  -->  *  |
+        * |  s    -->  *    |
+        */
+       if (RB_CHILDLESS_P(self)) {
+               if (RB_RED_P(self) || RB_ROOT_P(self)) {
+                       rb_tree_prune_node(rbt, self, false);
+                       return;
+               }
+               rb_tree_prune_node(rbt, self, true);
+               return;
+       }
+       KASSERT(!RB_CHILDLESS_P(self));
+       if (!RB_TWOCHILDREN_P(self)) {
+               /*
+                * The next simpliest case is the node we are deleting is
+                * black and has one red child.
+                *
+                * |      T  -->      T  -->      T  |
+                * |    S    -->  R      -->  R      |
+                * |  r      -->    s    -->    *    |
+                */
+               which = RB_LEFT_SENTINEL_P(self) ? RB_NODE_RIGHT : RB_NODE_LEFT;
+               KASSERT(RB_BLACK_P(self));
+               KASSERT(RB_RED_P(self->rb_nodes[which]));
+               KASSERT(RB_CHILDLESS_P(self->rb_nodes[which]));
+               rb_tree_prune_blackred_branch(rbt, self, which);
+               return;
+       }
+       KASSERT(RB_TWOCHILDREN_P(self));
+
+       /*
+        * We invert these because we prefer to remove from the inside of
+        * the tree.
+        */
+       which = self->rb_position ^ RB_NODE_OTHER;
+
+       /*
+        * Let's find the node closes to us opposite of our parent
+        * Now swap it with ourself, "prune" it, and rebalance, if needed.
+        */
+       standin = _prop_rb_tree_iterate(rbt, self, which);
+       rb_tree_swap_prune_and_rebalance(rbt, self, standin);
+}
+
+static void
+rb_tree_removal_rebalance(struct rb_tree *rbt, struct rb_node *parent,
+       unsigned int which)
+{
+       KASSERT(!RB_SENTINEL_P(parent));
+       KASSERT(RB_SENTINEL_P(parent->rb_nodes[which]));
+       KASSERT(which == RB_NODE_LEFT || which == RB_NODE_RIGHT);
+
+       while (RB_BLACK_P(parent->rb_nodes[which])) {
+               unsigned int other = which ^ RB_NODE_OTHER;
+               struct rb_node *brother = parent->rb_nodes[other];
+
+               KASSERT(!RB_SENTINEL_P(brother));
+               /*
+                * For cases 1, 2a, and 2b, our brother's children must
+                * be black and our father must be black
+                */
+               if (RB_BLACK_P(parent)
+                   && RB_BLACK_P(brother->rb_left)
+                   && RB_BLACK_P(brother->rb_right)) {
+                       /*
+                        * Case 1: Our brother is red, swap its position
+                        * (and colors) with our parent.  This is now case 2b.
+                        *
+                        *    B         ->        D
+                        *  x     d     ->    b     E
+                        *      C   E   ->  x   C
+                        */
+                       if (RB_RED_P(brother)) {
+                               KASSERT(RB_BLACK_P(parent));
+                               rb_tree_reparent_nodes(rbt, parent, other);
+                               brother = parent->rb_nodes[other];
+                               KASSERT(!RB_SENTINEL_P(brother));
+                               KASSERT(RB_BLACK_P(brother));
+                               KASSERT(RB_RED_P(parent));
+                               KASSERT(rb_tree_check_node(rbt, brother, NULL, false));
+                               KASSERT(rb_tree_check_node(rbt, parent, NULL, false));
+                       } else {
+                               /*
+                                * Both our parent and brother are black.
+                                * Change our brother to red, advance up rank
+                                * and go through the loop again.
+                                *
+                                *    B         ->    B
+                                *  A     D     ->  A     d
+                                *      C   E   ->      C   E
+                                */
+                               RB_MARK_RED(brother);
+                               KASSERT(RB_BLACK_P(brother->rb_left));
+                               KASSERT(RB_BLACK_P(brother->rb_right));
+                               if (RB_ROOT_P(parent))
+                                       return;
+                               KASSERT(rb_tree_check_node(rbt, brother, NULL, false));
+                               KASSERT(rb_tree_check_node(rbt, parent, NULL, false));
+                               which = parent->rb_position;
+                               parent = parent->rb_parent;
+                       }
+               } else if (RB_RED_P(parent)
+                   && RB_BLACK_P(brother)
+                   && RB_BLACK_P(brother->rb_left)
+                   && RB_BLACK_P(brother->rb_right)) {
+                       KASSERT(RB_BLACK_P(brother));
+                       KASSERT(RB_BLACK_P(brother->rb_left));
+                       KASSERT(RB_BLACK_P(brother->rb_right));
+                       RB_MARK_BLACK(parent);
+                       RB_MARK_RED(brother);
+                       KASSERT(rb_tree_check_node(rbt, brother, NULL, true));
+                       break;          /* We're done! */
+               } else {
+                       KASSERT(RB_BLACK_P(brother));
+                       KASSERT(!RB_CHILDLESS_P(brother));
+                       /*
+                        * Case 3: our brother is black, our left nephew is
+                        * red, and our right nephew is black.  Swap our
+                        * brother with our left nephew.   This result in a
+                        * tree that matches case 4.
+                        *
+                        *     B         ->       D
+                        * A       D     ->   B     E
+                        *       c   e   -> A   C
+                        */
+                       if (RB_BLACK_P(brother->rb_nodes[other])) {
+                               KASSERT(RB_RED_P(brother->rb_nodes[which]));
+                               rb_tree_reparent_nodes(rbt, brother, which);
+                               KASSERT(brother->rb_parent == parent->rb_nodes[other]);
+                               brother = parent->rb_nodes[other];
+                               KASSERT(RB_RED_P(brother->rb_nodes[other]));
+                       }
+                       /*
+                        * Case 4: our brother is black and our right nephew
+                        * is red.  Swap our parent and brother locations and
+                        * change our right nephew to black.  (these can be
+                        * done in either order so we change the color first).
+                        * The result is a valid red-black tree and is a
+                        * terminal case.
+                        *
+                        *     B         ->       D
+                        * A       D     ->   B     E
+                        *       c   e   -> A   C
+                        */
+                       RB_MARK_BLACK(brother->rb_nodes[other]);
+                       rb_tree_reparent_nodes(rbt, parent, other);
+                       break;          /* We're done! */
+               }
+       }
+       KASSERT(rb_tree_check_node(rbt, parent, NULL, true));
+}
+
+struct rb_node *
+_prop_rb_tree_iterate(struct rb_tree *rbt, struct rb_node *self,
+       unsigned int direction)
+{
+       const unsigned int other = direction ^ RB_NODE_OTHER;
+       KASSERT(direction == RB_NODE_LEFT || direction == RB_NODE_RIGHT);
+
+       if (self == NULL) {
+               self = rbt->rbt_root;
+               if (RB_SENTINEL_P(self))
+                       return NULL;
+               while (!RB_SENTINEL_P(self->rb_nodes[other]))
+                       self = self->rb_nodes[other];
+               return self;
+       }
+       KASSERT(!RB_SENTINEL_P(self));
+       /*
+        * We can't go any further in this direction.  We proceed up in the
+        * opposite direction until our parent is in direction we want to go.
+        */
+       if (RB_SENTINEL_P(self->rb_nodes[direction])) {
+               while (!RB_ROOT_P(self)) {
+                       if (other == self->rb_position)
+                               return self->rb_parent;
+                       self = self->rb_parent;
+               }
+               return NULL;
+       }
+
+       /*
+        * Advance down one in current direction and go down as far as possible
+        * in the opposite direction.
+        */
+       self = self->rb_nodes[direction];
+       KASSERT(!RB_SENTINEL_P(self));
+       while (!RB_SENTINEL_P(self->rb_nodes[other]))
+               self = self->rb_nodes[other];
+       return self;
+}
+
+#ifdef RBDEBUG
+static const struct rb_node *
+rb_tree_iterate_const(const struct rb_tree *rbt, const struct rb_node *self,
+       unsigned int direction)
+{
+       const unsigned int other = direction ^ RB_NODE_OTHER;
+       KASSERT(direction == RB_NODE_LEFT || direction == RB_NODE_RIGHT);
+
+       if (self == NULL) {
+               self = rbt->rbt_root;
+               if (RB_SENTINEL_P(self))
+                       return NULL;
+               while (!RB_SENTINEL_P(self->rb_nodes[other]))
+                       self = self->rb_nodes[other];
+               return self;
+       }
+       KASSERT(!RB_SENTINEL_P(self));
+       /*
+        * We can't go any further in this direction.  We proceed up in the
+        * opposite direction until our parent is in direction we want to go.
+        */
+       if (RB_SENTINEL_P(self->rb_nodes[direction])) {
+               while (!RB_ROOT_P(self)) {
+                       if (other == self->rb_position)
+                               return self->rb_parent;
+                       self = self->rb_parent;
+               }
+               return NULL;
+       }
+
+       /*
+        * Advance down one in current direction and go down as far as possible
+        * in the opposite direction.
+        */
+       self = self->rb_nodes[direction];
+       KASSERT(!RB_SENTINEL_P(self));
+       while (!RB_SENTINEL_P(self->rb_nodes[other]))
+               self = self->rb_nodes[other];
+       return self;
+}
+
+static bool
+rb_tree_check_node(const struct rb_tree *rbt, const struct rb_node *self,
+       const struct rb_node *prev, bool red_check)
+{
+       KASSERT(!self->rb_sentinel);
+       KASSERT(self->rb_left);
+       KASSERT(self->rb_right);
+       KASSERT(prev == NULL ||
+               (*rbt->rbt_ops->rbto_compare_nodes)(prev, self) > 0);
+
+       /*
+        * Verify our relationship to our parent.
+        */
+       if (RB_ROOT_P(self)) {
+               KASSERT(self == rbt->rbt_root);
+               KASSERT(self->rb_position == RB_NODE_LEFT);
+               KASSERT(self->rb_parent->rb_nodes[RB_NODE_LEFT] == self);
+               KASSERT(self->rb_parent == (const struct rb_node *) &rbt->rbt_root);
+       } else {
+               KASSERT(self != rbt->rbt_root);
+               KASSERT(!RB_PARENT_SENTINEL_P(self));
+               if (self->rb_position == RB_NODE_LEFT) {
+                       KASSERT((*rbt->rbt_ops->rbto_compare_nodes)(self, self->rb_parent) > 0);
+                       KASSERT(self->rb_parent->rb_nodes[RB_NODE_LEFT] == self);
+               } else {
+                       KASSERT((*rbt->rbt_ops->rbto_compare_nodes)(self, self->rb_parent) < 0);
+                       KASSERT(self->rb_parent->rb_nodes[RB_NODE_RIGHT] == self);
+               }
+       }
+
+       /*
+        * Verify our position in the linked list against the tree itself.
+        */
+       {
+               const struct rb_node *prev0 = rb_tree_iterate_const(rbt, self, RB_NODE_LEFT);
+               const struct rb_node *next0 = rb_tree_iterate_const(rbt, self, RB_NODE_RIGHT);
+               KASSERT(prev0 == TAILQ_PREV(self, rb_node_qh, rb_link));
+               if (next0 != TAILQ_NEXT(self, rb_link))
+                       next0 = rb_tree_iterate_const(rbt, self, RB_NODE_RIGHT);
+               KASSERT(next0 == TAILQ_NEXT(self, rb_link));
+       }
+
+       /*
+        * The root must be black.
+        * There can never be two adjacent red nodes. 
+        */
+       if (red_check) {
+               KASSERT(!RB_ROOT_P(self) || RB_BLACK_P(self));
+               if (RB_RED_P(self)) {
+                       const struct rb_node *brother;
+                       KASSERT(!RB_ROOT_P(self));
+                       brother = self->rb_parent->rb_nodes[self->rb_position ^ RB_NODE_OTHER];
+                       KASSERT(RB_BLACK_P(self->rb_parent));
+                       /* 
+                        * I'm red and have no children, then I must either
+                        * have no brother or my brother also be red and
+                        * also have no children.  (black count == 0)
+                        */
+                       KASSERT(!RB_CHILDLESS_P(self)
+                               || RB_SENTINEL_P(brother)
+                               || RB_RED_P(brother)
+                               || RB_CHILDLESS_P(brother));
+                       /*
+                        * If I'm not childless, I must have two children
+                        * and they must be both be black.
+                        */
+                       KASSERT(RB_CHILDLESS_P(self)
+                               || (RB_TWOCHILDREN_P(self)
+                                   && RB_BLACK_P(self->rb_left)
+                                   && RB_BLACK_P(self->rb_right)));
+                       /*
+                        * If I'm not childless, thus I have black children,
+                        * then my brother must either be black or have two
+                        * black children.
+                        */
+                       KASSERT(RB_CHILDLESS_P(self)
+                               || RB_BLACK_P(brother)
+                               || (RB_TWOCHILDREN_P(brother)
+                                   && RB_BLACK_P(brother->rb_left)
+                                   && RB_BLACK_P(brother->rb_right)));
+               } else {
+                       /*
+                        * If I'm black and have one child, that child must
+                        * be red and childless.
+                        */
+                       KASSERT(RB_CHILDLESS_P(self)
+                               || RB_TWOCHILDREN_P(self)
+                               || (!RB_LEFT_SENTINEL_P(self)
+                                   && RB_RIGHT_SENTINEL_P(self)
+                                   && RB_RED_P(self->rb_left)
+                                   && RB_CHILDLESS_P(self->rb_left))
+                               || (!RB_RIGHT_SENTINEL_P(self)
+                                   && RB_LEFT_SENTINEL_P(self)
+                                   && RB_RED_P(self->rb_right)
+                                   && RB_CHILDLESS_P(self->rb_right)));
+
+                       /*
+                        * If I'm a childless black node and my parent is
+                        * black, my 2nd closet relative away from my parent
+                        * is either red or has a red parent or red children.
+                        */
+                       if (!RB_ROOT_P(self)
+                           && RB_CHILDLESS_P(self)
+                           && RB_BLACK_P(self->rb_parent)) {
+                               const unsigned int which = self->rb_position;
+                               const unsigned int other = which ^ RB_NODE_OTHER;
+                               const struct rb_node *relative0, *relative;
+
+                               relative0 = rb_tree_iterate_const(rbt,
+                                   self, other);
+                               KASSERT(relative0 != NULL);
+                               relative = rb_tree_iterate_const(rbt,
+                                   relative0, other);
+                               KASSERT(relative != NULL);
+                               KASSERT(RB_SENTINEL_P(relative->rb_nodes[which]));
+#if 0
+                               KASSERT(RB_RED_P(relative)
+                                       || RB_RED_P(relative->rb_left)
+                                       || RB_RED_P(relative->rb_right)
+                                       || RB_RED_P(relative->rb_parent));
+#endif
+                       }
+               }
+               /*
+                * A grandparent's children must be real nodes and not
+                * sentinels.  First check out grandparent.
+                */
+               KASSERT(RB_ROOT_P(self)
+                       || RB_ROOT_P(self->rb_parent)
+                       || RB_TWOCHILDREN_P(self->rb_parent->rb_parent));
+               /*
+                * If we are have grandchildren on our left, then
+                * we must have a child on our right.
+                */
+               KASSERT(RB_LEFT_SENTINEL_P(self)
+                       || RB_CHILDLESS_P(self->rb_left)
+                       || !RB_RIGHT_SENTINEL_P(self));
+               /*
+                * If we are have grandchildren on our right, then
+                * we must have a child on our left.
+                */
+               KASSERT(RB_RIGHT_SENTINEL_P(self)
+                       || RB_CHILDLESS_P(self->rb_right)
+                       || !RB_LEFT_SENTINEL_P(self));
+
+               /*
+                * If we have a child on the left and it doesn't have two
+                * children make sure we don't have great-great-grandchildren on
+                * the right.
+                */
+               KASSERT(RB_TWOCHILDREN_P(self->rb_left)
+                       || RB_CHILDLESS_P(self->rb_right)
+                       || RB_CHILDLESS_P(self->rb_right->rb_left)
+                       || RB_CHILDLESS_P(self->rb_right->rb_left->rb_left)
+                       || RB_CHILDLESS_P(self->rb_right->rb_left->rb_right)
+                       || RB_CHILDLESS_P(self->rb_right->rb_right)
+                       || RB_CHILDLESS_P(self->rb_right->rb_right->rb_left)
+                       || RB_CHILDLESS_P(self->rb_right->rb_right->rb_right));
+
+               /*
+                * If we have a child on the right and it doesn't have two
+                * children make sure we don't have great-great-grandchildren on
+                * the left.
+                */
+               KASSERT(RB_TWOCHILDREN_P(self->rb_right)
+                       || RB_CHILDLESS_P(self->rb_left)
+                       || RB_CHILDLESS_P(self->rb_left->rb_left)
+                       || RB_CHILDLESS_P(self->rb_left->rb_left->rb_left)
+                       || RB_CHILDLESS_P(self->rb_left->rb_left->rb_right)
+                       || RB_CHILDLESS_P(self->rb_left->rb_right)
+                       || RB_CHILDLESS_P(self->rb_left->rb_right->rb_left)
+                       || RB_CHILDLESS_P(self->rb_left->rb_right->rb_right));
+
+               /*
+                * If we are fully interior node, then our predecessors and
+                * successors must have no children in our direction.
+                */
+               if (RB_TWOCHILDREN_P(self)) {
+                       const struct rb_node *prev0;
+                       const struct rb_node *next0;
+
+                       prev0 = rb_tree_iterate_const(rbt, self, RB_NODE_LEFT);
+                       KASSERT(prev0 != NULL);
+                       KASSERT(RB_RIGHT_SENTINEL_P(prev0));
+
+                       next0 = rb_tree_iterate_const(rbt, self, RB_NODE_RIGHT);
+                       KASSERT(next0 != NULL);
+                       KASSERT(RB_LEFT_SENTINEL_P(next0));
+               }
+       }
+
+       return true;
+}
+
+static unsigned int
+rb_tree_count_black(const struct rb_node *self)
+{
+       unsigned int left, right;
+
+       if (RB_SENTINEL_P(self))
+               return 0;
+
+       left = rb_tree_count_black(self->rb_left);
+       right = rb_tree_count_black(self->rb_right);
+
+       KASSERT(left == right);
+
+       return left + RB_BLACK_P(self);
+}
+
+void
+_prop_rb_tree_check(const struct rb_tree *rbt, bool red_check)
+{
+       const struct rb_node *self;
+       const struct rb_node *prev;
+       unsigned int count;
+
+       KASSERT(rbt->rbt_root == NULL || rbt->rbt_root->rb_position == RB_NODE_LEFT);
+
+       prev = NULL;
+       count = 0;
+       TAILQ_FOREACH(self, &rbt->rbt_nodes, rb_link) {
+               rb_tree_check_node(rbt, self, prev, false);
+               count++;
+       }
+       KASSERT(rbt->rbt_count == count);
+       KASSERT(RB_SENTINEL_P(rbt->rbt_root)
+               || rb_tree_count_black(rbt->rbt_root));
+
+       /*
+        * The root must be black.
+        * There can never be two adjacent red nodes. 
+        */
+       if (red_check) {
+               KASSERT(rbt->rbt_root == NULL || RB_BLACK_P(rbt->rbt_root));
+               TAILQ_FOREACH(self, &rbt->rbt_nodes, rb_link) {
+                       rb_tree_check_node(rbt, self, NULL, true);
+               }
+       }
+}
+#endif /* RBDEBUG */
diff --git a/common/lib/libprop/prop_rb_impl.h b/common/lib/libprop/prop_rb_impl.h
new file mode 100644 (file)
index 0000000..682731f
--- /dev/null
@@ -0,0 +1,154 @@
+/*     $NetBSD: prop_rb_impl.h,v 1.8 2010/09/25 01:42:38 matt Exp $    */
+
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Matt Thomas <matt@3am-software.com>.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _PROP_RB_IMPL_H_
+#define        _PROP_RB_IMPL_H_
+
+#if defined(__NetBSD__) || defined(__minix)
+#include <sys/rbtree.h>
+
+/*
+ * Define local names for common rb_tree functions.
+ */
+#define        _prop_rb_tree_init              rb_tree_init
+#define        _prop_rb_tree_insert_node       rb_tree_insert_node
+#define        _prop_rb_tree_find              rb_tree_find_node
+#define        _prop_rb_tree_remove_node       rb_tree_remove_node
+#define        _prop_rb_tree_iterate           rb_tree_iterate
+
+#else  /* __NetBSD__ */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <machine/endian.h>
+
+struct rb_node {
+       struct rb_node *rb_nodes[3];
+#define        RB_NODE_LEFT            0
+#define        RB_NODE_RIGHT           1
+#define        RB_NODE_OTHER           1
+#define        RB_NODE_PARENT          2
+#define        rb_left         rb_nodes[RB_NODE_LEFT]
+#define        rb_right        rb_nodes[RB_NODE_RIGHT]
+#define        rb_parent       rb_nodes[RB_NODE_PARENT]
+       union {
+               struct {
+#if BYTE_ORDER == LITTLE_ENDIAN
+                       unsigned int : 28;
+                       unsigned int s_root : 1;
+                       unsigned int s_position : 1;
+                       unsigned int s_color : 1;
+                       unsigned int s_sentinel : 1;
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
+                       unsigned int s_sentinel : 1;
+                       unsigned int s_color : 1;
+                       unsigned int s_position : 1;
+                       unsigned int s_root : 1;
+                       unsigned int : 28;
+#endif
+               } u_s;
+               unsigned int u_i;
+       } rb_u;
+#define        rb_root                         rb_u.u_s.s_root
+#define        rb_position                     rb_u.u_s.s_position
+#define        rb_color                        rb_u.u_s.s_color
+#define        rb_sentinel                     rb_u.u_s.s_sentinel
+#define        rb_properties                   rb_u.u_i
+#define        RB_SENTINEL_P(rb)               ((rb)->rb_sentinel + 0)
+#define        RB_LEFT_SENTINEL_P(rb)          ((rb)->rb_left->rb_sentinel + 0)
+#define        RB_RIGHT_SENTINEL_P(rb)         ((rb)->rb_right->rb_sentinel + 0)
+#define        RB_PARENT_SENTINEL_P(rb)        ((rb)->rb_parent->rb_sentinel + 0)
+#define        RB_CHILDLESS_P(rb)              (RB_LEFT_SENTINEL_P(rb) \
+                                        && RB_RIGHT_SENTINEL_P(rb))
+#define        RB_TWOCHILDREN_P(rb)            (!RB_LEFT_SENTINEL_P(rb) \
+                                        && !RB_RIGHT_SENTINEL_P(rb))
+#define        RB_ROOT_P(rb)                   ((rb)->rb_root != false)
+#define        RB_RED_P(rb)                    ((rb)->rb_color + 0)
+#define        RB_BLACK_P(rb)                  (!(rb)->rb_color)
+#define        RB_MARK_RED(rb)                 ((void)((rb)->rb_color = 1))
+#define        RB_MARK_BLACK(rb)               ((void)((rb)->rb_color = 0))
+#define        RB_MARK_ROOT(rb)                ((void)((rb)->rb_root = 1))
+#ifdef RBDEBUG
+       TAILQ_ENTRY(rb_node) rb_link;
+#endif
+};
+
+#ifdef RBDEBUG
+TAILQ_HEAD(rb_node_qh, rb_node);
+
+#define        RB_TAILQ_REMOVE                         TAILQ_REMOVE
+#define        RB_TAILQ_INIT                           TAILQ_INIT
+#define        RB_TAILQ_INSERT_HEAD(a, b, c)           TAILQ_INSERT_HEAD
+#define        RB_TAILQ_INSERT_BEFORE(a, b, c)         TAILQ_INSERT_BEFORE
+#define        RB_TAILQ_INSERT_AFTER(a, b, c, d)       TAILQ_INSERT_AFTER
+#else
+#define        RB_TAILQ_REMOVE(a, b, c)                do { } while (/*CONSTCOND*/0)
+#define        RB_TAILQ_INIT(a)                        do { } while (/*CONSTCOND*/0)
+#define        RB_TAILQ_INSERT_HEAD(a, b, c)           do { } while (/*CONSTCOND*/0)
+#define        RB_TAILQ_INSERT_BEFORE(a, b, c)         do { } while (/*CONSTCOND*/0)
+#define        RB_TAILQ_INSERT_AFTER(a, b, c, d)       do { } while (/*CONSTCOND*/0)
+#endif
+
+typedef int (*rb_compare_nodes_fn)(const struct rb_node *,
+    const struct rb_node *);
+typedef int (*rb_compare_key_fn)(const struct rb_node *, const void *);
+
+struct rb_tree_ops {
+       rb_compare_nodes_fn     rbto_compare_nodes;
+       rb_compare_key_fn       rbto_compare_key;
+};
+
+struct rb_tree {
+       struct rb_node *rbt_root;
+#ifdef RBDEBUG
+       struct rb_node_qh rbt_nodes;
+#endif
+       const struct rb_tree_ops *rbt_ops;
+#ifdef RBDEBUG
+       unsigned int rbt_count;
+#endif
+};
+
+void   _prop_rb_tree_init(struct rb_tree *, const struct rb_tree_ops *);
+bool   _prop_rb_tree_insert_node(struct rb_tree *, struct rb_node *);
+struct rb_node *
+       _prop_rb_tree_find(struct rb_tree *, const void *);
+void   _prop_rb_tree_remove_node(struct rb_tree *, struct rb_node *);
+#ifdef RBDEBUG
+void   _prop_rb_tree_check(const struct rb_tree *, bool);
+#endif
+struct rb_node *
+       _prop_rb_tree_iterate(struct rb_tree *, struct rb_node *, unsigned int);
+
+#endif /* __NetBSD__ */
+
+#endif /* _PROP_RB_IMPL_H_*/
diff --git a/common/lib/libprop/prop_send_ioctl.3 b/common/lib/libprop/prop_send_ioctl.3
new file mode 100644 (file)
index 0000000..22d34d5
--- /dev/null
@@ -0,0 +1,151 @@
+.\"    $NetBSD: prop_send_ioctl.3,v 1.8 2011/09/27 11:12:49 jym Exp $
+.\"
+.\" Copyright (c) 2006 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by Jason R. Thorpe.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd January 21, 2008
+.Dt PROP_SEND_IOCTL 3
+.Os
+.Sh NAME
+.Nm prop_array_send_ioctl ,
+.Nm prop_array_recv_ioctl ,
+.Nm prop_dictionary_send_ioctl ,
+.Nm prop_dictionary_recv_ioctl ,
+.Nm prop_dictionary_sendrecv_ioctl
+.Nd Send and receive propertly lists to and from the kernel using ioctl
+.Sh SYNOPSIS
+.In prop/proplib.h
+.Ft int
+.Fn prop_array_send_ioctl "prop_array_t array" "int fd" "unsigned long cmd"
+.Ft int
+.Fn prop_array_recv_ioctl "int fd" "unsigned long cmd" "prop_array_t *arrayp"
+.Ft int
+.Fn prop_dictionary_send_ioctl "prop_dictionary_t dict" "int fd" \
+    "unsigned long cmd"
+.Ft int
+.Fn prop_dictionary_recv_ioctl "int fd" "unsigned long cmd" \
+    "prop_dictionary_t *dictp"
+.Ft int
+.Fn prop_dictionary_sendrecv_ioctl "prop_dictionary_t dict" "int fd" \
+    "unsigned long cmd" "prop_dictionary_t *dictp"
+.Sh DESCRIPTION
+The
+.Nm prop_array_send_ioctl ,
+.Nm prop_array_recv_ioctl ,
+.Nm prop_dictionary_send_ioctl ,
+.Nm prop_dictionary_recv_ioctl ,
+and
+.Nm prop_dictionary_sendrecv_ioctl
+functions implement the user space side of a protocol for sending property
+lists to and from the kernel using
+.Xr ioctl 2 .
+.Sh RETURN VALUES
+If successful, functions return zero.
+Otherwise, an error number is returned to indicate the error.
+.Sh EXAMPLES
+The following
+.Pq simplified
+example demonstrates using
+.Fn prop_dictionary_send_ioctl
+and
+.Fn prop_dictionary_recv_ioctl
+in an application:
+.Bd -literal
+void
+foo_setprops(prop_dictionary_t dict)
+{
+    int fd;
+
+    fd = open("/dev/foo", O_RDWR, 0640);
+    if (fd == -1)
+        return;
+
+    (void) prop_dictionary_send_ioctl(dict, fd, FOOSETPROPS);
+
+    (void) close(fd);
+}
+
+prop_dictionary_t
+foo_getprops(void)
+{
+    prop_dictionary_t dict;
+    int fd;
+
+    fd = open("/dev/foo", O_RDONLY, 0640);
+    if (fd == -1)
+       return (NULL);
+
+    if (prop_dictionary_recv_ioctl(fd, FOOGETPROPS, \*[Am]dict) != 0)
+        return (NULL);
+
+    (void) close(fd);
+
+    return (dict);
+}
+.Ed
+.Pp
+The
+.Nm prop_dictionary_sendrecv_ioctl
+function combines the send and receive functionality, allowing for
+ioctls that require two-way communication
+.Pq for example to specify arguments for the ioctl operation .
+.Sh ERRORS
+.Fn prop_array_send_ioctl
+and
+.Fn prop_dictionary_send_ioctl
+will fail if:
+.Bl -tag -width Er
+.It Bq Er ENOMEM
+Cannot allocate memory
+.It Bq Er ENOTSUP
+Not supported
+.El
+.Pp
+.Fn prop_array_recv_ioctl
+and
+.Fn prop_dictionary_recv_ioctl
+will fail if:
+.Bl -tag -width Er
+.It Bq Er EIO
+Input/output error
+.It Bq Er ENOTSUP
+Not supported
+.El
+.Pp
+In addition to these,
+.Xr ioctl 2
+errors may be returned.
+.Sh SEE ALSO
+.Xr prop_array 3 ,
+.Xr prop_dictionary 3 ,
+.Xr proplib 3 ,
+.Xr prop_copyin_ioctl 9
+.Sh HISTORY
+The
+.Nm proplib
+property container object library first appeared in
+.Nx 4.0 .
diff --git a/common/lib/libprop/prop_send_syscall.3 b/common/lib/libprop/prop_send_syscall.3
new file mode 100644 (file)
index 0000000..4150fd2
--- /dev/null
@@ -0,0 +1,128 @@
+.\"    $NetBSD: prop_send_syscall.3,v 1.5 2011/09/30 22:08:18 jym Exp $
+.\"
+.\" Copyright (c) 2006 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by Jason R. Thorpe.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd January 17, 2011
+.Dt PROP_SEND_SYCALL 3
+.Os
+.Sh NAME
+.Nm prop_array_send_syscall ,
+.Nm prop_array_recv_syscall ,
+.Nm prop_dictionary_send_syscall ,
+.Nm prop_dictionary_recv_syscall
+.Nd send and receive property lists to and from the kernel using syscalls
+.Sh SYNOPSIS
+.In prop/proplib.h
+.Ft int
+.Fn prop_array_send_syscall "prop_array_t array" "struct plistref *prefp"
+.Ft int
+.Fn prop_array_recv_syscall "const struct plistref *prefp" \
+    "prop_array_t *arrayp"
+.Ft int
+.Fn prop_dictionary_send_syscall "prop_dictionary_t dict" \
+    "struct plistref *prefp"
+.Ft int
+.Fn prop_dictionary_recv_syscall "const struct plistref *prefp" \
+    "prop_dictionary_t *dictp"
+.Sh DESCRIPTION
+The
+.Nm prop_array_send_syscall ,
+.Nm prop_array_recv_syscall ,
+.Nm prop_dictionary_send_syscall ,
+and
+.Nm prop_dictionary_recv_syscall
+functions implement the user space side of a protocol for sending property
+lists to and from the kernel using
+.Xr syscall 2 .
+.Sh RETURN VALUES
+If successful, functions return zero.
+Otherwise, an error number is returned to indicate the error.
+.Sh EXAMPLES
+The following
+.Pq simplified
+example demonstrates using
+.Fn prop_dictionary_send_syscall
+and
+.Fn prop_dictionary_recv_syscall
+in an application:
+.Bd -literal
+void
+foo_setprops(prop_dictionary_t dict)
+{
+    struct pref pref;
+
+    (void) prop_dictionary_send_syscall(dict, \*[Am]pref);
+    (void) my_syscall_set(\*[Am]pref);
+
+}
+
+prop_dictionary_t
+foo_getprops(void)
+{
+    prop_dictionary_t dict;
+    struct pref pref;
+
+    (void) my_syscall_get(\*[Am]pref);
+    if (prop_dictionary_recv_syscall(\*[Am]pref, \*[Am]dict) != 0)
+        return (NULL);
+
+    return (dict);
+}
+.Ed
+.Sh ERRORS
+.Fn prop_array_send_syscall
+and
+.Fn prop_dictionary_send_syscall
+will fail if:
+.Bl -tag -width Er
+.It Bq Er ENOMEM
+Cannot allocate memory
+.It Bq Er ENOTSUP
+Not supported
+.El
+.Pp
+.Fn prop_array_recv_syscall
+and
+.Fn prop_dictionary_recv_syscall
+will fail if:
+.Bl -tag -width Er
+.It Bq Er EIO
+Input/output error
+.It Bq Er ENOTSUP
+Not supported
+.El
+.Sh SEE ALSO
+.Xr prop_array 3 ,
+.Xr prop_dictionary 3 ,
+.Xr proplib 3 ,
+.Xr prop_copyin_ioctl 9
+.Sh HISTORY
+The
+.Nm proplib
+property container object library first appeared in
+.Nx 4.0 .
diff --git a/common/lib/libprop/prop_stack.c b/common/lib/libprop/prop_stack.c
new file mode 100644 (file)
index 0000000..a08118c
--- /dev/null
@@ -0,0 +1,118 @@
+/* $NetBSD: prop_stack.c,v 1.2 2007/08/30 12:23:54 joerg Exp $ */
+
+/*-
+ * Copyright (c) 2007 Joerg Sonnenberger <joerg@NetBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "prop_stack.h"
+#include "prop_object_impl.h"
+
+void
+_prop_stack_init(prop_stack_t stack)
+{
+       stack->used_intern_elems = 0;
+       SLIST_INIT(&stack->extern_elems);
+}
+
+bool
+_prop_stack_push(prop_stack_t stack, prop_object_t obj, void *data1,
+    void *data2, void *data3)
+{
+       struct _prop_stack_extern_elem *eelem;
+       struct _prop_stack_intern_elem *ielem;
+
+       if (stack->used_intern_elems == PROP_STACK_INTERN_ELEMS) {
+               eelem = _PROP_MALLOC(sizeof(*eelem), M_TEMP);
+
+               if (eelem == NULL)
+                       return false;
+
+               eelem->object = obj;
+               eelem->object_data[0] = data1;
+               eelem->object_data[1] = data2;
+               eelem->object_data[2] = data3;
+
+               SLIST_INSERT_HEAD(&stack->extern_elems, eelem, stack_link);
+
+               return true;
+       }
+
+       _PROP_ASSERT(stack->used_intern_elems < PROP_STACK_INTERN_ELEMS);
+       _PROP_ASSERT(SLIST_EMPTY(&stack->extern_elems));
+
+       ielem = &stack->intern_elems[stack->used_intern_elems];
+       ielem->object = obj;
+       ielem->object_data[0] = data1;
+       ielem->object_data[1] = data2;
+       ielem->object_data[2] = data3;
+
+       ++stack->used_intern_elems;
+
+       return true;
+}
+
+bool
+_prop_stack_pop(prop_stack_t stack, prop_object_t *obj, void **data1,
+    void **data2, void **data3)
+{
+       struct _prop_stack_extern_elem *eelem;
+       struct _prop_stack_intern_elem *ielem;
+
+       if (stack->used_intern_elems == 0)
+               return false;
+
+       if ((eelem = SLIST_FIRST(&stack->extern_elems)) != NULL) {
+               _PROP_ASSERT(stack->used_intern_elems == PROP_STACK_INTERN_ELEMS);
+
+               SLIST_REMOVE_HEAD(&stack->extern_elems, stack_link);
+               if (obj)
+                       *obj = eelem->object;
+               if (data1)
+                       *data1 = eelem->object_data[0];
+               if (data2)
+                       *data2 = eelem->object_data[1];
+               if (data3)
+                       *data3 = eelem->object_data[2];
+               _PROP_FREE(eelem, M_TEMP);
+               return true;
+       }
+
+       --stack->used_intern_elems;
+       ielem = &stack->intern_elems[stack->used_intern_elems];
+
+       if (obj)
+               *obj = ielem->object;
+       if (data1)
+               *data1 = ielem->object_data[0];
+       if (data2)
+               *data2 = ielem->object_data[1];
+       if (data3)
+               *data3 = ielem->object_data[2];
+
+       return true;
+}
diff --git a/common/lib/libprop/prop_stack.h b/common/lib/libprop/prop_stack.h
new file mode 100644 (file)
index 0000000..dca99c1
--- /dev/null
@@ -0,0 +1,64 @@
+/* $NetBSD: prop_stack.h,v 1.2 2007/08/30 12:23:54 joerg Exp $ */
+
+/*-
+ * Copyright (c) 2007 Joerg Sonnenberger <joerg@NetBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _PROP_STACK_H
+#define _PROP_STACK_H
+
+#include <sys/queue.h>
+
+#include <prop/prop_object.h>
+
+struct _prop_stack_intern_elem {
+       prop_object_t object;
+       void *object_data[3];
+};
+
+struct _prop_stack_extern_elem {
+       SLIST_ENTRY(_prop_stack_extern_elem) stack_link;
+       prop_object_t object;
+       void *object_data[3];
+};
+
+#define        PROP_STACK_INTERN_ELEMS 16
+
+struct _prop_stack {
+       struct _prop_stack_intern_elem intern_elems[PROP_STACK_INTERN_ELEMS];
+       size_t used_intern_elems;
+       SLIST_HEAD(, _prop_stack_extern_elem) extern_elems;
+};
+
+typedef struct _prop_stack *prop_stack_t;
+
+void   _prop_stack_init(prop_stack_t);
+bool   _prop_stack_push(prop_stack_t, prop_object_t, void *, void *, void *);
+bool   _prop_stack_pop(prop_stack_t, prop_object_t *, void **, void **, void **);
+
+#endif
diff --git a/common/lib/libprop/prop_string.3 b/common/lib/libprop/prop_string.3
new file mode 100644 (file)
index 0000000..4b96071
--- /dev/null
@@ -0,0 +1,193 @@
+.\"    $NetBSD: prop_string.3,v 1.8 2011/02/26 12:56:36 wiz Exp $
+.\"
+.\" Copyright (c) 2006 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by Jason R. Thorpe.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd January 21, 2008
+.Dt PROP_STRING 3
+.Os
+.Sh NAME
+.Nm prop_string ,
+.Nm prop_string_create ,
+.Nm prop_string_create_cstring ,
+.Nm prop_string_create_cstring_nocopy ,
+.Nm prop_string_copy ,
+.Nm prop_string_copy_mutable ,
+.Nm prop_string_size ,
+.Nm prop_string_mutable ,
+.Nm prop_string_cstring ,
+.Nm prop_string_cstring_nocopy ,
+.Nm prop_string_append ,
+.Nm prop_string_append_cstring ,
+.Nm prop_string_equals ,
+.Nm prop_string_equals_cstring
+.Nd string value property object
+.Sh LIBRARY
+.Lb libprop
+.Sh SYNOPSIS
+.In prop/proplib.h
+.\"
+.Ft prop_string_t
+.Fn prop_string_create "void"
+.Ft prop_string_t
+.Fn prop_string_create_cstring "const char *cstring"
+.Ft prop_string_t
+.Fn prop_string_create_cstring_nocopy "const char *cstring"
+.\"
+.Ft prop_string_t
+.Fn prop_string_copy "prop_string_t string"
+.Ft prop_string_t
+.Fn prop_string_copy_mutable "prop_string_t string"
+.\"
+.Ft size_t
+.Fn prop_string_size "prop_string_t string"
+.Ft bool
+.Fn prop_string_mutable "prop_string_t string"
+.\"
+.Ft char *
+.Fn prop_string_cstring "prop_string_t string"
+.Ft const char *
+.Fn prop_string_cstring_nocopy "prop_string_t string"
+.\"
+.Ft bool
+.Fn prop_string_append "prop_string_t str1" "prop_string_t str2"
+.Ft bool
+.Fn prop_string_append_cstring "prop_string_t string" "const char *cstring"
+.\"
+.Ft bool
+.Fn prop_string_equals "prop_string_t str1" "prop_string_t str2"
+.Ft bool
+.Fn prop_string_equals_cstring "prop_string_t string" "const char *cstring"
+.Sh DESCRIPTION
+The
+.Nm prop_string
+family of functions operate on a string value property object type.
+.Bl -tag -width "xxxxx"
+.It Fn prop_string_create "void"
+Create an empty mutable string.
+Returns
+.Dv NULL
+on failure.
+.It Fn prop_string_create_cstring "const char *cstring"
+Create a mutable string that contains a copy of
+.Fa cstring .
+Returns
+.Dv NULL
+on failure.
+.It Fn prop_string_create_cstring_nocopy "const char *cstring"
+Create an immutable string that contains a reference to
+.Fa cstring .
+Returns
+.Dv NULL
+on failure.
+.It Fn prop_string_copy "prop_string_t string"
+Copy a string.
+If the string being copied is an immutable external C string reference,
+then the copy is also immutable and references the same external C string.
+Returns
+.Dv NULL
+on failure.
+.It Fn prop_string_copy_mutable "prop_string_t string"
+Copy a string, always creating a mutable copy.
+Returns
+.Dv NULL
+on failure.
+.It Fn prop_string_size "prop_string_t string"
+Returns the size of the string, not including the terminating NUL.
+If the supplied object isn't a string, zero is returned.
+.It Fn prop_string_mutable "prop_string_t string"
+Returns
+.Dv true
+if the string is mutable.
+If the supplied object isn't a string,
+.Dv false
+is returned.
+.It Fn prop_string_cstring "prop_string_t string"
+Returns a copy of the string's contents as a C string.
+The caller is responsible for freeing the returned buffer.
+.Pp
+In user space, the buffer is allocated using
+.Xr malloc 3 .
+In the kernel, the buffer is allocated using
+.Xr malloc 9
+using the malloc type
+.Dv M_TEMP .
+.Pp
+Returns
+.Dv NULL
+on failure.
+.It Fn prop_string_cstring_nocopy "prop_string_t string"
+Returns an immutable reference to the contents of the string as a
+C string.
+If the supplied object isn't a string,
+.Dv NULL
+is returned.
+.It Fn prop_string_append "prop_string_t str1" "prop_string_t str2"
+Append the contents of
+.Fa str2
+to
+.Fa str1 ,
+which must be mutable.
+Returns
+.Dv true
+upon success and
+.Dv false
+otherwise.
+.It Fn prop_string_append_cstring "prop_string_t string" "const char *cstring"
+Append the C string
+.Fa cstring
+to
+.Fa string ,
+which must be mutable.
+Returns
+.Dv true
+upon success and
+.Dv false
+otherwise.
+.It Fn prop_string_equals "prop_string_t str1" "prop_string_t str2"
+Returns
+.Dv true
+if the two string objects are equivalent.
+.It Fn prop_string_equals_cstring "prop_string_t string" "const char *cstring"
+Returns
+.Dv true
+if the string's value is equivalent to
+.Fa cstring .
+.El
+.Sh SEE ALSO
+.Xr prop_array 3 ,
+.Xr prop_bool 3 ,
+.Xr prop_data 3 ,
+.Xr prop_dictionary 3 ,
+.Xr prop_number 3 ,
+.Xr prop_object 3 ,
+.Xr proplib 3
+.Sh HISTORY
+The
+.Nm proplib
+property container object library first appeared in
+.Nx 4.0 .
diff --git a/common/lib/libprop/prop_string.c b/common/lib/libprop/prop_string.c
new file mode 100644 (file)
index 0000000..4f9ed88
--- /dev/null
@@ -0,0 +1,471 @@
+/*     $NetBSD: prop_string.c,v 1.11 2008/08/03 04:00:12 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 2006 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jason R. Thorpe.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <prop/prop_string.h>
+#include "prop_object_impl.h"
+
+struct _prop_string {
+       struct _prop_object     ps_obj;
+       union {
+               char *          psu_mutable;
+               const char *    psu_immutable;
+       } ps_un;
+#define        ps_mutable              ps_un.psu_mutable
+#define        ps_immutable            ps_un.psu_immutable
+       size_t                  ps_size;        /* not including \0 */
+       int                     ps_flags;
+};
+
+#define        PS_F_NOCOPY             0x01
+
+_PROP_POOL_INIT(_prop_string_pool, sizeof(struct _prop_string), "propstng")
+
+_PROP_MALLOC_DEFINE(M_PROP_STRING, "prop string",
+                   "property string container object")
+
+static _prop_object_free_rv_t
+               _prop_string_free(prop_stack_t, prop_object_t *);
+static bool    _prop_string_externalize(
+                               struct _prop_object_externalize_context *,
+                               void *);
+static _prop_object_equals_rv_t
+               _prop_string_equals(prop_object_t, prop_object_t,
+                                   void **, void **,
+                                   prop_object_t *, prop_object_t *);
+
+static const struct _prop_object_type _prop_object_type_string = {
+       .pot_type       =       PROP_TYPE_STRING,
+       .pot_free       =       _prop_string_free,
+       .pot_extern     =       _prop_string_externalize,
+       .pot_equals     =       _prop_string_equals,
+};
+
+#define        prop_object_is_string(x)        \
+       ((x) != NULL && (x)->ps_obj.po_type == &_prop_object_type_string)
+#define        prop_string_contents(x)  ((x)->ps_immutable ? (x)->ps_immutable : "")
+
+/* ARGSUSED */
+static _prop_object_free_rv_t
+_prop_string_free(prop_stack_t stack, prop_object_t *obj)
+{
+       prop_string_t ps = *obj;
+
+       if ((ps->ps_flags & PS_F_NOCOPY) == 0 && ps->ps_mutable != NULL)
+               _PROP_FREE(ps->ps_mutable, M_PROP_STRING);
+       _PROP_POOL_PUT(_prop_string_pool, ps);
+
+       return (_PROP_OBJECT_FREE_DONE);
+}
+
+static bool
+_prop_string_externalize(struct _prop_object_externalize_context *ctx,
+                        void *v)
+{
+       prop_string_t ps = v;
+
+       if (ps->ps_size == 0)
+               return (_prop_object_externalize_empty_tag(ctx, "string"));
+
+       if (_prop_object_externalize_start_tag(ctx, "string") == false ||
+           _prop_object_externalize_append_encoded_cstring(ctx,
+                                               ps->ps_immutable) == false ||
+           _prop_object_externalize_end_tag(ctx, "string") == false)
+               return (false);
+       
+       return (true);
+}
+
+/* ARGSUSED */
+static _prop_object_equals_rv_t
+_prop_string_equals(prop_object_t v1, prop_object_t v2,
+    void **stored_pointer1, void **stored_pointer2,
+    prop_object_t *next_obj1, prop_object_t *next_obj2)
+{
+       prop_string_t str1 = v1;
+       prop_string_t str2 = v2;
+
+       if (str1 == str2)
+               return (_PROP_OBJECT_EQUALS_TRUE);
+       if (str1->ps_size != str2->ps_size)
+               return (_PROP_OBJECT_EQUALS_FALSE);
+       if (strcmp(prop_string_contents(str1), prop_string_contents(str2)))
+               return (_PROP_OBJECT_EQUALS_FALSE);
+       else
+               return (_PROP_OBJECT_EQUALS_TRUE);
+}
+
+static prop_string_t
+_prop_string_alloc(void)
+{
+       prop_string_t ps;
+
+       ps = _PROP_POOL_GET(_prop_string_pool);
+       if (ps != NULL) {
+               _prop_object_init(&ps->ps_obj, &_prop_object_type_string);
+
+               ps->ps_mutable = NULL;
+               ps->ps_size = 0;
+               ps->ps_flags = 0;
+       }
+
+       return (ps);
+}
+
+/*
+ * prop_string_create --
+ *     Create an empty mutable string.
+ */
+prop_string_t
+prop_string_create(void)
+{
+
+       return (_prop_string_alloc());
+}
+
+/*
+ * prop_string_create_cstring --
+ *     Create a string that contains a copy of the provided C string.
+ */
+prop_string_t
+prop_string_create_cstring(const char *str)
+{
+       prop_string_t ps;
+       char *cp;
+       size_t len;
+
+       ps = _prop_string_alloc();
+       if (ps != NULL) {
+               len = strlen(str);
+               cp = _PROP_MALLOC(len + 1, M_PROP_STRING);
+               if (cp == NULL) {
+                       prop_object_release(ps);
+                       return (NULL);
+               }
+               strcpy(cp, str);
+               ps->ps_mutable = cp;
+               ps->ps_size = len;
+       }
+       return (ps);
+}
+
+/*
+ * prop_string_create_cstring_nocopy --
+ *     Create an immutable string that contains a refrence to the
+ *     provided C string.
+ */
+prop_string_t
+prop_string_create_cstring_nocopy(const char *str)
+{
+       prop_string_t ps;
+       
+       ps = _prop_string_alloc();
+       if (ps != NULL) {
+               ps->ps_immutable = str;
+               ps->ps_size = strlen(str);
+               ps->ps_flags |= PS_F_NOCOPY;
+       }
+       return (ps);
+}
+
+/*
+ * prop_string_copy --
+ *     Copy a string.  If the original string is immutable, then the
+ *     copy is also immutable and references the same external data.
+ */
+prop_string_t
+prop_string_copy(prop_string_t ops)
+{
+       prop_string_t ps;
+
+       if (! prop_object_is_string(ops))
+               return (NULL);
+
+       ps = _prop_string_alloc();
+       if (ps != NULL) {
+               ps->ps_size = ops->ps_size;
+               ps->ps_flags = ops->ps_flags;
+               if (ops->ps_flags & PS_F_NOCOPY)
+                       ps->ps_immutable = ops->ps_immutable;
+               else {
+                       char *cp = _PROP_MALLOC(ps->ps_size + 1, M_PROP_STRING);
+                       if (cp == NULL) {
+                               prop_object_release(ps);
+                               return (NULL);
+                       }
+                       strcpy(cp, prop_string_contents(ops));
+                       ps->ps_mutable = cp;
+               }
+       }
+       return (ps);
+}
+
+/*
+ * prop_string_copy_mutable --
+ *     Copy a string, always returning a mutable copy.
+ */
+prop_string_t
+prop_string_copy_mutable(prop_string_t ops)
+{
+       prop_string_t ps;
+       char *cp;
+
+       if (! prop_object_is_string(ops))
+               return (NULL);
+
+       ps = _prop_string_alloc();
+       if (ps != NULL) {
+               ps->ps_size = ops->ps_size;
+               cp = _PROP_MALLOC(ps->ps_size + 1, M_PROP_STRING);
+               if (cp == NULL) {
+                       prop_object_release(ps);
+                       return (NULL);
+               }
+               strcpy(cp, prop_string_contents(ops));
+               ps->ps_mutable = cp;
+       }
+       return (ps);
+}
+
+/*
+ * prop_string_size --
+ *     Return the size of the string, not including the terminating NUL.
+ */
+size_t
+prop_string_size(prop_string_t ps)
+{
+
+       if (! prop_object_is_string(ps))
+               return (0);
+
+       return (ps->ps_size);
+}
+
+/*
+ * prop_string_mutable --
+ *     Return true if the string is a mutable string.
+ */
+bool
+prop_string_mutable(prop_string_t ps)
+{
+
+       if (! prop_object_is_string(ps))
+               return (false);
+
+       return ((ps->ps_flags & PS_F_NOCOPY) == 0);
+}
+
+/*
+ * prop_string_cstring --
+ *     Return a copy of the contents of the string as a C string.
+ *     The string is allocated with the M_TEMP malloc type.
+ */
+char *
+prop_string_cstring(prop_string_t ps)
+{
+       char *cp;
+
+       if (! prop_object_is_string(ps))
+               return (NULL);
+
+       cp = _PROP_MALLOC(ps->ps_size + 1, M_TEMP);
+       if (cp != NULL)
+               strcpy(cp, prop_string_contents(ps));
+       
+       return (cp);
+}
+
+/*
+ * prop_string_cstring_nocopy --
+ *     Return an immutable reference to the contents of the string
+ *     as a C string.
+ */
+const char *
+prop_string_cstring_nocopy(prop_string_t ps)
+{
+
+       if (! prop_object_is_string(ps))
+               return (NULL);
+
+       return (prop_string_contents(ps));
+}
+
+/*
+ * prop_string_append --
+ *     Append the contents of one string to another.  Returns true
+ *     upon success.  The destination string must be mutable.
+ */
+bool
+prop_string_append(prop_string_t dst, prop_string_t src)
+{
+       char *ocp, *cp;
+       size_t len;
+
+       if (! (prop_object_is_string(dst) &&
+              prop_object_is_string(src)))
+               return (false);
+
+       if (dst->ps_flags & PS_F_NOCOPY)
+               return (false);
+
+       len = dst->ps_size + src->ps_size;
+       cp = _PROP_MALLOC(len + 1, M_PROP_STRING);
+       if (cp == NULL)
+               return (false);
+       sprintf(cp, "%s%s", prop_string_contents(dst),
+               prop_string_contents(src));
+       ocp = dst->ps_mutable;
+       dst->ps_mutable = cp;
+       dst->ps_size = len;
+       if (ocp != NULL)
+               _PROP_FREE(ocp, M_PROP_STRING);
+       
+       return (true);
+}
+
+/*
+ * prop_string_append_cstring --
+ *     Append a C string to a string.  Returns true upon success.
+ *     The destination string must be mutable.
+ */
+bool
+prop_string_append_cstring(prop_string_t dst, const char *src)
+{
+       char *ocp, *cp;
+       size_t len;
+
+       if (! prop_object_is_string(dst))
+               return (false);
+
+       _PROP_ASSERT(src != NULL);
+
+       if (dst->ps_flags & PS_F_NOCOPY)
+               return (false);
+       
+       len = dst->ps_size + strlen(src);
+       cp = _PROP_MALLOC(len + 1, M_PROP_STRING);
+       if (cp == NULL)
+               return (false);
+       sprintf(cp, "%s%s", prop_string_contents(dst), src);
+       ocp = dst->ps_mutable;
+       dst->ps_mutable = cp;
+       dst->ps_size = len;
+       if (ocp != NULL)
+               _PROP_FREE(ocp, M_PROP_STRING);
+       
+       return (true);
+}
+
+/*
+ * prop_string_equals --
+ *     Return true if two strings are equivalent.
+ */
+bool
+prop_string_equals(prop_string_t str1, prop_string_t str2)
+{
+       if (!prop_object_is_string(str1) || !prop_object_is_string(str2))
+               return (false);
+
+       return prop_object_equals(str1, str2);
+}
+
+/*
+ * prop_string_equals_cstring --
+ *     Return true if the string is equivalent to the specified
+ *     C string.
+ */
+bool
+prop_string_equals_cstring(prop_string_t ps, const char *cp)
+{
+
+       if (! prop_object_is_string(ps))
+               return (false);
+
+       return (strcmp(prop_string_contents(ps), cp) == 0);
+}
+
+/*
+ * _prop_string_internalize --
+ *     Parse a <string>...</string> and return the object created from the
+ *     external representation.
+ */
+/* ARGSUSED */
+bool
+_prop_string_internalize(prop_stack_t stack, prop_object_t *obj,
+    struct _prop_object_internalize_context *ctx)
+{
+       prop_string_t string;
+       char *str;
+       size_t len, alen;
+
+       if (ctx->poic_is_empty_element) {
+               *obj = prop_string_create();
+               return (true);
+       }
+       
+       /* No attributes recognized here. */
+       if (ctx->poic_tagattr != NULL)
+               return (true);
+
+       /* Compute the length of the result. */
+       if (_prop_object_internalize_decode_string(ctx, NULL, 0, &len,
+                                                  NULL) == false)
+               return (true);
+       
+       str = _PROP_MALLOC(len + 1, M_PROP_STRING);
+       if (str == NULL)
+               return (true);
+       
+       if (_prop_object_internalize_decode_string(ctx, str, len, &alen,
+                                                  &ctx->poic_cp) == false ||
+           alen != len) {
+               _PROP_FREE(str, M_PROP_STRING);
+               return (true);
+       }
+       str[len] = '\0';
+
+       if (_prop_object_internalize_find_tag(ctx, "string",
+                                             _PROP_TAG_TYPE_END) == false) {
+               _PROP_FREE(str, M_PROP_STRING);
+               return (true);
+       }
+
+       string = _prop_string_alloc();
+       if (string == NULL) {
+               _PROP_FREE(str, M_PROP_STRING);
+               return (true);
+       }
+
+       string->ps_mutable = str;
+       string->ps_size = len;
+       *obj = string;
+
+       return (true);
+}
diff --git a/common/lib/libprop/proplib.3 b/common/lib/libprop/proplib.3
new file mode 100644 (file)
index 0000000..1d92f87
--- /dev/null
@@ -0,0 +1,139 @@
+.\"    $NetBSD: proplib.3,v 1.7 2011/01/19 20:34:23 bouyer Exp $
+.\"
+.\" Copyright (c) 2006 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by Jason R. Thorpe.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd January 17, 2011
+.Dt PROPLIB 3
+.Os
+.Sh NAME
+.Nm proplib
+.Nd property container object library
+.Sh LIBRARY
+.Lb libprop
+.Sh SYNOPSIS
+.In prop/proplib.h
+.Sh DESCRIPTION
+The
+.Nm
+library provides an abstract interface for creating and manipulating
+property lists.
+Property lists have object types for boolean values, opaque data, numbers,
+and strings.
+Structure is provided by the array and dictionary collection types.
+.Pp
+Property lists can be passed across protection boundaries by translating
+them to an external representation.
+This external representation is an XML document whose format is described
+by the following DTD:
+.Bd -literal -offset indent
+http://www.apple.com/DTDs/PropertyList-1.0.dtd
+.Ed
+.Pp
+Property container objects are reference counted.
+When an object is created, its reference count is set to 1.
+Any code that keeps a reference to an object, including the collection
+types
+.Pq arrays and dictionaries ,
+must
+.Dq retain
+the object
+.Pq increment its reference count .
+When that reference is dropped, the object must be
+.Dq released
+.Pq reference count decremented .
+When an object's reference count drops to 0, it is automatically freed.
+.Pp
+The rules for managing reference counts are very simple:
+.Bl -bullet
+.It
+If you create an object and do not explicitly maintain a reference to it,
+you must release it.
+.It
+If you get a reference to an object from other code and wish to maintain
+a reference to it, you must retain the object.
+You are responsible for
+releasing the object once you drop that reference.
+.It
+You must never release an object unless you create it or retain it.
+.El
+.Pp
+Object collections may be iterated by creating a special iterator object.
+Iterator objects are special; they may not be retained, and they are
+released using an iterator-specific release function.
+.Sh SEE ALSO
+.Xr prop_array 3 ,
+.Xr prop_bool 3 ,
+.Xr prop_data 3 ,
+.Xr prop_dictionary 3 ,
+.Xr prop_dictionary_util 3 ,
+.Xr prop_number 3 ,
+.Xr prop_object 3 ,
+.Xr prop_send_ioctl 3 ,
+.Xr prop_send_syscall 3 ,
+.Xr prop_string 3
+.Sh HISTORY
+The
+.Nm
+property container object library first appeared in
+.Nx 4.0 .
+.Sh CAVEATS
+.Nm
+does not have a
+.Sq date
+object type, and thus will not parse
+.Sq date
+elements from an Apple XML property list.
+.Pp
+The
+.Nm
+.Sq number
+object type differs from the Apple XML property list format in the following
+ways:
+.Bl -bullet
+.It
+The external representation is in base 16, not base 10.
+.Nm
+is able to parse base 8, base 10, and base 16
+.Sq integer
+elements.
+.It
+Internally, integers are always stored as unsigned numbers
+.Pq uint64_t .
+Therefore, the external representation will never be negative.
+.It
+Because floating point numbers are not supported,
+.Sq real
+elements from an Apple XML property list will not be parsed.
+.El
+.Pp
+In order to facilitate use of
+.Nm
+in kernel, standalone, and user space environments, the
+.Nm
+parser is not a real XML parser.
+It is hard-coded to parse only the property list external representation.
index f8f28a683ae8834c0dab547abb09961a662563d3..329abe1521fb1daff981b0bbdb4dded0126f52ff 100644 (file)
@@ -61,6 +61,7 @@
 755  root    operator  /usr/include/arpa
 755  root    operator  /usr/include/compat
 755  root    operator  /usr/include/compat/machine
+755  root    operator  /usr/include/prop
 755  root    operator  /usr/include/ddekit
 755  root    operator  /usr/include/ddekit/minix
 755  root    operator  /usr/include/minix
index a4c2127cb5e11d468d672feb1b561503d2bc71d4..8657b796437076bfcf9ee002499dd2fb1a1011a1 100644 (file)
@@ -24,7 +24,7 @@ SUBDIR= csu ${LIBCOMPAT_DIR} ${LIBC_DIR} libblockdriver libchardriver     \
 
 .if defined(NBSD_LIBC) && (${NBSD_LIBC} != "no")
 SUBDIR+= libelf libminc libcrypt libterminfo libcurses libvassert libutil \
-        libpuffs librefuse libbz2 libarchive
+        libpuffs librefuse libbz2 libarchive libprop
 .endif
 
 .if ${COMPILER_TYPE} == "ack"
diff --git a/lib/libprop/Makefile b/lib/libprop/Makefile
new file mode 100644 (file)
index 0000000..0fd3d3f
--- /dev/null
@@ -0,0 +1,191 @@
+#      $NetBSD: Makefile,v 1.19 2011/09/30 22:08:19 jym Exp $
+
+.include <bsd.own.mk>
+
+WARNS=4
+USE_SHLIBDIR=  yes
+
+PROPLIBDIR=${NETBSDSRCDIR}/common/lib/libprop
+
+.include "${PROPLIBDIR}/Makefile.inc"
+
+CPPFLAGS+=-D_LIBPROP
+CPPFLAGS+= -I${NETBSDSRCDIR}/lib/nbsd_libc/include # -D_REENTRANT
+
+LINTFLAGS+=-w
+LIB=   prop
+
+MAN=   prop_array.3 prop_bool.3 prop_data.3 prop_dictionary.3 \
+       prop_ingest.3 prop_number.3 prop_object.3 prop_string.3 proplib.3
+
+MAN+=  prop_copyin_ioctl.9
+MLINKS+= prop_copyin_ioctl.9 prop_array_copyin_ioctl.9
+MLINKS+= prop_copyin_ioctl.9 prop_array_copyout_ioctl.9
+MLINKS+= prop_copyin_ioctl.9 prop_dictionary_copyin_ioctl.9
+MLINKS+= prop_copyin_ioctl.9 prop_dictionary_copyout_ioctl.9
+
+MAN+=  prop_send_ioctl.3
+MLINKS+= prop_send_ioctl.3 prop_array_send_ioctl.3
+MLINKS+= prop_send_ioctl.3 prop_array_recv_ioctl.3
+MLINKS+= prop_send_ioctl.3 prop_dictionary_send_ioctl.3
+MLINKS+= prop_send_ioctl.3 prop_dictionary_recv_ioctl.3
+MLINKS+= prop_send_ioctl.3 prop_dictionary_sendrecv_ioctl.3
+
+MAN+=  prop_send_syscall.3
+MLINKS+= prop_send_syscall.3 prop_array_send_syscall.3
+MLINKS+= prop_send_syscall.3 prop_array_recv_syscall.3
+MLINKS+= prop_send_syscall.3 prop_dictionary_send_syscall.3
+MLINKS+= prop_send_syscall.3 prop_dictionary_recv_syscall.3
+
+MAN+=  prop_dictionary_util.3
+MLINKS+= prop_dictionary_util.3 prop_dictionary_get_bool.3
+MLINKS+= prop_dictionary_util.3 prop_dictionary_set_bool.3
+MLINKS+= prop_dictionary_util.3 prop_dictionary_get_int8.3
+MLINKS+= prop_dictionary_util.3 prop_dictionary_get_uint8.3
+MLINKS+= prop_dictionary_util.3 prop_dictionary_set_int8.3
+MLINKS+= prop_dictionary_util.3 prop_dictionary_set_uint8.3
+MLINKS+= prop_dictionary_util.3 prop_dictionary_get_int16.3
+MLINKS+= prop_dictionary_util.3 prop_dictionary_get_uint16.3
+MLINKS+= prop_dictionary_util.3 prop_dictionary_set_int16.3
+MLINKS+= prop_dictionary_util.3 prop_dictionary_set_uint16.3
+MLINKS+= prop_dictionary_util.3 prop_dictionary_get_int32.3
+MLINKS+= prop_dictionary_util.3 prop_dictionary_get_uint32.3
+MLINKS+= prop_dictionary_util.3 prop_dictionary_set_int32.3
+MLINKS+= prop_dictionary_util.3 prop_dictionary_set_uint32.3
+MLINKS+= prop_dictionary_util.3 prop_dictionary_get_int64.3
+MLINKS+= prop_dictionary_util.3 prop_dictionary_get_uint64.3
+MLINKS+= prop_dictionary_util.3 prop_dictionary_set_int64.3
+MLINKS+= prop_dictionary_util.3 prop_dictionary_set_uint64.3
+MLINKS+= prop_dictionary_util.3 prop_dictionary_get_cstring.3
+MLINKS+= prop_dictionary_util.3 prop_dictionary_set_cstring.3
+MLINKS+= prop_dictionary_util.3 prop_dictionary_get_cstring_nocopy.3
+MLINKS+= prop_dictionary_util.3 prop_dictionary_set_cstring_nocopy.3
+
+MLINKS+= prop_array.3 prop_array_add.3
+MLINKS+= prop_array.3 prop_array_capacity.3
+MLINKS+= prop_array.3 prop_array_copy.3
+MLINKS+= prop_array.3 prop_array_copy_mutable.3
+MLINKS+= prop_array.3 prop_array_count.3
+MLINKS+= prop_array.3 prop_array_create.3
+MLINKS+= prop_array.3 prop_array_create_with_capacity.3
+MLINKS+= prop_array.3 prop_array_ensure_capacity.3
+MLINKS+= prop_array.3 prop_array_equals.3
+MLINKS+= prop_array.3 prop_array_externalize.3
+MLINKS+= prop_array.3 prop_array_externalize_to_file.3
+MLINKS+= prop_array.3 prop_array_externalize_to_pref.3
+MLINKS+= prop_array.3 prop_array_get.3
+MLINKS+= prop_array.3 prop_array_internalize.3
+MLINKS+= prop_array.3 prop_array_internalize_from_file.3
+MLINKS+= prop_array.3 prop_array_internalize_from_pref.3
+MLINKS+= prop_array.3 prop_array_iterator.3
+MLINKS+= prop_array.3 prop_array_make_immutable.3
+MLINKS+= prop_array.3 prop_array_mutable.3
+MLINKS+= prop_array.3 prop_array_remove.3
+MLINKS+= prop_array.3 prop_array_set.3
+
+MAN+=  prop_array_util.3
+MLINKS+= prop_array_util.3 prop_array_get_bool.3
+MLINKS+= prop_array_util.3 prop_array_set_bool.3
+MLINKS+= prop_array_util.3 prop_array_get_int8.3
+MLINKS+= prop_array_util.3 prop_array_get_uint8.3
+MLINKS+= prop_array_util.3 prop_array_set_int8.3
+MLINKS+= prop_array_util.3 prop_array_set_uint8.3
+MLINKS+= prop_array_util.3 prop_array_get_int16.3
+MLINKS+= prop_array_util.3 prop_array_get_uint16.3
+MLINKS+= prop_array_util.3 prop_array_set_int16.3
+MLINKS+= prop_array_util.3 prop_array_set_uint16.3
+MLINKS+= prop_array_util.3 prop_array_get_int32.3
+MLINKS+= prop_array_util.3 prop_array_get_uint32.3
+MLINKS+= prop_array_util.3 prop_array_set_int32.3
+MLINKS+= prop_array_util.3 prop_array_set_uint32.3
+MLINKS+= prop_array_util.3 prop_array_get_int64.3
+MLINKS+= prop_array_util.3 prop_array_get_uint64.3
+MLINKS+= prop_array_util.3 prop_array_set_int64.3
+MLINKS+= prop_array_util.3 prop_array_set_uint64.3
+MLINKS+= prop_array_util.3 prop_array_get_cstring.3
+MLINKS+= prop_array_util.3 prop_array_set_cstring.3
+MLINKS+= prop_array_util.3 prop_array_get_cstring_nocopy.3
+MLINKS+= prop_array_util.3 prop_array_set_cstring_nocopy.3
+
+MLINKS+= prop_bool.3 prop_bool_copy.3
+MLINKS+= prop_bool.3 prop_bool_create.3
+MLINKS+= prop_bool.3 prop_bool_true.3
+
+MLINKS+= prop_data.3 prop_data_copy.3
+MLINKS+= prop_data.3 prop_data_create_data.3
+MLINKS+= prop_data.3 prop_data_create_data_nocopy.3
+MLINKS+= prop_data.3 prop_data_data.3
+MLINKS+= prop_data.3 prop_data_data_nocopy.3
+MLINKS+= prop_data.3 prop_data_equals.3
+MLINKS+= prop_data.3 prop_data_equals_data.3
+MLINKS+= prop_data.3 prop_data_size.3
+
+MLINKS+= prop_dictionary.3 prop_dictionary_all_keys.3
+MLINKS+= prop_dictionary.3 prop_dictionary_capacity.3
+MLINKS+= prop_dictionary.3 prop_dictionary_copy.3
+MLINKS+= prop_dictionary.3 prop_dictionary_copy_mutable.3
+MLINKS+= prop_dictionary.3 prop_dictionary_count.3
+MLINKS+= prop_dictionary.3 prop_dictionary_create.3
+MLINKS+= prop_dictionary.3 prop_dictionary_create_with_capacity.3
+MLINKS+= prop_dictionary.3 prop_dictionary_ensure_capacity.3
+MLINKS+= prop_dictionary.3 prop_dictionary_equals.3
+MLINKS+= prop_dictionary.3 prop_dictionary_externalize.3
+MLINKS+= prop_dictionary.3 prop_dictionary_externalize_to_file.3
+MLINKS+= prop_dictionary.3 prop_dictionary_externalize_to_pref.3
+MLINKS+= prop_dictionary.3 prop_dictionary_get.3
+MLINKS+= prop_dictionary.3 prop_dictionary_get_keysym.3
+MLINKS+= prop_dictionary.3 prop_dictionary_internalize.3
+MLINKS+= prop_dictionary.3 prop_dictionary_internalize_from_file.3
+MLINKS+= prop_dictionary.3 prop_dictionary_internalize_from_pref.3
+MLINKS+= prop_dictionary.3 prop_dictionary_iterator.3
+MLINKS+= prop_dictionary.3 prop_dictionary_keysym_cstring_nocopy.3
+MLINKS+= prop_dictionary.3 prop_dictionary_keysym_equals.3
+MLINKS+= prop_dictionary.3 prop_dictionary_make_immutable.3
+MLINKS+= prop_dictionary.3 prop_dictionary_mutable.3
+MLINKS+= prop_dictionary.3 prop_dictionary_remove.3
+MLINKS+= prop_dictionary.3 prop_dictionary_remove_keysym.3
+MLINKS+= prop_dictionary.3 prop_dictionary_set.3
+MLINKS+= prop_dictionary.3 prop_dictionary_set_keysym.3
+
+MLINKS+= prop_ingest.3 prop_ingest_context_alloc.3
+MLINKS+= prop_ingest.3 prop_ingest_context_error.3
+MLINKS+= prop_ingest.3 prop_ingest_context_free.3
+MLINKS+= prop_ingest.3 prop_ingest_context_key.3
+MLINKS+= prop_ingest.3 prop_ingest_context_private.3
+MLINKS+= prop_ingest.3 prop_ingest_context_type.3
+MLINKS+= prop_ingest.3 prop_dictionary_ingest.3
+
+MLINKS+= prop_number.3 prop_number_copy.3
+MLINKS+= prop_number.3 prop_number_create_integer.3
+MLINKS+= prop_number.3 prop_number_create_unsigned_integer.3
+MLINKS+= prop_number.3 prop_number_equals.3
+MLINKS+= prop_number.3 prop_number_equals_integer.3
+MLINKS+= prop_number.3 prop_number_equals_unsigned_integer.3
+MLINKS+= prop_number.3 prop_number_size.3
+MLINKS+= prop_number.3 prop_number_unsigned.3
+MLINKS+= prop_number.3 prop_number_integer_value.3
+MLINKS+= prop_number.3 prop_number_unsigned_integer_value.3
+
+MLINKS+= prop_object.3 prop_object_equals.3
+MLINKS+= prop_object.3 prop_object_iterator_next.3
+MLINKS+= prop_object.3 prop_object_iterator_release.3
+MLINKS+= prop_object.3 prop_object_iterator_reset.3
+MLINKS+= prop_object.3 prop_object_release.3
+MLINKS+= prop_object.3 prop_object_retain.3
+MLINKS+= prop_object.3 prop_object_type.3
+
+MLINKS+= prop_string.3 prop_string_append.3
+MLINKS+= prop_string.3 prop_string_append_cstring.3
+MLINKS+= prop_string.3 prop_string_copy.3
+MLINKS+= prop_string.3 prop_string_copy_mutable.3
+MLINKS+= prop_string.3 prop_string_create.3
+MLINKS+= prop_string.3 prop_string_create_cstring.3
+MLINKS+= prop_string.3 prop_string_create_cstring_nocopy.3
+MLINKS+= prop_string.3 prop_string_cstring.3
+MLINKS+= prop_string.3 prop_string_cstring_nocopy.3
+MLINKS+= prop_string.3 prop_string_equals.3
+MLINKS+= prop_string.3 prop_string_equals_cstring.3
+MLINKS+= prop_string.3 prop_string_mutable.3
+MLINKS+= prop_string.3 prop_string_size.3
+
+.include <bsd.lib.mk>
diff --git a/lib/libprop/shlib_version b/lib/libprop/shlib_version
new file mode 100644 (file)
index 0000000..cb55805
--- /dev/null
@@ -0,0 +1,4 @@
+#      $NetBSD: shlib_version,v 1.10 2009/10/10 18:06:54 bad Exp $
+#      Remember to update distrib/sets/lists/base/shl.* when changing
+major=1
+minor=1
index 085a6d0ad42769242652d0e6f0d75d6179336f1b..f5e5e19bb87114450354771e061310070cbebe98 100644 (file)
@@ -67,7 +67,9 @@ INCSDIR=      /usr/include
 .else
 SUBDIR=                rpc
 .endif
-.if !defined(__MINIX)
+.if defined(__MINIX)
+SUBDIR+=       prop
+.else
 SUBDIR+=       ../common/include/prop
 .endif
 
diff --git a/nbsd_include/prop/Makefile b/nbsd_include/prop/Makefile
new file mode 100644 (file)
index 0000000..0efa775
--- /dev/null
@@ -0,0 +1,9 @@
+#      $NetBSD: Makefile,v 1.3 2007/08/17 11:05:04 pavel Exp $
+
+INCS=  prop_array.h prop_bool.h prop_data.h prop_dictionary.h \
+       prop_ingest.h prop_number.h prop_object.h prop_string.h \
+       proplib.h plistref.h
+
+INCSDIR=       /usr/include/prop
+
+.include <bsd.prog.mk>
diff --git a/nbsd_include/prop/plistref.h b/nbsd_include/prop/plistref.h
new file mode 100644 (file)
index 0000000..2075519
--- /dev/null
@@ -0,0 +1,48 @@
+/*     $NetBSD: plistref.h,v 1.2 2008/04/28 20:22:51 martin Exp $      */
+
+/*-
+ * Copyright (c) 2006 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jason R. Thorpe.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _PROPLIB_PLISTREF_H_
+#define        _PROPLIB_PLISTREF_H_
+
+/* for size_t */
+#include <sys/types.h>
+
+/*
+ * Property List Reference --
+ *     Used to pass externalized property lists across protection
+ *     boundaries (ioctls, syscalls, etc.).
+ */
+struct plistref {
+       void *pref_plist;               /* plist data */
+       size_t pref_len;                /* total length of plist data */
+};
+
+#endif /* _PROPLIB_PLISTREF_H_ */
diff --git a/nbsd_include/prop/prop_array.h b/nbsd_include/prop/prop_array.h
new file mode 100644 (file)
index 0000000..d01a3e0
--- /dev/null
@@ -0,0 +1,163 @@
+/*     $NetBSD: prop_array.h,v 1.13 2011/09/30 22:08:18 jym Exp $    */
+
+/*-
+ * Copyright (c) 2006, 2009 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jason R. Thorpe.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _PROPLIB_PROP_ARRAY_H_
+#define        _PROPLIB_PROP_ARRAY_H_
+
+#include <prop/prop_object.h>
+
+typedef struct _prop_array *prop_array_t;
+
+__BEGIN_DECLS
+prop_array_t   prop_array_create(void);
+prop_array_t   prop_array_create_with_capacity(unsigned int);
+
+prop_array_t   prop_array_copy(prop_array_t);
+prop_array_t   prop_array_copy_mutable(prop_array_t);
+
+unsigned int   prop_array_capacity(prop_array_t);
+unsigned int   prop_array_count(prop_array_t);
+bool           prop_array_ensure_capacity(prop_array_t, unsigned int);
+
+void           prop_array_make_immutable(prop_array_t);
+bool           prop_array_mutable(prop_array_t);
+
+prop_object_iterator_t prop_array_iterator(prop_array_t);
+
+prop_object_t  prop_array_get(prop_array_t, unsigned int);
+bool           prop_array_set(prop_array_t, unsigned int, prop_object_t);
+bool           prop_array_add(prop_array_t, prop_object_t);
+void           prop_array_remove(prop_array_t, unsigned int);
+
+bool           prop_array_equals(prop_array_t, prop_array_t);
+
+char *         prop_array_externalize(prop_array_t);
+prop_array_t   prop_array_internalize(const char *);
+
+bool           prop_array_externalize_to_file(prop_array_t, const char *);
+prop_array_t   prop_array_internalize_from_file(const char *);
+
+#if defined(__NetBSD__)
+struct plistref;
+
+#if !defined(_KERNEL) && !defined(_STANDALONE)
+bool           prop_array_externalize_to_pref(prop_array_t, struct plistref *);
+bool           prop_array_internalize_from_pref(const struct plistref *,
+                                                prop_array_t *);
+int            prop_array_send_ioctl(prop_array_t, int, unsigned long);
+int            prop_array_recv_ioctl(int, unsigned long, prop_array_t *);
+int            prop_array_send_syscall(prop_array_t, struct plistref *);
+int            prop_array_recv_syscall(const struct plistref *,
+                                       prop_array_t *);
+#elif defined(_KERNEL)
+int            prop_array_copyin(const struct plistref *, prop_array_t *);
+int            prop_array_copyout(struct plistref *, prop_array_t);
+int            prop_array_copyin_ioctl(const struct plistref *, const u_long,
+                                       prop_array_t *);
+int            prop_array_copyout_ioctl(struct plistref *, const u_long,
+                                        prop_array_t);
+#endif
+#endif /* __NetBSD__ */
+
+/*
+ * Utility routines to make it more convenient to work with values
+ * stored in dictionaries.
+ */
+bool           prop_array_get_bool(prop_array_t, unsigned int,
+                                        bool *);
+bool           prop_array_set_bool(prop_array_t, unsigned int,
+                                        bool);
+
+bool           prop_array_get_int8(prop_array_t, unsigned int,
+                                        int8_t *);
+bool           prop_array_get_uint8(prop_array_t, unsigned int,
+                                         uint8_t *);
+bool           prop_array_set_int8(prop_array_t, unsigned int,
+                                        int8_t);
+bool           prop_array_set_uint8(prop_array_t, unsigned int,
+                                         uint8_t);
+
+bool           prop_array_get_int16(prop_array_t, unsigned int,
+                                         int16_t *);
+bool           prop_array_get_uint16(prop_array_t, unsigned int,
+                                          uint16_t *);
+bool           prop_array_set_int16(prop_array_t, unsigned int,
+                                         int16_t);
+bool           prop_array_set_uint16(prop_array_t, unsigned int,
+                                          uint16_t);
+
+bool           prop_array_get_int32(prop_array_t, unsigned int,
+                                         int32_t *);
+bool           prop_array_get_uint32(prop_array_t, unsigned int,
+                                          uint32_t *);
+bool           prop_array_set_int32(prop_array_t, unsigned int,
+                                         int32_t);
+bool           prop_array_set_uint32(prop_array_t, unsigned int,
+                                          uint32_t);
+
+bool           prop_array_get_int64(prop_array_t, unsigned int,
+                                         int64_t *);
+bool           prop_array_get_uint64(prop_array_t, unsigned int,
+                                          uint64_t *);
+bool           prop_array_set_int64(prop_array_t, unsigned int,
+                                         int64_t);
+bool           prop_array_set_uint64(prop_array_t, unsigned int,
+                                          uint64_t);
+
+bool           prop_array_add_int8(prop_array_t, int8_t);
+bool           prop_array_add_uint8(prop_array_t, uint8_t);
+
+bool           prop_array_add_int16(prop_array_t, int16_t);
+bool           prop_array_add_uint16(prop_array_t, uint16_t);
+
+bool           prop_array_add_int32(prop_array_t, int32_t);
+bool           prop_array_add_uint32(prop_array_t, uint32_t);
+
+bool           prop_array_add_int64(prop_array_t, int64_t);
+bool           prop_array_add_uint64(prop_array_t, uint64_t);
+
+bool           prop_array_get_cstring(prop_array_t, unsigned int,
+                                            char **);
+bool           prop_array_set_cstring(prop_array_t, unsigned int,
+                                           const char *);
+
+bool           prop_array_get_cstring_nocopy(prop_array_t,
+                                                   unsigned int,
+                                                  const char **);
+bool           prop_array_set_cstring_nocopy(prop_array_t,
+                                                  unsigned int,
+                                                  const char *);
+
+bool           prop_array_add_and_rel(prop_array_t, prop_object_t);
+
+__END_DECLS
+
+#endif /* _PROPLIB_PROP_ARRAY_H_ */
diff --git a/nbsd_include/prop/prop_bool.h b/nbsd_include/prop/prop_bool.h
new file mode 100644 (file)
index 0000000..f727ff4
--- /dev/null
@@ -0,0 +1,48 @@
+/*     $NetBSD: prop_bool.h,v 1.4 2008/04/28 20:22:51 martin Exp $     */
+
+/*-
+ * Copyright (c) 2006 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jason R. Thorpe.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _PROPLIB_PROP_BOOL_H_
+#define        _PROPLIB_PROP_BOOL_H_
+
+#include <prop/prop_object.h>
+
+typedef struct _prop_bool *prop_bool_t;
+
+__BEGIN_DECLS
+prop_bool_t    prop_bool_create(bool);
+prop_bool_t    prop_bool_copy(prop_bool_t);
+
+bool           prop_bool_true(prop_bool_t);
+
+bool           prop_bool_equals(prop_bool_t, prop_bool_t);
+__END_DECLS
+
+#endif /* _PROPLIB_PROP_BOOL_H_ */
diff --git a/nbsd_include/prop/prop_data.h b/nbsd_include/prop/prop_data.h
new file mode 100644 (file)
index 0000000..ead37c5
--- /dev/null
@@ -0,0 +1,54 @@
+/*     $NetBSD: prop_data.h,v 1.3 2008/04/28 20:22:51 martin Exp $     */
+
+/*-
+ * Copyright (c) 2006 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jason R. Thorpe.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _PROPLIB_PROP_DATA_H_
+#define        _PROPLIB_PROP_DATA_H_
+
+#include <prop/prop_object.h>
+
+typedef struct _prop_data *prop_data_t;
+
+__BEGIN_DECLS
+prop_data_t    prop_data_create_data(const void *, size_t);
+prop_data_t    prop_data_create_data_nocopy(const void *, size_t);
+
+prop_data_t    prop_data_copy(prop_data_t);
+
+size_t         prop_data_size(prop_data_t);
+
+void *         prop_data_data(prop_data_t);
+const void *   prop_data_data_nocopy(prop_data_t);
+
+bool           prop_data_equals(prop_data_t, prop_data_t);
+bool           prop_data_equals_data(prop_data_t, const void *, size_t);
+__END_DECLS
+
+#endif /* _PROPLIB_PROP_DATA_H_ */
diff --git a/nbsd_include/prop/prop_dictionary.h b/nbsd_include/prop/prop_dictionary.h
new file mode 100644 (file)
index 0000000..6cfef9f
--- /dev/null
@@ -0,0 +1,181 @@
+/*     $NetBSD: prop_dictionary.h,v 1.14 2011/09/30 22:08:18 jym Exp $ */
+
+/*-
+ * Copyright (c) 2006, 2009 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jason R. Thorpe.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _PROPLIB_PROP_DICTIONARY_H_
+#define        _PROPLIB_PROP_DICTIONARY_H_
+
+#include <prop/prop_object.h>
+
+typedef struct _prop_dictionary *prop_dictionary_t;
+typedef struct _prop_dictionary_keysym *prop_dictionary_keysym_t;
+
+__BEGIN_DECLS
+prop_dictionary_t prop_dictionary_create(void);
+prop_dictionary_t prop_dictionary_create_with_capacity(unsigned int);
+
+prop_dictionary_t prop_dictionary_copy(prop_dictionary_t);
+prop_dictionary_t prop_dictionary_copy_mutable(prop_dictionary_t);
+
+unsigned int   prop_dictionary_count(prop_dictionary_t);
+bool           prop_dictionary_ensure_capacity(prop_dictionary_t,
+                                               unsigned int);
+
+void           prop_dictionary_make_immutable(prop_dictionary_t);
+bool           prop_dictionary_mutable(prop_dictionary_t);
+
+prop_object_iterator_t prop_dictionary_iterator(prop_dictionary_t);
+prop_array_t   prop_dictionary_all_keys(prop_dictionary_t);
+
+prop_object_t  prop_dictionary_get(prop_dictionary_t, const char *);
+bool           prop_dictionary_set(prop_dictionary_t, const char *,
+                                   prop_object_t);
+void           prop_dictionary_remove(prop_dictionary_t, const char *);
+
+prop_object_t  prop_dictionary_get_keysym(prop_dictionary_t,
+                                          prop_dictionary_keysym_t);
+bool           prop_dictionary_set_keysym(prop_dictionary_t,
+                                          prop_dictionary_keysym_t,
+                                          prop_object_t);
+void           prop_dictionary_remove_keysym(prop_dictionary_t,
+                                             prop_dictionary_keysym_t);
+
+bool           prop_dictionary_equals(prop_dictionary_t, prop_dictionary_t);
+
+char *         prop_dictionary_externalize(prop_dictionary_t);
+prop_dictionary_t prop_dictionary_internalize(const char *);
+
+bool           prop_dictionary_externalize_to_file(prop_dictionary_t,
+                                                   const char *);
+prop_dictionary_t prop_dictionary_internalize_from_file(const char *);
+
+const char *   prop_dictionary_keysym_cstring_nocopy(prop_dictionary_keysym_t);
+
+bool           prop_dictionary_keysym_equals(prop_dictionary_keysym_t,
+                                             prop_dictionary_keysym_t);
+
+#if defined(__NetBSD__)
+struct plistref;
+
+#if !defined(_KERNEL) && !defined(_STANDALONE)
+bool           prop_dictionary_externalize_to_pref(prop_dictionary_t, struct plistref *);
+bool           prop_dictionary_internalize_from_pref(const struct plistref *,
+                                                     prop_dictionary_t *);
+int            prop_dictionary_send_ioctl(prop_dictionary_t, int,
+                                          unsigned long);
+int            prop_dictionary_recv_ioctl(int, unsigned long,
+                                          prop_dictionary_t *);
+int            prop_dictionary_sendrecv_ioctl(prop_dictionary_t,
+                                              int, unsigned long,
+                                              prop_dictionary_t *);
+int            prop_dictionary_send_syscall(prop_dictionary_t,
+                    struct plistref *);
+int            prop_dictionary_recv_syscall(const struct plistref *,
+                                          prop_dictionary_t *);
+#elif defined(_KERNEL)
+int            prop_dictionary_copyin(const struct plistref *,
+                                      prop_dictionary_t *);
+int            prop_dictionary_copyout(struct plistref *,
+                                      prop_dictionary_t);
+int            prop_dictionary_copyin_ioctl(const struct plistref *,
+                                            const u_long,
+                                            prop_dictionary_t *);
+int            prop_dictionary_copyout_ioctl(struct plistref *,
+                                             const u_long,
+                                             prop_dictionary_t);
+#endif
+#endif /* __NetBSD__ */
+
+/*
+ * Utility routines to make it more convenient to work with values
+ * stored in dictionaries.
+ */
+bool           prop_dictionary_get_dict(prop_dictionary_t, const char *,
+                                        prop_dictionary_t *);
+bool           prop_dictionary_get_bool(prop_dictionary_t, const char *,
+                                        bool *);
+bool           prop_dictionary_set_bool(prop_dictionary_t, const char *,
+                                        bool);
+
+bool           prop_dictionary_get_int8(prop_dictionary_t, const char *,
+                                        int8_t *);
+bool           prop_dictionary_get_uint8(prop_dictionary_t, const char *,
+                                         uint8_t *);
+bool           prop_dictionary_set_int8(prop_dictionary_t, const char *,
+                                        int8_t);
+bool           prop_dictionary_set_uint8(prop_dictionary_t, const char *,
+                                         uint8_t);
+
+bool           prop_dictionary_get_int16(prop_dictionary_t, const char *,
+                                         int16_t *);
+bool           prop_dictionary_get_uint16(prop_dictionary_t, const char *,
+                                          uint16_t *);
+bool           prop_dictionary_set_int16(prop_dictionary_t, const char *,
+                                         int16_t);
+bool           prop_dictionary_set_uint16(prop_dictionary_t, const char *,
+                                          uint16_t);
+
+bool           prop_dictionary_get_int32(prop_dictionary_t, const char *,
+                                         int32_t *);
+bool           prop_dictionary_get_uint32(prop_dictionary_t, const char *,
+                                          uint32_t *);
+bool           prop_dictionary_set_int32(prop_dictionary_t, const char *,
+                                         int32_t);
+bool           prop_dictionary_set_uint32(prop_dictionary_t, const char *,
+                                          uint32_t);
+
+bool           prop_dictionary_get_int64(prop_dictionary_t, const char *,
+                                         int64_t *);
+bool           prop_dictionary_get_uint64(prop_dictionary_t, const char *,
+                                          uint64_t *);
+bool           prop_dictionary_set_int64(prop_dictionary_t, const char *,
+                                         int64_t);
+bool           prop_dictionary_set_uint64(prop_dictionary_t, const char *,
+                                          uint64_t);
+
+bool           prop_dictionary_get_cstring(prop_dictionary_t, const char *,
+                                            char **);
+bool           prop_dictionary_set_cstring(prop_dictionary_t, const char *,
+                                           const char *);
+
+bool           prop_dictionary_get_cstring_nocopy(prop_dictionary_t,
+                                                  const char *,
+                                                  const char **);
+bool           prop_dictionary_set_cstring_nocopy(prop_dictionary_t,
+                                                  const char *,
+                                                  const char *);
+
+bool           prop_dictionary_set_and_rel(prop_dictionary_t,
+                                                  const char *,
+                                                  prop_object_t);
+
+__END_DECLS
+
+#endif /* _PROPLIB_PROP_DICTIONARY_H_ */
diff --git a/nbsd_include/prop/prop_ingest.h b/nbsd_include/prop/prop_ingest.h
new file mode 100644 (file)
index 0000000..f82a57d
--- /dev/null
@@ -0,0 +1,90 @@
+/*     $NetBSD: prop_ingest.h,v 1.3 2008/04/28 20:22:51 martin Exp $   */
+
+/*-
+ * Copyright (c) 2006 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jason R. Thorpe.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _PROPLIB_PROP_INGEST_H_
+#define        _PROPLIB_PROP_INGEST_H_
+
+#include <prop/prop_dictionary.h>
+
+typedef enum {
+       PROP_INGEST_ERROR_NO_ERROR              = 0,
+       PROP_INGEST_ERROR_NO_KEY                = 1,
+       PROP_INGEST_ERROR_WRONG_TYPE            = 2,
+       PROP_INGEST_ERROR_HANDLER_FAILED        = 3
+} prop_ingest_error_t;
+
+typedef enum {
+       PROP_INGEST_FLAG_OPTIONAL               = 0x01
+} prop_ingest_flag_t;
+
+typedef struct _prop_ingest_context *prop_ingest_context_t;
+
+typedef bool (*prop_ingest_handler_t)(prop_ingest_context_t, prop_object_t);
+
+typedef struct {
+       const char *pite_key;
+       prop_type_t pite_type;
+       unsigned int pite_flags;
+       prop_ingest_handler_t pite_handler;
+} prop_ingest_table_entry;
+
+#define        PROP_INGEST(key_, type_, handler_)                              \
+       { .pite_key = key_ ,                                            \
+         .pite_type = type_ ,                                          \
+         .pite_flags = 0 ,                                             \
+         .pite_handler = handler_ }
+
+#define        PROP_INGEST_OPTIONAL(key_, type_, handler_)                     \
+       { .pite_key = key_ ,                                            \
+         .pite_type = type_ ,                                          \
+         .pite_flags = PROP_INGEST_FLAG_OPTIONAL ,                     \
+         .pite_handler = handler_ }
+
+#define        PROP_INGEST_END                                                 \
+       { .pite_key = NULL }
+
+__BEGIN_DECLS
+prop_ingest_context_t
+               prop_ingest_context_alloc(void *);
+void           prop_ingest_context_free(prop_ingest_context_t);
+
+prop_ingest_error_t
+               prop_ingest_context_error(prop_ingest_context_t);
+prop_type_t    prop_ingest_context_type(prop_ingest_context_t);
+const char *   prop_ingest_context_key(prop_ingest_context_t);
+void *         prop_ingest_context_private(prop_ingest_context_t);
+
+bool           prop_dictionary_ingest(prop_dictionary_t,
+                                      const prop_ingest_table_entry[],
+                                      prop_ingest_context_t);
+__END_DECLS
+
+#endif /* _PROPLIB_PROP_INGEST_H_ */
diff --git a/nbsd_include/prop/prop_number.h b/nbsd_include/prop/prop_number.h
new file mode 100644 (file)
index 0000000..a6a2a14
--- /dev/null
@@ -0,0 +1,59 @@
+/*     $NetBSD: prop_number.h,v 1.6 2008/04/28 20:22:51 martin Exp $   */
+
+/*-
+ * Copyright (c) 2006 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jason R. Thorpe.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _PROPLIB_PROP_NUMBER_H_
+#define        _PROPLIB_PROP_NUMBER_H_
+
+#if !defined(_KERNEL) && !defined(_STANDALONE)
+#include <stdint.h>
+#endif
+#include <prop/prop_object.h>
+
+typedef struct _prop_number *prop_number_t;
+
+__BEGIN_DECLS
+prop_number_t  prop_number_create_integer(int64_t);
+prop_number_t  prop_number_create_unsigned_integer(uint64_t);
+
+prop_number_t  prop_number_copy(prop_number_t);
+
+int            prop_number_size(prop_number_t);
+bool           prop_number_unsigned(prop_number_t);
+
+int64_t                prop_number_integer_value(prop_number_t);
+uint64_t       prop_number_unsigned_integer_value(prop_number_t);
+
+bool           prop_number_equals(prop_number_t, prop_number_t);
+bool           prop_number_equals_integer(prop_number_t, int64_t);
+bool           prop_number_equals_unsigned_integer(prop_number_t, uint64_t);
+__END_DECLS
+
+#endif /* _PROPLIB_PROP_NUMBER_H_ */
diff --git a/nbsd_include/prop/prop_object.h b/nbsd_include/prop/prop_object.h
new file mode 100644 (file)
index 0000000..e120333
--- /dev/null
@@ -0,0 +1,72 @@
+/*     $NetBSD: prop_object.h,v 1.8 2008/12/05 13:11:41 ad Exp $       */
+
+/*-
+ * Copyright (c) 2006 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jason R. Thorpe.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _PROPLIB_PROP_OBJECT_H_
+#define        _PROPLIB_PROP_OBJECT_H_
+
+#include <sys/types.h>
+
+#if !defined(_KERNEL) && !defined(_STANDALONE)
+#include <stdbool.h>
+#endif /* ! _KERNEL && ! _STANDALONE */
+
+typedef void *prop_object_t;
+
+typedef enum {
+       PROP_TYPE_UNKNOWN       =       0x00000000,
+#ifndef _PROPLIB_ZFS_CONFLICT
+       PROP_TYPE_BOOL          =       0x626f6f6c,     /* 'bool' */
+       PROP_TYPE_NUMBER        =       0x6e6d6272,     /* 'nmbr' */
+       PROP_TYPE_STRING        =       0x73746e67,     /* 'stng' */
+       PROP_TYPE_DATA          =       0x64617461,     /* 'data' */
+       PROP_TYPE_ARRAY         =       0x61726179,     /* 'aray' */
+       PROP_TYPE_DICTIONARY    =       0x64696374,     /* 'dict' */
+       PROP_TYPE_DICT_KEYSYM   =       0x646b6579      /* 'dkey' */
+#endif /* !_PROPLIB_ZFS_CONFLICT */
+} prop_type_t;
+
+__BEGIN_DECLS
+void           prop_object_retain(prop_object_t);
+void           prop_object_release(prop_object_t);
+
+prop_type_t    prop_object_type(prop_object_t);
+
+bool           prop_object_equals(prop_object_t, prop_object_t);
+bool           prop_object_equals_with_error(prop_object_t, prop_object_t, bool *);
+
+typedef struct _prop_object_iterator *prop_object_iterator_t;
+
+prop_object_t  prop_object_iterator_next(prop_object_iterator_t);
+void           prop_object_iterator_reset(prop_object_iterator_t);
+void           prop_object_iterator_release(prop_object_iterator_t);
+__END_DECLS
+
+#endif /* _PROPLIB_PROP_OBJECT_H_ */
diff --git a/nbsd_include/prop/prop_string.h b/nbsd_include/prop/prop_string.h
new file mode 100644 (file)
index 0000000..eb64e87
--- /dev/null
@@ -0,0 +1,60 @@
+/*     $NetBSD: prop_string.h,v 1.3 2008/04/28 20:22:51 martin Exp $   */
+
+/*-
+ * Copyright (c) 2006 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jason R. Thorpe.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _PROPLIB_PROP_STRING_H_
+#define        _PROPLIB_PROP_STRING_H_
+
+#include <prop/prop_object.h>
+
+typedef struct _prop_string *prop_string_t;
+
+__BEGIN_DECLS
+prop_string_t  prop_string_create(void);
+prop_string_t  prop_string_create_cstring(const char *);
+prop_string_t  prop_string_create_cstring_nocopy(const char *);
+
+prop_string_t  prop_string_copy(prop_string_t);
+prop_string_t  prop_string_copy_mutable(prop_string_t);
+
+size_t         prop_string_size(prop_string_t);
+bool           prop_string_mutable(prop_string_t);
+
+char *         prop_string_cstring(prop_string_t);
+const char *   prop_string_cstring_nocopy(prop_string_t);
+
+bool           prop_string_append(prop_string_t, prop_string_t);
+bool           prop_string_append_cstring(prop_string_t, const char *);
+
+bool           prop_string_equals(prop_string_t, prop_string_t);
+bool           prop_string_equals_cstring(prop_string_t, const char *);
+__END_DECLS
+
+#endif /* _PROPLIB_PROP_STRING_H_ */
diff --git a/nbsd_include/prop/proplib.h b/nbsd_include/prop/proplib.h
new file mode 100644 (file)
index 0000000..f136265
--- /dev/null
@@ -0,0 +1,50 @@
+/*     $NetBSD: proplib.h,v 1.7 2009/09/13 18:45:10 pooka Exp $        */
+
+/*-
+ * Copyright (c) 2006 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jason R. Thorpe.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _PROPLIB_PROPLIB_H_
+#define        _PROPLIB_PROPLIB_H_
+
+#include <prop/prop_array.h>
+#include <prop/prop_bool.h>
+#include <prop/prop_data.h>
+#include <prop/prop_dictionary.h>
+#include <prop/prop_number.h>
+#include <prop/prop_string.h>
+
+#include <prop/prop_ingest.h>
+
+#include <prop/plistref.h>
+
+#ifdef _KERNEL
+void   prop_kern_init(void);
+#endif
+
+#endif /* _PROPLIB_PROPLIB_H_ */
index a077535362ff4c4c337239ae0ab37b584e10ff0e..b8e43afae8d35143bf82e3a4a62982cb5232d674 100644 (file)
@@ -1,3 +1,4 @@
+common/lib/libprop      src/common/lib/libprop
 common/lib/libc                src/common/lib/libc
 lib/nbsd_libc          src/lib/libc
 lib/nbsd_libm          src/lib/libm