Index: common/include/prop/Makefile =================================================================== RCS file: /cvsroot/src/common/include/prop/Makefile,v retrieving revision 1.2 diff -d -p -u -u -r1.2 Makefile --- common/include/prop/Makefile 21 Aug 2006 04:13:28 -0000 1.2 +++ common/include/prop/Makefile 9 Jul 2007 20:55:17 -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 9 Jul 2007 20:55:17 -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 + * + * 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 -u -r1.4 proplib.h --- common/include/prop/proplib.h 22 Sep 2006 04:20:23 -0000 1.4 +++ common/include/prop/proplib.h 9 Jul 2007 20:55:17 -0000 @@ -46,6 +46,7 @@ #include #include +#include #include /* Index: common/lib/libprop/Makefile.inc =================================================================== RCS file: /cvsroot/src/common/lib/libprop/Makefile.inc,v retrieving revision 1.5 diff -d -p -u -u -r1.5 Makefile.inc --- common/lib/libprop/Makefile.inc 26 Oct 2006 05:02:12 -0000 1.5 +++ common/lib/libprop/Makefile.inc 9 Jul 2007 20:55:24 -0000 @@ -2,8 +2,20 @@ .PATH: ${.PARSEDIR} -SRCS+= prop_array.c prop_bool.c prop_data.c prop_dictionary.c \ - prop_dictionary_util.c prop_ingest.c prop_kern.c prop_number.c \ - prop_object.c prop_string.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_descent.c \ + prop_dictionary.c prop_dictionary_util.c prop_equals.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 -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 9 Jul 2007 20:55:24 -0000 @@ -43,34 +43,15 @@ #include #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, }; #define prop_object_is_array(x) \ @@ -78,11 +59,6 @@ static const struct _prop_object_type _p #define prop_array_is_immutable(x) (((x)->pa_flags & PA_F_IMMUTABLE) != 0) -struct _prop_array_iterator { - struct _prop_object_iterator pai_base; - unsigned int pai_index; -}; - #define EXPAND_STEP 16 static void @@ -110,99 +86,6 @@ _prop_array_free(void *v) _PROP_POOL_PUT(_prop_array_pool, pa); } -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; - prop_array_t array2 = v2; - unsigned int idx; - boolean_t rv = FALSE; - - if (! (prop_object_is_array(array1) && - prop_object_is_array(array2))) - return (FALSE); - - if (array1 == array2) - return (TRUE); - - if ((uintptr_t)array1 < (uintptr_t)array2) { - _PROP_RWLOCK_RDLOCK(array1->pa_rwlock); - _PROP_RWLOCK_RDLOCK(array2->pa_rwlock); - } else { - _PROP_RWLOCK_RDLOCK(array2->pa_rwlock); - _PROP_RWLOCK_RDLOCK(array1->pa_rwlock); - } - - if (array1->pa_count != array2->pa_count) - goto out; - - for (idx = 0; idx < array1->pa_count; idx++) { - if (prop_object_equals(array1->pa_array[idx], - array2->pa_array[idx]) == FALSE) - goto out; - } - - rv = TRUE; - - out: - _PROP_RWLOCK_UNLOCK(array1->pa_rwlock); - _PROP_RWLOCK_UNLOCK(array2->pa_rwlock); - return (rv); -} - static prop_array_t _prop_array_alloc(unsigned int capacity) { @@ -644,144 +527,11 @@ prop_array_remove(prop_array_t pa, unsig boolean_t prop_array_equals(prop_array_t array1, prop_array_t array2) { + if (! (prop_object_is_array(array1) && + prop_object_is_array(array2))) + return (FALSE); - return (_prop_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 ... 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 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 . */ - 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 . Now we want . */ - 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); + return (prop_object_equals(array1, array2)); } #if !defined(_KERNEL) && !defined(_STANDALONE) 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 -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 9 Jul 2007 20:55:24 -0000 @@ -51,16 +51,10 @@ _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, }; #define prop_object_is_bool(x) \ @@ -78,33 +72,6 @@ _prop_bool_free(void *v _PROP_ARG_UNUSED /* XXX forced assertion failure? */ } -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; - prop_bool_t b2 = v2; - - if (! (prop_object_is_bool(b1) && - prop_object_is_bool(b2))) - return (FALSE); - - /* - * Since we only ever allocate one true and one false, - * save ourselves a couple of memory operations. - */ - return (b1 == b2); -} - static prop_bool_t _prop_bool_alloc(boolean_t val) { @@ -184,31 +151,8 @@ prop_bool_true(prop_bool_t pb) boolean_t prop_bool_equals(prop_bool_t b1, prop_bool_t b2) { + if (! (prop_object_is_bool(b1) && prop_object_is_bool(b2))) + return (FALSE); return (_prop_bool_equals(b1, b2)); } - -/* - * _prop_bool_internalize -- - * Parse a or 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 9 Jul 2007 20:55:24 -0000 @@ -0,0 +1,308 @@ +/* $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 . + * + * 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 + +#include + +#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 + +const char * +_prop_skip_spaces(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(const u_char *plist, size_t len) +{ + int i; + + for (i = 0; prop_codec_table[i]; i++) + if (prop_codec_table[i]->codec_probe(plist, len)) + return (prop_codec_table[i]); + + return (NULL); +} + +#define CHUNKSIZE 1024 + +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 pq; + prop_object_t po = NULL; + size_t len = strlen(str); + size_t now = 0; + size_t n; + + if (prop_parser_create(co, &pa)) + goto fail_0; + + while (now < len) { + if ((n = len - now) > CHUNKSIZE) + n = CHUNKSIZE; + + if (prop_parser_exec(co, pa, (const u_char *)(str + now), n)) + goto fail_1; + + if ((po = prop_parser_yield(co, pa)) != NULL) + break; + + now += n; + } + if (po == 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); +} + +#undef CHUNKSIZE + +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_spaces(str)) == NULL) + return (NULL); + + if ((co = prop_codec_guess((const u_char *)str, strlen(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_spaces(str)) == NULL) + return (NULL); + + if ((co = prop_codec_guess((const u_char *)str, strlen(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); + + if (pd == NULL || prop_object_type(pd) != PROP_TYPE_DICTIONARY) + return (NULL); + + _PROP_ASSERT(co); + _PROP_ASSERT(co->codec_externalize_compound); + + return ((co->codec_externalize_compound)(pd)); +} + +char * +prop_array_externalize(prop_array_t pa) +{ + prop_codec_t co = prop_codec_lookup(_PROP_DEFAULT_CODEC); + + if (pa == NULL || prop_object_type(pa) != PROP_TYPE_ARRAY) + return (NULL); + + _PROP_ASSERT(co); + _PROP_ASSERT(co->codec_externalize_compound); + + return ((co->codec_externalize_compound)(pa)); +} + +char * +prop_codec_externalize(prop_codec_t co, prop_object_t po) +{ + prop_type_t ty = prop_object_type(po); + + if (po == NULL || + (ty != PROP_TYPE_ARRAY && ty != PROP_TYPE_DICTIONARY)) + return (NULL); + + _PROP_ASSERT(co); + _PROP_ASSERT(co->codec_externalize_compound); + + return ((co->codec_externalize_compound)(po)); +} + +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 9 Jul 2007 20:55:24 -0000 @@ -0,0 +1,60 @@ +/* $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 + * + * 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; + + /* Detect if we can parse this. */ + boolean_t (*codec_probe)(const u_char *, size_t); + + /* Externalize a dictionary or an array. */ + char * (*codec_externalize_compound)(prop_object_t); + + prop_dictionary_t (*codec_dictionary_internalize)(const char *); + prop_array_t (*codec_array_internalize)(const char *); + + 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 -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 9 Jul 2007 20:55:24 -0000 @@ -50,36 +50,16 @@ #include #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, }; #define prop_object_is_data(x) \ @@ -95,109 +75,26 @@ _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) +boolean_t +_prop_data_equals(prop_object_t o1, prop_object_t o2) { - prop_data_t pd1 = v1; - prop_data_t pd2 = v2; - - if (! (prop_object_is_data(pd1) && - prop_object_is_data(pd2))) - return (FALSE); + prop_data_t pd1 = o1; + prop_data_t pd2 = o2; - if (pd1 == pd2) - return (TRUE); if (pd1->pd_size != pd2->pd_size) return (FALSE); + if (pd1->pd_size == 0) { _PROP_ASSERT(pd1->pd_immutable == NULL); _PROP_ASSERT(pd2->pd_immutable == NULL); return (TRUE); } + return (memcmp(pd1->pd_immutable, pd2->pd_immutable, pd1->pd_size) == 0); } -static prop_data_t +prop_data_t _prop_data_alloc(void) { prop_data_t pd; @@ -356,6 +253,11 @@ prop_data_data_nocopy(prop_data_t pd) boolean_t prop_data_equals(prop_data_t pd1, prop_data_t pd2) { + if (! (prop_object_is_data(pd1) && prop_object_is_data(pd2))) + return (FALSE); + + if (pd1 == pd2) + return (TRUE); return (_prop_data_equals(pd1, pd2)); } @@ -376,230 +278,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 ... 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 -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 9 Jul 2007 20:55:24 -0000 @@ -82,59 +82,27 @@ 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_keysym16_pool, PDK_SIZE_16, "pdict16") -_PROP_POOL_INIT(_prop_dictionary_keysym32_pool, PDK_SIZE_32, "pdict32") -_PROP_POOL_INIT(_prop_dictionary_keysym128_pool, PDK_SIZE_128, "pdict128") - -struct _prop_dict_entry { - prop_dictionary_keysym_t pde_key; - prop_object_t pde_objref; -}; - -struct _prop_dictionary { - struct _prop_object pd_obj; - _PROP_RWLOCK_DECL(pd_rwlock) - struct _prop_dict_entry *pd_array; - unsigned int pd_capacity; - unsigned int pd_count; - int pd_flags; - - uint32_t pd_version; -}; - -#define PD_F_IMMUTABLE 0x01 /* dictionary is immutable */ - _PROP_POOL_INIT(_prop_dictionary_pool, sizeof(struct _prop_dictionary), "propdict") _PROP_MALLOC_DEFINE(M_PROP_DICT, "prop dictionary", "property dictionary container object") +_PROP_POOL_INIT(_prop_dictionary_keysym16_pool, PDK_SIZE_16, "pdict16") +_PROP_POOL_INIT(_prop_dictionary_keysym32_pool, PDK_SIZE_32, "pdict32") +_PROP_POOL_INIT(_prop_dictionary_keysym128_pool, PDK_SIZE_128, "pdict128") + 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, }; #define prop_object_is_dictionary(x) \ @@ -145,11 +113,6 @@ static const struct _prop_object_type _p #define prop_dictionary_is_immutable(x) \ (((x)->pd_flags & PD_F_IMMUTABLE) != 0) -struct _prop_dictionary_iterator { - struct _prop_object_iterator pdi_base; - unsigned int pdi_index; -}; - /* * Dictionary key symbols are immutable, and we are likely to have many * duplicated key symbols. So, to save memory, we unique'ify key symbols @@ -212,34 +175,11 @@ _prop_dict_keysym_free(void *v) _prop_dict_keysym_put(pdk); } -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) +boolean_t +_prop_dict_keysym_equals(prop_object_t o1, prop_object_t o2) { - prop_dictionary_keysym_t pdk1 = v1; - prop_dictionary_keysym_t pdk2 = v2; - - if (! (prop_object_is_dictionary_keysym(pdk1) && - prop_object_is_dictionary_keysym(pdk2))) - return (FALSE); + prop_dictionary_keysym_t pdk1 = o1; + prop_dictionary_keysym_t pdk2 = o2; /* * There is only ever one copy of a keysym at any given time, @@ -345,112 +285,6 @@ _prop_dictionary_free(void *v) _PROP_POOL_PUT(_prop_dictionary_pool, pd); } -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; - prop_dictionary_t dict2 = v2; - const struct _prop_dict_entry *pde1, *pde2; - unsigned int idx; - boolean_t rv = FALSE; - - if (! (prop_object_is_dictionary(dict1) && - prop_object_is_dictionary(dict2))) - return (FALSE); - - if (dict1 == dict2) - return (TRUE); - - if ((uintptr_t)dict1 < (uintptr_t)dict2) { - _PROP_RWLOCK_RDLOCK(dict1->pd_rwlock); - _PROP_RWLOCK_RDLOCK(dict2->pd_rwlock); - } else { - _PROP_RWLOCK_RDLOCK(dict2->pd_rwlock); - _PROP_RWLOCK_RDLOCK(dict1->pd_rwlock); - } - - if (dict1->pd_count != dict2->pd_count) - goto out; - - for (idx = 0; idx < dict1->pd_count; idx++) { - pde1 = &dict1->pd_array[idx]; - pde2 = &dict2->pd_array[idx]; - - if (prop_dictionary_keysym_equals(pde1->pde_key, - pde2->pde_key) == FALSE) - goto out; - if (prop_object_equals(pde1->pde_objref, - pde2->pde_objref) == FALSE) - goto out; - } - - rv = TRUE; - - out: - _PROP_RWLOCK_UNLOCK(dict1->pd_rwlock); - _PROP_RWLOCK_UNLOCK(dict2->pd_rwlock); - return (rv); -} - static prop_dictionary_t _prop_dictionary_alloc(unsigned int capacity) { @@ -511,9 +345,9 @@ _prop_dictionary_iterator_next_object(vo { struct _prop_dictionary_iterator *pdi = v; prop_dictionary_t pd = pdi->pdi_base.pi_obj; - prop_dictionary_keysym_t pdk = NULL; _PROP_ASSERT(prop_object_is_dictionary(pd)); + pdi->pdi_keysym = NULL; _PROP_RWLOCK_RDLOCK(pd->pd_rwlock); @@ -525,12 +359,12 @@ _prop_dictionary_iterator_next_object(vo if (pdi->pdi_index == pd->pd_count) goto out; /* we've iterated all objects */ - pdk = pd->pd_array[pdi->pdi_index].pde_key; + pdi->pdi_keysym = pd->pd_array[pdi->pdi_index].pde_key; pdi->pdi_index++; out: _PROP_RWLOCK_UNLOCK(pd->pd_rwlock); - return (pdk); + return (pdi->pdi_keysym); } static void @@ -546,6 +380,11 @@ _prop_dictionary_iterator_reset(void *v) pdi->pdi_index = 0; pdi->pdi_base.pi_version = pd->pd_version; + if (pd->pd_array) + pdi->pdi_keysym = pd->pd_array[0].pde_key; + else + pdi->pdi_keysym = NULL; + _PROP_RWLOCK_UNLOCK(pd->pd_rwlock); } @@ -697,6 +536,19 @@ prop_dictionary_iterator(prop_dictionary return (&pdi->pdi_base); } +/* + * prop_dictionary_make_immutable -- + * + * Mark the dictionary as immutable. + */ +void +prop_dictionary_make_immutable(prop_dictionary_t pd) +{ + _PROP_RWLOCK_WRLOCK(pd->pd_rwlock); + pd->pd_flags |= PD_F_IMMUTABLE; + _PROP_RWLOCK_UNLOCK(pd->pd_rwlock); +} + /* * prop_dictionary_all_keys -- * Return an array containing a snapshot of all of the keys @@ -998,8 +850,11 @@ prop_dictionary_remove_keysym(prop_dicti boolean_t prop_dictionary_equals(prop_dictionary_t dict1, prop_dictionary_t dict2) { + if (! (prop_object_is_dictionary(dict1) && + prop_object_is_dictionary(dict2))) + return (FALSE); - return (_prop_dictionary_equals(dict1, dict2)); + return (prop_object_equals(dict1, dict2)); } /* @@ -1025,178 +880,13 @@ boolean_t prop_dictionary_keysym_equals(prop_dictionary_keysym_t pdk1, prop_dictionary_keysym_t pdk2) { + if (! (prop_object_is_dictionary_keysym(pdk1) && + prop_object_is_dictionary_keysym(pdk2))) + return (FALSE); return (_prop_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 ... 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 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 . */ - 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 . Now we want . */ - 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 -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 9 Jul 2007 20:55:24 -0000 @@ -50,19 +50,11 @@ #include #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,16 +64,10 @@ 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, }; #define prop_object_is_number(x) \ @@ -161,47 +147,11 @@ _prop_number_free(void *v) _PROP_POOL_PUT(_prop_number_pool, pn); } -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) +boolean_t +_prop_number_equals(prop_object_t o1, prop_object_t o2) { - prop_number_t num1 = v1; - prop_number_t num2 = v2; - - if (! (prop_object_is_number(num1) && - prop_object_is_number(num2))) - return (FALSE); - - /* - * There is only ever one copy of a number object at any given - * time, so we can reduce this to a simple pointer equality check - * in the common case. - */ - if (num1 == num2) - return (TRUE); + prop_number_t num1 = o1; + prop_number_t num2 = o2; /* * If the numbers are the same signed-ness, then we know they @@ -239,7 +189,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; @@ -436,6 +386,16 @@ prop_number_unsigned_integer_value(prop_ boolean_t prop_number_equals(prop_number_t num1, prop_number_t num2) { + if (! (prop_object_is_number(num1) && prop_object_is_number(num2))) + return (FALSE); + + /* + * There is only ever one copy of a number object at any given + * time, so we can reduce this to a simple pointer equality check + * in the common case. + */ + if (num1 == num2) + return (TRUE); return (_prop_number_equals(num1, num2)); } @@ -476,91 +436,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 ... 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 -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 9 Jul 2007 20:55:25 -0000 @@ -102,150 +102,46 @@ _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_extern_alloc -- + * Allocate an externalize context. */ -boolean_t -_prop_object_externalize_append_encoded_cstring( - struct _prop_object_externalize_context *ctx, const char *cp) +struct _prop_extern * +_prop_extern_alloc(void) { + struct _prop_extern *ctx; - while (*cp != '\0') { - switch (*cp) { - case '<': - if (_prop_object_externalize_append_cstring(ctx, - "<") == FALSE) - return (FALSE); - break; - case '>': - if (_prop_object_externalize_append_cstring(ctx, - ">") == FALSE) - return (FALSE); - break; - case '&': - if (_prop_object_externalize_append_cstring(ctx, - "&") == 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 -- + * _prop_extern_putc -- * Append a single character to the externalize buffer. */ boolean_t -_prop_object_externalize_append_char( - struct _prop_object_externalize_context *ctx, unsigned char c) +_prop_extern_putc(struct _prop_extern *ctx, unsigned char c) { - _PROP_ASSERT(ctx->poec_capacity != 0); _PROP_ASSERT(ctx->poec_buf != NULL); _PROP_ASSERT(ctx->poec_len <= ctx->poec_capacity); if (ctx->poec_len == ctx->poec_capacity) { char *cp = _PROP_REALLOC(ctx->poec_buf, - ctx->poec_capacity + BUF_EXPAND, + 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,457 +151,29 @@ _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[] = -"\n" -"\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_externalize_context_alloc -- - * Allocate an externalize context. - */ -struct _prop_object_externalize_context * -_prop_object_externalize_context_alloc(void) -{ - struct _prop_object_externalize_context *ctx; - - ctx = _PROP_MALLOC(sizeof(*ctx), M_TEMP); - if (ctx != NULL) { - ctx->poec_buf = _PROP_MALLOC(BUF_EXPAND, M_TEMP); - if (ctx->poec_buf == NULL) { - _PROP_FREE(ctx, M_TEMP); - return (NULL); - } - ctx->poec_len = 0; - ctx->poec_capacity = BUF_EXPAND; - ctx->poec_depth = 0; - } - return (ctx); -} - -/* - * _prop_object_externalize_context_free -- - * Free an externalize context. - */ -void -_prop_object_externalize_context_free( - struct _prop_object_externalize_context *ctx) -{ - - /* Buffer is always freed by the caller. */ - _PROP_FREE(ctx, M_TEMP); -} - -/* - * _prop_object_internalize_skip_comment -- - * Skip the body and end tag of a comment. - */ -static 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. + * _prop_extern_puts -- + * Append a C string to the externalize buffer. */ boolean_t -_prop_object_internalize_find_tag(struct _prop_object_internalize_context *ctx, - const char *tag, _prop_tag_type_t type) +_prop_extern_puts(struct _prop_extern *ctx, const char *cp) { - 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 != '>') + while (*cp != '\0') { + if (_prop_extern_putc(ctx, (unsigned char) *cp) == FALSE) 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(" %-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 _scn_frame *e) +{ + if (e->se_object != NULL) + prop_object_release(e->se_object); + _PROP_FREE(e, M_TEMP); +} + +static boolean_t +parser_frame_enter(struct _scn_parser *pa, prop_object_t o) +{ + struct _scn_frame *e; + + if (o == NULL) + return (TRUE); + + e = _PROP_MALLOC(sizeof(struct _scn_frame), M_TEMP); + if (e == NULL) + return (TRUE); + + memset(e, 0, sizeof(struct _scn_frame)); + e->se_object = o; + + SIMPLEQ_INSERT_HEAD(&pa->pa_stack, e, se_link); + return (FALSE); +} + +static int +parser_frame_store(struct _scn_parser *pa, prop_object_t o) +{ + struct _scn_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: + if (prop_array_add(the, o) == FALSE) + return (ENOMEM); + break; + + case PROP_TYPE_DICTIONARY: + if (prop_dictionary_set(the, e->se_symbol, o) == FALSE) + return (ENOMEM); + break; + + default: + VERBOSE(" parser: wrong object on stack, not compound"); + return (EINVAL); + } + + prop_object_release(o); + return (0); +} + +static int +parser_frame_leave(struct _scn_parser *pa) +{ + struct _scn_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 _scn_parser *pa; + + pa = _PROP_MALLOC(sizeof(struct _scn_parser), M_TEMP); + if (pa == NULL) + return (ENOMEM); + memset(pa, 0, sizeof(struct _scn_parser)); + + SIMPLEQ_INIT(&pa->pa_tokens); + SIMPLEQ_INIT(&pa->pa_freeq); + 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 _scn_parser *pa = arg; + struct _scn_token *t; + struct _scn_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(pa, t, TRUE); + } + + /* Free any cached tokens. */ + while ((t = SIMPLEQ_FIRST(&pa->pa_freeq)) != NULL) { + SIMPLEQ_REMOVE_HEAD(&pa->pa_freeq, tok_link); + parser_token_free(pa, 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 _scn_parser *pa = arg; + struct _scn_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 _scn_frame *frame; + struct _scn_token *t; + struct _scn_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); + + memcpy(frame->se_symbol, (const char *)t->tok_string, + strlen((const char *)t->tok_string) + 1); + + 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(pa, t, FALSE); + + goto advance; + } else { + goto dispatch; + } + /* UNREACHED */ +} + +static boolean_t +format_string_quote(struct _prop_extern *ec, const char *s) +{ + const char *t; + boolean_t ret; + + if (! _prop_extern_putc(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 = _prop_extern_puts(ec, t); + else + ret = _prop_extern_putc(ec, *s); + if (ret == FALSE) + return (TRUE); + + s++; + } + + if (! _prop_extern_putc(ec, '"')) + return (TRUE); + return (FALSE); +} + +static boolean_t +format_data_base64(struct _prop_extern *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 */ + + _prop_extern_putc(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 = _prop_extern_putc(ec, base64abc[b[i] >> 2]); + n = b[i] & 0x03; + break; + case 1: + /* LINTED: b[] is u_char */ + ret = _prop_extern_putc(ec, + base64abc[(n << 4) | (b[i] >> 4)]); + n = b[i] & 0x0f; + break; + case 2: + /* LINTED: b[] is u_char */ + if (! _prop_extern_putc(ec, + base64abc[(n << 2) | (b[i] >> 6)]) || + ! _prop_extern_putc(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 (!_prop_extern_putc(ec, base64abc[n << 4])) + return (TRUE); + break; + case 2: + if (!_prop_extern_putc(ec, base64abc[n << 2])) + return (TRUE); + break; + } + + /* Finally, pad to multiple of four characters of encoded text. */ + switch (size % 3) { + case 1: + if (!_prop_extern_putc(ec, '=')) + return (TRUE); + /* FALLTHROUGH */ + case 2: + if (!_prop_extern_putc(ec, '=')) + return (TRUE); + } + + return (FALSE); +} + +static int +format_indent(struct _prop_extern *ec) +{ + int i; + + for (i = 0; i < ec->poec_depth; i++) + if (! _prop_extern_putc(ec, '\t')) { + VERBOSE(" format: ENOMEM indent"); + return (ENOMEM); + } + return (0); +} + +static int +prop_scn_externalize_single(struct _prop_descent *de, struct _prop_extern *ec, + prop_object_t po, int visit) +{ + char buf[32]; + int ret; + + if (visit != _PROP_DESCENT_VISIT_LEAVE) { + if (prop_descent_container_type(de, PROP_TYPE_DICTIONARY)) { + if ((ret = format_indent(ec)) != 0) + return (ret); + + if (! _prop_extern_puts(ec, + prop_descent_container_key(de)) || + ! _prop_extern_putc(ec, '\t')) + return (ENOMEM); + } + if (prop_descent_container_type(de, PROP_TYPE_ARRAY)) { + if ((ret = format_indent(ec)) != 0) + return (ret); + } + } + + switch (prop_object_type(po)) { + case PROP_TYPE_BOOL: + VERBOSE(" format: bool [%d]", ec->poec_depth); + if (prop_bool_true(po)) { + if (! _prop_extern_puts(ec, "True")) + return (ENOMEM); + } else { + if (! _prop_extern_puts(ec, "False")) + return (ENOMEM); + } + break; + + case PROP_TYPE_NUMBER: + VERBOSE(" format: number [%d]", ec->poec_depth); + if (prop_number_unsigned(po)) + snprintf(buf, sizeof(buf), "#%" PRIx64, + prop_number_unsigned_integer_value(po)); + else + snprintf(buf, sizeof(buf), "%" PRId64, + prop_number_integer_value(po)); + + if (! _prop_extern_puts(ec, buf)) + return (ENOMEM); + break; + + case PROP_TYPE_STRING: + VERBOSE(" format: string [%d]", ec->poec_depth); + if (format_string_quote(ec, prop_string_cstring_nocopy(po))) + return (ENOMEM); + break; + + case PROP_TYPE_DATA: + VERBOSE(" format: data [%d]", ec->poec_depth); + if (format_data_base64(ec, prop_data_data_nocopy(po), + prop_data_size(po))) + return (ENOMEM); + break; + + case PROP_TYPE_ARRAY: + VERBOSE(" format: array [%d]", ec->poec_depth); + if (visit == _PROP_DESCENT_VISIT_ENTER) { + if (! _prop_extern_putc(ec, '[')) + return (ENOMEM); + ec->poec_depth++; + } else + if (visit == _PROP_DESCENT_VISIT_LEAVE) { + ec->poec_depth--; + + if ((ret = format_indent(ec)) != 0) + return (ret); + if (! _prop_extern_putc(ec, ']')) + return (ENOMEM); + } + break; + + case PROP_TYPE_DICTIONARY: + VERBOSE(" format: dictionary [%d]", ec->poec_depth); + if (visit == _PROP_DESCENT_VISIT_ENTER) { + if (! _prop_extern_putc(ec, '{')) + return (ENOMEM); + ec->poec_depth++; + } else + if (visit == _PROP_DESCENT_VISIT_LEAVE) { + ec->poec_depth--; + + if ((ret = format_indent(ec)) != 0) + return (ret); + if (! _prop_extern_putc(ec, '}')) + return (ENOMEM); + } + break; + + default: + VERBOSE(" format: object %p wrong type %d", po, + prop_object_type(po)); + return (EINVAL); + } + if (! _prop_extern_putc(ec, '\n')) + return (ENOMEM); + + return (0); +} + +static char * +prop_scn_externalize(prop_object_t o) +{ + struct _prop_descent de; + struct _prop_extern *ec; + char *s; + prop_object_t po; + int visit; + int ret; + + if ((ec = _prop_extern_alloc()) == NULL) + return (NULL); + + prop_descent_init(&de, o); + + while ((ret = prop_descent_next(&de, &po, &visit)) == 0) { + if (prop_scn_externalize_single(&de, ec, po, visit)) + goto lose; + } + if (ret != ENOENT) + goto lose; + + /* Prepare result for the caller. */ + ec->poec_buf[ec->poec_len] = '\0'; + s = ec->poec_buf; + _prop_extern_free(ec); + + _PROP_ASSERT(de.de_pend == NULL); + return (s); + + lose: + prop_descent_free(&de, NULL, NULL); + _PROP_FREE(ec->poec_buf, M_TEMP); + _prop_extern_free(ec); + return (NULL); +} + +/* ARGSUSED */ +static boolean_t +prop_scn_probe(const u_char *plist, size_t len _PROP_ARG_UNUSED) +{ + const char *s = (const char *)plist; + + if ((s = _prop_skip_spaces(s)) == NULL) + return (FALSE); + + if (*s == ';' || *s == '{' || *s == '[') + return (TRUE); + + return (FALSE); +} + +const struct _prop_codec prop_codec_scn = { + .codec_name = "scn", + .codec_probe = prop_scn_probe, + .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 -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 9 Jul 2007 20:55:25 -0000 @@ -39,36 +39,16 @@ #include #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, }; #define prop_object_is_string(x) \ @@ -85,43 +65,20 @@ _prop_string_free(void *v) _PROP_POOL_PUT(_prop_string_pool, 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) +boolean_t +_prop_string_equals(prop_object_t o1, prop_object_t o2) { - prop_string_t str1 = v1; - prop_string_t str2 = v2; - - if (! (prop_object_is_string(str1) && - prop_object_is_string(str2))) - return (FALSE); + prop_string_t str1 = o1; + prop_string_t str2 = o2; - if (str1 == str2) - return (TRUE); if (str1->ps_size != str2->ps_size) return (FALSE); + return (strcmp(prop_string_contents(str1), prop_string_contents(str2)) == 0); } -static prop_string_t +prop_string_t _prop_string_alloc(void) { prop_string_t ps; @@ -389,6 +346,11 @@ prop_string_append_cstring(prop_string_t boolean_t prop_string_equals(prop_string_t str1, prop_string_t str2) { + if (! (prop_object_is_string(str1) && prop_object_is_string(str2))) + return (FALSE); + + if (str1 == str2) + return (TRUE); return (_prop_string_equals(str1, str2)); } @@ -407,57 +369,3 @@ prop_string_equals_cstring(prop_string_t return (strcmp(prop_string_contents(ps), cp) == 0); } - -/* - * _prop_string_internalize -- - * Parse a ... 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 9 Jul 2007 20:55:25 -0000 @@ -0,0 +1,212 @@ +/* $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 +#include +#include +#include +#include + +#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 + +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 +#include +#include +#include +#include + +#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 +#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 +#define _PROP_ARG_UNUSED __unused +#define _PROP_PREDICT_FALSE(e) __predict_false(e) +#define _PROP_PREDICT_TRUE(e) __predict_true(e) +#else +#define _PROP_ARG_UNUSED /* delete */ +#define _PROP_PREDICT_FALSE(e) (e) +#define _PROP_PREDICT_TRUE(e) (e) +#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 9 Jul 2007 20:55:26 -0000 @@ -0,0 +1,1600 @@ +/* $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 + +#include "prop_codec_impl.h" +#include "prop_descent_impl.h" +#include "prop_object_impl.h" + +#if defined(_KERNEL) +#include +#elif defined(_STANDALONE) +#include +#include +#else +#include +#include +#include +#endif + +static boolean_t _prop_xml_start_tag(struct _prop_extern *, const char *); +static boolean_t _prop_xml_end_tag(struct _prop_extern *, const char *); +static boolean_t _prop_xml_empty_tag(struct _prop_extern *, const char *); +static boolean_t _prop_xml_append_encoded_cstring(struct _prop_extern *, + const char *); +static boolean_t _prop_xml_header(struct _prop_extern *); +static boolean_t _prop_xml_footer(struct _prop_extern *); + +typedef enum { + _PROP_TAG_TYPE_START, /* e.g. */ + _PROP_TAG_TYPE_END, /* e.g. */ + _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 prop_dictionary_t prop_dictionary_internalize_xml(const char *); +static prop_array_t prop_array_internalize_xml(const char *); +static boolean_t prop_probe_xml(const u_char *, size_t); +static char *prop_xml_externalize(prop_object_t); + +const struct _prop_codec prop_codec_xml = { + .codec_name = "xml", + .codec_probe = prop_probe_xml, + .codec_dictionary_internalize = prop_dictionary_internalize_xml, + .codec_array_internalize = prop_array_internalize_xml, + .codec_externalize_compound = prop_xml_externalize, +}; + +/* ARGSUSED */ +static boolean_t +prop_probe_xml(const u_char *plist, size_t len _PROP_ARG_UNUSED) +{ + const char *s = (const char *)plist; + + if ((s = _prop_skip_spaces(s)) == NULL) + return (FALSE); + + if (*s == '<') + return (TRUE); + + return (FALSE); +} + +/* + * _prop_array_internalize -- + * Parse an ... 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 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 . */ + 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 . Now we want . */ + 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_extern *ctx, + void *v) +{ + prop_bool_t pb = v; + + return (_prop_xml_empty_tag(ctx, + prop_bool_true(pb) ? "true" : "false")); +} + + +/* + * _prop_bool_internalize -- + * Parse a or 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_extern *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_xml_empty_tag(ctx, "data")); + + if (_prop_xml_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_extern_putc(ctx, _prop_data_base64[output[0]]) || + ! _prop_extern_putc(ctx, _prop_data_base64[output[1]]) || + ! _prop_extern_putc(ctx, _prop_data_base64[output[2]]) || + ! _prop_extern_putc(ctx, _prop_data_base64[output[3]])) + 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_extern_putc(ctx, _prop_data_base64[output[0]]) || + ! _prop_extern_putc(ctx, _prop_data_base64[output[1]]) || + ! _prop_extern_putc(ctx, srclen == 1 ? _prop_data_pad64 : + _prop_data_base64[output[2]]) || + ! _prop_extern_putc(ctx, _prop_data_pad64)) + return (FALSE); + } + + if (_prop_xml_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 ... 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_extern *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_xml_start_tag(ctx, "string") == FALSE || + _prop_xml_append_encoded_cstring(ctx, s) == FALSE || + _prop_xml_end_tag(ctx, "string") == FALSE) + return (FALSE); + + return (TRUE); +} + +/* + * _prop_dictionary_internalize -- + * Parse a ... 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 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 . */ + 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 . Now we want . */ + 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_extern *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_xml_start_tag(ctx, "integer") == FALSE || + _prop_extern_puts(ctx, tmpstr) == FALSE || + _prop_xml_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 ... 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_xml_start_tag( + struct _prop_extern *ctx, const char *tag) +{ + unsigned int i; + + for (i = 0; i < ctx->poec_depth; i++) { + if (_prop_extern_putc(ctx, '\t') == FALSE) + return (FALSE); + } + if (_prop_extern_putc(ctx, '<') == FALSE || + _prop_extern_puts(ctx, tag) == FALSE || + _prop_extern_putc(ctx, '>') == FALSE) + return (FALSE); + + return (TRUE); +} + +/* + * _prop_object_externalize_end_tag -- + * Append an XML-style end tag to the externalize buffer. + */ +boolean_t +_prop_xml_end_tag( + struct _prop_extern *ctx, const char *tag) +{ + + if (_prop_extern_putc(ctx, '<') == FALSE || + _prop_extern_putc(ctx, '/') == FALSE || + _prop_extern_puts(ctx, tag) == FALSE || + _prop_extern_putc(ctx, '>') == FALSE || + _prop_extern_putc(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_xml_empty_tag( + struct _prop_extern *ctx, const char *tag) +{ + unsigned int i; + + for (i = 0; i < ctx->poec_depth; i++) { + if (_prop_extern_putc(ctx, '\t') == FALSE) + return (FALSE); + } + + if (_prop_extern_putc(ctx, '<') == FALSE || + _prop_extern_puts(ctx, tag) == FALSE || + _prop_extern_putc(ctx, '/') == FALSE || + _prop_extern_putc(ctx, '>') == FALSE || + _prop_extern_putc(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_xml_append_encoded_cstring( + struct _prop_extern *ctx, const char *cp) +{ + + while (*cp != '\0') { + switch (*cp) { + case '<': + if (_prop_extern_puts(ctx, "<") == FALSE) + return (FALSE); + break; + case '>': + if (_prop_extern_puts(ctx, ">") == FALSE) + return (FALSE); + break; + case '&': + if (_prop_extern_puts(ctx, "&") == FALSE) + return (FALSE); + break; + default: + if (! _prop_extern_putc(ctx, (unsigned char) *cp)) + return (FALSE); + break; + } + cp++; + } + + return (TRUE); +} + +/* + * _prop_object_externalize_header -- + * Append the standard XML header to the externalize buffer. + */ +boolean_t +_prop_xml_header(struct _prop_extern *ctx) +{ + static const char _plist_xml_header[] = +"\n" +"\n"; + + if (_prop_extern_puts(ctx, _plist_xml_header) == FALSE || + _prop_xml_start_tag(ctx, "plist version=\"1.0\"") == FALSE || + _prop_extern_putc(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_xml_footer(struct _prop_extern *ctx) +{ + + if (_prop_xml_end_tag(ctx, "plist") == FALSE || + _prop_extern_putc(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("