Index: common/include/prop/Makefile
===================================================================
RCS file: /cvsroot/src/common/include/prop/Makefile,v
retrieving revision 1.2
diff -d -p -u -r1.2 Makefile
--- common/include/prop/Makefile        21 Aug 2006 04:13:28 -0000      1.2
+++ common/include/prop/Makefile        7 Jun 2007 13:26:36 -0000
@@ -1,6 +1,6 @@
#      $NetBSD: Makefile,v 1.2 2006/08/21 04:13:28 thorpej Exp $

-INCS=  prop_array.h prop_bool.h prop_data.h prop_dictionary.h \
+INCS=  prop_array.h prop_bool.h prop_codec.h prop_data.h prop_dictionary.h \
       prop_ingest.h prop_number.h prop_object.h prop_string.h proplib.h

INCSDIR=       /usr/include/prop
Index: common/include/prop/prop_codec.h
===================================================================
RCS file: common/include/prop/prop_codec.h
diff -N common/include/prop/prop_codec.h
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ common/include/prop/prop_codec.h    7 Jun 2007 13:26:36 -0000
@@ -0,0 +1,58 @@
+/*     $NetBSD$ */
+
+/*-
+ * Copyright (c) 2006 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jachym Holecek <[email protected]>
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by the NetBSD
+ *      Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * 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_CODEC_H_
+#define _PROPLIB_PROP_CODEC_H_
+
+typedef const struct _prop_codec       *prop_codec_t;
+typedef void                           *prop_parser_t;
+
+__BEGIN_DECLS
+const char     *prop_codec_list(void);
+prop_codec_t   prop_codec_lookup(const char *);
+
+int            prop_parser_create(prop_codec_t, prop_parser_t *);
+int            prop_parser_exec(prop_codec_t, prop_parser_t, const u_char *,
+                   size_t);
+prop_object_t  prop_parser_yield(prop_codec_t, prop_parser_t);
+void           prop_parser_destroy(prop_codec_t, prop_parser_t);
+
+char           *prop_codec_externalize(prop_codec_t, prop_object_t);
+__END_DECLS
+
+#endif /* _PROPLIB_PROP_CODEC_H_ */
Index: common/include/prop/proplib.h
===================================================================
RCS file: /cvsroot/src/common/include/prop/proplib.h,v
retrieving revision 1.4
diff -d -p -u -r1.4 proplib.h
--- common/include/prop/proplib.h       22 Sep 2006 04:20:23 -0000      1.4
+++ common/include/prop/proplib.h       7 Jun 2007 13:26:36 -0000
@@ -46,6 +46,7 @@
#include <prop/prop_number.h>
#include <prop/prop_string.h>

+#include <prop/prop_codec.h>
#include <prop/prop_ingest.h>

/*
Index: common/lib/libprop/Makefile.inc
===================================================================
RCS file: /cvsroot/src/common/lib/libprop/Makefile.inc,v
retrieving revision 1.5
diff -d -p -u -r1.5 Makefile.inc
--- common/lib/libprop/Makefile.inc     26 Oct 2006 05:02:12 -0000      1.5
+++ common/lib/libprop/Makefile.inc     7 Jun 2007 13:26:39 -0000
@@ -2,8 +2,20 @@

.PATH: ${.PARSEDIR}

-SRCS+= prop_array.c prop_bool.c prop_data.c prop_dictionary.c \
+# User may restrict available internalizer/externalizer codecs.
+PROPLIB_CODECS?=       xml scn
+
+SRCS+= prop_array.c prop_bool.c prop_codec.c prop_data.c prop_dictionary.c \
       prop_dictionary_util.c prop_ingest.c prop_kern.c prop_number.c \
       prop_object.c prop_string.c
-
SRCS+= prop_rb.c
+
+.if "${PROPLIB_CODECS:M*xml*}" != ""
+SRCS+=                 prop_xml.c
+CPPFLAGS+=     -D_PROPLIB_CODEC_XML
+.endif
+
+.if "${PROPLIB_CODECS:M*scn*}" != ""
+SRCS+=                 prop_scn.c
+CPPFLAGS+=     -D_PROPLIB_CODEC_SCN
+.endif
Index: common/lib/libprop/prop_array.c
===================================================================
RCS file: /cvsroot/src/common/lib/libprop/prop_array.c,v
retrieving revision 1.7
diff -d -p -u -r1.7 prop_array.c
--- common/lib/libprop/prop_array.c     3 Oct 2006 15:45:04 -0000       1.7
+++ common/lib/libprop/prop_array.c     7 Jun 2007 13:26:42 -0000
@@ -43,33 +43,16 @@
#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 void            _prop_array_free(void *);
-static boolean_t       _prop_array_externalize(
-                               struct _prop_object_externalize_context *,
-                               void *);
static boolean_t       _prop_array_equals(void *, void *);

static const struct _prop_object_type _prop_object_type_array = {
       .pot_type       =       PROP_TYPE_ARRAY,
       .pot_free       =       _prop_array_free,
-       .pot_extern     =       _prop_array_externalize,
       .pot_equals     =       _prop_array_equals,
};

@@ -111,59 +94,6 @@ _prop_array_free(void *v)
}

static boolean_t
-_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;
-       boolean_t 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(pa);
-       if (pi == NULL)
-               goto out;
-
-       ctx->poec_depth++;
-       _PROP_ASSERT(ctx->poec_depth != 0);
-
-       while ((po = prop_object_iterator_next(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);
-}
-
-static boolean_t
_prop_array_equals(void *v1, void *v2)
{
       prop_array_t array1 = v1;
@@ -648,142 +578,6 @@ prop_array_equals(prop_array_t array1, p
       return (_prop_array_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.
- */
-prop_object_t
-_prop_array_internalize(struct _prop_object_internalize_context *ctx)
-{
-       prop_array_t array;
-       prop_object_t obj;
-
-       /* We don't currently understand any attributes. */
-       if (ctx->poic_tagattr != NULL)
-               return (NULL);
-
-       array = prop_array_create();
-       if (array == NULL)
-               return (NULL);
-
-       if (ctx->poic_is_empty_element)
-               return (array);
-
-       for (;;) {
-               /* 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)
-                       break;
-
-               /* Fetch the object. */
-               obj = _prop_object_internalize_by_tag(ctx);
-               if (obj == NULL)
-                       goto bad;
-
-               if (prop_array_add(array, obj) == FALSE) {
-                       prop_object_release(obj);
-                       goto bad;
-               }
-               prop_object_release(obj);
-       }
-
-       return (array);
-
- bad:
-       prop_object_release(array);
-       return (NULL);
-}
-
-/*
- * prop_array_internalize --
- *     Create an array by parsing the XML-style representation.
- */
-prop_array_t
-prop_array_internalize(const char *xml)
-{
-       prop_array_t array = 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 <array>. */
-       if (_prop_object_internalize_find_tag(ctx, "array",
-                                             _PROP_TAG_TYPE_START) == FALSE)
-               goto out;
-
-       array = _prop_array_internalize(ctx);
-       if (array == NULL)
-               goto out;
-
-       /* We've advanced past </array>.  Now we want </plist>. */
-       if (_prop_object_internalize_find_tag(ctx, "plist",
-                                             _PROP_TAG_TYPE_END) == FALSE) {
-               prop_object_release(array);
-               array = NULL;
-       }
-
- out:
-       _prop_object_internalize_context_free(ctx);
-       return (array);
-}
-
#if !defined(_KERNEL) && !defined(_STANDALONE)
/*
 * prop_array_externalize_to_file --
Index: common/lib/libprop/prop_bool.c
===================================================================
RCS file: /cvsroot/src/common/lib/libprop/prop_bool.c,v
retrieving revision 1.9
diff -d -p -u -r1.9 prop_bool.c
--- common/lib/libprop/prop_bool.c      16 Oct 2006 03:21:07 -0000      1.9
+++ common/lib/libprop/prop_bool.c      7 Jun 2007 13:26:42 -0000
@@ -51,15 +51,11 @@ _PROP_MUTEX_DECL_STATIC(_prop_bool_initi
static boolean_t       _prop_bool_initialized;

static void            _prop_bool_free(void *);
-static boolean_t       _prop_bool_externalize(
-                               struct _prop_object_externalize_context *,
-                               void *);
static boolean_t       _prop_bool_equals(void *, void *);

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,
};

@@ -79,16 +75,6 @@ _prop_bool_free(void *v _PROP_ARG_UNUSED
}

static boolean_t
-_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"));
-}
-
-static boolean_t
_prop_bool_equals(void *v1, void *v2)
{
       prop_bool_t b1 = v1;
@@ -187,28 +173,3 @@ prop_bool_equals(prop_bool_t b1, prop_bo

       return (_prop_bool_equals(b1, b2));
}
-
-/*
- * _prop_bool_internalize --
- *     Parse a <true/> or <false/> and return the object created from
- *     the external representation.
- */
-prop_object_t
-_prop_bool_internalize(struct _prop_object_internalize_context *ctx)
-{
-       boolean_t val;
-
-       /* No attributes, and it must be an empty element. */
-       if (ctx->poic_tagattr != NULL ||
-           ctx->poic_is_empty_element == FALSE)
-               return (NULL);
-
-       if (_PROP_TAG_MATCH(ctx, "true"))
-               val = TRUE;
-       else {
-               _PROP_ASSERT(_PROP_TAG_MATCH(ctx, "false"));
-               val = FALSE;
-       }
-
-       return (prop_bool_create(val));
-}
Index: common/lib/libprop/prop_codec.c
===================================================================
RCS file: common/lib/libprop/prop_codec.c
diff -N common/lib/libprop/prop_codec.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ common/lib/libprop/prop_codec.c     7 Jun 2007 13:26:42 -0000
@@ -0,0 +1,304 @@
+/*     $NetBSD$ */
+
+/*-
+ * Copyright (c) 2007 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jachym Holecek <[email protected]>.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by the NetBSD
+ *      Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * 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 <sys/errno.h>
+
+#include <prop/proplib.h>
+
+#include "prop_object_impl.h"
+#include "prop_codec_impl.h"
+
+
+extern const struct _prop_codec        prop_codec_xml;
+extern const struct _prop_codec        prop_codec_scn;
+
+static prop_codec_t                    prop_codec_table[] = {
+#if defined(_PROPLIB_CODEC_XML)
+       &prop_codec_xml,
+#endif
+#if defined(_PROPLIB_CODEC_SCN)
+       &prop_codec_scn,
+#endif
+       NULL
+};
+
+/* Default to the codec with smallest external representation available. */
+#if !defined(_PROP_DEFAULT_CODEC)
+#if defined(_PROPLIB_CODEC_SCN)
+#define _PROP_DEFAULT_CODEC    "scn"
+#elif defined(_PROPLIB_CODEC_XML)
+#define _PROP_DEFAULT_CODEC    "xml"
+#else
+#define _PROP_DEFAULT_CODEC    ""      /* Runtime failure */
+#endif
+#endif
+
+static const char *
+prop_skip_space(const char *str)
+{
+       if (str == NULL)
+               return (NULL);
+
+       while (*str == ' ' || *str == '\t' || *str == '\n' || *str == '\r')
+               str++;
+
+       return (str);
+}
+
+static prop_codec_t
+prop_codec_guess(char c)
+{
+       int                     i;
+
+       /*
+        * Must be able to tell codec by the first non-white character.
+        * For binary codecs this implies their header must not begin
+        * with whitespace characters.
+        */
+       for (i = 0; prop_codec_table[i]; i++)
+               if (strchr((const char *)prop_codec_table[i]->codec_sense,
+                   c) != NULL)
+                       return (prop_codec_table[i]);
+
+       return (NULL);
+}
+
+static prop_object_t
+prop_parse_single(prop_codec_t co, const char *str, prop_type_t type)
+{
+       prop_parser_t           pa;
+       prop_object_t           po, pq;
+
+       if (prop_parser_create(co, &pa))
+               goto fail_0;
+
+       if (prop_parser_exec(co, pa, (const u_char *)str, strlen(str)))
+               goto fail_1;
+
+       if ((po = prop_parser_yield(co, pa)) == NULL)
+               goto fail_1;
+
+       /* Expect ${str} contains exactly one object. */
+       if ((pq = prop_parser_yield(co, pa)) != NULL) {
+               prop_object_release(pq);
+               goto fail_2;
+       }
+
+       if (prop_object_type(po) != type)
+               goto fail_2;
+
+       prop_parser_destroy(co, pa);
+       return (po);
+
+ fail_2:
+       prop_object_release(po);
+ fail_1:
+       prop_parser_destroy(co, pa);
+ fail_0:
+       return (NULL);
+}
+
+const char *
+prop_codec_list(void)
+{
+       static boolean_t        doinit = TRUE;
+       static char             names[8];       /* XXX Large enough */
+       size_t                  idx = 0;
+       int                     i;
+
+       /* XXX locking? */
+       if (doinit) {
+               for (i = 0; prop_codec_table[i]; i++) {
+                       strcpy(names + idx, prop_codec_table[i]->codec_name);
+                       idx += strlen(prop_codec_table[i]->codec_name);
+
+                       if (prop_codec_table[i + 1])
+                               names[idx++] = ' ';
+               }
+
+               names[idx] = '\0';
+               doinit = FALSE;
+       }
+
+       return (names);
+}
+
+prop_codec_t
+prop_codec_lookup(const char *name)
+{
+       int                     i;
+
+       if (name == NULL)
+               name = _PROP_DEFAULT_CODEC;
+
+       for (i = 0; prop_codec_table[i]; i++)
+               if (strcmp(prop_codec_table[i]->codec_name, name) == 0)
+                       return (prop_codec_table[i]);
+
+       return (NULL);
+}
+
+prop_dictionary_t
+prop_dictionary_internalize(const char *str)
+{
+       prop_codec_t            co;
+
+       if ((str = prop_skip_space(str)) == NULL)
+               return (NULL);
+
+       if ((co = prop_codec_guess(*str)) == NULL)
+               return (NULL);
+
+       if (co->codec_dictionary_internalize)
+               return ((co->codec_dictionary_internalize)(str));
+
+       return (prop_parse_single(co, str, PROP_TYPE_DICTIONARY));
+}
+
+prop_array_t
+prop_array_internalize(const char *str)
+{
+       prop_codec_t            co;
+
+       if ((str = prop_skip_space(str)) == NULL)
+               return (NULL);
+
+       if ((co = prop_codec_guess(*str)) == NULL)
+               return (NULL);
+
+       if (co->codec_array_internalize)
+               return ((co->codec_array_internalize)(str));
+
+       return (prop_parse_single(co, str, PROP_TYPE_ARRAY));
+}
+
+char *
+prop_dictionary_externalize(prop_dictionary_t pd)
+{
+       prop_codec_t            co = prop_codec_lookup(_PROP_DEFAULT_CODEC);
+
+       _PROP_ASSERT(co);
+
+       if (co->codec_dictionary_externalize)
+               return ((co->codec_dictionary_externalize)(pd));
+
+       if (co->codec_externalize_compound)
+               return ((co->codec_externalize_compound)(pd));
+
+       return (NULL);
+}
+
+char *
+prop_array_externalize(prop_array_t pa)
+{
+       prop_codec_t            co = prop_codec_lookup(_PROP_DEFAULT_CODEC);
+
+       _PROP_ASSERT(co);
+
+       if (co->codec_array_externalize)
+               return ((co->codec_array_externalize)(pa));
+
+       if (co->codec_externalize_compound)
+               return ((co->codec_externalize_compound)(pa));
+
+       return (NULL);
+}
+
+char *
+prop_codec_externalize(prop_codec_t co, prop_object_t po)
+{
+       _PROP_ASSERT(co);
+
+       if (co->codec_externalize_compound)
+               return ((co->codec_externalize_compound)(po));
+
+       switch (prop_object_type(po)) {
+       case PROP_TYPE_DICTIONARY:
+               return ((co->codec_dictionary_externalize)(po));
+       case PROP_TYPE_ARRAY:
+               return ((co->codec_array_externalize)(po));
+       default:
+               return (NULL);
+       }
+}
+
+int
+prop_parser_create(prop_codec_t co, prop_parser_t *pp)
+{
+       _PROP_ASSERT(co);
+
+       if (pp == NULL)
+               return (EINVAL);
+
+       if (co->codec_parser_create)
+               return ((co->codec_parser_create)(pp));
+
+       return (EOPNOTSUPP);
+}
+
+int
+prop_parser_exec(prop_codec_t co, prop_parser_t pa, const u_char *str,
+    size_t len)
+{
+       _PROP_ASSERT(co);
+
+       if (co->codec_parser_exec)
+               return ((co->codec_parser_exec)(pa, str, len));
+
+       return (EOPNOTSUPP);
+}
+
+prop_object_t
+prop_parser_yield(prop_codec_t co, prop_parser_t pa)
+{
+       _PROP_ASSERT(co);
+
+       if (co->codec_parser_yield)
+               return ((co->codec_parser_yield)(pa));
+
+       return (NULL);
+}
+
+void
+prop_parser_destroy(prop_codec_t co, prop_parser_t pa)
+{
+       _PROP_ASSERT(co);
+
+       if (co->codec_parser_destroy)
+               (co->codec_parser_destroy)(pa);
+}
Index: common/lib/libprop/prop_codec_impl.h
===================================================================
RCS file: common/lib/libprop/prop_codec_impl.h
diff -N common/lib/libprop/prop_codec_impl.h
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ common/lib/libprop/prop_codec_impl.h        7 Jun 2007 13:26:44 -0000
@@ -0,0 +1,61 @@
+/*     $NetBSD: prop_object_impl.h,v 1.12 2007/03/12 18:18:39 ad Exp $ */
+
+/*-
+ * Copyright (c) 2007 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jachym Holecek <[email protected]>
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by the NetBSD
+ *      Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * 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_CODEC_IMPL_H_
+#define _PROPLIB_PROP_CODEC_IMPL_H_
+
+struct _prop_codec {
+       const char      *codec_name;
+       const u_char    *codec_sense;
+
+       /* Either we provide direct backends for public functions. */
+       char *          (*codec_dictionary_externalize)(prop_dictionary_t);
+       char *          (*codec_array_externalize)(prop_array_t);
+       prop_dictionary_t (*codec_dictionary_internalize)(const char *);
+       prop_array_t    (*codec_array_internalize)(const char *);
+
+       /* Or we provide "stream-friendly" API and emulate the above. */
+       char *          (*codec_externalize_compound)(prop_object_t);
+       int             (*codec_parser_create)(prop_parser_t *);
+       int             (*codec_parser_exec)(prop_parser_t, const u_char *,
+                           size_t);
+       prop_object_t   (*codec_parser_yield)(prop_parser_t);
+       void            (*codec_parser_destroy)(prop_parser_t);
+};
+
+#endif /* _PROPLIB_PROP_CODEC_IMPL_H_ */
Index: common/lib/libprop/prop_data.c
===================================================================
RCS file: /cvsroot/src/common/lib/libprop/prop_data.c,v
retrieving revision 1.6
diff -d -p -u -r1.6 prop_data.c
--- common/lib/libprop/prop_data.c      4 Mar 2007 22:31:43 -0000       1.6
+++ common/lib/libprop/prop_data.c      7 Jun 2007 13:26:44 -0000
@@ -50,35 +50,17 @@
#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 void            _prop_data_free(void *);
-static boolean_t       _prop_data_externalize(
-                               struct _prop_object_externalize_context *,
-                               void *);
static boolean_t       _prop_data_equals(void *, void *);

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,
};

@@ -95,85 +77,6 @@ _prop_data_free(void *v)
       _PROP_POOL_PUT(_prop_data_pool, v);
}

-static const char _prop_data_base64[] =
-    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-static const char _prop_data_pad64 = '=';
-
-static boolean_t
-_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] = ((u_int32_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] = ((u_int32_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);
-}
-
static boolean_t
_prop_data_equals(void *v1, void *v2)
{
@@ -197,7 +100,7 @@ _prop_data_equals(void *v1, void *v2)
                      pd1->pd_size) == 0);
}

-static prop_data_t
+prop_data_t
_prop_data_alloc(void)
{
       prop_data_t pd;
@@ -376,230 +279,3 @@ prop_data_equals_data(prop_data_t pd, co
               return (FALSE);
       return (memcmp(pd->pd_immutable, v, size) == 0);
}
-
-static boolean_t
-_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.
- */
-prop_object_t
-_prop_data_internalize(struct _prop_object_internalize_context *ctx)
-{
-       prop_data_t data;
-       uint8_t *buf;
-       size_t len, alen;
-
-       /* We don't accept empty elements. */
-       if (ctx->poic_is_empty_element)
-               return (NULL);
-
-       /*
-        * 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 (NULL);
-
-#ifndef _KERNEL
-               errno = 0;
-#endif
-               /* XXX Assumes size_t and unsigned long are the same size. */
-               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 (NULL);
-#endif
-               if (cp != ctx->poic_tagattrval + ctx->poic_tagattrval_len)
-                       return (NULL);
-               _PROP_ASSERT(*cp == '\"');
-       } else if (_prop_data_internalize_decode(ctx, NULL, 0, &len,
-                                               NULL) == FALSE)
-               return (NULL);
-
-       /*
-        * 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 (NULL);
-
-       if (_prop_data_internalize_decode(ctx, buf, len + 1, &alen,
-                                         &ctx->poic_cp) == FALSE) {
-               _PROP_FREE(buf, M_PROP_DATA);
-               return (NULL);
-       }
-       if (alen != len) {
-               _PROP_FREE(buf, M_PROP_DATA);
-               return (NULL);
-       }
-
-       if (_prop_object_internalize_find_tag(ctx, "data",
-                                             _PROP_TAG_TYPE_END) == FALSE) {
-               _PROP_FREE(buf, M_PROP_DATA);
-               return (NULL);
-       }
-
-       data = _prop_data_alloc();
-       if (data == NULL) {
-               _PROP_FREE(buf, M_PROP_DATA);
-               return (NULL);
-       }
-
-       data->pd_mutable = buf;
-       data->pd_size = len;
-
-       return (data);
-}
Index: common/lib/libprop/prop_dictionary.c
===================================================================
RCS file: /cvsroot/src/common/lib/libprop/prop_dictionary.c,v
retrieving revision 1.16
diff -d -p -u -r1.16 prop_dictionary.c
--- common/lib/libprop/prop_dictionary.c        26 Oct 2006 05:02:12 -0000      1.16
+++ common/lib/libprop/prop_dictionary.c        7 Jun 2007 13:26:49 -0000
@@ -82,7 +82,10 @@ struct _prop_dictionary_keysym {
#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_pool, sizeof(struct _prop_dictionary),
+               "propdict")
+_PROP_MALLOC_DEFINE(M_PROP_DICT, "prop dictionary",
+                   "property dictionary container object")

_PROP_POOL_INIT(_prop_dictionary_keysym16_pool, PDK_SIZE_16, "pdict16")
_PROP_POOL_INIT(_prop_dictionary_keysym32_pool, PDK_SIZE_32, "pdict32")
@@ -93,47 +96,21 @@ struct _prop_dict_entry {
       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 void            _prop_dictionary_free(void *);
-static boolean_t       _prop_dictionary_externalize(
-                               struct _prop_object_externalize_context *,
-                               void *);
static boolean_t       _prop_dictionary_equals(void *, void *);

static const struct _prop_object_type _prop_object_type_dictionary = {
       .pot_type       =       PROP_TYPE_DICTIONARY,
       .pot_free       =       _prop_dictionary_free,
-       .pot_extern     =       _prop_dictionary_externalize,
       .pot_equals     =       _prop_dictionary_equals,
};

static void            _prop_dict_keysym_free(void *);
-static boolean_t       _prop_dict_keysym_externalize(
-                               struct _prop_object_externalize_context *,
-                               void *);
static boolean_t       _prop_dict_keysym_equals(void *, void *);

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,
};

@@ -213,25 +190,6 @@ _prop_dict_keysym_free(void *v)
}

static boolean_t
-_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);
-}
-
-static boolean_t
_prop_dict_keysym_equals(void *v1, void *v2)
{
       prop_dictionary_keysym_t pdk1 = v1;
@@ -346,65 +304,6 @@ _prop_dictionary_free(void *v)
}

static boolean_t
-_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;
-       boolean_t 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(pd);
-       if (pi == NULL)
-               goto out;
-
-       ctx->poec_depth++;
-       _PROP_ASSERT(ctx->poec_depth != 0);
-
-       while ((pdk = prop_object_iterator_next(pi)) != NULL) {
-               po = prop_dictionary_get_keysym(pd, pdk);
-               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);
-}
-
-static boolean_t
_prop_dictionary_equals(void *v1, void *v2)
{
       prop_dictionary_t dict1 = v1;
@@ -1029,174 +928,6 @@ prop_dictionary_keysym_equals(prop_dicti
       return (_prop_dict_keysym_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.
- */
-prop_object_t
-_prop_dictionary_internalize(struct _prop_object_internalize_context *ctx)
-{
-       prop_dictionary_t dict;
-       prop_object_t val;
-       size_t keylen;
-       char *tmpkey;
-
-       /* We don't currently understand any attributes. */
-       if (ctx->poic_tagattr != NULL)
-               return (NULL);
-
-       dict = prop_dictionary_create();
-       if (dict == NULL)
-               return (NULL);
-
-       if (ctx->poic_is_empty_element)
-               return (dict);
-
-       tmpkey = _PROP_MALLOC(PDK_MAXKEY + 1, M_TEMP);
-       if (tmpkey == NULL)
-               goto bad;
-
-       for (;;) {
-               /* 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)
-                       break;
-
-               /* 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;
-
-               val = _prop_object_internalize_by_tag(ctx);
-               if (val == NULL)
-                       goto bad;
-
-               if (prop_dictionary_set(dict, tmpkey, val) == FALSE) {
-                       prop_object_release(val);
-                       goto bad;
-               }
-               prop_object_release(val);
-       }
-
-       _PROP_FREE(tmpkey, M_TEMP);
-       return (dict);
-
- bad:
-       if (tmpkey != NULL)
-               _PROP_FREE(tmpkey, M_TEMP);
-       prop_object_release(dict);
-       return (NULL);
-}
-
-/*
- * prop_dictionary_internalize --
- *     Create a dictionary by parsing the NUL-terminated XML-style
- *     representation.
- */
-prop_dictionary_t
-prop_dictionary_internalize(const char *xml)
-{
-       prop_dictionary_t dict = 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 <dict>. */
-       if (_prop_object_internalize_find_tag(ctx, "dict",
-                                             _PROP_TAG_TYPE_START) == FALSE)
-               goto out;
-
-       dict = _prop_dictionary_internalize(ctx);
-       if (dict == NULL)
-               goto out;
-
-       /* We've advanced past </dict>.  Now we want </plist>. */
-       if (_prop_object_internalize_find_tag(ctx, "plist",
-                                             _PROP_TAG_TYPE_END) == FALSE) {
-               prop_object_release(dict);
-               dict = NULL;
-       }
-
- out:
-       _prop_object_internalize_context_free(ctx);
-       return (dict);
-}
-
#if !defined(_KERNEL) && !defined(_STANDALONE)
/*
 * prop_dictionary_externalize_to_file --
Index: common/lib/libprop/prop_number.c
===================================================================
RCS file: /cvsroot/src/common/lib/libprop/prop_number.c,v
retrieving revision 1.11
diff -d -p -u -r1.11 prop_number.c
--- common/lib/libprop/prop_number.c    15 Oct 2006 19:11:58 -0000      1.11
+++ common/lib/libprop/prop_number.c    7 Jun 2007 13:26:52 -0000
@@ -50,19 +50,11 @@
#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;
+       struct _prop_object             pn_obj;
+       struct rb_node                  pn_link;
+       struct _prop_number_value       pn_value;
};

#define        RBNODE_TO_PN(n)                                                 \
@@ -72,15 +64,11 @@ struct _prop_number {
_PROP_POOL_INIT(_prop_number_pool, sizeof(struct _prop_number), "propnmbr")

static void            _prop_number_free(void *);
-static boolean_t       _prop_number_externalize(
-                               struct _prop_object_externalize_context *,
-                               void *);
static boolean_t       _prop_number_equals(void *, 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,
};

@@ -162,30 +150,6 @@ _prop_number_free(void *v)
}

static boolean_t
-_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);
-}
-
-static boolean_t
_prop_number_equals(void *v1, void *v2)
{
       prop_number_t num1 = v1;
@@ -239,7 +203,7 @@ _prop_number_equals(void *v1, void *v2)
       return (num1->pn_value.pnv_signed == num2->pn_value.pnv_signed);
}

-static prop_number_t
+prop_number_t
_prop_number_alloc(const struct _prop_number_value *pnv)
{
       prop_number_t opn, pn;
@@ -476,91 +440,3 @@ prop_number_equals_unsigned_integer(prop

       return (pn->pn_value.pnv_unsigned == val);
}
-
-static boolean_t
-_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 boolean_t
-_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.
- */
-prop_object_t
-_prop_number_internalize(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 (NULL);
-
-       /*
-        * 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 (NULL);
-       } else if (ctx->poic_cp[0] == '0' && ctx->poic_cp[1] == 'x') {
-               if (_prop_number_internalize_unsigned(ctx, &pnv) == FALSE)
-                       return (NULL);
-       } else {
-               if (_prop_number_internalize_signed(ctx, &pnv) == FALSE &&
-                   _prop_number_internalize_unsigned(ctx, &pnv) == FALSE)
-                       return (NULL);
-       }
-
-       if (_prop_object_internalize_find_tag(ctx, "integer",
-                                             _PROP_TAG_TYPE_END) == FALSE)
-               return (NULL);
-
-       return (_prop_number_alloc(&pnv));
-}
Index: common/lib/libprop/prop_object.c
===================================================================
RCS file: /cvsroot/src/common/lib/libprop/prop_object.c,v
retrieving revision 1.12
diff -d -p -u -r1.12 prop_object.c
--- common/lib/libprop/prop_object.c    19 Oct 2006 10:10:35 -0000      1.12
+++ common/lib/libprop/prop_object.c    7 Jun 2007 13:26:52 -0000
@@ -102,130 +102,28 @@ _prop_object_fini(struct _prop_object *p
}

/*
- * _prop_object_externalize_start_tag --
- *     Append an XML-style start tag to the externalize buffer.
- */
-boolean_t
-_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.
- */
-boolean_t
-_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.
- */
-boolean_t
-_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.
- */
-boolean_t
-_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.
+ * _prop_object_externalize_context_alloc --
+ *     Allocate an externalize context.
 */
-boolean_t
-_prop_object_externalize_append_encoded_cstring(
-    struct _prop_object_externalize_context *ctx, const char *cp)
+struct _prop_object_externalize_context *
+_prop_object_externalize_context_alloc(void)
{
+       struct _prop_object_externalize_context *ctx;

-       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;
+       ctx = _PROP_MALLOC(sizeof(*ctx), M_TEMP);
+       if (ctx != NULL) {
+               ctx->poec_buf = _PROP_MALLOC(_PROP_BUF_EXPAND, M_TEMP);
+               if (ctx->poec_buf == NULL) {
+                       _PROP_FREE(ctx, M_TEMP);
+                       return (NULL);
               }
-               cp++;
+               ctx->poec_len = 0;
+               ctx->poec_capacity = _PROP_BUF_EXPAND;
+               ctx->poec_depth = 0;
       }
-
-       return (TRUE);
+       return (ctx);
}

-#define        BUF_EXPAND              256
-
/*
 * _prop_object_externalize_append_char --
 *     Append a single character to the externalize buffer.
@@ -241,11 +139,11 @@ _prop_object_externalize_append_char(

       if (ctx->poec_len == ctx->poec_capacity) {
               char *cp = _PROP_REALLOC(ctx->poec_buf,
-                                        ctx->poec_capacity + BUF_EXPAND,
+                                        ctx->poec_capacity + _PROP_BUF_EXPAND,
                                        M_TEMP);
               if (cp == NULL)
                       return (FALSE);
-               ctx->poec_capacity = ctx->poec_capacity + BUF_EXPAND;
+               ctx->poec_capacity = ctx->poec_capacity + _PROP_BUF_EXPAND;
               ctx->poec_buf = cp;
       }

@@ -255,66 +153,25 @@ _prop_object_externalize_append_char(
}

/*
- * _prop_object_externalize_header --
- *     Append the standard XML header to the externalize buffer.
- */
-boolean_t
-_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.
+ * _prop_object_externalize_append_cstring --
+ *     Append a C string to the externalize buffer.
 */
boolean_t
-_prop_object_externalize_footer(struct _prop_object_externalize_context *ctx)
+_prop_object_externalize_append_cstring(
+    struct _prop_object_externalize_context *ctx, const char *cp)
{

-       if (_prop_object_externalize_end_tag(ctx, "plist") == FALSE ||
-           _prop_object_externalize_append_char(ctx, '\0') == FALSE)
-               return (FALSE);
+       while (*cp != '\0') {
+               if (_prop_object_externalize_append_char(ctx,
+                                               (unsigned char) *cp) == FALSE)
+                       return (FALSE);
+               cp++;
+       }

       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.
 */
@@ -327,388 +184,6 @@ _prop_object_externalize_context_free(
       _PROP_FREE(ctx, M_TEMP);
}

-/*
- * _prop_object_internalize_skip_comment --
- *     Skip the body and end tag of a comment.
- */
-static boolean_t
-_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.
- */
-boolean_t
-_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.
- */
-boolean_t
-_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.
- */
-boolean_t
-_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_t   (*poi_intern)(
-                               struct _prop_object_internalize_context *);
-} _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;
-
-       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))
-                       return ((*poi->poi_intern)(ctx));
-       }
-
-       return (NULL);
-}
-
-/*
- * _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 --
Index: common/lib/libprop/prop_object_impl.h
===================================================================
RCS file: /cvsroot/src/common/lib/libprop/prop_object_impl.h,v
retrieving revision 1.12
diff -d -p -u -r1.12 prop_object_impl.h
--- common/lib/libprop/prop_object_impl.h       12 Mar 2007 18:18:39 -0000      1.12
+++ common/lib/libprop/prop_object_impl.h       7 Jun 2007 13:26:54 -0000
@@ -45,6 +45,13 @@
#include <inttypes.h>
#endif

+#include "prop_system_impl.h"
+
+_PROP_MALLOC_DECLARE(M_PROP_ARRAY)
+_PROP_MALLOC_DECLARE(M_PROP_DATA)
+_PROP_MALLOC_DECLARE(M_PROP_DICT)
+_PROP_MALLOC_DECLARE(M_PROP_STRING)
+
struct _prop_object_externalize_context {
       char *          poec_buf;               /* string buffer */
       size_t          poec_capacity;          /* capacity of buffer */
@@ -52,92 +59,18 @@ struct _prop_object_externalize_context
       unsigned int    poec_depth;             /* nesting depth */
};

-boolean_t      _prop_object_externalize_start_tag(
-                               struct _prop_object_externalize_context *,
-                               const char *);
-boolean_t      _prop_object_externalize_end_tag(
-                               struct _prop_object_externalize_context *,
-                               const char *);
-boolean_t      _prop_object_externalize_empty_tag(
-                               struct _prop_object_externalize_context *,
-                               const char *);
boolean_t      _prop_object_externalize_append_cstring(
                               struct _prop_object_externalize_context *,
                               const char *);
-boolean_t      _prop_object_externalize_append_encoded_cstring(
-                               struct _prop_object_externalize_context *,
-                               const char *);
boolean_t      _prop_object_externalize_append_char(
                               struct _prop_object_externalize_context *,
                               unsigned char);
-boolean_t      _prop_object_externalize_header(
-                               struct _prop_object_externalize_context *);
-boolean_t      _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;
-
-       boolean_t   poic_is_empty_element;
-       _prop_tag_type_t poic_tag_type;
-};
-
-#define        _PROP_EOF(c)            ((c) == '\0')
-#define        _PROP_ISSPACE(c)        \
-       ((c) == ' ' || (c) == '\t' || (c) == '\n' || _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))
-
-boolean_t      _prop_object_internalize_find_tag(
-                               struct _prop_object_internalize_context *,
-                               const char *, _prop_tag_type_t);
-boolean_t      _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 *);
-boolean_t      _prop_object_internalize_decode_string(
-                               struct _prop_object_internalize_context *,
-                               char *, size_t, size_t *, 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)
boolean_t      _prop_object_externalize_write_file(const char *,
                                                   const char *, size_t);
@@ -153,26 +86,9 @@ void                _prop_object_internalize_unmap_fil
                               struct _prop_object_internalize_mapped_file *);
#endif /* !_KERNEL && !_STANDALONE */

-       /* These are here because they're required by shared code. */
-prop_object_t  _prop_array_internalize(
-                               struct _prop_object_internalize_context *);
-prop_object_t  _prop_bool_internalize(
-                               struct _prop_object_internalize_context *);
-prop_object_t  _prop_data_internalize(
-                               struct _prop_object_internalize_context *);
-prop_object_t  _prop_dictionary_internalize(
-                               struct _prop_object_internalize_context *);
-prop_object_t  _prop_number_internalize(
-                               struct _prop_object_internalize_context *);
-prop_object_t  _prop_string_internalize(
-                               struct _prop_object_internalize_context *);
-
struct _prop_object_type {
       uint32_t        pot_type;               /* type indicator */
       void            (*pot_free)(void *);    /* func to free object */
-       boolean_t       (*pot_extern)           /* func to externalize object */
-                           (struct _prop_object_externalize_context *,
-                            void *);
       boolean_t       (*pot_equals)           /* func to test quality */
                           (void *, void *);
};
@@ -193,166 +109,81 @@ struct _prop_object_iterator {
       uint32_t        pi_version;
};

-#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/lock.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))
-
-#define        _PROP_POOL_INIT(p, s, d)                                        \
-               POOL_INIT(p, s, 0, 0, 0, d, &pool_allocator_nointr, IPL_NONE);
-
-#define        _PROP_MALLOC_DEFINE(t, s, l)                                    \
-               MALLOC_DEFINE(t, s, l);
-
-#define        _PROP_MUTEX_DECL_STATIC(x)                                      \
-               static struct simplelock x = SIMPLELOCK_INITIALIZER;
-#define        _PROP_MUTEX_LOCK(x)     simple_lock(&(x))
-#define        _PROP_MUTEX_UNLOCK(x)   simple_unlock(&(x))
-
-#define        _PROP_RWLOCK_DECL(x)    struct lock x ;
-#define        _PROP_RWLOCK_INIT(x)    lockinit(&(x), PZERO, "proprwlk", 0, 0)
-#define        _PROP_RWLOCK_RDLOCK(x)  lockmgr(&(x), LK_SHARED, NULL)
-#define        _PROP_RWLOCK_WRLOCK(x)  lockmgr(&(x), LK_EXCLUSIVE, NULL)
-#define        _PROP_RWLOCK_UNLOCK(x)  lockmgr(&(x), LK_RELEASE, NULL)
-#define        _PROP_RWLOCK_DESTROY(x) lockmgr(&(x), LK_DRAIN, NULL)
-
-#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_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 */
-
+#ifdef _STANDALONE
+#define        _PROP_BUF_EXPAND        32
#else
+#define        _PROP_BUF_EXPAND        256
+#endif

-/*
- * proplib in user space...
- */
+#define        _PROP_PDK_MAXKEY        128

-#include <assert.h>
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <stddef.h>
+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;

-#define        _PROP_ASSERT(x)         /*LINTED*/assert(x)
+       uint32_t                pa_version;
+};

-#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        PA_F_IMMUTABLE          0x01    /* array is immutable */

-#define        _PROP_POOL_GET(p)       malloc((p))
-#define        _PROP_POOL_PUT(p, v)    free((v))
+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;

-#define        _PROP_POOL_INIT(p, s, d)        static const size_t p = s;
+       uint32_t                pd_version;
+};

-#define        _PROP_MALLOC_DEFINE(t, s, l)    /* nothing */
+#define        PD_F_IMMUTABLE          0x01    /* dictionary is immutable */

-#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 = MUTEX_INITIALIZER;
-#define        _PROP_MUTEX_LOCK(x)             mutex_lock(&(x))
-#define        _PROP_MUTEX_UNLOCK(x)           mutex_unlock(&(x))
+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        _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))
-#elif defined(HAVE_NBTOOL_CONFIG_H)
-/*
- * None of NetBSD's build tools are multi-threaded.
- */
-#define        _PROP_MUTEX_DECL_STATIC(x)      /* nothing */
-#define        _PROP_MUTEX_LOCK(x)             /* nothing */
-#define        _PROP_MUTEX_UNLOCK(x)           /* nothing */
+#define        PD_F_NOCOPY             0x01

-#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 */
-#else
-/*
- * Use pthread mutexes everywhere else.
- */
-#include <pthread.h>
-#define        _PROP_MUTEX_DECL_STATIC(x)                                      \
-               static pthread_mutex_t x = PTHREAD_MUTEX_INITIALIZER;
-#define        _PROP_MUTEX_LOCK(x)     pthread_mutex_lock(&(x))
-#define        _PROP_MUTEX_UNLOCK(x)   pthread_mutex_unlock(&(x))
+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;
+};

-#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))
-#endif
+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;
+};

-#endif /* _KERNEL */
+#define        PS_F_NOCOPY             0x01

-/*
- * Language features.
- */
-#if defined(__NetBSD__)
-#include <sys/cdefs.h>
-#define        _PROP_ARG_UNUSED        __unused
-#else
-#define        _PROP_ARG_UNUSED        /* delete */
-#endif /* __NetBSD__ */
+struct _prop_data      *_prop_data_alloc(void);
+struct _prop_number    *_prop_number_alloc(const struct _prop_number_value *);
+struct _prop_string    *_prop_string_alloc(void);

#endif /* _PROPLIB_PROP_OBJECT_IMPL_H_ */
Index: common/lib/libprop/prop_scn.c
===================================================================
RCS file: common/lib/libprop/prop_scn.c
diff -N common/lib/libprop/prop_scn.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ common/lib/libprop/prop_scn.c       7 Jun 2007 13:27:02 -0000
@@ -0,0 +1,1672 @@
+/*     $NetBSD$ */
+
+/*-
+ * Copyright (c) 2007 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jachym Holecek <[email protected]>.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by the NetBSD
+ *      Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * 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 <sys/queue.h>
+#include <sys/param.h>                 /* roundup() */
+
+#if defined(_KERNEL)
+#include <sys/systm.h>
+#elif defined(_STANDALONE)
+#include <lib/libkern/libkern.h>
+#else
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#if defined(DEBUG)
+#include <stdarg.h>
+#include <stdio.h>
+#endif
+#include <stdlib.h>
+#endif
+
+#include <prop/proplib.h>
+#include "prop_object_impl.h"
+#include "prop_codec_impl.h"
+
+
+#if defined(_STANDALONE)
+#define BUFINCR                16
+#else
+#define BUFINCR                128
+#endif
+
+#if !defined(__UNCONST)
+#define __UNCONST(a)           ((void *)(unsigned long)(const void *)(a))
+#endif
+
+/*
+ * This needs to go via a macro -- even if verbose()'s body would be empty
+ * ifndef DEBUG, GCC still isn't smart enough to eliminate calls to funcs
+ * that only exist if DEBUG. And we don't want to clutter the code with
+ * preprocessor conditionals.
+ */
+#if defined(DEBUG)
+#define VERBOSE(s, ...)        verbose(s "\n", ## __VA_ARGS__)
+#else
+#define VERBOSE(s, ...)        /* silence */
+#endif
+
+/* Ewww, depolymerise function names. */
+#define        poec                            _prop_object_externalize_context
+#define poec_create                    _prop_object_externalize_context_alloc
+#define poec_append                    _prop_object_externalize_append_cstring
+#define poec_push                      _prop_object_externalize_append_char
+#define poec_destroy                   _prop_object_externalize_context_free
+#define        prop_keysym_str                 prop_dictionary_keysym_cstring_nocopy
+
+#define _TK_COMPOUND_VALUES            \
+                       TK_ARRAYO:      \
+       case            TK_DICTO
+
+typedef enum {
+       /* Self representing. */
+       TK_FIRST                = 0,    /* So that memset(0) DTRT. */
+       TK_ARRAYC,
+       TK_ARRAYO,
+       TK_DATA,
+       TK_DICTC,
+       TK_DICTO,
+       TK_WHITE,
+       TK_LAST,
+
+       /* Values. */
+       TK_STRING,
+       TK_SINT64,
+       TK_SYMBOL,
+       TK_UINT64
+} token_type_t;
+
+#define _PA_TOKEN_LAST                 TK_UINT64
+#define _PA_TOKEN_NAMES        \
+       "FIRST", "ARRAYC", "ARRAYO", "DATA", "DICTC", "DICTO", "WHITE", \
+       "LAST", "STRING", "SINT64", "SYMBOL", "UINT64",
+
+typedef enum {
+       SC_FIRST                = 0,
+       SC_WHITE,
+       SC_SINT64,
+       SC_UINT64,
+       SC_QUOTED,
+       SC_SYMBOL,
+       SC_TOPLEVEL,
+       SC_ERROR,
+       SC_BASE64_APPEND,
+       SC_BASE64_MAYBE_NEXT,
+       SC_BASE64_NEXT,
+       SC_BASE64_CLOSE,
+       SC_STRING_APPEND,
+       SC_STRING_MAYBE_NEXT,
+       SC_STRING_CONCAT,
+       SC_STRING_CLOSE,
+       SC_COMMENT
+} scan_state_t;
+
+#define _SC_STATE_LAST                 SC_COMMENT
+#define _SC_STATE_NAMES        \
+       "FIRST", "WHITE", "SINT64", "UINT64", "QUOTED", "SYMBOL", "TOPLEVEL", \
+       "ERROR", "BASE64_APPEND", "BASE64_MAYBE_NEXT", "BASE64_NEXT", \
+       "SC_BASE64_CLOSE", "STRING_APPEND", "STRING_MAYBE_NEXT", \
+       "STRING_CONCAT", "STRING_CLOSE", "COMMENT"
+
+typedef enum {
+       PA_TOPLEVEL             = 0,
+       PA_ARRAY,
+       PA_DICTIONARY,
+       PA_ERROR,
+       PA_HOME,
+       PA_OBJECT
+} parse_state_t;
+
+#define _PA_STATE_LAST                 PA_OBJECT
+#define _PA_STATE_NAMES        \
+       "TOPLEVEL", "ARRAY", "DICTIONARY", "ERROR", "HOME", "OBJECT"
+
+union value {
+        char                   *val_string;
+       uint64_t                val_unsigned;
+       int64_t                 val_signed;
+       struct {
+               const u_char    *vd_buf;
+               size_t          vd_len;
+       }                       val_data;
+};
+
+/* Scanner constructs tokens and enqueues them to parser. */
+struct token {
+       SIMPLEQ_ENTRY(token)    tok_link;
+       token_type_t            tok_type;
+
+#if !defined(_STANDALONE)
+       size_t                  tok_pos_line;
+       size_t                  tok_pos_col;
+#endif
+       union value             tok_value;
+#define        tok_string              tok_value.val_string
+#define        tok_signed              tok_value.val_signed
+#define        tok_unsigned            tok_value.val_unsigned
+#define tok_data_buf           tok_value.val_data.vd_buf
+#define tok_data_len           tok_value.val_data.vd_len
+};
+
+/* Stack for nested objects, entries relinked to consq on completion. */
+struct frame {
+       SIMPLEQ_ENTRY(frame)    se_link;        /* Stack entry link. */
+       prop_object_t           se_object;      /* Compound object. */
+
+       /* Dec: key to parent dict, Enc: next object from parent. */
+       union {
+               const char              *un_symbol;
+               prop_object_iterator_t  un_iter;
+       }                       se_un;
+#define se_symbol              se_un.un_symbol
+#define se_iter                se_un.un_iter
+};
+
+SIMPLEQ_HEAD(stack, frame);
+
+struct parser {
+       /* Incoming tokens, operation stack, list of constructed objects. */
+       SIMPLEQ_HEAD(, token)   pa_tokens;      /* FIFO */
+       struct stack            pa_stack;       /* LIFO */
+       struct stack            pa_consq;       /* FIFO */
+
+       /* Store parser state across calls. */
+       parse_state_t           pa_state;
+       parse_state_t           pa_prev;
+       parse_state_t           pa_last;
+
+       /* Store scanner state across calls. */
+       scan_state_t            sc_state; /* Current state. */
+       scan_state_t            sc_prev;  /* State at start of this cycle. */
+       scan_state_t            sc_last;  /* Last value of prev != state. */
+
+#if !defined(_STANDALONE)
+       /* Position in input stream. */
+       size_t                  sc_pos_line;
+       size_t                  sc_pos_col;
+#endif
+       /* Scanner internalization buffer. */
+       u_char                  *sc_string;
+       size_t                  sc_strcur;
+       size_t                  sc_strlen;
+
+       /* Base64 decoder. */
+       size_t                  sc_base64_size;         /* in bits */
+};
+
+/* Base64 code space (plus '='). */
+static const char base64abc[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopq"
+    "rstuvwxyz0123456789+/";
+
+
+
+#if defined(_KERNEL) || defined(_STANDALONE)
+static char *
+strdup(const char *s)
+{
+       char                    *p;
+       size_t                  l;
+
+       l = strlen(s) + 1;
+       p = _PROP_MALLOC(l, M_TEMP);
+
+       return (memcpy(p, s, l));
+}
+
+static int
+isprint(int c)
+{
+       return (c >= ' ' && c <= '~');
+}
+#endif /* _KERNEL || _STANDALONE */
+
+static boolean_t
+stroneof(const char *s, const char *t[])
+{
+       int                     i;
+
+       for (i = 0; t[i]; i++)
+               if (strcasecmp(s, t[i]) == 0)
+                       return (TRUE);
+       return (FALSE);
+}
+
+#if defined(DEBUG)
+
+static void
+verbose(const char *fmt, ...)
+{
+       static FILE             *tracefile = NULL;
+       char                    *s;
+       va_list                 ap;
+
+       /* XXX locking */
+       if (tracefile == NULL) {
+               s = getenv("PROPLIB_SCN_TRACEFILE");
+               if (s == NULL)
+                       s = "__prop_scn.out";
+
+               tracefile = fopen(s, "w");
+               if (tracefile == NULL)
+                       abort();
+       }
+
+       va_start(ap, fmt);
+       vfprintf(tracefile, fmt, ap);
+       va_end(ap);
+}
+
+static const char *
+scanner_quote_char(u_char c)
+{
+       static char             buf[8];         /* 'c', '\n', 0xab */
+       u_char                  d = 0;
+
+       switch (c) {
+       case '\f':      d = 'f';        break;
+       case '\n':      d = 'n';        break;
+       case '\r':      d = 'r';        break;
+       case '\t':      d = 't';        break;
+       case '\v':      d = 'v';        break;
+       }
+       if (d) {
+               sprintf(buf, "'\\%c'", d);
+               return (buf);
+       }
+
+       if (isprint(c))
+               sprintf(buf, "'%c'", (char)c);
+       else
+               sprintf(buf, "0x%02x", (u_int)c);
+       return (buf);
+}
+
+static const char *
+scanner_state_name(scan_state_t n)
+{
+       static const char *const        names[] = { _SC_STATE_NAMES };
+       static int                      idx = 0;        /* XXX hack, printf */
+       static char                     buf[5][32];
+
+       if (n < 0 || n > _SC_STATE_LAST) {
+               if (++idx == 5)
+                       idx = 0;
+
+               snprintf(buf[idx], 32, "<wrong %d>", n);
+               return (buf[idx]);
+       }
+
+       return (names[n]);
+}
+
+static const char *
+parser_state_name(parse_state_t n)
+{
+       static const char *const        names[] = { _PA_STATE_NAMES };
+       static int                      idx = 0;        /* XXX hack, printf */
+       static char                     buf[5][32];
+
+       if (++idx == 5)
+               idx = 0;
+
+       if (n < 0 || n > _PA_STATE_LAST) {
+               snprintf(buf[idx], 32, "<wrong %d>", n);
+               return (buf[idx]);
+       }
+
+       return (names[n]);
+}
+
+static const char *
+parser_token_name(token_type_t n)
+{
+       static const char *const        names[] = { _PA_TOKEN_NAMES };
+
+       if (n < 0 || n > _PA_TOKEN_LAST)
+               return ("<wrong token>");
+       return (names[n]);
+}
+
+static const char *
+parser_token_desc(struct token *t)
+{
+       static char                     buf[32];
+       static const size_t             sof = sizeof(buf);
+       const char                      *s;
+       int                             n = t->tok_type;
+
+       s = parser_token_name(n);
+
+       switch (n) {
+       case TK_SYMBOL:
+               snprintf(buf, sof, "%s '%s'", s, t->tok_string);
+               return (buf);
+       case TK_SINT64:
+               snprintf(buf, sof, "%s %"PRId64, s, t->tok_signed);
+               return (buf);
+       case TK_STRING:
+               snprintf(buf, sof, "%s \"%s\"", s, t->tok_string);
+               return (buf);
+       case TK_UINT64:
+               snprintf(buf, sof, "%s #%"PRIx64, s, t->tok_unsigned);
+               return (buf);
+       case TK_DATA:
+               snprintf(buf, sof, "%s %zdB", s, t->tok_data_len);
+               return (buf);
+       }
+       return (s);
+}
+
+#endif /* PROP_SCN_DEBUG */
+
+static prop_string_t
+prop_scn_create_string(struct token *t)
+{
+       prop_string_t                   ps;
+       char                            *str = t->tok_string;
+
+       ps = _prop_string_alloc();
+       if (ps == NULL)
+               return (NULL);
+
+       ps->ps_mutable = str;
+       ps->ps_size = strlen(str);
+
+       return (ps);
+}
+
+static prop_number_t
+prop_scn_create_uint(struct token *t)
+{
+       return (prop_number_create_unsigned_integer(t->tok_unsigned));
+}
+
+static prop_number_t
+prop_scn_create_sint(struct token *t)
+{
+       return (prop_number_create_integer(t->tok_signed));
+}
+
+static boolean_t
+scanner_ensure_strlen(struct parser *pa)
+{
+       void                            *b;
+
+       /* Enough room for <char, '\0'> or base64 triplet. */
+       if ((pa->sc_strlen - pa->sc_strcur) < 3) {
+               b = _PROP_REALLOC(pa->sc_string, pa->sc_strlen + BUFINCR,
+                   M_TEMP);
+               if (b == NULL) {
+                       VERBOSE("scanner: ENOMEM internalization buffer");
+                       return (TRUE);
+               }
+
+               pa->sc_string = b;
+               pa->sc_strlen += BUFINCR;
+       }
+
+       /* No error == success. */
+       return (FALSE);
+}
+
+static struct token *
+parser_token_put(struct parser *pa, token_type_t tok)
+{
+       struct token                    *t;
+
+       t = _PROP_MALLOC(sizeof(struct token), M_TEMP);
+       if (t == NULL) {
+               VERBOSE(" parser: ENOMEM token %s", parser_token_name(tok));
+               return (NULL);
+       }
+
+       memset(t, 0, sizeof(struct token));
+       t->tok_type = tok;
+
+#if !defined(_STANDALONE)
+       t->tok_pos_line = pa->sc_pos_line;
+       t->tok_pos_col = pa->sc_pos_col;
+#endif
+
+       SIMPLEQ_INSERT_TAIL(&pa->pa_tokens, t, tok_link);
+       return (t);
+}
+
+static void
+parser_token_free(struct token *t, boolean_t force)
+{
+       /* Normally, tok_string is donated upwards, not duplicated. */
+       if (force && (t->tok_type == TK_STRING || t->tok_type == TK_SYMBOL) &&
+           t->tok_string != NULL)
+               _PROP_FREE(t->tok_string, M_TEMP);
+       _PROP_FREE(t, M_TEMP);
+}
+
+#define SC_ARROW_P(src, dst)   (pa->sc_prev == (src) && pa->sc_state == (dst))
+#define SC_CYCLE_P(s)          SC_ARROW_P(s, s)
+
+static u_char
+scanner_base64_decode(u_char c)
+{
+       const char                      *s;
+
+       if (c == '=')
+               return (0);
+
+       s = strchr(base64abc, (int)(u_int)c);
+       _PROP_ASSERT(s);
+
+       return ((u_char)(s - base64abc));
+}
+
+static int
+prop_scanner_exec(struct parser *pa, const u_char *input, size_t length)
+{
+       const u_char                    *p = input;
+       const u_char                    *pe = input + length;
+       struct token                    *t;
+       boolean_t                       keepchar;
+       u_char                          c;
+
+       if (input == NULL || length == 0) {
+               if (parser_token_put(pa, TK_LAST) == NULL)
+                       return (ENOMEM);
+               return (0);
+       }
+
+ advance:
+       if (p == pe)
+               return (0);
+       c = *p++;
+
+ dispatch:
+       keepchar = TRUE;        /* for callcc */
+
+       switch (pa->sc_state) {
+       case SC_FIRST:
+               /* Force transition. */
+               pa->sc_state = SC_TOPLEVEL;
+               goto callcc;
+
+       case SC_TOPLEVEL:
+               switch (c) {
+               case '#':
+                       pa->sc_state = SC_UINT64;
+                       goto callnext;
+               case '{':
+                       if (parser_token_put(pa, TK_DICTO) == NULL)
+                               return (ENOMEM);
+                       goto callnext;
+               case '}':
+                       if (parser_token_put(pa, TK_DICTC) == NULL)
+                               return (ENOMEM);
+                       goto callnext;
+               case '[':
+                       if (parser_token_put(pa, TK_ARRAYO) == NULL)
+                               return (ENOMEM);
+                       goto callnext;
+               case ']':
+                       if (parser_token_put(pa, TK_ARRAYC) == NULL)
+                               return (ENOMEM);
+                       goto callnext;
+               case '"':
+                       /* Opening quote, clear from edge action.  */
+                       pa->sc_state = SC_STRING_APPEND;
+                       goto callnext;
+               case ';':
+                       pa->sc_state = SC_COMMENT;
+                       goto callnext;
+               case ':':
+                       pa->sc_state = SC_BASE64_APPEND;
+                       goto callnext;
+               }
+
+               if (c == '-' || c == '+' || isdigit(c)) {
+                       pa->sc_state = SC_SINT64;
+                       goto callnext;
+               } else
+               if (isspace(c)) {
+                       pa->sc_state = SC_WHITE;
+                       goto callnext;
+               }
+               pa->sc_state = SC_SYMBOL;
+               goto callnext;
+
+       case SC_BASE64_APPEND:
+               if (strchr(base64abc, c) != NULL || c == '=') {
+                       char                    d = scanner_base64_decode(c);
+
+                       if (scanner_ensure_strlen(pa))
+                               return (ENOMEM);
+
+                       switch (pa->sc_base64_size % 24) {
+                       case 0:
+                               pa->sc_string[pa->sc_strcur] = d << 2;
+                               break;
+                       case 6:
+                               /* LINTED: sc_string & d are u_char */
+                               pa->sc_string[pa->sc_strcur] |= d >> 4;
+                               pa->sc_strcur++;
+                               pa->sc_string[pa->sc_strcur] = d << 4;
+                               break;
+                       case 12:
+                               /* LINTED: sc_string & d are u_char */
+                               pa->sc_string[pa->sc_strcur] |= d >> 2;
+                               pa->sc_strcur++;
+                               pa->sc_string[pa->sc_strcur] = d << 6;
+                               break;
+                       case 18:
+                               pa->sc_string[pa->sc_strcur++] |= d;
+                               break;
+                       }
+
+                       pa->sc_base64_size += 6;
+               } else {
+                       pa->sc_state = SC_BASE64_MAYBE_NEXT;
+                       goto callcc;
+               }
+               break;
+
+       case SC_BASE64_MAYBE_NEXT:
+               if (pa->sc_base64_size == 0) {
+                       VERBOSE("scanner: BASE64 first chunk empty");
+                       pa->sc_state = SC_ERROR;
+                       goto callcc;
+               }
+               if (c == '.') {
+                       pa->sc_state = SC_BASE64_NEXT;
+                       goto callnext;
+               }
+               if (! isspace(c)) {
+                       pa->sc_state = SC_BASE64_CLOSE;
+                       goto callcc;
+               }
+               break;
+
+       case SC_BASE64_NEXT:
+               if (strchr(base64abc, c) != NULL || c == '=') {
+                       pa->sc_state = SC_BASE64_APPEND;
+                       goto callcc;
+               }
+               if (! isspace(c)) {
+                       pa->sc_state = SC_ERROR;
+                       goto callcc;
+               }
+               break;
+
+       case SC_BASE64_CLOSE:
+               {
+                       u_char                  *s;
+
+                       /* Length must always be multiple of 3 bytes. */
+                       if (pa->sc_base64_size % 24) {
+                               /* Warrants we're long enough to roundup. */
+                               if (scanner_ensure_strlen(pa))
+                                       return (ENOMEM);
+
+                               /* Don't overwrite string[strcur]! */
+                               memset(pa->sc_string + pa->sc_strcur + 1, 0,
+                                   pa->sc_strlen - pa->sc_strcur - 1);
+
+                               pa->sc_base64_size =
+                                   roundup(pa->sc_base64_size, 24);
+                       }
+
+                       /* Convert length to bytes. */
+                       pa->sc_base64_size = roundup(pa->sc_base64_size, 8)/8;
+
+                       s = _PROP_MALLOC(pa->sc_base64_size, M_TEMP);
+                       if (s == NULL) {
+                               VERBOSE("scanner: ENOMEM data");
+                               return (ENOMEM);
+                       }
+                       memcpy(s, pa->sc_string, pa->sc_base64_size);
+
+                       if ((t = parser_token_put(pa, TK_DATA)) == NULL)
+                               return (ENOMEM);
+                       t->tok_data_buf = s;
+                       t->tok_data_len = pa->sc_base64_size;
+
+                       pa->sc_state = SC_TOPLEVEL;
+                       goto callcc;
+               }
+               /* UNREACHED */
+
+       case SC_STRING_APPEND:
+               if (c == '\\') {
+                       pa->sc_state = SC_QUOTED;
+                       goto callnext;
+               }
+               if (c == '"') {
+                       /* Closing quote, see if concatenation follows. */
+                       pa->sc_state = SC_STRING_MAYBE_NEXT;
+                       goto callnext;
+               }
+               if (! isprint(c)) {
+                       pa->sc_state = SC_ERROR;
+                       goto callcc;
+               }
+               /* Append from edge action. */
+               break;
+
+       case SC_STRING_MAYBE_NEXT:
+               if (c == '.') {
+                       pa->sc_state = SC_STRING_CONCAT;
+                       goto callnext;
+               }
+               if (! isspace(c)) {
+                       pa->sc_state = SC_STRING_CLOSE;
+                       goto callcc;
+               }
+               break;
+
+       case SC_STRING_CONCAT:
+               if (c == '"') {
+                       /* Opening quote of concat string. */
+                       pa->sc_state = SC_STRING_APPEND;
+                       goto callnext;
+               }
+               if (! isspace(c)) {
+                       pa->sc_state = SC_ERROR;
+                       goto callcc;
+               }
+               break;
+
+       case SC_STRING_CLOSE:
+               {
+                       char                    *s;
+
+                       s = strdup((const char *)pa->sc_string);
+                       if (s == NULL) {
+                               VERBOSE("scanner: ENOMEM string");
+                               return (ENOMEM);
+                       }
+
+                       if ((t = parser_token_put(pa, TK_STRING)) == NULL)
+                               return (ENOMEM);
+                       t->tok_string = s;
+
+                       pa->sc_state = SC_TOPLEVEL;
+                       goto callcc;
+               }
+               /* NOTREACHED */
+
+       case SC_QUOTED:
+               {
+                       u_char                  d;
+
+                       if (scanner_ensure_strlen(pa))
+                               return (ENOMEM);
+
+                       switch (d = c) {
+                       case '\\':      d = '\\';       break;
+                       case 'n':       d = '\n';       break;
+                       case 't':       d = '\t';       break;
+                       case '"':       d = '\"';       break;
+                       }
+
+                       pa->sc_string[pa->sc_strcur++] = d;
+                       pa->sc_string[pa->sc_strcur] = '\0';
+
+                       pa->sc_state = SC_STRING_APPEND;
+                       goto callnext;
+               }
+               /* NOTREACHED */
+
+       case SC_WHITE:
+               if (! isspace(c)) {
+                       if ((t = parser_token_put(pa, TK_WHITE)) == NULL)
+                               return (ENOMEM);
+
+                       pa->sc_state = SC_TOPLEVEL;
+                       goto callcc;
+               }
+               break;
+
+       case SC_SINT64:
+               if (! isdigit(c) && c != '+' && c != '-') {
+                       long long               n;
+                       char                    *end;
+
+                       n = strtoll((const char *)pa->sc_string, &end, 10);
+                       if (*end != '\0' || n > INT64_MAX || n < INT64_MIN) {
+                               VERBOSE("scanner: wrong SINT64 '%s'",
+                                   pa->sc_string);
+                               return (EINVAL);
+                       }
+
+                       if ((t = parser_token_put(pa, TK_SINT64)) == NULL)
+                               return (ENOMEM);
+                       t->tok_unsigned = (int64_t)n;
+
+                       pa->sc_state = SC_TOPLEVEL;
+                       goto callcc;
+               }
+               break;
+
+       case SC_UINT64:
+               if (! isxdigit(c)) {
+                       unsigned long long      u;
+                       char                    *end;
+
+                       u = strtoull((const char *)pa->sc_string, &end, 16);
+                       if (*end != '\0' || u > UINT64_MAX) {
+                               VERBOSE("scanner: wrong UINT64 '%s'",
+                                   pa->sc_string);
+                               return (EINVAL);
+                       }
+
+                       if ((t = parser_token_put(pa, TK_UINT64)) == NULL)
+                               return (ENOMEM);
+                       t->tok_unsigned = (uint64_t)u;
+
+                       pa->sc_state = SC_TOPLEVEL;
+                       goto callcc;
+               }
+               break;
+
+       case SC_SYMBOL:
+               if (isspace(c) || c == '\\') {
+                       char                    *s;
+
+                       s = strdup((const char *)pa->sc_string);
+                       if (s == NULL) {
+                               VERBOSE("scanner: ENOMEM symbol");
+                               return (ENOMEM);
+                       }
+
+                       if ((t = parser_token_put(pa, TK_SYMBOL)) == NULL) {
+                               _PROP_FREE(s, M_TEMP);
+                               return (ENOMEM);
+                       }
+                       t->tok_string = s;
+
+                       pa->sc_state = SC_TOPLEVEL;
+                       goto callcc;
+               }
+               if (! isprint(c)) {
+                       pa->sc_state = SC_ERROR;
+                       goto callcc;
+               }
+               break;
+
+       case SC_COMMENT:
+               if (c == '\n' || c == '\r') {
+                       pa->sc_state = SC_TOPLEVEL;
+                       goto callnext;
+               }
+               break;
+
+       case SC_ERROR:
+               VERBOSE("scanner: wrong char '%c' (line %zd char %zd) in "
+                   "state %s", c, pa->sc_pos_line, pa->sc_pos_col,
+                   scanner_state_name(pa->sc_last));
+               return (EINVAL);
+       }
+
+ callnext:
+       keepchar = FALSE;
+
+ callcc:
+       if (pa->sc_state != pa->sc_prev) {
+               VERBOSE("scanner: %-17s --> %-17s %s\t[%d, %d]",
+                   ((keepchar && pa->sc_prev != SC_FIRST) ?
+                    "" : scanner_state_name(pa->sc_prev)),
+                   scanner_state_name(pa->sc_state),
+                   scanner_quote_char(c), pa->sc_pos_line, pa->sc_pos_col);
+       } else {
+               VERBOSE("scanner: %-17s ::: %-17s %s\t[%d, %d]", "", "",
+                   scanner_quote_char(c), pa->sc_pos_line, pa->sc_pos_col);
+       }
+
+       /* Be specific about transitions, let compiler deal w/redundancy. */
+       if (SC_ARROW_P(SC_TOPLEVEL, SC_BASE64_APPEND)) {
+               pa->sc_base64_size = 0;
+               pa->sc_strcur = 0;
+       }
+       if (SC_ARROW_P(SC_TOPLEVEL, SC_STRING_APPEND) ||
+           SC_ARROW_P(SC_TOPLEVEL, SC_SYMBOL) ||
+           SC_ARROW_P(SC_TOPLEVEL, SC_UINT64) ||
+           SC_ARROW_P(SC_TOPLEVEL, SC_SINT64)) {
+               pa->sc_strcur = 0;
+       }
+       if (SC_CYCLE_P(SC_UINT64) ||
+           SC_CYCLE_P(SC_SINT64) ||
+           SC_CYCLE_P(SC_STRING_APPEND) ||
+           SC_CYCLE_P(SC_SYMBOL) ||
+           SC_ARROW_P(SC_TOPLEVEL, SC_SINT64) ||
+           SC_ARROW_P(SC_TOPLEVEL, SC_SYMBOL)) {
+               if (scanner_ensure_strlen(pa))
+                       return (ENOMEM);
+
+               pa->sc_string[pa->sc_strcur++] = c;
+               pa->sc_string[pa->sc_strcur] = '\0';
+       }
+
+#if !defined(_STANDALONE)
+       if (! keepchar) {
+               if (c == '\n') {
+                       pa->sc_pos_line++;
+                       pa->sc_pos_col = 1;
+               } else {
+                       if (c == '\t')
+                               pa->sc_pos_col = roundup(pa->sc_pos_col, 8);
+                       else
+                               pa->sc_pos_col++;
+               }
+       }
+#endif
+
+       /* Commited to new state. */
+       if (pa->sc_state != pa->sc_prev)
+               pa->sc_last = pa->sc_prev;
+       pa->sc_prev = pa->sc_state;
+
+       if (keepchar)
+               goto dispatch;
+       else
+               goto advance;
+
+       /* UNREACHED */
+}
+
+#undef SC_CYCLE_P
+#undef SC_ARROW_P
+
+static void
+parser_frame_free(struct frame *e)
+{
+       if (e->se_symbol)
+               _PROP_FREE(__UNCONST(e->se_symbol), M_TEMP);
+       if (e->se_object != NULL)
+               prop_object_release(e->se_object);
+       _PROP_FREE(e, M_TEMP);
+}
+
+static boolean_t
+parser_frame_enter(struct parser *pa, prop_object_t o)
+{
+       struct frame                    *e;
+
+       if (o == NULL)
+               return (TRUE);
+
+       e = _PROP_MALLOC(sizeof(struct frame), M_TEMP);
+       if (e == NULL)
+               return (TRUE);
+
+       memset(e, 0, sizeof(struct frame));
+       e->se_object = o;
+
+       SIMPLEQ_INSERT_HEAD(&pa->pa_stack, e, se_link);
+       return (FALSE);
+}
+
+static int
+parser_frame_store(struct parser *pa, prop_object_t o)
+{
+       struct frame                    *e;
+       prop_object_t                   the;
+
+       e = SIMPLEQ_FIRST(&pa->pa_stack);
+       if (e == NULL) {
+               VERBOSE(" parser: stack underflow");
+               return (EINVAL);
+       }
+       the = e->se_object;
+
+       switch (prop_object_type(the)) {
+       case PROP_TYPE_ARRAY:
+               _PROP_ASSERT(e->se_symbol == NULL);
+               if (prop_array_add(the, o) == FALSE)
+                       return (ENOMEM);
+               break;
+
+       case PROP_TYPE_DICTIONARY:
+               _PROP_ASSERT(e->se_symbol != NULL);
+               if (prop_dictionary_set(the, e->se_symbol, o) == FALSE)
+                       return (ENOMEM);
+
+               _PROP_FREE(__UNCONST(e->se_symbol), M_TEMP);
+               e->se_symbol = NULL;
+               break;
+
+       default:
+               VERBOSE(" parser: wrong object on stack, not compound");
+               return (EINVAL);
+       }
+
+       prop_object_release(o);
+       return (0);
+}
+
+static int
+parser_frame_leave(struct parser *pa)
+{
+       struct frame                    *e;
+       prop_object_t                   o;
+
+       /* Get hold of the lower object. */
+       if ((e = SIMPLEQ_FIRST(&pa->pa_stack)) == NULL) {
+               VERBOSE(" parser: stack underflow");
+               return (EINVAL);
+       }
+       SIMPLEQ_REMOVE_HEAD(&pa->pa_stack, se_link);
+
+       /* Move it to finished objects if it's toplevel. */
+       if (SIMPLEQ_EMPTY(&pa->pa_stack)) {
+               SIMPLEQ_INSERT_TAIL(&pa->pa_consq, e, se_link);
+               _PROP_ASSERT(e->se_object);
+               return (0);
+       }
+
+       /* Otherwise insert into current compound. */
+       o = e->se_object;
+
+       /* Make sure ${o} isn't released, parser_frame_store() will do it. */
+       e->se_object = NULL;
+       parser_frame_free(e);
+
+       return (parser_frame_store(pa, o));
+}
+
+static int
+prop_scn_parser_create(prop_parser_t *pp)
+{
+       struct parser                   *pa;
+
+       pa = _PROP_MALLOC(sizeof(struct parser), M_TEMP);
+       if (pa == NULL)
+               return (ENOMEM);
+       memset(pa, 0, sizeof(struct parser));
+
+       SIMPLEQ_INIT(&pa->pa_tokens);
+       SIMPLEQ_INIT(&pa->pa_stack);
+       SIMPLEQ_INIT(&pa->pa_consq);
+
+#if !defined(_STANDALONE)
+       /* Text editors tend to count from 1, be friendly. */
+       pa->sc_pos_line = 1;
+       pa->sc_pos_col = 1;
+#endif
+
+       if (parser_token_put(pa, TK_FIRST) == NULL) {
+               _PROP_FREE(pa, M_TEMP);
+               return (ENOMEM);
+       }
+
+       *pp = pa;
+       return (0);
+}
+
+static void
+prop_scn_parser_destroy(prop_parser_t arg)
+{
+       struct parser                   *pa = arg;
+       struct token                    *t;
+       struct frame                    *e;
+
+       if (pa->sc_string)
+               _PROP_FREE(pa->sc_string, M_TEMP);
+
+       /* Free any pending tokens. */
+       while ((t = SIMPLEQ_FIRST(&pa->pa_tokens)) != NULL) {
+               SIMPLEQ_REMOVE_HEAD(&pa->pa_tokens, tok_link);
+               parser_token_free(t, TRUE);
+       }
+
+       /* Free any active stack frames. */
+       while ((e = SIMPLEQ_FIRST(&pa->pa_stack)) != NULL) {
+               SIMPLEQ_REMOVE_HEAD(&pa->pa_stack, se_link);
+               parser_frame_free(e);
+       }
+
+       /* Free any finished objects. */
+       while ((e = SIMPLEQ_FIRST(&pa->pa_consq)) != NULL) {
+               SIMPLEQ_REMOVE_HEAD(&pa->pa_consq, se_link);
+               parser_frame_free(e);
+       }
+
+       _PROP_FREE(pa, M_TEMP);
+}
+
+static prop_object_t
+prop_scn_parser_yield(prop_parser_t arg)
+{
+       struct parser                   *pa = arg;
+       struct frame                    *e;
+       prop_object_t                   o;
+
+       if ((e = SIMPLEQ_FIRST(&pa->pa_consq)) == NULL)
+               return (NULL);
+       SIMPLEQ_REMOVE_HEAD(&pa->pa_consq, se_link);
+
+       o = e->se_object;
+       e->se_object = NULL;
+
+       _PROP_ASSERT(e);
+       _PROP_ASSERT(o);
+
+       parser_frame_free(e);
+       return (o);
+}
+
+static int
+prop_scn_parser_exec(prop_parser_t arg, const u_char *input, size_t length)
+{
+       static const char *__truths[] = { "true", "yes", "on", NULL };
+       static const char *__lies[] = { "false", "no", "off", NULL };
+       prop_object_t                   the;
+       struct frame                    *frame;
+       struct token                    *t;
+       struct parser                   *pa = arg;
+       int                             nexttoken, ret;
+
+       ret = prop_scanner_exec(pa, input, length);
+       if (ret)
+               return (ret);
+
+ advance:
+       if ((t = SIMPLEQ_FIRST(&pa->pa_tokens)) == NULL)
+               return (0);
+
+ dispatch:
+       pa->pa_prev = pa->pa_state;
+       nexttoken = FALSE;      /* for callcc */
+
+       switch (pa->pa_state) {
+       case PA_TOPLEVEL:
+               switch (t->tok_type) {
+               case TK_FIRST:
+                       /* XXX read version token */
+                       goto callnext;
+
+               case TK_DICTO:
+                       if (parser_frame_enter(pa,
+                           (prop_object_t)prop_dictionary_create())) {
+                               VERBOSE(" parser: ENOMEM dictionary");
+                               return (ENOMEM);
+                       }
+                       pa->pa_state = PA_DICTIONARY;
+                       goto callnext;
+
+               case TK_ARRAYO:
+                       if (parser_frame_enter(pa,
+                           (prop_object_t)prop_array_create())) {
+                               VERBOSE(" parser: ENOMEM array");
+                               return (ENOMEM);
+                       }
+                       pa->pa_state = PA_ARRAY;
+                       goto callnext;
+
+               case TK_LAST:
+                       if (SIMPLEQ_NEXT(t, tok_link) != NULL) {
+                               VERBOSE(" parser: stack not empty at EOF");
+                               return (EINVAL);
+                       }
+                       return (0);
+
+               default:
+                       /* GCC tries to be smart but fails. */
+                       break;
+               }
+               if (t->tok_type != TK_WHITE) {
+                       pa->pa_state = PA_ERROR;
+                       goto callcc;
+               }
+               break;
+
+       case PA_ARRAY:
+               if (t->tok_type == TK_ARRAYC) {
+                       if ((ret = parser_frame_leave(pa)) != 0) {
+                               if (ret == EINVAL)
+                                       VERBOSE(" parser: [%d, %d] "
+                                           "misplaced ']'",
+                                           t->tok_pos_line, t->tok_pos_col);
+                               return (ret);
+                       }
+                       pa->pa_state = PA_HOME;
+                       goto callcc;
+               } else
+               if (t->tok_type != TK_WHITE) {
+                       pa->pa_state = PA_OBJECT;
+                       goto callcc;
+               }
+               break;
+
+       case PA_DICTIONARY:
+               switch (t->tok_type) {
+               case TK_DICTC:
+                       if ((ret = parser_frame_leave(pa)) != 0) {
+                               if (ret == EINVAL)
+                                       VERBOSE(" parser: [%d, %d] "
+                                           "misplaced '}'",
+                                           t->tok_pos_line, t->tok_pos_col);
+                               return (ret);
+                       }
+                       pa->pa_state = PA_HOME;
+                       goto callcc;
+
+               case TK_SYMBOL:
+                       frame = SIMPLEQ_FIRST(&pa->pa_stack);
+                       _PROP_ASSERT(frame && frame->se_symbol == NULL);
+                       frame->se_symbol = (const char *)t->tok_string;
+
+                       pa->pa_state = PA_OBJECT;
+                       goto callnext;
+
+               default:
+                       /* GCC */
+                       break;
+               }
+               /* WHITE or ERROR */
+               break;
+
+       case PA_OBJECT:
+               if (t->tok_type == TK_WHITE)
+                       break;
+
+               switch (t->tok_type) {
+               case TK_STRING:
+                       the = prop_scn_create_string(t);
+                       break;
+
+               case TK_UINT64:
+                       the = prop_scn_create_uint(t);
+                       break;
+
+               case TK_SINT64:
+                       the = prop_scn_create_sint(t);
+                       break;
+
+               case TK_DATA:
+                       the = prop_data_create_data_nocopy(t->tok_data_buf,
+                           t->tok_data_len);
+                       break;
+
+               case TK_SYMBOL:
+                       /* Coerce SYMBOL to bool at value position. */
+                       if (stroneof((const char *)t->tok_string, __truths))
+                               the = prop_bool_create(TRUE);
+                       else
+                       if (stroneof((const char *)t->tok_string, __lies))
+                               the = prop_bool_create(FALSE);
+                       else {
+                               VERBOSE(" parser: [%d, %d] wrong BOOL '%s'",
+                                   t->tok_pos_line, t->tok_pos_col,
+                                   t->tok_string);
+                               return (EINVAL);
+                       }
+                       break;
+
+               case _TK_COMPOUND_VALUES:
+                       /* Descend one level deeper via TOPLEVEL actions. */
+                       pa->pa_state = PA_TOPLEVEL;
+                       goto callcc;
+
+               default:
+                       pa->pa_state = PA_ERROR;
+                       goto callcc;
+               }
+
+               /* We're supposed to have valid simple object now. */
+               if (the == NULL) {
+                       VERBOSE(" parser: ENOMEM for %s",
+                           parser_token_name(t->tok_type));
+                       return (ENOMEM);
+               }
+
+               /* Store it in current container. */
+               if (parser_frame_store(pa, the))
+                       return (ENOMEM);
+
+               pa->pa_state = PA_HOME;
+               goto callcc;
+
+       case PA_HOME:
+               /*
+                * We've just finished an object (simple or compound).
+                * Continue where we came from -- at the parent container's
+                * main entry point. We get here through callcc.
+                */
+               frame = SIMPLEQ_FIRST(&pa->pa_stack);
+               if (frame == NULL) {
+                       pa->pa_state = PA_TOPLEVEL;
+                       break;
+               }
+               _PROP_ASSERT(frame->se_object);
+
+               switch (prop_object_type(frame->se_object)) {
+               case PROP_TYPE_ARRAY:
+                       pa->pa_state = PA_ARRAY;
+                       break;
+
+               case PROP_TYPE_DICTIONARY:
+                       pa->pa_state = PA_DICTIONARY;
+                       break;
+
+               default:
+                       VERBOSE(" parser: wrong object on stack");
+                       return (EINVAL);
+               }
+               break;
+
+       case PA_ERROR:
+               VERBOSE(" parser: [%d, %d] wrong token %s in state %s",
+                   t->tok_pos_line, t->tok_pos_col,
+                   parser_token_desc(t), parser_state_name(pa->pa_last));
+               return (EINVAL);
+       }
+
+       /* Call to next implies token was accepted, so stay above. */
+       if (pa->pa_prev == pa->pa_state &&
+           (pa->pa_state == PA_TOPLEVEL || pa->pa_state == PA_ARRAY ||
+            pa->pa_state == PA_DICTIONARY))
+               if (t->tok_type != TK_WHITE) {
+                       pa->pa_state = PA_ERROR;
+                       goto callcc;
+               }
+
+ callnext:
+       nexttoken = TRUE;
+
+ callcc:
+       if (pa->pa_state != pa->pa_last) {
+               VERBOSE(" parser: %-17s --> %-17s %s",
+                   (nexttoken ? parser_state_name(pa->pa_prev) : ""),
+                   parser_state_name(pa->pa_state),
+                   parser_token_desc(t));
+
+               pa->pa_last = pa->pa_prev;
+       } else {
+               VERBOSE(" parser: %-17s ::: %-17s %s", "", "",
+                   parser_token_desc(t));
+       }
+
+       if (nexttoken) {
+               SIMPLEQ_REMOVE_HEAD(&pa->pa_tokens, tok_link);
+               parser_token_free(t, FALSE);
+
+               goto advance;
+       } else {
+               goto dispatch;
+       }
+       /* UNREACHED */
+}
+
+static boolean_t
+format_string_quote(struct poec *ec, const char *s)
+{
+       const char                      *t;
+       boolean_t                       ret;
+
+       if (! poec_push(ec, '"'))
+               return (TRUE);
+
+       while (*s) {
+               t = NULL;
+
+               switch (*s) {
+               case '\f':      t = "\\f";      break;
+               case '\n':      t = "\\n";      break;
+               case '\r':      t = "\\r";      break;
+               case '\t':      t = "\\t";      break;
+               case '\v':      t = "\\v";      break;
+               case '"':       t = "\\\"";     break;
+               }
+
+               if (t)
+                       ret = poec_append(ec, t);
+               else
+                       ret = poec_push(ec, *s);
+               if (ret == FALSE)
+                       return (TRUE);
+
+               s++;
+       }
+
+       if (! poec_push(ec, '"'))
+               return (TRUE);
+       return (FALSE);
+}
+
+static boolean_t
+format_data_base64(struct poec *ec, const char *s, size_t size)
+{
+       const u_char                    *b = (const u_char *)s;
+       size_t                          i;
+       u_int                           n;
+       int                             ret = 0;        /* XXX gcc sux */
+
+       poec_push(ec, ':');
+
+       /* LINTED n & b[] are unsigned */
+       n = b[0] >> 2;
+
+       for (i = 0; i < size; i++) {
+               switch (i % 3) {
+               case 0:
+                       /* LINTED: b[] is u_char */
+                       ret = poec_push(ec, base64abc[b[i] >> 2]);
+                       n = b[i] & 0x03;
+                       break;
+               case 1:
+                       /* LINTED: b[] is u_char */
+                       ret = poec_push(ec,
+                           base64abc[(n << 4) | (b[i] >> 4)]);
+                       n = b[i] & 0x0f;
+                       break;
+               case 2:
+                       /* LINTED: b[] is u_char */
+                       if (!poec_push(ec, base64abc[(n << 2) | (b[i] >> 6)]) ||
+                           !poec_push(ec, base64abc[b[i] & 0x3f]))
+                               ret = FALSE;
+                       else
+                               ret = TRUE;
+                       break;
+               }
+               if (ret == FALSE)
+                       return (TRUE);
+       }
+
+       /* Finish based on how many bytes of a triplet we already have. */
+       switch (size % 3) {
+       case 1:
+               if (!poec_push(ec, base64abc[n << 4]))
+                       return (TRUE);
+               break;
+       case 2:
+               if (!poec_push(ec, base64abc[n << 2]))
+                       return (TRUE);
+               break;
+       }
+
+       /* Finally, pad to multiple of four characters of encoded text. */
+       switch (size % 3) {
+       case 1:
+               if (!poec_push(ec, '='))
+                       return (TRUE);
+               /* FALLTHROUGH */
+       case 2:
+               if (!poec_push(ec, '='))
+                       return (TRUE);
+       }
+
+       return (FALSE);
+}
+
+static boolean_t
+format_indent(struct poec *ec)
+{
+       int                             i;
+
+#if 0
+       VERBOSE(" format: %zd/%zdB used, nesting depth %d",
+           ec->poec_len, ec->poec_capacity, ec->poec_depth);
+#endif
+
+       for (i = 0; i < ec->poec_depth; i++)
+               if (poec_push(ec, '\t') == FALSE) {
+                       VERBOSE(" format: ENOMEM indent");
+                       return (TRUE);
+               }
+       return (FALSE);
+}
+
+static char *
+prop_scn_externalize(prop_object_t o)
+{
+       char                            buf[32];
+       struct stack                    sp;
+       struct frame                    *e;
+       struct poec                     *ec;
+       char                            *s;
+       prop_object_t                   the;
+       boolean_t                       ret;
+
+       SIMPLEQ_INIT(&sp);
+       if ((ec = poec_create()) == NULL)
+               return (NULL);
+
+       /*
+        * We only work for compound objects, like XML codec does.
+        * Note that this ensures we'll record ${o} on the stack.
+        */
+       switch (prop_object_type(o)) {
+       case PROP_TYPE_ARRAY:
+       case PROP_TYPE_DICTIONARY:
+               break;
+       default:
+               goto lose;
+       }
+       the = o;
+
+ again:
+       switch (prop_object_type(the)) {
+       case PROP_TYPE_BOOL:
+               VERBOSE(" format: bool       [%d]", ec->poec_depth);
+               if (prop_bool_true(the))
+                       ret = poec_append(ec, "True");
+               else
+                       ret = poec_append(ec, "False");
+               if (ret == FALSE)
+                       goto lose;
+               break;
+
+       case PROP_TYPE_NUMBER:
+               VERBOSE(" format: number     [%d]", ec->poec_depth);
+               if (prop_number_unsigned(the))
+                       snprintf(buf, sizeof(buf), "#%" PRIx64,
+                           prop_number_unsigned_integer_value(the));
+               else
+                       snprintf(buf, sizeof(buf), "%" PRId64,
+                           prop_number_integer_value(the));
+               if (poec_append(ec, buf) == FALSE)
+                       goto lose;
+               break;
+
+       case PROP_TYPE_STRING:
+               VERBOSE(" format: string     [%d]", ec->poec_depth);
+               if (format_string_quote(ec, prop_string_cstring_nocopy(the)))
+                       goto lose;
+               break;
+
+       case PROP_TYPE_DATA:
+               VERBOSE(" format: data       [%d]", ec->poec_depth);
+               if (format_data_base64(ec, prop_data_data_nocopy(the),
+                   prop_data_size(the)))
+                       goto lose;
+               break;
+
+       case PROP_TYPE_ARRAY:
+               VERBOSE(" format: array      [%d]", ec->poec_depth);
+               if (!poec_append(ec, "["))
+                       goto lose;
+               ec->poec_depth++;
+
+               if ((e = _PROP_MALLOC(sizeof(struct frame), M_TEMP)) == NULL)
+                       goto lose;
+               memset(e, 0, sizeof(struct frame));
+               SIMPLEQ_INSERT_HEAD(&sp, e, se_link);
+
+               e->se_object = the;
+               e->se_iter = prop_array_iterator(the);
+               if (e->se_iter == NULL)
+                       goto lose;
+               break;
+
+       case PROP_TYPE_DICTIONARY:
+               VERBOSE(" format: dictionary [%d]", ec->poec_depth);
+               if (!poec_append(ec, "{")) {
+                       VERBOSE(" format: ENOMEM dictionary open");
+                       goto lose;
+               }
+               ec->poec_depth++;
+
+               if ((e = _PROP_MALLOC(sizeof(struct frame), M_TEMP)) == NULL) {
+                       VERBOSE(" format: ENOMEM stack frame");
+                       goto lose;
+               }
+               memset(e, 0, sizeof(struct frame));
+               SIMPLEQ_INSERT_HEAD(&sp, e, se_link);
+
+               e->se_object = the;
+               e->se_iter = prop_dictionary_iterator(the);
+               if (e->se_iter == NULL) {
+                       VERBOSE(" format: ENOMEM dictionary iterator");
+                       goto lose;
+               }
+               break;
+
+       default:
+               VERBOSE(" format: object %p wrong type %d", the,
+                   prop_object_type(the));
+               goto lose;
+       }
+       if (! poec_append(ec, "\n")) {
+               VERBOSE(" format: ENOMEM newline after object");
+               goto lose;
+       }
+
+ pop:
+       e = SIMPLEQ_FIRST(&sp);
+       if (e == NULL) {
+               _PROP_ASSERT(ec->poec_depth == 0);
+               printf("DONE\n"); /* XXX done */
+       }
+       _PROP_ASSERT(e->se_iter != NULL);
+
+       /* Grab next object. */
+       o = prop_object_iterator_next(e->se_iter);
+       if (o == NULL) {
+               SIMPLEQ_REMOVE_HEAD(&sp, se_link);
+
+               _PROP_ASSERT(ec->poec_depth != 0);
+               ec->poec_depth --;
+
+               if (format_indent(ec)) {
+                       VERBOSE(" format: ENOMEM close indent");
+                       goto lose;
+               }
+               if (prop_object_type(e->se_object) == PROP_TYPE_DICTIONARY)
+                       ret = poec_append(ec, "}\n");
+               else
+                       ret = poec_append(ec, "]\n");
+               if (ret == FALSE) {
+                       VERBOSE(" format: ENOMEM close compound");
+                       goto lose;
+               }
+
+               prop_object_iterator_release(e->se_iter);
+               _PROP_FREE(e, M_TEMP);
+
+               if (SIMPLEQ_EMPTY(&sp))
+                       goto done;
+               else
+                       goto pop;
+       }
+
+       /* Lookup the real object if we got indirect reference. */
+       if (prop_object_type(o) == PROP_TYPE_DICT_KEYSYM) {
+               const char                      *r;
+               prop_object_t                   p;
+
+               p = prop_dictionary_get_keysym((prop_dictionary_t)e->se_object,
+                   (prop_dictionary_keysym_t)o);
+               _PROP_ASSERT(p != NULL);
+
+               r = prop_keysym_str((prop_dictionary_keysym_t)o);
+               if (r == NULL) {
+                       VERBOSE(" format: EINVAL dictionary key");
+                       goto lose;
+               }
+
+               if (format_indent(ec) || poec_append(ec, r) == FALSE ||
+                   poec_push(ec, '\t') == FALSE) {
+                       VERBOSE(" format: ENOMEM dictionary key");
+                       goto lose;
+               }
+
+               the = p;
+       } else {
+               if (format_indent(ec)) {
+                       VERBOSE(" format: ENOMEM array indent");
+                       goto lose;
+               }
+               the = o;
+       }
+
+       /* We've got a fresh object from the compound, analyse it. */
+       goto again;
+
+ done:
+       /* Prepare the result for caller. */
+       ec->poec_buf[ec->poec_len] = '\0';
+       s = ec->poec_buf;
+
+       /* The stack is empty at this point, just free externalize context. */
+       poec_destroy(ec);
+
+       return (s);
+       /*NOTREACHED*/
+
+ lose:
+       VERBOSE(" format: LOST");
+       while ((e = SIMPLEQ_FIRST(&sp)) != NULL) {
+               SIMPLEQ_REMOVE_HEAD(&sp, se_link);
+               if (e->se_iter)
+                       prop_object_iterator_release(e->se_iter);
+               _PROP_FREE(e, M_TEMP);
+       }
+
+       if (ec->poec_buf)
+               _PROP_FREE(ec->poec_buf, M_TEMP);
+       poec_destroy(ec);
+
+       return (NULL);
+}
+
+const struct _prop_codec prop_codec_scn = {
+       .codec_name                     = "scn",
+       .codec_sense                    = (const u_char *)".{[;",
+       .codec_externalize_compound     = prop_scn_externalize,
+       .codec_parser_create            = prop_scn_parser_create,
+       .codec_parser_exec              = prop_scn_parser_exec,
+       .codec_parser_yield             = prop_scn_parser_yield,
+       .codec_parser_destroy           = prop_scn_parser_destroy,
+};
Index: common/lib/libprop/prop_string.c
===================================================================
RCS file: /cvsroot/src/common/lib/libprop/prop_string.c,v
retrieving revision 1.6
diff -d -p -u -r1.6 prop_string.c
--- common/lib/libprop/prop_string.c    18 Oct 2006 19:15:46 -0000      1.6
+++ common/lib/libprop/prop_string.c    7 Jun 2007 13:27:02 -0000
@@ -39,35 +39,17 @@
#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 void            _prop_string_free(void *);
-static boolean_t       _prop_string_externalize(
-                               struct _prop_object_externalize_context *,
-                               void *);
static boolean_t       _prop_string_equals(void *, void *);

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,
};

@@ -86,24 +68,6 @@ _prop_string_free(void *v)
}

static boolean_t
-_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);
-}
-
-static boolean_t
_prop_string_equals(void *v1, void *v2)
{
       prop_string_t str1 = v1;
@@ -121,7 +85,7 @@ _prop_string_equals(void *v1, void *v2)
                      prop_string_contents(str2)) == 0);
}

-static prop_string_t
+prop_string_t
_prop_string_alloc(void)
{
       prop_string_t ps;
@@ -407,57 +371,3 @@ prop_string_equals_cstring(prop_string_t

       return (strcmp(prop_string_contents(ps), cp) == 0);
}
-
-/*
- * _prop_string_internalize --
- *     Parse a <string>...</string> and return the object created from the
- *     external representation.
- */
-prop_object_t
-_prop_string_internalize(struct _prop_object_internalize_context *ctx)
-{
-       prop_string_t string;
-       char *str;
-       size_t len, alen;
-
-       if (ctx->poic_is_empty_element)
-               return (prop_string_create());
-
-       /* No attributes recognized here. */
-       if (ctx->poic_tagattr != NULL)
-               return (NULL);
-
-       /* Compute the length of the result. */
-       if (_prop_object_internalize_decode_string(ctx, NULL, 0, &len,
-                                                  NULL) == FALSE)
-               return (NULL);
-
-       str = _PROP_MALLOC(len + 1, M_PROP_STRING);
-       if (str == NULL)
-               return (NULL);
-
-       if (_prop_object_internalize_decode_string(ctx, str, len, &alen,
-                                                  &ctx->poic_cp) == FALSE ||
-           alen != len) {
-               _PROP_FREE(str, M_PROP_STRING);
-               return (NULL);
-       }
-       str[len] = '\0';
-
-       if (_prop_object_internalize_find_tag(ctx, "string",
-                                             _PROP_TAG_TYPE_END) == FALSE) {
-               _PROP_FREE(str, M_PROP_STRING);
-               return (NULL);
-       }
-
-       string = _prop_string_alloc();
-       if (string == NULL) {
-               _PROP_FREE(str, M_PROP_STRING);
-               return (NULL);
-       }
-
-       string->ps_mutable = str;
-       string->ps_size = len;
-
-       return (string);
-}
Index: common/lib/libprop/prop_system_impl.h
===================================================================
RCS file: common/lib/libprop/prop_system_impl.h
diff -N common/lib/libprop/prop_system_impl.h
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ common/lib/libprop/prop_system_impl.h       7 Jun 2007 13:27:02 -0000
@@ -0,0 +1,208 @@
+/*     $NetBSD$ */
+
+/*-
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by the NetBSD
+ *      Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * 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_SYSTEM_IMPL_H_
+#define        _PROPLIB_PROP_SYSTEM_IMPL_H_
+
+#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/lock.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))
+
+#define        _PROP_POOL_INIT(p, s, d)                                        \
+               POOL_INIT(p, s, 0, 0, 0, d, &pool_allocator_nointr, IPL_NONE);
+
+#define        _PROP_MALLOC_DEFINE(t, s, l)                                    \
+               MALLOC_DEFINE(t, s, l);
+#define _PROP_MALLOC_DECLARE(t)                                        \
+               MALLOC_DECLARE(t);
+
+#define        _PROP_MUTEX_DECL_STATIC(x)                                      \
+               static struct simplelock x = SIMPLELOCK_INITIALIZER;
+#define        _PROP_MUTEX_LOCK(x)     simple_lock(&(x))
+#define        _PROP_MUTEX_UNLOCK(x)   simple_unlock(&(x))
+
+#define        _PROP_RWLOCK_DECL(x)    struct lock x ;
+#define        _PROP_RWLOCK_INIT(x)    lockinit(&(x), PZERO, "proprwlk", 0, 0)
+#define        _PROP_RWLOCK_RDLOCK(x)  lockmgr(&(x), LK_SHARED, NULL)
+#define        _PROP_RWLOCK_WRLOCK(x)  lockmgr(&(x), LK_EXCLUSIVE, NULL)
+#define        _PROP_RWLOCK_UNLOCK(x)  lockmgr(&(x), LK_RELEASE, NULL)
+#define        _PROP_RWLOCK_DESTROY(x) lockmgr(&(x), LK_DRAIN, NULL)
+
+#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_MALLOC_DECLARE(t)        /* nothing */
+
+#define        _PROP_MUTEX_DECL_STATIC(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 */
+
+#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 */
+#define _PROP_MALLOC_DECLARE(t)        /* 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 = MUTEX_INITIALIZER;
+#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))
+#elif defined(HAVE_NBTOOL_CONFIG_H)
+/*
+ * None of NetBSD's build tools are multi-threaded.
+ */
+#define        _PROP_MUTEX_DECL_STATIC(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 */
+#else
+/*
+ * Use pthread mutexes everywhere else.
+ */
+#include <pthread.h>
+#define        _PROP_MUTEX_DECL_STATIC(x)                                      \
+               static pthread_mutex_t x = PTHREAD_MUTEX_INITIALIZER;
+#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))
+#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_SYSTEM_IMPL_H_ */
Index: common/lib/libprop/prop_xml.c
===================================================================
RCS file: common/lib/libprop/prop_xml.c
diff -N common/lib/libprop/prop_xml.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ common/lib/libprop/prop_xml.c       7 Jun 2007 13:27:09 -0000
@@ -0,0 +1,1663 @@
+/*     $NetBSD$ */
+
+/*-
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by the NetBSD
+ *      Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * 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_codec_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 <stdint.h>
+#endif
+
+boolean_t      _prop_object_externalize_start_tag(
+                               struct _prop_object_externalize_context *,
+                               const char *);
+boolean_t      _prop_object_externalize_end_tag(
+                               struct _prop_object_externalize_context *,
+                               const char *);
+boolean_t      _prop_object_externalize_empty_tag(
+                               struct _prop_object_externalize_context *,
+                               const char *);
+boolean_t      _prop_object_externalize_append_encoded_cstring(
+                               struct _prop_object_externalize_context *,
+                               const char *);
+boolean_t      _prop_object_externalize_header(
+                               struct _prop_object_externalize_context *);
+boolean_t      _prop_object_externalize_footer(
+                               struct _prop_object_externalize_context *);
+
+static boolean_t _prop_object_externalize(
+                               struct _prop_object_externalize_context *,
+                               prop_object_t);
+
+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;
+
+       boolean_t   poic_is_empty_element;
+       _prop_tag_type_t poic_tag_type;
+};
+
+#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))
+
+boolean_t      _prop_object_internalize_find_tag(
+                               struct _prop_object_internalize_context *,
+                               const char *, _prop_tag_type_t);
+boolean_t      _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 *);
+boolean_t      _prop_object_internalize_decode_string(
+                               struct _prop_object_internalize_context *,
+                               char *, size_t, size_t *, 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 *);
+
+prop_object_t  _prop_array_internalize(
+                               struct _prop_object_internalize_context *);
+prop_object_t  _prop_bool_internalize(
+                               struct _prop_object_internalize_context *);
+prop_object_t  _prop_data_internalize(
+                               struct _prop_object_internalize_context *);
+prop_object_t  _prop_dictionary_internalize(
+                               struct _prop_object_internalize_context *);
+prop_object_t  _prop_number_internalize(
+                               struct _prop_object_internalize_context *);
+prop_object_t  _prop_string_internalize(
+                               struct _prop_object_internalize_context *);
+
+static char *prop_dictionary_externalize_xml(prop_dictionary_t);
+static char *prop_array_externalize_xml(prop_array_t);
+static prop_dictionary_t prop_dictionary_internalize_xml(const char *);
+static prop_array_t prop_array_internalize_xml(const char *);
+
+const struct _prop_codec prop_codec_xml = {
+       .codec_name                     = "xml",
+       .codec_sense                    = (const u_char *)"<",
+       .codec_dictionary_externalize   = prop_dictionary_externalize_xml,
+       .codec_array_externalize        = prop_array_externalize_xml,
+       .codec_dictionary_internalize   = prop_dictionary_internalize_xml,
+       .codec_array_internalize        = prop_array_internalize_xml,
+};
+
+static boolean_t
+_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;
+       boolean_t 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(pa);
+       if (pi == NULL)
+               goto out;
+
+       ctx->poec_depth++;
+       _PROP_ASSERT(ctx->poec_depth != 0);
+
+       while ((po = prop_object_iterator_next(pi)) != NULL) {
+               if (_prop_object_externalize(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);
+}
+
+/*
+ * 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.
+ */
+static char *
+prop_array_externalize_xml(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 ||
+           _prop_object_externalize(ctx, &pa->pa_obj) == 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.
+ */
+prop_object_t
+_prop_array_internalize(struct _prop_object_internalize_context *ctx)
+{
+       prop_array_t array;
+       prop_object_t obj;
+
+       /* We don't currently understand any attributes. */
+       if (ctx->poic_tagattr != NULL)
+               return (NULL);
+
+       array = prop_array_create();
+       if (array == NULL)
+               return (NULL);
+
+       if (ctx->poic_is_empty_element)
+               return (array);
+
+       for (;;) {
+               /* 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)
+                       break;
+
+               /* Fetch the object. */
+               obj = _prop_object_internalize_by_tag(ctx);
+               if (obj == NULL)
+                       goto bad;
+
+               if (prop_array_add(array, obj) == FALSE) {
+                       prop_object_release(obj);
+                       goto bad;
+               }
+               prop_object_release(obj);
+       }
+
+       return (array);
+
+ bad:
+       prop_object_release(array);
+       return (NULL);
+}
+
+/*
+ * prop_array_internalize --
+ *     Create an array by parsing the XML-style representation.
+ */
+static prop_array_t
+prop_array_internalize_xml(const char *xml)
+{
+       prop_array_t array = 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 <array>. */
+       if (_prop_object_internalize_find_tag(ctx, "array",
+                                             _PROP_TAG_TYPE_START) == FALSE)
+               goto out;
+
+       array = _prop_array_internalize(ctx);
+       if (array == NULL)
+               goto out;
+
+       /* We've advanced past </array>.  Now we want </plist>. */
+       if (_prop_object_internalize_find_tag(ctx, "plist",
+                                             _PROP_TAG_TYPE_END) == FALSE) {
+               prop_object_release(array);
+               array = NULL;
+       }
+
+ out:
+       _prop_object_internalize_context_free(ctx);
+       return (array);
+}
+
+static boolean_t
+_prop_bool_externalize(struct _prop_object_externalize_context *ctx,
+                      void *v)
+{
+       prop_bool_t pb = v;
+
+       return (_prop_object_externalize_empty_tag(ctx,
+           prop_bool_true(pb) ? "true" : "false"));
+}
+
+
+/*
+ * _prop_bool_internalize --
+ *     Parse a <true/> or <false/> and return the object created from
+ *     the external representation.
+ */
+prop_object_t
+_prop_bool_internalize(struct _prop_object_internalize_context *ctx)
+{
+       boolean_t val;
+
+       /* No attributes, and it must be an empty element. */
+       if (ctx->poic_tagattr != NULL ||
+           ctx->poic_is_empty_element == FALSE)
+               return (NULL);
+
+       if (_PROP_TAG_MATCH(ctx, "true"))
+               val = TRUE;
+       else {
+               _PROP_ASSERT(_PROP_TAG_MATCH(ctx, "false"));
+               val = FALSE;
+       }
+
+       return (prop_bool_create(val));
+}
+static const char _prop_data_base64[] =
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+static const char _prop_data_pad64 = '=';
+
+static boolean_t
+_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] = ((u_int32_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] = ((u_int32_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);
+}
+
+static boolean_t
+_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 != '<'; ch = (unsigned char) *src++) {
+                               if (_PROP_EOF(ch))
+                                       return (FALSE);
+                               if (!_PROP_ISSPACE(ch))
+                                       return (FALSE);
+                       }
+               }
+       } 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.
+ */
+prop_object_t
+_prop_data_internalize(struct _prop_object_internalize_context *ctx)
+{
+       prop_data_t data;
+       uint8_t *buf;
+       size_t len, alen;
+
+       /* We don't accept empty elements. */
+       if (ctx->poic_is_empty_element)
+               return (NULL);
+
+       /*
+        * 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 (NULL);
+
+#ifndef _KERNEL
+               errno = 0;
+#endif
+               /* XXX Assumes size_t and unsigned long are the same size. */
+               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 (NULL);
+#endif
+               if (cp != ctx->poic_tagattrval + ctx->poic_tagattrval_len)
+                       return (NULL);
+               _PROP_ASSERT(*cp == '\"');
+       } else if (_prop_data_internalize_decode(ctx, NULL, 0, &len,
+                                               NULL) == FALSE)
+               return (NULL);
+
+       /*
+        * 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 (NULL);
+
+       if (_prop_data_internalize_decode(ctx, buf, len + 1, &alen,
+                                         &ctx->poic_cp) == FALSE) {
+               _PROP_FREE(buf, M_PROP_DATA);
+               return (NULL);
+       }
+       if (alen != len) {
+               _PROP_FREE(buf, M_PROP_DATA);
+               return (NULL);
+       }
+
+       if (_prop_object_internalize_find_tag(ctx, "data",
+                                             _PROP_TAG_TYPE_END) == FALSE) {
+               _PROP_FREE(buf, M_PROP_DATA);
+               return (NULL);
+       }
+
+       data = _prop_data_alloc();
+       if (data == NULL) {
+               _PROP_FREE(buf, M_PROP_DATA);
+               return (NULL);
+       }
+
+       data->pd_mutable = buf;
+       data->pd_size = len;
+
+       return (data);
+}
+static boolean_t
+_prop_dict_keysym_externalize(struct _prop_object_externalize_context *ctx,
+                            void *v)
+{
+       prop_dictionary_keysym_t pdk = v;
+       const char *s = prop_dictionary_keysym_cstring_nocopy(pdk);
+
+       /* We externalize these as strings, and they're never empty. */
+
+       _PROP_ASSERT(s[0] != '\0');
+
+       if (_prop_object_externalize_start_tag(ctx, "string") == FALSE ||
+           _prop_object_externalize_append_encoded_cstring(ctx, s) == FALSE ||
+           _prop_object_externalize_end_tag(ctx, "string") == FALSE)
+               return (FALSE);
+
+       return (TRUE);
+}
+
+static boolean_t
+_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;
+       boolean_t 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(pd);
+       if (pi == NULL)
+               goto out;
+
+       ctx->poec_depth++;
+       _PROP_ASSERT(ctx->poec_depth != 0);
+
+       while ((pdk = prop_object_iterator_next(pi)) != NULL) {
+               po = prop_dictionary_get_keysym(pd, pdk);
+               if (po == NULL ||
+                   _prop_object_externalize_start_tag(ctx, "key") == FALSE ||
+                   _prop_object_externalize_append_encoded_cstring(ctx,
+                       prop_dictionary_keysym_cstring_nocopy(pdk)) == FALSE ||
+                   _prop_object_externalize_end_tag(ctx, "key") == FALSE ||
+                   _prop_object_externalize(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);
+}
+
+/*
+ * 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.
+ */
+static char *
+prop_dictionary_externalize_xml(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 ||
+           _prop_object_externalize(ctx, &pd->pd_obj) == 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.
+ */
+prop_object_t
+_prop_dictionary_internalize(struct _prop_object_internalize_context *ctx)
+{
+       prop_dictionary_t dict;
+       prop_object_t val;
+       size_t keylen;
+       char *tmpkey;
+
+       /* We don't currently understand any attributes. */
+       if (ctx->poic_tagattr != NULL)
+               return (NULL);
+
+       dict = prop_dictionary_create();
+       if (dict == NULL)
+               return (NULL);
+
+       if (ctx->poic_is_empty_element)
+               return (dict);
+
+       tmpkey = _PROP_MALLOC(_PROP_PDK_MAXKEY + 1, M_TEMP);
+       if (tmpkey == NULL)
+               goto bad;
+
+       for (;;) {
+               /* 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)
+                       break;
+
+               /* 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, _PROP_PDK_MAXKEY,
+                                               &keylen, &ctx->poic_cp) ==
+                                               FALSE)
+                       goto bad;
+
+               _PROP_ASSERT(keylen <= _PROP_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;
+
+               val = _prop_object_internalize_by_tag(ctx);
+               if (val == NULL)
+                       goto bad;
+
+               if (prop_dictionary_set(dict, tmpkey, val) == FALSE) {
+                       prop_object_release(val);
+                       goto bad;
+               }
+               prop_object_release(val);
+       }
+
+       _PROP_FREE(tmpkey, M_TEMP);
+       return (dict);
+
+ bad:
+       if (tmpkey != NULL)
+               _PROP_FREE(tmpkey, M_TEMP);
+       prop_object_release(dict);
+       return (NULL);
+}
+
+/*
+ * prop_dictionary_internalize --
+ *     Create a dictionary by parsing the NUL-terminated XML-style
+ *     representation.
+ */
+static prop_dictionary_t
+prop_dictionary_internalize_xml(const char *xml)
+{
+       prop_dictionary_t dict = 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 <dict>. */
+       if (_prop_object_internalize_find_tag(ctx, "dict",
+                                             _PROP_TAG_TYPE_START) == FALSE)
+               goto out;
+
+       dict = _prop_dictionary_internalize(ctx);
+       if (dict == NULL)
+               goto out;
+
+       /* We've advanced past </dict>.  Now we want </plist>. */
+       if (_prop_object_internalize_find_tag(ctx, "plist",
+                                             _PROP_TAG_TYPE_END) == FALSE) {
+               prop_object_release(dict);
+               dict = NULL;
+       }
+
+ out:
+       _prop_object_internalize_context_free(ctx);
+       return (dict);
+}
+
+static boolean_t
+_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 (prop_number_unsigned(pn))
+               sprintf(tmpstr, "0x%" PRIx64,
+                   prop_number_unsigned_integer_value(pn));
+       else
+               sprintf(tmpstr, "%" PRIi64, prop_number_integer_value(pn));
+
+       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);
+}
+
+static boolean_t
+_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 boolean_t
+_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.
+ */
+prop_object_t
+_prop_number_internalize(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 (NULL);
+
+       /*
+        * 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 (NULL);
+       } else if (ctx->poic_cp[0] == '0' && ctx->poic_cp[1] == 'x') {
+               if (_prop_number_internalize_unsigned(ctx, &pnv) == FALSE)
+                       return (NULL);
+       } else {
+               if (_prop_number_internalize_signed(ctx, &pnv) == FALSE &&
+                   _prop_number_internalize_unsigned(ctx, &pnv) == FALSE)
+                       return (NULL);
+       }
+
+       if (_prop_object_internalize_find_tag(ctx, "integer",
+                                             _PROP_TAG_TYPE_END) == FALSE)
+               return (NULL);
+
+       return (_prop_number_alloc(&pnv));
+}
+/*
+ * _prop_object_externalize_start_tag --
+ *     Append an XML-style start tag to the externalize buffer.
+ */
+boolean_t
+_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.
+ */
+boolean_t
+_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.
+ */
+boolean_t
+_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_encoded_cstring --
+ *     Append an encoded C string to the externalize buffer.
+ */
+boolean_t
+_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);
+}
+
+/*
+ * _prop_object_externalize_header --
+ *     Append the standard XML header to the externalize buffer.
+ */
+boolean_t
+_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.
+ */
+boolean_t
+_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_internalize_skip_comment --
+ *     Skip the body and end tag of a comment.
+ */
+static boolean_t
+_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.
+ */
+boolean_t
+_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.
+ */
+boolean_t
+_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.
+ */
+boolean_t
+_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_t   (*poi_intern)(
+                               struct _prop_object_internalize_context *);
+} _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;
+
+       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))
+                       return ((*poi->poi_intern)(ctx));
+       }
+
+       return (NULL);
+}
+
+/*
+ * _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);
+}
+
+static boolean_t
+_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);
+}
+
+/*
+ * _prop_string_internalize --
+ *     Parse a <string>...</string> and return the object created from the
+ *     external representation.
+ */
+prop_object_t
+_prop_string_internalize(struct _prop_object_internalize_context *ctx)
+{
+       prop_string_t string;
+       char *str;
+       size_t len, alen;
+
+       if (ctx->poic_is_empty_element)
+               return (prop_string_create());
+
+       /* No attributes recognized here. */
+       if (ctx->poic_tagattr != NULL)
+               return (NULL);
+
+       /* Compute the length of the result. */
+       if (_prop_object_internalize_decode_string(ctx, NULL, 0, &len,
+                                                  NULL) == FALSE)
+               return (NULL);
+
+       str = _PROP_MALLOC(len + 1, M_PROP_STRING);
+       if (str == NULL)
+               return (NULL);
+
+       if (_prop_object_internalize_decode_string(ctx, str, len, &alen,
+                                                  &ctx->poic_cp) == FALSE ||
+           alen != len) {
+               _PROP_FREE(str, M_PROP_STRING);
+               return (NULL);
+       }
+       str[len] = '\0';
+
+       if (_prop_object_internalize_find_tag(ctx, "string",
+                                             _PROP_TAG_TYPE_END) == FALSE) {
+               _PROP_FREE(str, M_PROP_STRING);
+               return (NULL);
+       }
+
+       string = _prop_string_alloc();
+       if (string == NULL) {
+               _PROP_FREE(str, M_PROP_STRING);
+               return (NULL);
+       }
+
+       string->ps_mutable = str;
+       string->ps_size = len;
+
+       return (string);
+}
+
+static boolean_t
+_prop_object_externalize(struct _prop_object_externalize_context *ctx,
+    prop_object_t o)
+{
+       switch (prop_object_type(o)) {
+       case PROP_TYPE_BOOL:
+               return _prop_bool_externalize(ctx, o);
+       case PROP_TYPE_NUMBER:
+               return _prop_number_externalize(ctx, o);
+       case PROP_TYPE_STRING:
+               return _prop_string_externalize(ctx, o);
+       case PROP_TYPE_DATA:
+               return _prop_data_externalize(ctx, o);
+       case PROP_TYPE_ARRAY:
+               return _prop_array_externalize(ctx, o);
+       case PROP_TYPE_DICTIONARY:
+               return _prop_dictionary_externalize(ctx, o);
+       case PROP_TYPE_DICT_KEYSYM:
+               return _prop_dict_keysym_externalize(ctx, o);
+       default:
+               return (FALSE);
+       }
+}
Index: distrib/sets/lists/comp/mi
===================================================================
RCS file: /cvsroot/src/distrib/sets/lists/comp/mi,v
retrieving revision 1.1032
diff -d -p -u -r1.1032 mi
--- distrib/sets/lists/comp/mi  1 Jun 2007 22:54:52 -0000       1.1032
+++ distrib/sets/lists/comp/mi  7 Jun 2007 13:30:30 -0000
@@ -1619,6 +1619,7 @@
./usr/include/poll.h                           comp-c-include
./usr/include/prop/prop_array.h                        comp-c-include
./usr/include/prop/prop_bool.h                 comp-c-include
+./usr/include/prop/prop_codec.h                comp-c-include
./usr/include/prop/prop_data.h                 comp-c-include
./usr/include/prop/prop_dictionary.h           comp-c-include
./usr/include/prop/prop_ingest.h               comp-c-include