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 <freza@NetBSD.org>
+ *
+ * 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 <freza@NetBSD.org>.
+ *
+ * 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 <freza@NetBSD.org>
+ *
+ * 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 <freza@NetBSD.org>.
+ *
+ * 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
