From 6256b34ccc56df0c0dc3c68199f56e55cc597894 Mon Sep 17 00:00:00 2001 From: Juan RP Date: Tue, 20 Apr 2010 14:22:38 +0200 Subject: [PATCH] Some changes that will appear in 0.5.0: * Add proplib-0.4.1 source and use it in XBPS. This is to avoid an external dependency, so that we depend on the features of the internal library. This also means that proplib is not required anymore. * Added support to read/write gzip compressed plists by default, thanks to proplib-0.4 that gained new functionality. That means that from now, XBPS will be able to write compressed gzip plist files for all metadata related work. This will vastly reduce bandwidth required for fetching remote repo's pkg index file and binary packages. --HG-- extra : convert_revision : xtraeme%40gmail.com-20100420122238-zcb85rudt9p34e10 --- 3RDPARTY | 5 + NEWS | 9 + README | 4 +- bin/xbps-bin/check.c | 4 +- bin/xbps-bin/show-deps.c | 2 +- bin/xbps-bin/show-info-files.c | 4 +- bin/xbps-repo/index.c | 4 +- bin/xbps-repo/repository.c | 2 +- bin/xbps-uhelper/main.c | 10 +- include/queue.h | 697 +++++++++ include/xbps_api.h | 4 +- lib/Makefile | 27 +- lib/config_files.c | 2 +- lib/plist.c | 6 +- lib/portableproplib/prop/prop_array.h | 142 ++ lib/portableproplib/prop/prop_bool.h | 49 + lib/portableproplib/prop/prop_data.h | 56 + lib/portableproplib/prop/prop_dictionary.h | 148 ++ lib/portableproplib/prop/prop_ingest.h | 91 ++ lib/portableproplib/prop/prop_number.h | 57 + lib/portableproplib/prop/prop_object.h | 67 + lib/portableproplib/prop/prop_string.h | 62 + lib/portableproplib/prop/proplib.h | 45 + lib/portableproplib/prop_array.c | 1044 +++++++++++++ lib/portableproplib/prop_array_util.c | 240 +++ lib/portableproplib/prop_bool.c | 223 +++ lib/portableproplib/prop_data.c | 616 ++++++++ lib/portableproplib/prop_dictionary.c | 1542 ++++++++++++++++++++ lib/portableproplib/prop_dictionary_util.c | 208 +++ lib/portableproplib/prop_ingest.c | 159 ++ lib/portableproplib/prop_number.c | 576 ++++++++ lib/portableproplib/prop_object.c | 1241 ++++++++++++++++ lib/portableproplib/prop_object_impl.h | 274 ++++ lib/portableproplib/prop_rb.c | 1056 ++++++++++++++ lib/portableproplib/prop_rb_impl.h | 137 ++ lib/portableproplib/prop_stack.c | 118 ++ lib/portableproplib/prop_stack.h | 64 + lib/portableproplib/prop_string.c | 471 ++++++ lib/purge.c | 2 +- lib/register.c | 4 +- lib/regpkgs_dictionary.c | 2 +- lib/remove.c | 2 +- lib/repository.c | 8 +- lib/repository_pool.c | 4 +- lib/requiredby.c | 4 +- lib/state.c | 4 +- lib/unpack.c | 4 +- vars.mk | 13 +- 48 files changed, 9472 insertions(+), 41 deletions(-) create mode 100644 3RDPARTY create mode 100644 include/queue.h create mode 100644 lib/portableproplib/prop/prop_array.h create mode 100644 lib/portableproplib/prop/prop_bool.h create mode 100644 lib/portableproplib/prop/prop_data.h create mode 100644 lib/portableproplib/prop/prop_dictionary.h create mode 100644 lib/portableproplib/prop/prop_ingest.h create mode 100644 lib/portableproplib/prop/prop_number.h create mode 100644 lib/portableproplib/prop/prop_object.h create mode 100644 lib/portableproplib/prop/prop_string.h create mode 100644 lib/portableproplib/prop/proplib.h create mode 100644 lib/portableproplib/prop_array.c create mode 100644 lib/portableproplib/prop_array_util.c create mode 100644 lib/portableproplib/prop_bool.c create mode 100644 lib/portableproplib/prop_data.c create mode 100644 lib/portableproplib/prop_dictionary.c create mode 100644 lib/portableproplib/prop_dictionary_util.c create mode 100644 lib/portableproplib/prop_ingest.c create mode 100644 lib/portableproplib/prop_number.c create mode 100644 lib/portableproplib/prop_object.c create mode 100644 lib/portableproplib/prop_object_impl.h create mode 100644 lib/portableproplib/prop_rb.c create mode 100644 lib/portableproplib/prop_rb_impl.h create mode 100644 lib/portableproplib/prop_stack.c create mode 100644 lib/portableproplib/prop_stack.h create mode 100644 lib/portableproplib/prop_string.c diff --git a/3RDPARTY b/3RDPARTY new file mode 100644 index 00000000..eba0f1ea --- /dev/null +++ b/3RDPARTY @@ -0,0 +1,5 @@ +XBPS includes the following software from third parties for internal +use in the code: + +- libfetch-2.30 from NetBSD +- portableproplib-0.4.1 from http://code.google.com/p/portableproplib diff --git a/NEWS b/NEWS index bbe090ed..0814ea9c 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,12 @@ +xbps-0.5.0 (2010-05-01): + + * Add proplib-0.4.1 source and use it in XBPS. This is to avoid + an external dependency, so that we depend on the features of the + internal library. This also means that proplib is not required anymore. + + * Added support to read/write gzip compressed plists by default, thanks + to proplib-0.4 that gained new functionality. + xbps-0.4.1 (2010-02-28): * xbps-bin(8): fixed the install target to work with package names containing diff --git a/README b/README index d4985224..b6a9ba2e 100644 --- a/README +++ b/README @@ -4,9 +4,7 @@ To build this you'll need: - pkg-config - openssl (development package with static libs) - - libarchive (development package with static libs) - - proplib (development package with static lib), - available at http://code.google.com/p/portableproplib + - libarchive >= 2.8.0 (development package with static libs) - asciidoc (to build the manpages) Optionally to build the API documentation (enabled with BUILD_API_DOCS diff --git a/bin/xbps-bin/check.c b/bin/xbps-bin/check.c index 19aa4f35..0893120f 100644 --- a/bin/xbps-bin/check.c +++ b/bin/xbps-bin/check.c @@ -126,7 +126,7 @@ xbps_check_pkg_integrity(const char *pkgname) goto out; } - propsd = prop_dictionary_internalize_from_file(path); + propsd = prop_dictionary_internalize_from_zfile(path); free(path); if (propsd == NULL) { fprintf(stderr, @@ -158,7 +158,7 @@ xbps_check_pkg_integrity(const char *pkgname) goto out; } - filesd = prop_dictionary_internalize_from_file(path); + filesd = prop_dictionary_internalize_from_zfile(path); free(path); if (filesd == NULL) { fprintf(stderr, diff --git a/bin/xbps-bin/show-deps.c b/bin/xbps-bin/show-deps.c index d6ff2f33..3c856e22 100644 --- a/bin/xbps-bin/show-deps.c +++ b/bin/xbps-bin/show-deps.c @@ -56,7 +56,7 @@ xbps_show_pkg_deps(const char *pkgname) if (path == NULL) return errno; - propsd = prop_dictionary_internalize_from_file(path); + propsd = prop_dictionary_internalize_from_zfile(path); free(path); if (propsd == NULL) { fprintf(stderr, diff --git a/bin/xbps-bin/show-info-files.c b/bin/xbps-bin/show-info-files.c index 76a166f4..d0571203 100644 --- a/bin/xbps-bin/show-info-files.c +++ b/bin/xbps-bin/show-info-files.c @@ -45,7 +45,7 @@ show_pkg_info_from_metadir(const char *pkgname) if (plist == NULL) return EINVAL; - pkgd = prop_dictionary_internalize_from_file(plist); + pkgd = prop_dictionary_internalize_from_zfile(plist); if (pkgd == NULL) { free(plist); return errno; @@ -70,7 +70,7 @@ show_pkg_files_from_metadir(const char *pkgname) if (plist == NULL) return EINVAL; - pkgd = prop_dictionary_internalize_from_file(plist); + pkgd = prop_dictionary_internalize_from_zfile(plist); if (pkgd == NULL) { free(plist); return errno; diff --git a/bin/xbps-repo/index.c b/bin/xbps-repo/index.c index bb3e3a67..6bfa5e7d 100644 --- a/bin/xbps-repo/index.c +++ b/bin/xbps-repo/index.c @@ -49,7 +49,7 @@ repoidx_getdict(const char *pkgdir) if (plist == NULL) return NULL; - dict = prop_dictionary_internalize_from_file(plist); + dict = prop_dictionary_internalize_from_zfile(plist); if (dict == NULL) { dict = prop_dictionary_create(); if (dict == NULL) @@ -365,7 +365,7 @@ xbps_repo_genindex(const char *pkgdir) * If any package was registered in package index, write * plist file to storage. */ - if (!prop_dictionary_externalize_to_file(idxdict, plist)) + if (!prop_dictionary_externalize_to_zfile(idxdict, plist)) rv = errno; } out: diff --git a/bin/xbps-repo/repository.c b/bin/xbps-repo/repository.c index a6c66cdd..bb42d2c0 100644 --- a/bin/xbps-repo/repository.c +++ b/bin/xbps-repo/repository.c @@ -44,7 +44,7 @@ pkgindex_verify(const char *plist, const char *uri, bool only_sync) assert(plist != NULL); - d = prop_dictionary_internalize_from_file(plist); + d = prop_dictionary_internalize_from_zfile(plist); if (d == NULL) { fprintf(stderr, "E: repository %s does not contain any " diff --git a/bin/xbps-uhelper/main.c b/bin/xbps-uhelper/main.c index 233dbcd1..04db1a46 100644 --- a/bin/xbps-uhelper/main.c +++ b/bin/xbps-uhelper/main.c @@ -45,7 +45,7 @@ write_plist_file(prop_dictionary_t dict, const char *file) { assert(dict != NULL || file != NULL); - if (!prop_dictionary_externalize_to_file(dict, file)) { + if (!prop_dictionary_externalize_to_zfile(dict, file)) { prop_object_release(dict); printf("=> ERROR: couldn't write to %s (%s)", file, strerror(errno)); @@ -221,7 +221,7 @@ main(int argc, char **argv) if (argc != 2) usage(); - dict = prop_dictionary_internalize_from_file(argv[1]); + dict = prop_dictionary_internalize_from_zfile(argv[1]); if (dict == NULL) { fprintf(stderr, "=> ERROR: couldn't sanitize %s plist file " @@ -334,6 +334,12 @@ main(int argc, char **argv) } } + } else if (strcasecmp(argv[0], "gzprint") == 0) { + if (argc != 2) + usage(); + + dict = prop_dictionary_internalize_from_zfile(argv[1]); + printf("%s", prop_dictionary_externalize(dict)); } else { usage(); } diff --git a/include/queue.h b/include/queue.h new file mode 100644 index 00000000..c1aff293 --- /dev/null +++ b/include/queue.h @@ -0,0 +1,697 @@ +/* $NetBSD: queue.h,v 1.49 2008/06/15 16:42:18 christos Exp $ */ + +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University 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 REGENTS 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 REGENTS 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. + * + * @(#)queue.h 8.5 (Berkeley) 8/20/94 + */ + +#ifndef _SYS_QUEUE_H_ +#define _SYS_QUEUE_H_ + +/* + * This file defines five types of data structures: singly-linked lists, + * lists, simple queues, tail queues, and circular queues. + * + * A singly-linked list is headed by a single forward pointer. The + * elements are singly linked for minimum space and pointer manipulation + * overhead at the expense of O(n) removal for arbitrary elements. New + * elements can be added to the list after an existing element or at the + * head of the list. Elements being removed from the head of the list + * should use the explicit macro for this purpose for optimum + * efficiency. A singly-linked list may only be traversed in the forward + * direction. Singly-linked lists are ideal for applications with large + * datasets and few or no removals or for implementing a LIFO queue. + * + * A list is headed by a single forward pointer (or an array of forward + * pointers for a hash table header). The elements are doubly linked + * so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before + * or after an existing element or at the head of the list. A list + * may only be traversed in the forward direction. + * + * A simple queue is headed by a pair of pointers, one the head of the + * list and the other to the tail of the list. The elements are singly + * linked to save space, so elements can only be removed from the + * head of the list. New elements can be added to the list after + * an existing element, at the head of the list, or at the end of the + * list. A simple queue may only be traversed in the forward direction. + * + * A tail queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or + * after an existing element, at the head of the list, or at the end of + * the list. A tail queue may be traversed in either direction. + * + * A circle queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or after + * an existing element, at the head of the list, or at the end of the list. + * A circle queue may be traversed in either direction, but has a more + * complex end of list detection. + * + * For details on the use of these macros, see the queue(3) manual page. + */ + +/* + * List definitions. + */ +#define LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#define LIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +/* + * List functions. + */ +#if defined(_KERNEL) && defined(QUEUEDEBUG) +#define QUEUEDEBUG_LIST_INSERT_HEAD(head, elm, field) \ + if ((head)->lh_first && \ + (head)->lh_first->field.le_prev != &(head)->lh_first) \ + panic("LIST_INSERT_HEAD %p %s:%d", (head), __FILE__, __LINE__); +#define QUEUEDEBUG_LIST_OP(elm, field) \ + if ((elm)->field.le_next && \ + (elm)->field.le_next->field.le_prev != \ + &(elm)->field.le_next) \ + panic("LIST_* forw %p %s:%d", (elm), __FILE__, __LINE__);\ + if (*(elm)->field.le_prev != (elm)) \ + panic("LIST_* back %p %s:%d", (elm), __FILE__, __LINE__); +#define QUEUEDEBUG_LIST_POSTREMOVE(elm, field) \ + (elm)->field.le_next = (void *)1L; \ + (elm)->field.le_prev = (void *)1L; +#else +#define QUEUEDEBUG_LIST_INSERT_HEAD(head, elm, field) +#define QUEUEDEBUG_LIST_OP(elm, field) +#define QUEUEDEBUG_LIST_POSTREMOVE(elm, field) +#endif + +#define LIST_INIT(head) do { \ + (head)->lh_first = NULL; \ +} while (/*CONSTCOND*/0) + +#define LIST_INSERT_AFTER(listelm, elm, field) do { \ + QUEUEDEBUG_LIST_OP((listelm), field) \ + if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ + (listelm)->field.le_next->field.le_prev = \ + &(elm)->field.le_next; \ + (listelm)->field.le_next = (elm); \ + (elm)->field.le_prev = &(listelm)->field.le_next; \ +} while (/*CONSTCOND*/0) + +#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ + QUEUEDEBUG_LIST_OP((listelm), field) \ + (elm)->field.le_prev = (listelm)->field.le_prev; \ + (elm)->field.le_next = (listelm); \ + *(listelm)->field.le_prev = (elm); \ + (listelm)->field.le_prev = &(elm)->field.le_next; \ +} while (/*CONSTCOND*/0) + +#define LIST_INSERT_HEAD(head, elm, field) do { \ + QUEUEDEBUG_LIST_INSERT_HEAD((head), (elm), field) \ + if (((elm)->field.le_next = (head)->lh_first) != NULL) \ + (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ + (head)->lh_first = (elm); \ + (elm)->field.le_prev = &(head)->lh_first; \ +} while (/*CONSTCOND*/0) + +#define LIST_REMOVE(elm, field) do { \ + QUEUEDEBUG_LIST_OP((elm), field) \ + if ((elm)->field.le_next != NULL) \ + (elm)->field.le_next->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = (elm)->field.le_next; \ + QUEUEDEBUG_LIST_POSTREMOVE((elm), field) \ +} while (/*CONSTCOND*/0) + +#define LIST_FOREACH(var, head, field) \ + for ((var) = ((head)->lh_first); \ + (var); \ + (var) = ((var)->field.le_next)) + +/* + * List access methods. + */ +#define LIST_EMPTY(head) ((head)->lh_first == NULL) +#define LIST_FIRST(head) ((head)->lh_first) +#define LIST_NEXT(elm, field) ((elm)->field.le_next) + + +/* + * Singly-linked List definitions. + */ +#define SLIST_HEAD(name, type) \ +struct name { \ + struct type *slh_first; /* first element */ \ +} + +#define SLIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define SLIST_ENTRY(type) \ +struct { \ + struct type *sle_next; /* next element */ \ +} + +/* + * Singly-linked List functions. + */ +#define SLIST_INIT(head) do { \ + (head)->slh_first = NULL; \ +} while (/*CONSTCOND*/0) + +#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ + (elm)->field.sle_next = (slistelm)->field.sle_next; \ + (slistelm)->field.sle_next = (elm); \ +} while (/*CONSTCOND*/0) + +#define SLIST_INSERT_HEAD(head, elm, field) do { \ + (elm)->field.sle_next = (head)->slh_first; \ + (head)->slh_first = (elm); \ +} while (/*CONSTCOND*/0) + +#define SLIST_REMOVE_HEAD(head, field) do { \ + (head)->slh_first = (head)->slh_first->field.sle_next; \ +} while (/*CONSTCOND*/0) + +#define SLIST_REMOVE(head, elm, type, field) do { \ + if ((head)->slh_first == (elm)) { \ + SLIST_REMOVE_HEAD((head), field); \ + } \ + else { \ + struct type *curelm = (head)->slh_first; \ + while(curelm->field.sle_next != (elm)) \ + curelm = curelm->field.sle_next; \ + curelm->field.sle_next = \ + curelm->field.sle_next->field.sle_next; \ + } \ +} while (/*CONSTCOND*/0) + +#define SLIST_REMOVE_AFTER(slistelm, field) do { \ + (slistelm)->field.sle_next = \ + SLIST_NEXT(SLIST_NEXT((slistelm), field), field); \ +} while (/*CONSTCOND*/0) + +#define SLIST_FOREACH(var, head, field) \ + for((var) = (head)->slh_first; (var); (var) = (var)->field.sle_next) + +/* + * Singly-linked List access methods. + */ +#define SLIST_EMPTY(head) ((head)->slh_first == NULL) +#define SLIST_FIRST(head) ((head)->slh_first) +#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) + + +/* + * Singly-linked Tail queue declarations. + */ +#define STAILQ_HEAD(name, type) \ +struct name { \ + struct type *stqh_first; /* first element */ \ + struct type **stqh_last; /* addr of last next element */ \ +} + +#define STAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).stqh_first } + +#define STAILQ_ENTRY(type) \ +struct { \ + struct type *stqe_next; /* next element */ \ +} + +/* + * Singly-linked Tail queue functions. + */ +#define STAILQ_INIT(head) do { \ + (head)->stqh_first = NULL; \ + (head)->stqh_last = &(head)->stqh_first; \ +} while (/*CONSTCOND*/0) + +#define STAILQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.stqe_next = (head)->stqh_first) == NULL) \ + (head)->stqh_last = &(elm)->field.stqe_next; \ + (head)->stqh_first = (elm); \ +} while (/*CONSTCOND*/0) + +#define STAILQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.stqe_next = NULL; \ + *(head)->stqh_last = (elm); \ + (head)->stqh_last = &(elm)->field.stqe_next; \ +} while (/*CONSTCOND*/0) + +#define STAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.stqe_next = (listelm)->field.stqe_next) == NULL)\ + (head)->stqh_last = &(elm)->field.stqe_next; \ + (listelm)->field.stqe_next = (elm); \ +} while (/*CONSTCOND*/0) + +#define STAILQ_REMOVE_HEAD(head, field) do { \ + if (((head)->stqh_first = (head)->stqh_first->field.stqe_next) == NULL) \ + (head)->stqh_last = &(head)->stqh_first; \ +} while (/*CONSTCOND*/0) + +#define STAILQ_REMOVE(head, elm, type, field) do { \ + if ((head)->stqh_first == (elm)) { \ + STAILQ_REMOVE_HEAD((head), field); \ + } else { \ + struct type *curelm = (head)->stqh_first; \ + while (curelm->field.stqe_next != (elm)) \ + curelm = curelm->field.stqe_next; \ + if ((curelm->field.stqe_next = \ + curelm->field.stqe_next->field.stqe_next) == NULL) \ + (head)->stqh_last = &(curelm)->field.stqe_next; \ + } \ +} while (/*CONSTCOND*/0) + +#define STAILQ_FOREACH(var, head, field) \ + for ((var) = ((head)->stqh_first); \ + (var); \ + (var) = ((var)->field.stqe_next)) + +#define STAILQ_CONCAT(head1, head2) do { \ + if (!STAILQ_EMPTY((head2))) { \ + *(head1)->stqh_last = (head2)->stqh_first; \ + (head1)->stqh_last = (head2)->stqh_last; \ + STAILQ_INIT((head2)); \ + } \ +} while (/*CONSTCOND*/0) + +/* + * Singly-linked Tail queue access methods. + */ +#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL) +#define STAILQ_FIRST(head) ((head)->stqh_first) +#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next) + + +/* + * Simple queue definitions. + */ +#define SIMPLEQ_HEAD(name, type) \ +struct name { \ + struct type *sqh_first; /* first element */ \ + struct type **sqh_last; /* addr of last next element */ \ +} + +#define SIMPLEQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).sqh_first } + +#define SIMPLEQ_ENTRY(type) \ +struct { \ + struct type *sqe_next; /* next element */ \ +} + +/* + * Simple queue functions. + */ +#define SIMPLEQ_INIT(head) do { \ + (head)->sqh_first = NULL; \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (head)->sqh_first = (elm); \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.sqe_next = NULL; \ + *(head)->sqh_last = (elm); \ + (head)->sqh_last = &(elm)->field.sqe_next; \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (listelm)->field.sqe_next = (elm); \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_REMOVE_HEAD(head, field) do { \ + if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_REMOVE(head, elm, type, field) do { \ + if ((head)->sqh_first == (elm)) { \ + SIMPLEQ_REMOVE_HEAD((head), field); \ + } else { \ + struct type *curelm = (head)->sqh_first; \ + while (curelm->field.sqe_next != (elm)) \ + curelm = curelm->field.sqe_next; \ + if ((curelm->field.sqe_next = \ + curelm->field.sqe_next->field.sqe_next) == NULL) \ + (head)->sqh_last = &(curelm)->field.sqe_next; \ + } \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_FOREACH(var, head, field) \ + for ((var) = ((head)->sqh_first); \ + (var); \ + (var) = ((var)->field.sqe_next)) + +/* + * Simple queue access methods. + */ +#define SIMPLEQ_EMPTY(head) ((head)->sqh_first == NULL) +#define SIMPLEQ_FIRST(head) ((head)->sqh_first) +#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) + + +/* + * Tail queue definitions. + */ +#define _TAILQ_HEAD(name, type, qual) \ +struct name { \ + qual type *tqh_first; /* first element */ \ + qual type *qual *tqh_last; /* addr of last next element */ \ +} +#define TAILQ_HEAD(name, type) _TAILQ_HEAD(name, struct type,) + +#define TAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).tqh_first } + +#define _TAILQ_ENTRY(type, qual) \ +struct { \ + qual type *tqe_next; /* next element */ \ + qual type *qual *tqe_prev; /* address of previous next element */\ +} +#define TAILQ_ENTRY(type) _TAILQ_ENTRY(struct type,) + +/* + * Tail queue functions. + */ +#if defined(_KERNEL) && defined(QUEUEDEBUG) +#define QUEUEDEBUG_TAILQ_INSERT_HEAD(head, elm, field) \ + if ((head)->tqh_first && \ + (head)->tqh_first->field.tqe_prev != &(head)->tqh_first) \ + panic("TAILQ_INSERT_HEAD %p %s:%d", (head), __FILE__, __LINE__); +#define QUEUEDEBUG_TAILQ_INSERT_TAIL(head, elm, field) \ + if (*(head)->tqh_last != NULL) \ + panic("TAILQ_INSERT_TAIL %p %s:%d", (head), __FILE__, __LINE__); +#define QUEUEDEBUG_TAILQ_OP(elm, field) \ + if ((elm)->field.tqe_next && \ + (elm)->field.tqe_next->field.tqe_prev != \ + &(elm)->field.tqe_next) \ + panic("TAILQ_* forw %p %s:%d", (elm), __FILE__, __LINE__);\ + if (*(elm)->field.tqe_prev != (elm)) \ + panic("TAILQ_* back %p %s:%d", (elm), __FILE__, __LINE__); +#define QUEUEDEBUG_TAILQ_PREREMOVE(head, elm, field) \ + if ((elm)->field.tqe_next == NULL && \ + (head)->tqh_last != &(elm)->field.tqe_next) \ + panic("TAILQ_PREREMOVE head %p elm %p %s:%d", \ + (head), (elm), __FILE__, __LINE__); +#define QUEUEDEBUG_TAILQ_POSTREMOVE(elm, field) \ + (elm)->field.tqe_next = (void *)1L; \ + (elm)->field.tqe_prev = (void *)1L; +#else +#define QUEUEDEBUG_TAILQ_INSERT_HEAD(head, elm, field) +#define QUEUEDEBUG_TAILQ_INSERT_TAIL(head, elm, field) +#define QUEUEDEBUG_TAILQ_OP(elm, field) +#define QUEUEDEBUG_TAILQ_PREREMOVE(head, elm, field) +#define QUEUEDEBUG_TAILQ_POSTREMOVE(elm, field) +#endif + +#define TAILQ_INIT(head) do { \ + (head)->tqh_first = NULL; \ + (head)->tqh_last = &(head)->tqh_first; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_INSERT_HEAD(head, elm, field) do { \ + QUEUEDEBUG_TAILQ_INSERT_HEAD((head), (elm), field) \ + if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ + (head)->tqh_first->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (head)->tqh_first = (elm); \ + (elm)->field.tqe_prev = &(head)->tqh_first; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_INSERT_TAIL(head, elm, field) do { \ + QUEUEDEBUG_TAILQ_INSERT_TAIL((head), (elm), field) \ + (elm)->field.tqe_next = NULL; \ + (elm)->field.tqe_prev = (head)->tqh_last; \ + *(head)->tqh_last = (elm); \ + (head)->tqh_last = &(elm)->field.tqe_next; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + QUEUEDEBUG_TAILQ_OP((listelm), field) \ + if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ + (elm)->field.tqe_next->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (listelm)->field.tqe_next = (elm); \ + (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ + QUEUEDEBUG_TAILQ_OP((listelm), field) \ + (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ + (elm)->field.tqe_next = (listelm); \ + *(listelm)->field.tqe_prev = (elm); \ + (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_REMOVE(head, elm, field) do { \ + QUEUEDEBUG_TAILQ_PREREMOVE((head), (elm), field) \ + QUEUEDEBUG_TAILQ_OP((elm), field) \ + if (((elm)->field.tqe_next) != NULL) \ + (elm)->field.tqe_next->field.tqe_prev = \ + (elm)->field.tqe_prev; \ + else \ + (head)->tqh_last = (elm)->field.tqe_prev; \ + *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ + QUEUEDEBUG_TAILQ_POSTREMOVE((elm), field); \ +} while (/*CONSTCOND*/0) + +#define TAILQ_FOREACH(var, head, field) \ + for ((var) = ((head)->tqh_first); \ + (var); \ + (var) = ((var)->field.tqe_next)) + +#define TAILQ_FOREACH_SAFE(var, head, field, next) \ + for ((var) = ((head)->tqh_first); \ + (var) != NULL && ((next) = TAILQ_NEXT(var, field), 1); \ + (var) = (next)) + +#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ + for ((var) = (*(((struct headname *)((head)->tqh_last))->tqh_last)); \ + (var); \ + (var) = (*(((struct headname *)((var)->field.tqe_prev))->tqh_last))) + +#define TAILQ_CONCAT(head1, head2, field) do { \ + if (!TAILQ_EMPTY(head2)) { \ + *(head1)->tqh_last = (head2)->tqh_first; \ + (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \ + (head1)->tqh_last = (head2)->tqh_last; \ + TAILQ_INIT((head2)); \ + } \ +} while (/*CONSTCOND*/0) + +/* + * Tail queue access methods. + */ +#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL) +#define TAILQ_FIRST(head) ((head)->tqh_first) +#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) + +#define TAILQ_LAST(head, headname) \ + (*(((struct headname *)((head)->tqh_last))->tqh_last)) +#define TAILQ_PREV(elm, headname, field) \ + (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) + + +/* + * Circular queue definitions. + */ +#if defined(_KERNEL) && defined(QUEUEDEBUG) +#define QUEUEDEBUG_CIRCLEQ_HEAD(head, field) \ + if ((head)->cqh_first != (void *)(head) && \ + (head)->cqh_first->field.cqe_prev != (void *)(head)) \ + panic("CIRCLEQ head forw %p %s:%d", (head), \ + __FILE__, __LINE__); \ + if ((head)->cqh_last != (void *)(head) && \ + (head)->cqh_last->field.cqe_next != (void *)(head)) \ + panic("CIRCLEQ head back %p %s:%d", (head), \ + __FILE__, __LINE__); +#define QUEUEDEBUG_CIRCLEQ_ELM(head, elm, field) \ + if ((elm)->field.cqe_next == (void *)(head)) { \ + if ((head)->cqh_last != (elm)) \ + panic("CIRCLEQ elm last %p %s:%d", (elm), \ + __FILE__, __LINE__); \ + } else { \ + if ((elm)->field.cqe_next->field.cqe_prev != (elm)) \ + panic("CIRCLEQ elm forw %p %s:%d", (elm), \ + __FILE__, __LINE__); \ + } \ + if ((elm)->field.cqe_prev == (void *)(head)) { \ + if ((head)->cqh_first != (elm)) \ + panic("CIRCLEQ elm first %p %s:%d", (elm), \ + __FILE__, __LINE__); \ + } else { \ + if ((elm)->field.cqe_prev->field.cqe_next != (elm)) \ + panic("CIRCLEQ elm prev %p %s:%d", (elm), \ + __FILE__, __LINE__); \ + } +#define QUEUEDEBUG_CIRCLEQ_POSTREMOVE(elm, field) \ + (elm)->field.cqe_next = (void *)1L; \ + (elm)->field.cqe_prev = (void *)1L; +#else +#define QUEUEDEBUG_CIRCLEQ_HEAD(head, field) +#define QUEUEDEBUG_CIRCLEQ_ELM(head, elm, field) +#define QUEUEDEBUG_CIRCLEQ_POSTREMOVE(elm, field) +#endif + +#define CIRCLEQ_HEAD(name, type) \ +struct name { \ + struct type *cqh_first; /* first element */ \ + struct type *cqh_last; /* last element */ \ +} + +#define CIRCLEQ_HEAD_INITIALIZER(head) \ + { (void *)&head, (void *)&head } + +#define CIRCLEQ_ENTRY(type) \ +struct { \ + struct type *cqe_next; /* next element */ \ + struct type *cqe_prev; /* previous element */ \ +} + +/* + * Circular queue functions. + */ +#define CIRCLEQ_INIT(head) do { \ + (head)->cqh_first = (void *)(head); \ + (head)->cqh_last = (void *)(head); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + QUEUEDEBUG_CIRCLEQ_HEAD((head), field) \ + QUEUEDEBUG_CIRCLEQ_ELM((head), (listelm), field) \ + (elm)->field.cqe_next = (listelm)->field.cqe_next; \ + (elm)->field.cqe_prev = (listelm); \ + if ((listelm)->field.cqe_next == (void *)(head)) \ + (head)->cqh_last = (elm); \ + else \ + (listelm)->field.cqe_next->field.cqe_prev = (elm); \ + (listelm)->field.cqe_next = (elm); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \ + QUEUEDEBUG_CIRCLEQ_HEAD((head), field) \ + QUEUEDEBUG_CIRCLEQ_ELM((head), (listelm), field) \ + (elm)->field.cqe_next = (listelm); \ + (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \ + if ((listelm)->field.cqe_prev == (void *)(head)) \ + (head)->cqh_first = (elm); \ + else \ + (listelm)->field.cqe_prev->field.cqe_next = (elm); \ + (listelm)->field.cqe_prev = (elm); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \ + QUEUEDEBUG_CIRCLEQ_HEAD((head), field) \ + (elm)->field.cqe_next = (head)->cqh_first; \ + (elm)->field.cqe_prev = (void *)(head); \ + if ((head)->cqh_last == (void *)(head)) \ + (head)->cqh_last = (elm); \ + else \ + (head)->cqh_first->field.cqe_prev = (elm); \ + (head)->cqh_first = (elm); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \ + QUEUEDEBUG_CIRCLEQ_HEAD((head), field) \ + (elm)->field.cqe_next = (void *)(head); \ + (elm)->field.cqe_prev = (head)->cqh_last; \ + if ((head)->cqh_first == (void *)(head)) \ + (head)->cqh_first = (elm); \ + else \ + (head)->cqh_last->field.cqe_next = (elm); \ + (head)->cqh_last = (elm); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_REMOVE(head, elm, field) do { \ + QUEUEDEBUG_CIRCLEQ_HEAD((head), field) \ + QUEUEDEBUG_CIRCLEQ_ELM((head), (elm), field) \ + if ((elm)->field.cqe_next == (void *)(head)) \ + (head)->cqh_last = (elm)->field.cqe_prev; \ + else \ + (elm)->field.cqe_next->field.cqe_prev = \ + (elm)->field.cqe_prev; \ + if ((elm)->field.cqe_prev == (void *)(head)) \ + (head)->cqh_first = (elm)->field.cqe_next; \ + else \ + (elm)->field.cqe_prev->field.cqe_next = \ + (elm)->field.cqe_next; \ + QUEUEDEBUG_CIRCLEQ_POSTREMOVE((elm), field) \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_FOREACH(var, head, field) \ + for ((var) = ((head)->cqh_first); \ + (var) != (const void *)(head); \ + (var) = ((var)->field.cqe_next)) + +#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \ + for ((var) = ((head)->cqh_last); \ + (var) != (const void *)(head); \ + (var) = ((var)->field.cqe_prev)) + +/* + * Circular queue access methods. + */ +#define CIRCLEQ_EMPTY(head) ((head)->cqh_first == (void *)(head)) +#define CIRCLEQ_FIRST(head) ((head)->cqh_first) +#define CIRCLEQ_LAST(head) ((head)->cqh_last) +#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next) +#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev) + +#define CIRCLEQ_LOOP_NEXT(head, elm, field) \ + (((elm)->field.cqe_next == (void *)(head)) \ + ? ((head)->cqh_first) \ + : (elm->field.cqe_next)) +#define CIRCLEQ_LOOP_PREV(head, elm, field) \ + (((elm)->field.cqe_prev == (void *)(head)) \ + ? ((head)->cqh_last) \ + : (elm->field.cqe_prev)) + +#endif /* !_SYS_QUEUE_H_ */ diff --git a/include/xbps_api.h b/include/xbps_api.h index 85532d2f..b6918931 100644 --- a/include/xbps_api.h +++ b/include/xbps_api.h @@ -30,7 +30,6 @@ #include #include #include -#include /** * @cond */ @@ -45,6 +44,9 @@ #include #include +/* Use our own queue header (from NetBSD) */ +#include "queue.h" + __BEGIN_DECLS /** diff --git a/lib/Makefile b/lib/Makefile index 5f815669..6e7dc3d8 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -4,9 +4,24 @@ LIBXBPS_MAJOR = 0 LIBXBPS_MINOR = 0 LIBXBPS_MICRO = 0 LIBXBPS_SHLIB = libxbps.so.$(LIBXBPS_MAJOR).$(LIBXBPS_MINOR).$(LIBXBPS_MICRO) -LDFLAGS += -lprop -larchive -lssl +LDFLAGS += -larchive -lssl -lz LDFLAGS += -shared -Wl,-soname,libxbps.so.$(LIBXBPS_MAJOR) +# portableproplib +LIBPROP_OBJS = portableproplib/prop_array.o portableproplib/prop_bool.o +LIBPROP_OBJS += portableproplib/prop_dictionary.o portableproplib/prop_ingest.o +LIBPROP_OBJS += portableproplib/prop_object.o portableproplib/prop_rb.o +LIBPROP_OBJS += portableproplib/prop_stack.o portableproplib/prop_string.o +LIBPROP_OBJS += portableproplib/prop_array_util.o portableproplib/prop_number.o +LIBPROP_OBJS += portableproplib/prop_dictionary_util.o +LIBPROP_OBJS += portableproplib/prop_data.o +LIBPROP_CFLAGS = -Wno-error -Wno-cast-qual -Wno-cast-align -Wno-extra + +ifdef USE_EXTERNAL_PROPLIB +LIBPROP_OBJS = +LDFLAGS += -lprop +endif + # libfetch LIBFETCH_OBJS = fetch/common.o fetch/fetch.o fetch/file.o LIBFETCH_OBJS += fetch/ftp.o fetch/http.o @@ -40,17 +55,21 @@ $(LIBFETCH_OBJS): %.o: %.c $(LIBFETCH_INCS) $(LIBFETCH_GEN) @$(CC) $(CPPFLAGS) $(LIBFETCH_CPPFLAGS) $(CFLAGS) \ $(LIBFETCH_CFLAGS) $(LIBFETCH_SHLIBCFLAGS) -c $< -o $@ +$(LIBPROP_OBJS): %.o: %.c + @printf " [CC]\t\t$@\n" + @$(CC) $(CPPFLAGS) $(CFLAGS) $(LIBPROP_CFLAGS) -c $< -o $@ + $(OBJS): %.o: %.c @printf " [CC]\t\t$@\n" @$(CC) $(CPPFLAGS) $(CFLAGS) $(SHAREDLIB_CFLAGS) -c $< -o $@ -libxbps.so: $(OBJS) $(LIBFETCH_OBJS) +libxbps.so: $(OBJS) $(LIBFETCH_OBJS) $(LIBPROP_OBJS) @printf " [CCLD]\t\t$@\n" @$(CC) $(LDFLAGS) $^ -o $(LIBXBPS_SHLIB) @-ln -sf $(LIBXBPS_SHLIB) libxbps.so.$(LIBXBPS_MAJOR) @-ln -sf $(LIBXBPS_SHLIB) libxbps.so -libxbps.a: $(OBJS) $(LIBFETCH_OBJS) +libxbps.a: $(OBJS) $(LIBFETCH_OBJS) $(LIBPROP_OBJS) @printf " [AR]\t\t$@\n" @$(AR) rcs $@ $^ @printf " [RANLIB]\t$@\n" @@ -70,5 +89,5 @@ uninstall: .PHONY: clean clean: - -rm -f libxbps* $(OBJS) $(LIBFETCH_OBJS) + -rm -f libxbps* $(OBJS) $(LIBFETCH_OBJS) $(LIBPROP_OBJS) -rm -f $(LIBFETCH_GEN) diff --git a/lib/config_files.c b/lib/config_files.c index d6d8a4cb..76ea1292 100644 --- a/lib/config_files.c +++ b/lib/config_files.c @@ -63,7 +63,7 @@ xbps_config_file_from_archive_entry(prop_dictionary_t d, if (buf == NULL) return errno; - forigd = prop_dictionary_internalize_from_file(buf); + forigd = prop_dictionary_internalize_from_zfile(buf); free(buf); if (forigd == NULL) { install_new = true; diff --git a/lib/plist.c b/lib/plist.c index 41795356..2019c0d4 100644 --- a/lib/plist.c +++ b/lib/plist.c @@ -138,7 +138,7 @@ xbps_find_pkg_from_plist(const char *plist, const char *pkgname) assert(plist != NULL); assert(pkgname != NULL); - dict = prop_dictionary_internalize_from_file(plist); + dict = prop_dictionary_internalize_from_zfile(plist); if (dict == NULL) return NULL; @@ -375,7 +375,7 @@ xbps_remove_pkg_dict_from_file(const char *pkg, const char *plist) assert(pkg != NULL); assert(plist != NULL); - pdict = prop_dictionary_internalize_from_file(plist); + pdict = prop_dictionary_internalize_from_zfile(plist); if (pdict == NULL) return errno; @@ -385,7 +385,7 @@ xbps_remove_pkg_dict_from_file(const char *pkg, const char *plist) return rv; } - if (!prop_dictionary_externalize_to_file(pdict, plist)) { + if (!prop_dictionary_externalize_to_zfile(pdict, plist)) { prop_object_release(pdict); return errno; } diff --git a/lib/portableproplib/prop/prop_array.h b/lib/portableproplib/prop/prop_array.h new file mode 100644 index 00000000..4baf7eb3 --- /dev/null +++ b/lib/portableproplib/prop/prop_array.h @@ -0,0 +1,142 @@ +/* $NetBSD: prop_array.h,v 1.8 2008/09/11 13:15:13 haad Exp $ */ + +/*- + * Copyright (c) 2006 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _PROPLIB_PROP_ARRAY_H_ +#define _PROPLIB_PROP_ARRAY_H_ + +#include +#include + +typedef struct _prop_array *prop_array_t; + +__BEGIN_DECLS +prop_array_t prop_array_create(void); +prop_array_t prop_array_create_with_capacity(unsigned int); + +prop_array_t prop_array_copy(prop_array_t); +prop_array_t prop_array_copy_mutable(prop_array_t); + +unsigned int prop_array_capacity(prop_array_t); +unsigned int prop_array_count(prop_array_t); +bool prop_array_ensure_capacity(prop_array_t, unsigned int); + +void prop_array_make_immutable(prop_array_t); +bool prop_array_mutable(prop_array_t); + +prop_object_iterator_t prop_array_iterator(prop_array_t); + +prop_object_t prop_array_get(prop_array_t, unsigned int); +bool prop_array_set(prop_array_t, unsigned int, prop_object_t); +bool prop_array_add(prop_array_t, prop_object_t); +void prop_array_remove(prop_array_t, unsigned int); + +bool prop_array_equals(prop_array_t, prop_array_t); + +char * prop_array_externalize(prop_array_t); +prop_array_t prop_array_internalize(const char *); + +bool prop_array_externalize_to_file(prop_array_t, const char *); +bool prop_array_externalize_to_zfile(prop_array_t, const char *); +prop_array_t prop_array_internalize_from_file(const char *); +prop_array_t prop_array_internalize_from_zfile(const char *); + +/* + * Utility routines to make it more convenient to work with values + * stored in dictionaries. + */ +bool prop_array_get_bool(prop_array_t, unsigned int, + bool *); +bool prop_array_set_bool(prop_array_t, unsigned int, + bool); + +bool prop_array_get_int8(prop_array_t, unsigned int, + int8_t *); +bool prop_array_get_uint8(prop_array_t, unsigned int, + uint8_t *); +bool prop_array_set_int8(prop_array_t, unsigned int, + int8_t); +bool prop_array_set_uint8(prop_array_t, unsigned int, + uint8_t); + +bool prop_array_get_int16(prop_array_t, unsigned int, + int16_t *); +bool prop_array_get_uint16(prop_array_t, unsigned int, + uint16_t *); +bool prop_array_set_int16(prop_array_t, unsigned int, + int16_t); +bool prop_array_set_uint16(prop_array_t, unsigned int, + uint16_t); + +bool prop_array_get_int32(prop_array_t, unsigned int, + int32_t *); +bool prop_array_get_uint32(prop_array_t, unsigned int, + uint32_t *); +bool prop_array_set_int32(prop_array_t, unsigned int, + int32_t); +bool prop_array_set_uint32(prop_array_t, unsigned int, + uint32_t); + +bool prop_array_get_int64(prop_array_t, unsigned int, + int64_t *); +bool prop_array_get_uint64(prop_array_t, unsigned int, + uint64_t *); +bool prop_array_set_int64(prop_array_t, unsigned int, + int64_t); +bool prop_array_set_uint64(prop_array_t, unsigned int, + uint64_t); + +bool prop_array_add_int8(prop_array_t, int8_t); +bool prop_array_add_uint8(prop_array_t, uint8_t); + +bool prop_array_add_int16(prop_array_t, int16_t); +bool prop_array_add_uint16(prop_array_t, uint16_t); + +bool prop_array_add_int32(prop_array_t, int32_t); +bool prop_array_add_uint32(prop_array_t, uint32_t); + +bool prop_array_add_int64(prop_array_t, int64_t); +bool prop_array_add_uint64(prop_array_t, uint64_t); + +bool prop_array_get_cstring(prop_array_t, unsigned int, + char **); +bool prop_array_set_cstring(prop_array_t, unsigned int, + const char *); + +bool prop_array_get_cstring_nocopy(prop_array_t, + unsigned int, + const char **); +bool prop_array_set_cstring_nocopy(prop_array_t, + unsigned int, + const char *); + +__END_DECLS + +#endif /* _PROPLIB_PROP_ARRAY_H_ */ diff --git a/lib/portableproplib/prop/prop_bool.h b/lib/portableproplib/prop/prop_bool.h new file mode 100644 index 00000000..c21d28bd --- /dev/null +++ b/lib/portableproplib/prop/prop_bool.h @@ -0,0 +1,49 @@ +/* $NetBSD: prop_bool.h,v 1.4 2008/04/28 20:22:51 martin Exp $ */ + +/*- + * Copyright (c) 2006 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _PROPLIB_PROP_BOOL_H_ +#define _PROPLIB_PROP_BOOL_H_ + +#include +#include + +typedef struct _prop_bool *prop_bool_t; + +__BEGIN_DECLS +prop_bool_t prop_bool_create(bool); +prop_bool_t prop_bool_copy(prop_bool_t); + +bool prop_bool_true(prop_bool_t); + +bool prop_bool_equals(prop_bool_t, prop_bool_t); +__END_DECLS + +#endif /* _PROPLIB_PROP_BOOL_H_ */ diff --git a/lib/portableproplib/prop/prop_data.h b/lib/portableproplib/prop/prop_data.h new file mode 100644 index 00000000..a8976e3f --- /dev/null +++ b/lib/portableproplib/prop/prop_data.h @@ -0,0 +1,56 @@ +/* $NetBSD: prop_data.h,v 1.3 2008/04/28 20:22:51 martin Exp $ */ + +/*- + * Copyright (c) 2006 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _PROPLIB_PROP_DATA_H_ +#define _PROPLIB_PROP_DATA_H_ + +#include +#include +#include + +typedef struct _prop_data *prop_data_t; + +__BEGIN_DECLS +prop_data_t prop_data_create_data(const void *, size_t); +prop_data_t prop_data_create_data_nocopy(const void *, size_t); + +prop_data_t prop_data_copy(prop_data_t); + +size_t prop_data_size(prop_data_t); + +void * prop_data_data(prop_data_t); +const void * prop_data_data_nocopy(prop_data_t); + +bool prop_data_equals(prop_data_t, prop_data_t); +bool prop_data_equals_data(prop_data_t, const void *, size_t); +__END_DECLS + +#endif /* _PROPLIB_PROP_DATA_H_ */ diff --git a/lib/portableproplib/prop/prop_dictionary.h b/lib/portableproplib/prop/prop_dictionary.h new file mode 100644 index 00000000..b4bc0290 --- /dev/null +++ b/lib/portableproplib/prop/prop_dictionary.h @@ -0,0 +1,148 @@ +/* $NetBSD: prop_dictionary.h,v 1.9 2008/04/28 20:22:51 martin Exp $ */ + +/*- + * Copyright (c) 2006 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _PROPLIB_PROP_DICTIONARY_H_ +#define _PROPLIB_PROP_DICTIONARY_H_ + +#include +#include +#include + +typedef struct _prop_dictionary *prop_dictionary_t; +typedef struct _prop_dictionary_keysym *prop_dictionary_keysym_t; + +__BEGIN_DECLS +prop_dictionary_t prop_dictionary_create(void); +prop_dictionary_t prop_dictionary_create_with_capacity(unsigned int); + +prop_dictionary_t prop_dictionary_copy(prop_dictionary_t); +prop_dictionary_t prop_dictionary_copy_mutable(prop_dictionary_t); + +unsigned int prop_dictionary_count(prop_dictionary_t); +bool prop_dictionary_ensure_capacity(prop_dictionary_t, + unsigned int); + +void prop_dictionary_make_immutable(prop_dictionary_t); +bool prop_dictionary_mutable(prop_dictionary_t); + +prop_object_iterator_t prop_dictionary_iterator(prop_dictionary_t); +prop_array_t prop_dictionary_all_keys(prop_dictionary_t); + +prop_object_t prop_dictionary_get(prop_dictionary_t, const char *); +bool prop_dictionary_set(prop_dictionary_t, const char *, + prop_object_t); +void prop_dictionary_remove(prop_dictionary_t, const char *); + +prop_object_t prop_dictionary_get_keysym(prop_dictionary_t, + prop_dictionary_keysym_t); +bool prop_dictionary_set_keysym(prop_dictionary_t, + prop_dictionary_keysym_t, + prop_object_t); +void prop_dictionary_remove_keysym(prop_dictionary_t, + prop_dictionary_keysym_t); + +bool prop_dictionary_equals(prop_dictionary_t, prop_dictionary_t); + +char * prop_dictionary_externalize(prop_dictionary_t); +prop_dictionary_t prop_dictionary_internalize(const char *); + +bool prop_dictionary_externalize_to_file(prop_dictionary_t, + const char *); +bool prop_dictionary_externalize_to_zfile(prop_dictionary_t, + const char *); +prop_dictionary_t prop_dictionary_internalize_from_file(const char *); +prop_dictionary_t prop_dictionary_internalize_from_zfile(const char *); + +const char * prop_dictionary_keysym_cstring_nocopy(prop_dictionary_keysym_t); + +bool prop_dictionary_keysym_equals(prop_dictionary_keysym_t, + prop_dictionary_keysym_t); + +/* + * Utility routines to make it more convenient to work with values + * stored in dictionaries. + */ +bool prop_dictionary_get_bool(prop_dictionary_t, const char *, + bool *); +bool prop_dictionary_set_bool(prop_dictionary_t, const char *, + bool); + +bool prop_dictionary_get_int8(prop_dictionary_t, const char *, + int8_t *); +bool prop_dictionary_get_uint8(prop_dictionary_t, const char *, + uint8_t *); +bool prop_dictionary_set_int8(prop_dictionary_t, const char *, + int8_t); +bool prop_dictionary_set_uint8(prop_dictionary_t, const char *, + uint8_t); + +bool prop_dictionary_get_int16(prop_dictionary_t, const char *, + int16_t *); +bool prop_dictionary_get_uint16(prop_dictionary_t, const char *, + uint16_t *); +bool prop_dictionary_set_int16(prop_dictionary_t, const char *, + int16_t); +bool prop_dictionary_set_uint16(prop_dictionary_t, const char *, + uint16_t); + +bool prop_dictionary_get_int32(prop_dictionary_t, const char *, + int32_t *); +bool prop_dictionary_get_uint32(prop_dictionary_t, const char *, + uint32_t *); +bool prop_dictionary_set_int32(prop_dictionary_t, const char *, + int32_t); +bool prop_dictionary_set_uint32(prop_dictionary_t, const char *, + uint32_t); + +bool prop_dictionary_get_int64(prop_dictionary_t, const char *, + int64_t *); +bool prop_dictionary_get_uint64(prop_dictionary_t, const char *, + uint64_t *); +bool prop_dictionary_set_int64(prop_dictionary_t, const char *, + int64_t); +bool prop_dictionary_set_uint64(prop_dictionary_t, const char *, + uint64_t); + +bool prop_dictionary_get_cstring(prop_dictionary_t, const char *, + char **); +bool prop_dictionary_set_cstring(prop_dictionary_t, const char *, + const char *); + +bool prop_dictionary_get_cstring_nocopy(prop_dictionary_t, + const char *, + const char **); +bool prop_dictionary_set_cstring_nocopy(prop_dictionary_t, + const char *, + const char *); + +__END_DECLS + +#endif /* _PROPLIB_PROP_DICTIONARY_H_ */ diff --git a/lib/portableproplib/prop/prop_ingest.h b/lib/portableproplib/prop/prop_ingest.h new file mode 100644 index 00000000..b6878a97 --- /dev/null +++ b/lib/portableproplib/prop/prop_ingest.h @@ -0,0 +1,91 @@ +/* $NetBSD: prop_ingest.h,v 1.3 2008/04/28 20:22:51 martin Exp $ */ + +/*- + * Copyright (c) 2006 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _PROPLIB_PROP_INGEST_H_ +#define _PROPLIB_PROP_INGEST_H_ + +#include +#include + +typedef enum { + PROP_INGEST_ERROR_NO_ERROR = 0, + PROP_INGEST_ERROR_NO_KEY = 1, + PROP_INGEST_ERROR_WRONG_TYPE = 2, + PROP_INGEST_ERROR_HANDLER_FAILED = 3 +} prop_ingest_error_t; + +typedef enum { + PROP_INGEST_FLAG_OPTIONAL = 0x01 +} prop_ingest_flag_t; + +typedef struct _prop_ingest_context *prop_ingest_context_t; + +typedef bool (*prop_ingest_handler_t)(prop_ingest_context_t, prop_object_t); + +typedef struct { + const char *pite_key; + prop_type_t pite_type; + unsigned int pite_flags; + prop_ingest_handler_t pite_handler; +} prop_ingest_table_entry; + +#define PROP_INGEST(key_, type_, handler_) \ + { .pite_key = key_ , \ + .pite_type = type_ , \ + .pite_flags = 0 , \ + .pite_handler = handler_ } + +#define PROP_INGEST_OPTIONAL(key_, type_, handler_) \ + { .pite_key = key_ , \ + .pite_type = type_ , \ + .pite_flags = PROP_INGEST_FLAG_OPTIONAL , \ + .pite_handler = handler_ } + +#define PROP_INGEST_END \ + { .pite_key = NULL } + +__BEGIN_DECLS +prop_ingest_context_t + prop_ingest_context_alloc(void *); +void prop_ingest_context_free(prop_ingest_context_t); + +prop_ingest_error_t + prop_ingest_context_error(prop_ingest_context_t); +prop_type_t prop_ingest_context_type(prop_ingest_context_t); +const char * prop_ingest_context_key(prop_ingest_context_t); +void * prop_ingest_context_private(prop_ingest_context_t); + +bool prop_dictionary_ingest(prop_dictionary_t, + const prop_ingest_table_entry[], + prop_ingest_context_t); +__END_DECLS + +#endif /* _PROPLIB_PROP_INGEST_H_ */ diff --git a/lib/portableproplib/prop/prop_number.h b/lib/portableproplib/prop/prop_number.h new file mode 100644 index 00000000..234e0df4 --- /dev/null +++ b/lib/portableproplib/prop/prop_number.h @@ -0,0 +1,57 @@ +/* $NetBSD: prop_number.h,v 1.6 2008/04/28 20:22:51 martin Exp $ */ + +/*- + * Copyright (c) 2006 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _PROPLIB_PROP_NUMBER_H_ +#define _PROPLIB_PROP_NUMBER_H_ + +#include +#include + +typedef struct _prop_number *prop_number_t; + +__BEGIN_DECLS +prop_number_t prop_number_create_integer(int64_t); +prop_number_t prop_number_create_unsigned_integer(uint64_t); + +prop_number_t prop_number_copy(prop_number_t); + +int prop_number_size(prop_number_t); +bool prop_number_unsigned(prop_number_t); + +int64_t prop_number_integer_value(prop_number_t); +uint64_t prop_number_unsigned_integer_value(prop_number_t); + +bool prop_number_equals(prop_number_t, prop_number_t); +bool prop_number_equals_integer(prop_number_t, int64_t); +bool prop_number_equals_unsigned_integer(prop_number_t, uint64_t); +__END_DECLS + +#endif /* _PROPLIB_PROP_NUMBER_H_ */ diff --git a/lib/portableproplib/prop/prop_object.h b/lib/portableproplib/prop/prop_object.h new file mode 100644 index 00000000..82f9c061 --- /dev/null +++ b/lib/portableproplib/prop/prop_object.h @@ -0,0 +1,67 @@ +/* $NetBSD: prop_object.h,v 1.7 2008/04/28 20:22:51 martin Exp $ */ + +/*- + * Copyright (c) 2006 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _PROPLIB_PROP_OBJECT_H_ +#define _PROPLIB_PROP_OBJECT_H_ + +#include +#include + +typedef void *prop_object_t; + +typedef enum { + PROP_TYPE_UNKNOWN = 0x00000000, + PROP_TYPE_BOOL = 0x626f6f6c, /* 'bool' */ + PROP_TYPE_NUMBER = 0x6e6d6272, /* 'nmbr' */ + PROP_TYPE_STRING = 0x73746e67, /* 'stng' */ + PROP_TYPE_DATA = 0x64617461, /* 'data' */ + PROP_TYPE_ARRAY = 0x61726179, /* 'aray' */ + PROP_TYPE_DICTIONARY = 0x64696374, /* 'dict' */ + PROP_TYPE_DICT_KEYSYM = 0x646b6579 /* 'dkey' */ +} prop_type_t; + +__BEGIN_DECLS +void prop_object_retain(prop_object_t); +void prop_object_release(prop_object_t); + +prop_type_t prop_object_type(prop_object_t); + +bool prop_object_equals(prop_object_t, prop_object_t); +bool prop_object_equals_with_error(prop_object_t, prop_object_t, bool *); + +typedef struct _prop_object_iterator *prop_object_iterator_t; + +prop_object_t prop_object_iterator_next(prop_object_iterator_t); +void prop_object_iterator_reset(prop_object_iterator_t); +void prop_object_iterator_release(prop_object_iterator_t); +__END_DECLS + +#endif /* _PROPLIB_PROP_OBJECT_H_ */ diff --git a/lib/portableproplib/prop/prop_string.h b/lib/portableproplib/prop/prop_string.h new file mode 100644 index 00000000..b5f04765 --- /dev/null +++ b/lib/portableproplib/prop/prop_string.h @@ -0,0 +1,62 @@ +/* $NetBSD: prop_string.h,v 1.3 2008/04/28 20:22:51 martin Exp $ */ + +/*- + * Copyright (c) 2006 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _PROPLIB_PROP_STRING_H_ +#define _PROPLIB_PROP_STRING_H_ + +#include +#include +#include + +typedef struct _prop_string *prop_string_t; + +__BEGIN_DECLS +prop_string_t prop_string_create(void); +prop_string_t prop_string_create_cstring(const char *); +prop_string_t prop_string_create_cstring_nocopy(const char *); + +prop_string_t prop_string_copy(prop_string_t); +prop_string_t prop_string_copy_mutable(prop_string_t); + +size_t prop_string_size(prop_string_t); +bool prop_string_mutable(prop_string_t); + +char * prop_string_cstring(prop_string_t); +const char * prop_string_cstring_nocopy(prop_string_t); + +bool prop_string_append(prop_string_t, prop_string_t); +bool prop_string_append_cstring(prop_string_t, const char *); + +bool prop_string_equals(prop_string_t, prop_string_t); +bool prop_string_equals_cstring(prop_string_t, const char *); +__END_DECLS + +#endif /* _PROPLIB_PROP_STRING_H_ */ diff --git a/lib/portableproplib/prop/proplib.h b/lib/portableproplib/prop/proplib.h new file mode 100644 index 00000000..74770ddc --- /dev/null +++ b/lib/portableproplib/prop/proplib.h @@ -0,0 +1,45 @@ +/* $NetBSD: proplib.h,v 1.6 2008/04/28 20:22:51 martin Exp $ */ + +/*- + * Copyright (c) 2006 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _PROPLIB_PROPLIB_H_ +#define _PROPLIB_PROPLIB_H_ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#endif /* _PROPLIB_PROPLIB_H_ */ diff --git a/lib/portableproplib/prop_array.c b/lib/portableproplib/prop_array.c new file mode 100644 index 00000000..15437fbd --- /dev/null +++ b/lib/portableproplib/prop_array.c @@ -0,0 +1,1044 @@ +/* $NetBSD: prop_array.c,v 1.20 2008/08/11 05:54:21 christos Exp $ */ + +/*- + * Copyright (c) 2010 Juan Romero Pardines (zlib/gzip support). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +/*- + * Copyright (c) 2006, 2007 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include "prop_object_impl.h" +#include + +#include + +struct _prop_array { + struct _prop_object pa_obj; + _PROP_RWLOCK_DECL(pa_rwlock) + prop_object_t * pa_array; + unsigned int pa_capacity; + unsigned int pa_count; + int pa_flags; + + uint32_t pa_version; +}; + +#define PA_F_IMMUTABLE 0x01 /* array is immutable */ + +_PROP_POOL_INIT(_prop_array_pool, sizeof(struct _prop_array), "proparay") +_PROP_MALLOC_DEFINE(M_PROP_ARRAY, "prop array", + "property array container object") + +static _prop_object_free_rv_t + _prop_array_free(prop_stack_t, prop_object_t *); +static void _prop_array_emergency_free(prop_object_t); +static bool _prop_array_externalize( + struct _prop_object_externalize_context *, + void *); +static _prop_object_equals_rv_t + _prop_array_equals(prop_object_t, prop_object_t, + void **, void **, + prop_object_t *, prop_object_t *); +static void _prop_array_equals_finish(prop_object_t, prop_object_t); +static prop_object_iterator_t + _prop_array_iterator_locked(prop_array_t); +static prop_object_t + _prop_array_iterator_next_object_locked(void *); +static void _prop_array_iterator_reset_locked(void *); + +static const struct _prop_object_type _prop_object_type_array = { + .pot_type = PROP_TYPE_ARRAY, + .pot_free = _prop_array_free, + .pot_emergency_free = _prop_array_emergency_free, + .pot_extern = _prop_array_externalize, + .pot_equals = _prop_array_equals, + .pot_equals_finish = _prop_array_equals_finish, +}; + +#define prop_object_is_array(x) \ + ((x) != NULL && (x)->pa_obj.po_type == &_prop_object_type_array) + +#define prop_array_is_immutable(x) (((x)->pa_flags & PA_F_IMMUTABLE) != 0) + +struct _prop_array_iterator { + struct _prop_object_iterator pai_base; + unsigned int pai_index; +}; + +#define EXPAND_STEP 16 + +static _prop_object_free_rv_t +_prop_array_free(prop_stack_t stack, prop_object_t *obj) +{ + prop_array_t pa = *obj; + prop_object_t po; + + _PROP_ASSERT(pa->pa_count <= pa->pa_capacity); + _PROP_ASSERT((pa->pa_capacity == 0 && pa->pa_array == NULL) || + (pa->pa_capacity != 0 && pa->pa_array != NULL)); + + /* The easy case is an empty array, just free and return. */ + if (pa->pa_count == 0) { + if (pa->pa_array != NULL) + _PROP_FREE(pa->pa_array, M_PROP_ARRAY); + + _PROP_RWLOCK_DESTROY(pa->pa_rwlock); + + _PROP_POOL_PUT(_prop_array_pool, pa); + + return (_PROP_OBJECT_FREE_DONE); + } + + po = pa->pa_array[pa->pa_count - 1]; + _PROP_ASSERT(po != NULL); + + if (stack == NULL) { + /* + * If we are in emergency release mode, + * just let caller recurse down. + */ + *obj = po; + return (_PROP_OBJECT_FREE_FAILED); + } + + /* Otherwise, try to push the current object on the stack. */ + if (!_prop_stack_push(stack, pa, NULL, NULL, NULL)) { + /* Push failed, entering emergency release mode. */ + return (_PROP_OBJECT_FREE_FAILED); + } + /* Object pushed on stack, caller will release it. */ + --pa->pa_count; + *obj = po; + return (_PROP_OBJECT_FREE_RECURSE); +} + +static void +_prop_array_emergency_free(prop_object_t obj) +{ + prop_array_t pa = obj; + + _PROP_ASSERT(pa->pa_count != 0); + --pa->pa_count; +} + +static bool +_prop_array_externalize(struct _prop_object_externalize_context *ctx, + void *v) +{ + prop_array_t pa = v; + struct _prop_object *po; + prop_object_iterator_t pi; + unsigned int i; + bool rv = false; + + _PROP_RWLOCK_RDLOCK(pa->pa_rwlock); + + if (pa->pa_count == 0) { + _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); + return (_prop_object_externalize_empty_tag(ctx, "array")); + } + + /* XXXJRT Hint "count" for the internalize step? */ + if (_prop_object_externalize_start_tag(ctx, "array") == false || + _prop_object_externalize_append_char(ctx, '\n') == false) + goto out; + + pi = _prop_array_iterator_locked(pa); + if (pi == NULL) + goto out; + + ctx->poec_depth++; + _PROP_ASSERT(ctx->poec_depth != 0); + + while ((po = _prop_array_iterator_next_object_locked(pi)) != NULL) { + if ((*po->po_type->pot_extern)(ctx, po) == false) { + prop_object_iterator_release(pi); + goto out; + } + } + + prop_object_iterator_release(pi); + + ctx->poec_depth--; + for (i = 0; i < ctx->poec_depth; i++) { + if (_prop_object_externalize_append_char(ctx, '\t') == false) + goto out; + } + if (_prop_object_externalize_end_tag(ctx, "array") == false) + goto out; + + rv = true; + + out: + _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); + return (rv); +} + +/* ARGSUSED */ +static _prop_object_equals_rv_t +_prop_array_equals(prop_object_t v1, prop_object_t v2, + void **stored_pointer1, void **stored_pointer2, + prop_object_t *next_obj1, prop_object_t *next_obj2) +{ + prop_array_t array1 = v1; + prop_array_t array2 = v2; + uintptr_t idx; + _prop_object_equals_rv_t rv = _PROP_OBJECT_EQUALS_FALSE; + + if (array1 == array2) + return (_PROP_OBJECT_EQUALS_TRUE); + + _PROP_ASSERT(*stored_pointer1 == *stored_pointer2); + idx = (uintptr_t)*stored_pointer1; + + /* For the first iteration, lock the objects. */ + if (idx == 0) { + if ((uintptr_t)array1 < (uintptr_t)array2) { + _PROP_RWLOCK_RDLOCK(array1->pa_rwlock); + _PROP_RWLOCK_RDLOCK(array2->pa_rwlock); + } else { + _PROP_RWLOCK_RDLOCK(array2->pa_rwlock); + _PROP_RWLOCK_RDLOCK(array1->pa_rwlock); + } + } + + if (array1->pa_count != array2->pa_count) + goto out; + if (idx == array1->pa_count) { + rv = _PROP_OBJECT_EQUALS_TRUE; + goto out; + } + _PROP_ASSERT(idx < array1->pa_count); + + *stored_pointer1 = (void *)(idx + 1); + *stored_pointer2 = (void *)(idx + 1); + + *next_obj1 = array1->pa_array[idx]; + *next_obj2 = array2->pa_array[idx]; + + return (_PROP_OBJECT_EQUALS_RECURSE); + + out: + _PROP_RWLOCK_UNLOCK(array1->pa_rwlock); + _PROP_RWLOCK_UNLOCK(array2->pa_rwlock); + return (rv); +} + +static void +_prop_array_equals_finish(prop_object_t v1, prop_object_t v2) +{ + _PROP_RWLOCK_UNLOCK(((prop_array_t)v1)->pa_rwlock); + _PROP_RWLOCK_UNLOCK(((prop_array_t)v2)->pa_rwlock); +} + +static prop_array_t +_prop_array_alloc(unsigned int capacity) +{ + prop_array_t pa; + prop_object_t *array; + + if (capacity != 0) { + array = _PROP_CALLOC(capacity * sizeof(prop_object_t), + M_PROP_ARRAY); + if (array == NULL) + return (NULL); + } else + array = NULL; + + pa = _PROP_POOL_GET(_prop_array_pool); + if (pa != NULL) { + _prop_object_init(&pa->pa_obj, &_prop_object_type_array); + pa->pa_obj.po_type = &_prop_object_type_array; + + _PROP_RWLOCK_INIT(pa->pa_rwlock); + pa->pa_array = array; + pa->pa_capacity = capacity; + pa->pa_count = 0; + pa->pa_flags = 0; + + pa->pa_version = 0; + } else if (array != NULL) + _PROP_FREE(array, M_PROP_ARRAY); + + return (pa); +} + +static bool +_prop_array_expand(prop_array_t pa, unsigned int capacity) +{ + prop_object_t *array, *oarray; + + /* + * Array must be WRITE-LOCKED. + */ + + oarray = pa->pa_array; + + array = _PROP_CALLOC(capacity * sizeof(*array), M_PROP_ARRAY); + if (array == NULL) + return (false); + if (oarray != NULL) + memcpy(array, oarray, pa->pa_capacity * sizeof(*array)); + pa->pa_array = array; + pa->pa_capacity = capacity; + + if (oarray != NULL) + _PROP_FREE(oarray, M_PROP_ARRAY); + + return (true); +} + +static prop_object_t +_prop_array_iterator_next_object_locked(void *v) +{ + struct _prop_array_iterator *pai = v; + prop_array_t pa = pai->pai_base.pi_obj; + prop_object_t po = NULL; + + _PROP_ASSERT(prop_object_is_array(pa)); + + if (pa->pa_version != pai->pai_base.pi_version) + goto out; /* array changed during iteration */ + + _PROP_ASSERT(pai->pai_index <= pa->pa_count); + + if (pai->pai_index == pa->pa_count) + goto out; /* we've iterated all objects */ + + po = pa->pa_array[pai->pai_index]; + pai->pai_index++; + + out: + return (po); +} + +static prop_object_t +_prop_array_iterator_next_object(void *v) +{ + struct _prop_array_iterator *pai = v; + prop_array_t pa = pai->pai_base.pi_obj; + prop_object_t po; + + _PROP_ASSERT(prop_object_is_array(pa)); + + _PROP_RWLOCK_RDLOCK(pa->pa_rwlock); + po = _prop_array_iterator_next_object_locked(pai); + _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); + return (po); +} + +static void +_prop_array_iterator_reset_locked(void *v) +{ + struct _prop_array_iterator *pai = v; + prop_array_t pa = pai->pai_base.pi_obj; + + _PROP_ASSERT(prop_object_is_array(pa)); + + pai->pai_index = 0; + pai->pai_base.pi_version = pa->pa_version; +} + +static void +_prop_array_iterator_reset(void *v) +{ + struct _prop_array_iterator *pai = v; + prop_array_t pa = pai->pai_base.pi_obj; + + _PROP_ASSERT(prop_object_is_array(pa)); + + _PROP_RWLOCK_RDLOCK(pa->pa_rwlock); + _prop_array_iterator_reset_locked(pai); + _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); +} + +/* + * prop_array_create -- + * Create an empty array. + */ +prop_array_t +prop_array_create(void) +{ + + return (_prop_array_alloc(0)); +} + +/* + * prop_array_create_with_capacity -- + * Create an array with the capacity to store N objects. + */ +prop_array_t +prop_array_create_with_capacity(unsigned int capacity) +{ + + return (_prop_array_alloc(capacity)); +} + +/* + * prop_array_copy -- + * Copy an array. The new array has an initial capacity equal to + * the number of objects stored in the original array. The new + * array contains references to the original array's objects, not + * copies of those objects (i.e. a shallow copy). + */ +prop_array_t +prop_array_copy(prop_array_t opa) +{ + prop_array_t pa; + prop_object_t po; + unsigned int idx; + + if (! prop_object_is_array(opa)) + return (NULL); + + _PROP_RWLOCK_RDLOCK(opa->pa_rwlock); + + pa = _prop_array_alloc(opa->pa_count); + if (pa != NULL) { + for (idx = 0; idx < opa->pa_count; idx++) { + po = opa->pa_array[idx]; + prop_object_retain(po); + pa->pa_array[idx] = po; + } + pa->pa_count = opa->pa_count; + pa->pa_flags = opa->pa_flags; + } + _PROP_RWLOCK_UNLOCK(opa->pa_rwlock); + return (pa); +} + +/* + * prop_array_copy_mutable -- + * Like prop_array_copy(), but the resulting array is mutable. + */ +prop_array_t +prop_array_copy_mutable(prop_array_t opa) +{ + prop_array_t pa; + + pa = prop_array_copy(opa); + if (pa != NULL) + pa->pa_flags &= ~PA_F_IMMUTABLE; + + return (pa); +} + +/* + * prop_array_capacity -- + * Return the capacity of the array. + */ +unsigned int +prop_array_capacity(prop_array_t pa) +{ + unsigned int rv; + + if (! prop_object_is_array(pa)) + return (0); + + _PROP_RWLOCK_RDLOCK(pa->pa_rwlock); + rv = pa->pa_capacity; + _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); + + return (rv); +} + +/* + * prop_array_count -- + * Return the number of objects stored in the array. + */ +unsigned int +prop_array_count(prop_array_t pa) +{ + unsigned int rv; + + if (! prop_object_is_array(pa)) + return (0); + + _PROP_RWLOCK_RDLOCK(pa->pa_rwlock); + rv = pa->pa_count; + _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); + + return (rv); +} + +/* + * prop_array_ensure_capacity -- + * Ensure that the array has the capacity to store the specified + * total number of objects (inluding the objects already stored + * in the array). + */ +bool +prop_array_ensure_capacity(prop_array_t pa, unsigned int capacity) +{ + bool rv; + + if (! prop_object_is_array(pa)) + return (false); + + _PROP_RWLOCK_WRLOCK(pa->pa_rwlock); + if (capacity > pa->pa_capacity) + rv = _prop_array_expand(pa, capacity); + else + rv = true; + _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); + + return (rv); +} + +static prop_object_iterator_t +_prop_array_iterator_locked(prop_array_t pa) +{ + struct _prop_array_iterator *pai; + + if (! prop_object_is_array(pa)) + return (NULL); + + pai = _PROP_CALLOC(sizeof(*pai), M_TEMP); + if (pai == NULL) + return (NULL); + pai->pai_base.pi_next_object = _prop_array_iterator_next_object; + pai->pai_base.pi_reset = _prop_array_iterator_reset; + prop_object_retain(pa); + pai->pai_base.pi_obj = pa; + _prop_array_iterator_reset_locked(pai); + + return (&pai->pai_base); +} + +/* + * prop_array_iterator -- + * Return an iterator for the array. The array is retained by + * the iterator. + */ +prop_object_iterator_t +prop_array_iterator(prop_array_t pa) +{ + prop_object_iterator_t pi; + + _PROP_RWLOCK_RDLOCK(pa->pa_rwlock); + pi = _prop_array_iterator_locked(pa); + _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); + return (pi); +} + +/* + * prop_array_make_immutable -- + * Make the array immutable. + */ +void +prop_array_make_immutable(prop_array_t pa) +{ + + _PROP_RWLOCK_WRLOCK(pa->pa_rwlock); + if (prop_array_is_immutable(pa) == false) + pa->pa_flags |= PA_F_IMMUTABLE; + _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); +} + +/* + * prop_array_mutable -- + * Returns true if the array is mutable. + */ +bool +prop_array_mutable(prop_array_t pa) +{ + bool rv; + + _PROP_RWLOCK_RDLOCK(pa->pa_rwlock); + rv = prop_array_is_immutable(pa) == false; + _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); + + return (rv); +} + +/* + * prop_array_get -- + * Return the object stored at the specified array index. + */ +prop_object_t +prop_array_get(prop_array_t pa, unsigned int idx) +{ + prop_object_t po = NULL; + + if (! prop_object_is_array(pa)) + return (NULL); + + _PROP_RWLOCK_RDLOCK(pa->pa_rwlock); + if (idx >= pa->pa_count) + goto out; + po = pa->pa_array[idx]; + _PROP_ASSERT(po != NULL); + out: + _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); + return (po); +} + +static bool +_prop_array_add(prop_array_t pa, prop_object_t po) +{ + + /* + * Array must be WRITE-LOCKED. + */ + + _PROP_ASSERT(pa->pa_count <= pa->pa_capacity); + + if (prop_array_is_immutable(pa) || + (pa->pa_count == pa->pa_capacity && + _prop_array_expand(pa, pa->pa_capacity + EXPAND_STEP) == false)) + return (false); + + prop_object_retain(po); + pa->pa_array[pa->pa_count++] = po; + pa->pa_version++; + + return (true); +} + +/* + * prop_array_set -- + * Store a reference to an object at the specified array index. + * This method is not allowed to create holes in the array; the + * caller must either be setting the object just beyond the existing + * count or replacing an already existing object reference. + */ +bool +prop_array_set(prop_array_t pa, unsigned int idx, prop_object_t po) +{ + prop_object_t opo; + bool rv = false; + + if (! prop_object_is_array(pa)) + return (false); + + _PROP_RWLOCK_WRLOCK(pa->pa_rwlock); + + if (prop_array_is_immutable(pa)) + goto out; + + if (idx == pa->pa_count) { + rv = _prop_array_add(pa, po); + goto out; + } + + _PROP_ASSERT(idx < pa->pa_count); + + opo = pa->pa_array[idx]; + _PROP_ASSERT(opo != NULL); + + prop_object_retain(po); + pa->pa_array[idx] = po; + pa->pa_version++; + + prop_object_release(opo); + + rv = true; + + out: + _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); + return (rv); +} + +/* + * prop_array_add -- + * Add a reference to an object to the specified array, appending + * to the end and growing the array's capacity, if necessary. + */ +bool +prop_array_add(prop_array_t pa, prop_object_t po) +{ + bool rv; + + if (! prop_object_is_array(pa)) + return (false); + + _PROP_RWLOCK_WRLOCK(pa->pa_rwlock); + rv = _prop_array_add(pa, po); + _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); + + return (rv); +} + +/* + * prop_array_remove -- + * Remove the reference to an object from an array at the specified + * index. The array will be compacted following the removal. + */ +void +prop_array_remove(prop_array_t pa, unsigned int idx) +{ + prop_object_t po; + + if (! prop_object_is_array(pa)) + return; + + _PROP_RWLOCK_WRLOCK(pa->pa_rwlock); + + _PROP_ASSERT(idx < pa->pa_count); + + /* XXX Should this be a _PROP_ASSERT()? */ + if (prop_array_is_immutable(pa)) { + _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); + return; + } + + po = pa->pa_array[idx]; + _PROP_ASSERT(po != NULL); + + for (++idx; idx < pa->pa_count; idx++) + pa->pa_array[idx - 1] = pa->pa_array[idx]; + pa->pa_count--; + pa->pa_version++; + + _PROP_RWLOCK_UNLOCK(pa->pa_rwlock); + + prop_object_release(po); +} + +/* + * prop_array_equals -- + * Return true if the two arrays are equivalent. Note we do a + * by-value comparison of the objects in the array. + */ +bool +prop_array_equals(prop_array_t array1, prop_array_t array2) +{ + if (!prop_object_is_array(array1) || !prop_object_is_array(array2)) + return (false); + + return (prop_object_equals(array1, array2)); +} + +/* + * prop_array_externalize -- + * Externalize an array, return a NUL-terminated buffer + * containing the XML-style representation. The buffer is allocated + * with the M_TEMP memory type. + */ +char * +prop_array_externalize(prop_array_t pa) +{ + struct _prop_object_externalize_context *ctx; + char *cp; + + ctx = _prop_object_externalize_context_alloc(); + if (ctx == NULL) + return (NULL); + + if (_prop_object_externalize_header(ctx) == false || + (*pa->pa_obj.po_type->pot_extern)(ctx, pa) == false || + _prop_object_externalize_footer(ctx) == false) { + /* We are responsible for releasing the buffer. */ + _PROP_FREE(ctx->poec_buf, M_TEMP); + _prop_object_externalize_context_free(ctx); + return (NULL); + } + + cp = ctx->poec_buf; + _prop_object_externalize_context_free(ctx); + + return (cp); +} + +/* + * _prop_array_internalize -- + * Parse an ... and return the object created from the + * external representation. + */ +static bool _prop_array_internalize_body(prop_stack_t, prop_object_t *, + struct _prop_object_internalize_context *); + +bool +_prop_array_internalize(prop_stack_t stack, prop_object_t *obj, + struct _prop_object_internalize_context *ctx) +{ + /* We don't currently understand any attributes. */ + if (ctx->poic_tagattr != NULL) + return (true); + + *obj = prop_array_create(); + /* + * We are done if the create failed or no child elements exist. + */ + if (*obj == NULL || ctx->poic_is_empty_element) + return (true); + + /* + * Opening tag is found, now continue to the first element. + */ + return (_prop_array_internalize_body(stack, obj, ctx)); +} + +static bool +_prop_array_internalize_continue(prop_stack_t stack, + prop_object_t *obj, + struct _prop_object_internalize_context *ctx, + void *data, prop_object_t child) +{ + prop_array_t array; + + _PROP_ASSERT(data == NULL); + + if (child == NULL) + goto bad; /* Element could not be parsed. */ + + array = *obj; + + if (prop_array_add(array, child) == false) { + prop_object_release(child); + goto bad; + } + prop_object_release(child); + + /* + * Current element is processed and added, look for next. + */ + return (_prop_array_internalize_body(stack, obj, ctx)); + + bad: + prop_object_release(*obj); + *obj = NULL; + return (true); +} + +static bool +_prop_array_internalize_body(prop_stack_t stack, prop_object_t *obj, + struct _prop_object_internalize_context *ctx) +{ + prop_array_t array = *obj; + + _PROP_ASSERT(array != NULL); + + /* Fetch the next tag. */ + if (_prop_object_internalize_find_tag(ctx, NULL, + _PROP_TAG_TYPE_EITHER) == false) + goto bad; + + /* Check to see if this is the end of the array. */ + if (_PROP_TAG_MATCH(ctx, "array") && + ctx->poic_tag_type == _PROP_TAG_TYPE_END) { + /* It is, so don't iterate any further. */ + return (true); + } + + if (_prop_stack_push(stack, array, + _prop_array_internalize_continue, NULL, NULL)) + return (false); + + bad: + prop_object_release(array); + *obj = NULL; + return (true); +} + +/* + * prop_array_internalize -- + * Create an array by parsing the XML-style representation. + */ +prop_array_t +prop_array_internalize(const char *xml) +{ + return _prop_generic_internalize(xml, "array"); +} + +/* + * prop_array_externalize_to_file -- + * Externalize an array to the specified file. + */ +bool +prop_array_externalize_to_file(prop_array_t array, const char *fname) +{ + char *xml; + bool rv; + int save_errno = 0; /* XXXGCC -Wuninitialized [mips, ...] */ + + xml = prop_array_externalize(array); + if (xml == NULL) + return (false); + rv = _prop_object_externalize_write_file(fname, xml, + strlen(xml), false); + if (rv == false) + save_errno = errno; + _PROP_FREE(xml, M_TEMP); + if (rv == false) + errno = save_errno; + + return (rv); +} + +/* + * prop_array_externalize_to_zfile --- + * Externalize an array to the specified file, and on the fly + * compressing the result with gzip (via zlib). + */ +bool +prop_array_externalize_to_zfile(prop_array_t array, const char *fname) +{ + char *xml; + bool rv; + int save_errno = 0; + + xml = prop_array_externalize(array); + if (xml == NULL) + return false; + rv = _prop_object_externalize_write_file(fname, xml, strlen(xml), true); + if (rv == false) + save_errno = errno; + _PROP_FREE(xml, M_TEMP); + if (rv == false) + errno = save_errno; + + return rv; +} + +/* + * prop_array_internalize_from_file -- + * Internalize an array from a file. + */ +prop_array_t +prop_array_internalize_from_file(const char *fname) +{ + struct _prop_object_internalize_mapped_file *mf; + prop_array_t array; + + mf = _prop_object_internalize_map_file(fname); + if (mf == NULL) + return (NULL); + array = prop_array_internalize(mf->poimf_xml); + _prop_object_internalize_unmap_file(mf); + + return (array); +} + +#define _READ_CHUNK 512 +/* + * prop_array_internalize_from_zfile --- + * Internalize an array from a compressed gzip file. + */ +prop_array_t +prop_array_internalize_from_zfile(const char *fname) +{ + struct _prop_object_internalize_mapped_file *mf; + prop_array_t array; + z_stream strm; + unsigned char out[_READ_CHUNK]; + char *uncomp_xml = NULL; + size_t have; + ssize_t totalsize = 0; + int rv = 0; + + mf = _prop_object_internalize_map_file(fname); + if (mf == NULL) + return NULL; + + /* Decompress the mmap'ed buffer with zlib */ + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + strm.avail_in = 0; + strm.next_in = Z_NULL; + + /* 15+16 to use gzip method */ + if (inflateInit2(&strm, 15+16) != Z_OK) { + _prop_object_internalize_unmap_file(mf); + return NULL; + } + + strm.avail_in = mf->poimf_mapsize; + strm.next_in = mf->poimf_xml; + + /* Output buffer (decompressed) */ + uncomp_xml = _PROP_MALLOC(_READ_CHUNK, M_TEMP); + if (uncomp_xml == NULL) { + _prop_object_internalize_unmap_file(mf); + (void)inflateEnd(&strm); + return NULL; + } + + /* Inflate the input buffer and copy into 'dest' */ + do { + strm.avail_out = _READ_CHUNK; + strm.next_out = out; + rv = inflate(&strm, Z_NO_FLUSH); + switch (rv) { + case Z_DATA_ERROR: + /* + * Wrong compressed data or uncompressed, try + * normal method as last resort. + */ + (void)inflateEnd(&strm); + _PROP_FREE(uncomp_xml, M_TEMP); + array = prop_array_internalize(mf->poimf_xml); + _prop_object_internalize_unmap_file(mf); + return array; + case Z_STREAM_ERROR: + case Z_NEED_DICT: + case Z_MEM_ERROR: + (void)inflateEnd(&strm); + _PROP_FREE(uncomp_xml, M_TEMP); + _prop_object_internalize_unmap_file(mf); + errno = rv; + return NULL; + } + have = _READ_CHUNK - strm.avail_out; + totalsize += have; + uncomp_xml = _PROP_REALLOC(uncomp_xml, totalsize, M_TEMP); + memcpy(uncomp_xml + totalsize - have, out, have); + } while (strm.avail_out == 0); + + /* we are done */ + (void)inflateEnd(&strm); + array = prop_array_internalize(uncomp_xml); + _PROP_FREE(uncomp_xml, M_TEMP); + _prop_object_internalize_unmap_file(mf); + + return array; +} diff --git a/lib/portableproplib/prop_array_util.c b/lib/portableproplib/prop_array_util.c new file mode 100644 index 00000000..34bb226f --- /dev/null +++ b/lib/portableproplib/prop_array_util.c @@ -0,0 +1,240 @@ +/* $NetBSD: prop_array_util.c,v 1.2 2008/09/11 13:15:13 haad Exp $ */ + +/*- + * Copyright (c) 2006 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Utility routines to make it more convenient to work with values + * stored in array. + * + * Note: There is no special magic going on here. We use the standard + * proplib(3) APIs to do all of this work. Any application could do + * exactly what we're doing here. + */ + +#include +#include "prop_object_impl.h" + +bool +prop_array_get_bool(prop_array_t array, + unsigned int indx, + bool *valp) +{ + prop_bool_t b; + + b = prop_array_get(array, indx); + if (prop_object_type(b) != PROP_TYPE_BOOL) + return (false); + + *valp = prop_bool_true(b); + + return (true); +} + +bool +prop_array_set_bool(prop_array_t array, + unsigned int indx, + bool val) +{ + prop_bool_t b; + int rv; + + b = prop_bool_create(val); + if (b == NULL) + return (false); + rv = prop_array_set(array, indx, b); + prop_object_release(b); + + return (rv); +} + +#define TEMPLATE(size) \ +bool \ +prop_array_get_int ## size (prop_array_t array, \ + unsigned int indx, \ + int ## size ## _t *valp) \ +{ \ + prop_number_t num; \ + \ + num = prop_array_get(array, indx); \ + if (prop_object_type(num) != PROP_TYPE_NUMBER) \ + return (false); \ + \ + if (prop_number_unsigned(num) && \ + prop_number_unsigned_integer_value(num) > \ + /*CONSTCOND*/((size) == 8 ? INT8_MAX : \ + (size) == 16 ? INT16_MAX : \ + (size) == 32 ? INT32_MAX : INT64_MAX)) { \ + return (false); \ + } \ + \ + if (prop_number_size(num) > (size)) \ + return (false); \ + \ + *valp = (int ## size ## _t) prop_number_integer_value(num); \ + \ + return (true); \ +} \ + \ +bool \ +prop_array_get_uint ## size (prop_array_t array, \ + unsigned int indx, \ + uint ## size ## _t *valp) \ +{ \ + prop_number_t num; \ + \ + num = prop_array_get(array, indx); \ + if (prop_object_type(num) != PROP_TYPE_NUMBER) \ + return (false); \ + \ + if (prop_number_unsigned(num) == false && \ + prop_number_integer_value(num) < 0) { \ + return (false); \ + } \ + \ + if (prop_number_size(num) > (size)) \ + return (false); \ + \ + *valp = (uint ## size ## _t) \ + prop_number_unsigned_integer_value(num); \ + \ + return (true); \ +} \ + \ +bool \ + prop_array_set_int ## size (prop_array_t array, \ + unsigned int indx, \ + int ## size ## _t val) \ +{ \ + prop_number_t num; \ + int rv; \ + \ + num = prop_number_create_integer((int64_t) val); \ + if (num == NULL) \ + return (false); \ + rv = prop_array_set(array, indx, num); \ + prop_object_release(num); \ + \ + return (rv); \ +} \ + \ +bool \ +prop_array_set_uint ## size (prop_array_t array, \ + unsigned int indx, \ + uint ## size ## _t val) \ +{ \ + prop_number_t num; \ + int rv; \ + \ + num = prop_number_create_unsigned_integer((uint64_t) val); \ + if (num == NULL) \ + return (false); \ + rv = prop_array_set(array, indx, num); \ + prop_object_release(num); \ + \ + return (rv); \ +} \ + \ +bool \ +prop_array_add_int ## size (prop_array_t array, \ + int ## size ## _t val) \ +{ \ + prop_number_t num; \ + int rv; \ + \ + num = prop_number_create_integer((int64_t) val); \ + if (num == NULL) \ + return (false); \ + rv = prop_array_add(array, num); \ + prop_object_release(num); \ + \ + return (rv); \ +} \ + \ +bool \ +prop_array_add_uint ## size (prop_array_t array, \ + uint ## size ## _t val) \ +{ \ + prop_number_t num; \ + int rv; \ + \ + num = prop_number_create_integer((int64_t) val); \ + if (num == NULL) \ + return (false); \ + rv = prop_array_add(array, num); \ + prop_object_release(num); \ + \ + return (rv); \ +} + +TEMPLATE(8) +TEMPLATE(16) +TEMPLATE(32) +TEMPLATE(64) + +#undef TEMPLATE + +#define TEMPLATE(variant, qualifier) \ +bool \ +prop_array_get_cstring ## variant (prop_array_t array, \ + unsigned int indx, \ + qualifier char **cpp) \ +{ \ + prop_string_t str; \ + \ + str = prop_array_get(array, indx); \ + if (prop_object_type(str) != PROP_TYPE_STRING) \ + return (false); \ + \ + *cpp = prop_string_cstring ## variant (str); \ + \ + return (*cpp == NULL ? false : true); \ +} \ + \ +bool \ +prop_array_set_cstring ## variant (prop_array_t array, \ + unsigned int indx, \ + const char *cp) \ +{ \ + prop_string_t str; \ + int rv; \ + \ + str = prop_string_create_cstring ## variant (cp); \ + if (str == NULL) \ + return (false); \ + rv = prop_array_set(array, indx, str); \ + prop_object_release(str); \ + \ + return (rv); \ +} + +TEMPLATE(,) +TEMPLATE(_nocopy,const) + +#undef TEMPLATE diff --git a/lib/portableproplib/prop_bool.c b/lib/portableproplib/prop_bool.c new file mode 100644 index 00000000..4e2bc2bd --- /dev/null +++ b/lib/portableproplib/prop_bool.c @@ -0,0 +1,223 @@ +/* $NetBSD: prop_bool.c,v 1.16 2008/08/03 04:00:12 thorpej Exp $ */ + +/*- + * Copyright (c) 2006 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include "prop_object_impl.h" + +struct _prop_bool { + struct _prop_object pb_obj; + bool pb_value; +}; + +static struct _prop_bool _prop_bool_true; +static struct _prop_bool _prop_bool_false; + +_PROP_MUTEX_DECL_STATIC(_prop_bool_initialized_mutex) +static bool _prop_bool_initialized; + +static _prop_object_free_rv_t + _prop_bool_free(prop_stack_t, prop_object_t *); +static bool _prop_bool_externalize( + struct _prop_object_externalize_context *, + void *); +static _prop_object_equals_rv_t + _prop_bool_equals(prop_object_t, prop_object_t, + void **, void **, + prop_object_t *, prop_object_t *); + +static const struct _prop_object_type _prop_object_type_bool = { + .pot_type = PROP_TYPE_BOOL, + .pot_free = _prop_bool_free, + .pot_extern = _prop_bool_externalize, + .pot_equals = _prop_bool_equals, +}; + +#define prop_object_is_bool(x) \ + ((x) != NULL && (x)->pb_obj.po_type == &_prop_object_type_bool) + +/* ARGSUSED */ +static _prop_object_free_rv_t +_prop_bool_free(prop_stack_t stack, prop_object_t *obj) +{ + /* + * This should never happen as we "leak" our initial reference + * count. + */ + + /* XXX forced assertion failure? */ + return (_PROP_OBJECT_FREE_DONE); +} + +static bool +_prop_bool_externalize(struct _prop_object_externalize_context *ctx, + void *v) +{ + prop_bool_t pb = v; + + return (_prop_object_externalize_empty_tag(ctx, + pb->pb_value ? "true" : "false")); +} + +/* ARGSUSED */ +static _prop_object_equals_rv_t +_prop_bool_equals(prop_object_t v1, prop_object_t v2, + void **stored_pointer1, void **stored_pointer2, + prop_object_t *next_obj1, prop_object_t *next_obj2) +{ + prop_bool_t b1 = v1; + prop_bool_t b2 = v2; + + if (! (prop_object_is_bool(b1) && + prop_object_is_bool(b2))) + return (_PROP_OBJECT_EQUALS_FALSE); + + /* + * Since we only ever allocate one true and one false, + * save ourselves a couple of memory operations. + */ + if (b1 == b2) + return (_PROP_OBJECT_EQUALS_TRUE); + else + return (_PROP_OBJECT_EQUALS_FALSE); +} + +static prop_bool_t +_prop_bool_alloc(bool val) +{ + prop_bool_t pb; + + if (! _prop_bool_initialized) { + _PROP_MUTEX_LOCK(_prop_bool_initialized_mutex); + if (! _prop_bool_initialized) { + _prop_object_init(&_prop_bool_true.pb_obj, + &_prop_object_type_bool); + _prop_bool_true.pb_value = true; + + _prop_object_init(&_prop_bool_false.pb_obj, + &_prop_object_type_bool); + _prop_bool_false.pb_value = false; + + _prop_bool_initialized = true; + } + _PROP_MUTEX_UNLOCK(_prop_bool_initialized_mutex); + } + + pb = val ? &_prop_bool_true : &_prop_bool_false; + prop_object_retain(pb); + + return (pb); +} + +/* + * prop_bool_create -- + * Create a prop_bool_t and initialize it with the + * provided boolean value. + */ +prop_bool_t +prop_bool_create(bool val) +{ + + return (_prop_bool_alloc(val)); +} + +/* + * prop_bool_copy -- + * Copy a prop_bool_t. + */ +prop_bool_t +prop_bool_copy(prop_bool_t opb) +{ + + if (! prop_object_is_bool(opb)) + return (NULL); + + /* + * Because we only ever allocate one true and one false, this + * can be reduced to a simple retain operation. + */ + prop_object_retain(opb); + return (opb); +} + +/* + * prop_bool_true -- + * Get the value of a prop_bool_t. + */ +bool +prop_bool_true(prop_bool_t pb) +{ + + if (! prop_object_is_bool(pb)) + return (false); + + return (pb->pb_value); +} + +/* + * prop_bool_equals -- + * Return true if the boolean values are equivalent. + */ +bool +prop_bool_equals(prop_bool_t b1, prop_bool_t b2) +{ + if (!prop_object_is_bool(b1) || !prop_object_is_bool(b2)) + return (false); + + return (prop_object_equals(b1, b2)); +} + +/* + * _prop_bool_internalize -- + * Parse a or and return the object created from + * the external representation. + */ + +/* ARGSUSED */ +bool +_prop_bool_internalize(prop_stack_t stack, prop_object_t *obj, + struct _prop_object_internalize_context *ctx) +{ + bool val; + + /* No attributes, and it must be an empty element. */ + if (ctx->poic_tagattr != NULL || + ctx->poic_is_empty_element == false) + return (true); + + if (_PROP_TAG_MATCH(ctx, "true")) + val = true; + else { + _PROP_ASSERT(_PROP_TAG_MATCH(ctx, "false")); + val = false; + } + *obj = prop_bool_create(val); + return (true); +} diff --git a/lib/portableproplib/prop_data.c b/lib/portableproplib/prop_data.c new file mode 100644 index 00000000..d4358104 --- /dev/null +++ b/lib/portableproplib/prop_data.c @@ -0,0 +1,616 @@ +/* $NetBSD: prop_data.c,v 1.13 2008/08/03 04:00:12 thorpej Exp $ */ + +/*- + * Copyright (c) 2006 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include "prop_object_impl.h" + +#include +#include +#include + +struct _prop_data { + struct _prop_object pd_obj; + union { + void * pdu_mutable; + const void * pdu_immutable; + } pd_un; +#define pd_mutable pd_un.pdu_mutable +#define pd_immutable pd_un.pdu_immutable + size_t pd_size; + int pd_flags; +}; + +#define PD_F_NOCOPY 0x01 + +_PROP_POOL_INIT(_prop_data_pool, sizeof(struct _prop_data), "propdata") + +_PROP_MALLOC_DEFINE(M_PROP_DATA, "prop data", + "property data container object") + +static _prop_object_free_rv_t + _prop_data_free(prop_stack_t, prop_object_t *); +static bool _prop_data_externalize( + struct _prop_object_externalize_context *, + void *); +static _prop_object_equals_rv_t + _prop_data_equals(prop_object_t, prop_object_t, + void **, void **, + prop_object_t *, prop_object_t *); + +static const struct _prop_object_type _prop_object_type_data = { + .pot_type = PROP_TYPE_DATA, + .pot_free = _prop_data_free, + .pot_extern = _prop_data_externalize, + .pot_equals = _prop_data_equals, +}; + +#define prop_object_is_data(x) \ + ((x) != NULL && (x)->pd_obj.po_type == &_prop_object_type_data) + +/* ARGSUSED */ +static _prop_object_free_rv_t +_prop_data_free(prop_stack_t stack, prop_object_t *obj) +{ + prop_data_t pd = *obj; + + if ((pd->pd_flags & PD_F_NOCOPY) == 0 && pd->pd_mutable != NULL) + _PROP_FREE(pd->pd_mutable, M_PROP_DATA); + _PROP_POOL_PUT(_prop_data_pool, pd); + + return (_PROP_OBJECT_FREE_DONE); +} + +static const char _prop_data_base64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static const char _prop_data_pad64 = '='; + +static bool +_prop_data_externalize(struct _prop_object_externalize_context *ctx, void *v) +{ + prop_data_t pd = v; + size_t i, srclen; + const uint8_t *src; + uint8_t output[4]; + uint8_t input[3]; + + if (pd->pd_size == 0) + return (_prop_object_externalize_empty_tag(ctx, "data")); + + if (_prop_object_externalize_start_tag(ctx, "data") == false) + return (false); + + for (src = pd->pd_immutable, srclen = pd->pd_size; + srclen > 2; srclen -= 3) { + input[0] = *src++; + input[1] = *src++; + input[2] = *src++; + + output[0] = (uint32_t)input[0] >> 2; + output[1] = ((uint32_t)(input[0] & 0x03) << 4) + + ((uint32_t)input[1] >> 4); + output[2] = ((uint32_t)(input[1] & 0x0f) << 2) + + ((uint32_t)input[2] >> 6); + output[3] = input[2] & 0x3f; + _PROP_ASSERT(output[0] < 64); + _PROP_ASSERT(output[1] < 64); + _PROP_ASSERT(output[2] < 64); + _PROP_ASSERT(output[3] < 64); + + if (_prop_object_externalize_append_char(ctx, + _prop_data_base64[output[0]]) == false || + _prop_object_externalize_append_char(ctx, + _prop_data_base64[output[1]]) == false || + _prop_object_externalize_append_char(ctx, + _prop_data_base64[output[2]]) == false || + _prop_object_externalize_append_char(ctx, + _prop_data_base64[output[3]]) == false) + return (false); + } + + if (srclen != 0) { + input[0] = input[1] = input[2] = '\0'; + for (i = 0; i < srclen; i++) + input[i] = *src++; + + output[0] = (uint32_t)input[0] >> 2; + output[1] = ((uint32_t)(input[0] & 0x03) << 4) + + ((uint32_t)input[1] >> 4); + output[2] = ((uint32_t)(input[1] & 0x0f) << 2) + + ((uint32_t)input[2] >> 6); + _PROP_ASSERT(output[0] < 64); + _PROP_ASSERT(output[1] < 64); + _PROP_ASSERT(output[2] < 64); + + if (_prop_object_externalize_append_char(ctx, + _prop_data_base64[output[0]]) == false || + _prop_object_externalize_append_char(ctx, + _prop_data_base64[output[1]]) == false || + _prop_object_externalize_append_char(ctx, + srclen == 1 ? _prop_data_pad64 + : _prop_data_base64[output[2]]) == false || + _prop_object_externalize_append_char(ctx, + _prop_data_pad64) == false) + return (false); + } + + if (_prop_object_externalize_end_tag(ctx, "data") == false) + return (false); + + return (true); +} + +/* ARGSUSED */ +static _prop_object_equals_rv_t +_prop_data_equals(prop_object_t v1, prop_object_t v2, + void **stored_pointer1, void **stored_pointer2, + prop_object_t *next_obj1, prop_object_t *next_obj2) +{ + prop_data_t pd1 = v1; + prop_data_t pd2 = v2; + + if (pd1 == pd2) + return (_PROP_OBJECT_EQUALS_TRUE); + if (pd1->pd_size != pd2->pd_size) + return (_PROP_OBJECT_EQUALS_FALSE); + if (pd1->pd_size == 0) { + _PROP_ASSERT(pd1->pd_immutable == NULL); + _PROP_ASSERT(pd2->pd_immutable == NULL); + return (_PROP_OBJECT_EQUALS_TRUE); + } + if (memcmp(pd1->pd_immutable, pd2->pd_immutable, pd1->pd_size) == 0) + return _PROP_OBJECT_EQUALS_TRUE; + else + return _PROP_OBJECT_EQUALS_FALSE; +} + +static prop_data_t +_prop_data_alloc(void) +{ + prop_data_t pd; + + pd = _PROP_POOL_GET(_prop_data_pool); + if (pd != NULL) { + _prop_object_init(&pd->pd_obj, &_prop_object_type_data); + + pd->pd_mutable = NULL; + pd->pd_size = 0; + pd->pd_flags = 0; + } + + return (pd); +} + +/* + * prop_data_create_data -- + * Create a data container that contains a copy of the data. + */ +prop_data_t +prop_data_create_data(const void *v, size_t size) +{ + prop_data_t pd; + void *nv; + + pd = _prop_data_alloc(); + if (pd != NULL && size != 0) { + nv = _PROP_MALLOC(size, M_PROP_DATA); + if (nv == NULL) { + prop_object_release(pd); + return (NULL); + } + memcpy(nv, v, size); + pd->pd_mutable = nv; + pd->pd_size = size; + } + return (pd); +} + +/* + * prop_data_create_data_nocopy -- + * Create an immutable data container that contains a refrence to the + * provided external data. + */ +prop_data_t +prop_data_create_data_nocopy(const void *v, size_t size) +{ + prop_data_t pd; + + pd = _prop_data_alloc(); + if (pd != NULL) { + pd->pd_immutable = v; + pd->pd_size = size; + pd->pd_flags |= PD_F_NOCOPY; + } + return (pd); +} + +/* + * prop_data_copy -- + * Copy a data container. If the original data is external, then + * the copy is also references the same external data. + */ +prop_data_t +prop_data_copy(prop_data_t opd) +{ + prop_data_t pd; + + if (! prop_object_is_data(opd)) + return (NULL); + + pd = _prop_data_alloc(); + if (pd != NULL) { + pd->pd_size = opd->pd_size; + pd->pd_flags = opd->pd_flags; + if (opd->pd_flags & PD_F_NOCOPY) + pd->pd_immutable = opd->pd_immutable; + else if (opd->pd_size != 0) { + void *nv = _PROP_MALLOC(pd->pd_size, M_PROP_DATA); + if (nv == NULL) { + prop_object_release(pd); + return (NULL); + } + memcpy(nv, opd->pd_immutable, opd->pd_size); + pd->pd_mutable = nv; + } + } + return (pd); +} + +/* + * prop_data_size -- + * Return the size of the data. + */ +size_t +prop_data_size(prop_data_t pd) +{ + + if (! prop_object_is_data(pd)) + return (0); + + return (pd->pd_size); +} + +/* + * prop_data_data -- + * Return a copy of the contents of the data container. + * The data is allocated with the M_TEMP malloc type. + * If the data container is empty, NULL is returned. + */ +void * +prop_data_data(prop_data_t pd) +{ + void *v; + + if (! prop_object_is_data(pd)) + return (NULL); + + if (pd->pd_size == 0) { + _PROP_ASSERT(pd->pd_immutable == NULL); + return (NULL); + } + + _PROP_ASSERT(pd->pd_immutable != NULL); + + v = _PROP_MALLOC(pd->pd_size, M_TEMP); + if (v != NULL) + memcpy(v, pd->pd_immutable, pd->pd_size); + + return (v); +} + +/* + * prop_data_data_nocopy -- + * Return an immutable reference to the contents of the data + * container. + */ +const void * +prop_data_data_nocopy(prop_data_t pd) +{ + + if (! prop_object_is_data(pd)) + return (NULL); + + _PROP_ASSERT((pd->pd_size == 0 && pd->pd_immutable == NULL) || + (pd->pd_size != 0 && pd->pd_immutable != NULL)); + + return (pd->pd_immutable); +} + +/* + * prop_data_equals -- + * Return true if two strings are equivalent. + */ +bool +prop_data_equals(prop_data_t pd1, prop_data_t pd2) +{ + if (!prop_object_is_data(pd1) || !prop_object_is_data(pd2)) + return (false); + + return (prop_object_equals(pd1, pd2)); +} + +/* + * prop_data_equals_data -- + * Return true if the contained data is equivalent to the specified + * external data. + */ +bool +prop_data_equals_data(prop_data_t pd, const void *v, size_t size) +{ + + if (! prop_object_is_data(pd)) + return (false); + + if (pd->pd_size != size) + return (false); + return (memcmp(pd->pd_immutable, v, size) == 0); +} + +static bool +_prop_data_internalize_decode(struct _prop_object_internalize_context *ctx, + uint8_t *target, size_t targsize, size_t *sizep, + const char **cpp) +{ + const char *src; + size_t tarindex; + int state, ch; + const char *pos; + + state = 0; + tarindex = 0; + src = ctx->poic_cp; + + for (;;) { + ch = (unsigned char) *src++; + if (_PROP_EOF(ch)) + return (false); + if (_PROP_ISSPACE(ch)) + continue; + if (ch == '<') { + src--; + break; + } + if (ch == _prop_data_pad64) + break; + + pos = strchr(_prop_data_base64, ch); + if (pos == NULL) + return (false); + + switch (state) { + case 0: + if (target) { + if (tarindex >= targsize) + return (false); + target[tarindex] = + (uint8_t)((pos - _prop_data_base64) << 2); + } + state = 1; + break; + + case 1: + if (target) { + if (tarindex + 1 >= targsize) + return (false); + target[tarindex] |= + (uint32_t)(pos - _prop_data_base64) >> 4; + target[tarindex + 1] = + (uint8_t)(((pos - _prop_data_base64) & 0xf) + << 4); + } + tarindex++; + state = 2; + break; + + case 2: + if (target) { + if (tarindex + 1 >= targsize) + return (false); + target[tarindex] |= + (uint32_t)(pos - _prop_data_base64) >> 2; + target[tarindex + 1] = + (uint8_t)(((pos - _prop_data_base64) + & 0x3) << 6); + } + tarindex++; + state = 3; + break; + + case 3: + if (target) { + if (tarindex >= targsize) + return (false); + target[tarindex] |= (uint8_t) + (pos - _prop_data_base64); + } + tarindex++; + state = 0; + break; + + default: + _PROP_ASSERT(/*CONSTCOND*/0); + } + } + + /* + * We are done decoding the Base64 characters. Let's see if we + * ended up on a byte boundary and/or with unrecognized trailing + * characters. + */ + if (ch == _prop_data_pad64) { + ch = (unsigned char) *src; /* src already advanced */ + if (_PROP_EOF(ch)) + return (false); + switch (state) { + case 0: /* Invalid = in first position */ + case 1: /* Invalid = in second position */ + return (false); + + case 2: /* Valid, one byte of info */ + /* Skip whitespace */ + for (ch = (unsigned char) *src++; + ch != '<'; ch = (unsigned char) *src++) { + if (_PROP_EOF(ch)) + return (false); + if (!_PROP_ISSPACE(ch)) + break; + } + /* Make sure there is another trailing = */ + if (ch != _prop_data_pad64) + return (false); + ch = (unsigned char) *src; + /* FALLTHROUGH */ + + case 3: /* Valid, two bytes of info */ + /* + * We know this char is a =. Is there anything but + * whitespace after it? + */ + for (ch = (unsigned char) *src++; + ch != '<'; ch = (unsigned char) *src++) { + if (_PROP_EOF(ch)) + return (false); + if (!_PROP_ISSPACE(ch)) + return (false); + } + /* back up to '<' */ + src--; + } + } else { + /* + * We ended by seeing the end of the Base64 string. Make + * sure there are no partial bytes lying around. + */ + if (state != 0) + return (false); + } + + _PROP_ASSERT(*src == '<'); + if (sizep != NULL) + *sizep = tarindex; + if (cpp != NULL) + *cpp = src; + + return (true); +} + +/* + * _prop_data_internalize -- + * Parse a ... and return the object created from the + * external representation. + */ + +/* strtoul is used for parsing, enforce. */ +typedef int PROP_DATA_ASSERT[/* CONSTCOND */sizeof(size_t) == sizeof(unsigned long) ? 1 : -1]; + +/* ARGSUSED */ +bool +_prop_data_internalize(prop_stack_t stack, prop_object_t *obj, + struct _prop_object_internalize_context *ctx) +{ + prop_data_t data; + uint8_t *buf; + size_t len, alen; + + /* + * We don't accept empty elements. + * This actually only checks for the node to be + * (Which actually causes another error if found.) + */ + if (ctx->poic_is_empty_element) + return (true); + + /* + * If we got a "size" attribute, get the size of the data blob + * from that. Otherwise, we have to figure it out from the base64. + */ + if (ctx->poic_tagattr != NULL) { + char *cp; + + if (!_PROP_TAGATTR_MATCH(ctx, "size") || + ctx->poic_tagattrval_len == 0) + return (true); + + errno = 0; + len = strtoul(ctx->poic_tagattrval, &cp, 0); + if (len == ULONG_MAX && errno == ERANGE) + return (true); + if (cp != ctx->poic_tagattrval + ctx->poic_tagattrval_len) + return (true); + _PROP_ASSERT(*cp == '\"'); + } else if (_prop_data_internalize_decode(ctx, NULL, 0, &len, + NULL) == false) + return (true); + + /* + * Always allocate one extra in case we don't land on an even byte + * boundary during the decode. + */ + buf = _PROP_MALLOC(len + 1, M_PROP_DATA); + if (buf == NULL) + return (true); + + if (_prop_data_internalize_decode(ctx, buf, len + 1, &alen, + &ctx->poic_cp) == false) { + _PROP_FREE(buf, M_PROP_DATA); + return (true); + } + if (alen != len) { + _PROP_FREE(buf, M_PROP_DATA); + return (true); + } + + if (_prop_object_internalize_find_tag(ctx, "data", + _PROP_TAG_TYPE_END) == false) { + _PROP_FREE(buf, M_PROP_DATA); + return (true); + } + + data = _prop_data_alloc(); + if (data == NULL) { + _PROP_FREE(buf, M_PROP_DATA); + return (true); + } + + /* + * Handle alternate type of empty node. + * XML document could contain open/close tags, yet still be empty. + */ + if (alen == 0) { + _PROP_FREE(buf, M_PROP_DATA); + data->pd_mutable = NULL; + } else { + data->pd_mutable = buf; + } + data->pd_size = len; + + *obj = data; + return (true); +} diff --git a/lib/portableproplib/prop_dictionary.c b/lib/portableproplib/prop_dictionary.c new file mode 100644 index 00000000..9a33c3e4 --- /dev/null +++ b/lib/portableproplib/prop_dictionary.c @@ -0,0 +1,1542 @@ +/* $NetBSD: prop_dictionary.c,v 1.33 2008/11/30 00:17:07 haad Exp $ */ + +/*- + * Copyright (c) 2010 Juan Romero Pardines (zlib/gzip support). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +/*- + * Copyright (c) 2006, 2007 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include "prop_object_impl.h" +#include "prop_rb_impl.h" + +#include +#include + +/* + * We implement these like arrays, but we keep them sorted by key. + * This allows us to binary-search as well as keep externalized output + * sane-looking for human eyes. + */ + +#define EXPAND_STEP 16 + +/* + * prop_dictionary_keysym_t is allocated with space at the end to hold the + * key. This must be a regular object so that we can maintain sane iterator + * semantics -- we don't want to require that the caller release the result + * of prop_object_iterator_next(). + * + * We'd like to have some small'ish keysym objects for up-to-16 characters + * in a key, some for up-to-32 characters in a key, and then a final bucket + * for up-to-128 characters in a key (not including NUL). Keys longer than + * 128 characters are not allowed. + */ +struct _prop_dictionary_keysym { + struct _prop_object pdk_obj; + size_t pdk_size; + struct rb_node pdk_link; + char pdk_key[1]; + /* actually variable length */ +}; + +#define RBNODE_TO_PDK(n) \ + ((struct _prop_dictionary_keysym *) \ + ((uintptr_t)n - offsetof(struct _prop_dictionary_keysym, pdk_link))) + + /* pdk_key[1] takes care of the NUL */ +#define PDK_SIZE_16 (sizeof(struct _prop_dictionary_keysym) + 16) +#define PDK_SIZE_32 (sizeof(struct _prop_dictionary_keysym) + 32) +#define PDK_SIZE_128 (sizeof(struct _prop_dictionary_keysym) + 128) + +#define PDK_MAXKEY 128 + +_PROP_POOL_INIT(_prop_dictionary_keysym16_pool, PDK_SIZE_16, "pdict16") +_PROP_POOL_INIT(_prop_dictionary_keysym32_pool, PDK_SIZE_32, "pdict32") +_PROP_POOL_INIT(_prop_dictionary_keysym128_pool, PDK_SIZE_128, "pdict128") + +struct _prop_dict_entry { + prop_dictionary_keysym_t pde_key; + prop_object_t pde_objref; +}; + +struct _prop_dictionary { + struct _prop_object pd_obj; + _PROP_RWLOCK_DECL(pd_rwlock) + struct _prop_dict_entry *pd_array; + unsigned int pd_capacity; + unsigned int pd_count; + int pd_flags; + + uint32_t pd_version; +}; + +#define PD_F_IMMUTABLE 0x01 /* dictionary is immutable */ + +_PROP_POOL_INIT(_prop_dictionary_pool, sizeof(struct _prop_dictionary), + "propdict") +_PROP_MALLOC_DEFINE(M_PROP_DICT, "prop dictionary", + "property dictionary container object") + +static _prop_object_free_rv_t + _prop_dictionary_free(prop_stack_t, prop_object_t *); +static void _prop_dictionary_emergency_free(prop_object_t); +static bool _prop_dictionary_externalize( + struct _prop_object_externalize_context *, + void *); +static _prop_object_equals_rv_t + _prop_dictionary_equals(prop_object_t, prop_object_t, + void **, void **, + prop_object_t *, prop_object_t *); +static void _prop_dictionary_equals_finish(prop_object_t, prop_object_t); +static prop_object_iterator_t + _prop_dictionary_iterator_locked(prop_dictionary_t); +static prop_object_t + _prop_dictionary_iterator_next_object_locked(void *); +static prop_object_t + _prop_dictionary_get_keysym(prop_dictionary_t, + prop_dictionary_keysym_t, bool); +static prop_object_t + _prop_dictionary_get(prop_dictionary_t, const char *, bool); + +static void _prop_dictionary_lock(void); +static void _prop_dictionary_unlock(void); + +static const struct _prop_object_type _prop_object_type_dictionary = { + .pot_type = PROP_TYPE_DICTIONARY, + .pot_free = _prop_dictionary_free, + .pot_emergency_free = _prop_dictionary_emergency_free, + .pot_extern = _prop_dictionary_externalize, + .pot_equals = _prop_dictionary_equals, + .pot_equals_finish = _prop_dictionary_equals_finish, + .pot_lock = _prop_dictionary_lock, + .pot_unlock = _prop_dictionary_unlock, +}; + +static _prop_object_free_rv_t + _prop_dict_keysym_free(prop_stack_t, prop_object_t *); +static bool _prop_dict_keysym_externalize( + struct _prop_object_externalize_context *, + void *); +static _prop_object_equals_rv_t + _prop_dict_keysym_equals(prop_object_t, prop_object_t, + void **, void **, + prop_object_t *, prop_object_t *); + +static const struct _prop_object_type _prop_object_type_dict_keysym = { + .pot_type = PROP_TYPE_DICT_KEYSYM, + .pot_free = _prop_dict_keysym_free, + .pot_extern = _prop_dict_keysym_externalize, + .pot_equals = _prop_dict_keysym_equals, +}; + +#define prop_object_is_dictionary(x) \ + ((x) != NULL && (x)->pd_obj.po_type == &_prop_object_type_dictionary) +#define prop_object_is_dictionary_keysym(x) \ + ((x) != NULL && (x)->pdk_obj.po_type == &_prop_object_type_dict_keysym) + +#define prop_dictionary_is_immutable(x) \ + (((x)->pd_flags & PD_F_IMMUTABLE) != 0) + +struct _prop_dictionary_iterator { + struct _prop_object_iterator pdi_base; + unsigned int pdi_index; +}; + +/* + * Dictionary key symbols are immutable, and we are likely to have many + * duplicated key symbols. So, to save memory, we unique'ify key symbols + * so we only have to have one copy of each string. + */ + +static int +_prop_dict_keysym_rb_compare_nodes(const struct rb_node *n1, + const struct rb_node *n2) +{ + const prop_dictionary_keysym_t pdk1 = RBNODE_TO_PDK(n1); + const prop_dictionary_keysym_t pdk2 = RBNODE_TO_PDK(n2); + + return (strcmp(pdk1->pdk_key, pdk2->pdk_key)); +} + +static int +_prop_dict_keysym_rb_compare_key(const struct rb_node *n, + const void *v) +{ + const prop_dictionary_keysym_t pdk = RBNODE_TO_PDK(n); + const char *cp = v; + + return (strcmp(pdk->pdk_key, cp)); +} + +static const struct rb_tree_ops _prop_dict_keysym_rb_tree_ops = { + .rbto_compare_nodes = _prop_dict_keysym_rb_compare_nodes, + .rbto_compare_key = _prop_dict_keysym_rb_compare_key, +}; + +static struct rb_tree _prop_dict_keysym_tree; +static bool _prop_dict_keysym_tree_initialized; + +_PROP_MUTEX_DECL_STATIC(_prop_dict_keysym_tree_mutex) + +static void +_prop_dict_keysym_put(prop_dictionary_keysym_t pdk) +{ + + if (pdk->pdk_size <= PDK_SIZE_16) + _PROP_POOL_PUT(_prop_dictionary_keysym16_pool, pdk); + else if (pdk->pdk_size <= PDK_SIZE_32) + _PROP_POOL_PUT(_prop_dictionary_keysym32_pool, pdk); + else { + _PROP_ASSERT(pdk->pdk_size <= PDK_SIZE_128); + _PROP_POOL_PUT(_prop_dictionary_keysym128_pool, pdk); + } +} + +/* ARGSUSED */ +static _prop_object_free_rv_t +_prop_dict_keysym_free(prop_stack_t stack, prop_object_t *obj) +{ + prop_dictionary_keysym_t pdk = *obj; + + _prop_rb_tree_remove_node(&_prop_dict_keysym_tree, &pdk->pdk_link); + _prop_dict_keysym_put(pdk); + + return _PROP_OBJECT_FREE_DONE; +} + +static bool +_prop_dict_keysym_externalize(struct _prop_object_externalize_context *ctx, + void *v) +{ + prop_dictionary_keysym_t pdk = v; + + /* We externalize these as strings, and they're never empty. */ + + _PROP_ASSERT(pdk->pdk_key[0] != '\0'); + + if (_prop_object_externalize_start_tag(ctx, "string") == false || + _prop_object_externalize_append_encoded_cstring(ctx, + pdk->pdk_key) == false || + _prop_object_externalize_end_tag(ctx, "string") == false) + return (false); + + return (true); +} + +/* ARGSUSED */ +static _prop_object_equals_rv_t +_prop_dict_keysym_equals(prop_object_t v1, prop_object_t v2, + void **stored_pointer1, void **stored_pointer2, + prop_object_t *next_obj1, prop_object_t *next_obj2) +{ + prop_dictionary_keysym_t pdk1 = v1; + prop_dictionary_keysym_t pdk2 = v2; + + /* + * There is only ever one copy of a keysym at any given time, + * so we can reduce this to a simple pointer equality check. + */ + if (pdk1 == pdk2) + return _PROP_OBJECT_EQUALS_TRUE; + else + return _PROP_OBJECT_EQUALS_FALSE; +} + +static prop_dictionary_keysym_t +_prop_dict_keysym_alloc(const char *key) +{ + prop_dictionary_keysym_t opdk, pdk; + const struct rb_node *n; + size_t size; + bool rv; + + /* + * Check to see if this already exists in the tree. If it does, + * we just retain it and return it. + */ + _PROP_MUTEX_LOCK(_prop_dict_keysym_tree_mutex); + if (! _prop_dict_keysym_tree_initialized) { + _prop_rb_tree_init(&_prop_dict_keysym_tree, + &_prop_dict_keysym_rb_tree_ops); + _prop_dict_keysym_tree_initialized = true; + } else { + n = _prop_rb_tree_find(&_prop_dict_keysym_tree, key); + if (n != NULL) { + opdk = RBNODE_TO_PDK(n); + prop_object_retain(opdk); + _PROP_MUTEX_UNLOCK(_prop_dict_keysym_tree_mutex); + return (opdk); + } + } + _PROP_MUTEX_UNLOCK(_prop_dict_keysym_tree_mutex); + + /* + * Not in the tree. Create it now. + */ + + size = sizeof(*pdk) + strlen(key) /* pdk_key[1] covers the NUL */; + + if (size <= PDK_SIZE_16) + pdk = _PROP_POOL_GET(_prop_dictionary_keysym16_pool); + else if (size <= PDK_SIZE_32) + pdk = _PROP_POOL_GET(_prop_dictionary_keysym32_pool); + else if (size <= PDK_SIZE_128) + pdk = _PROP_POOL_GET(_prop_dictionary_keysym128_pool); + else + pdk = NULL; /* key too long */ + + if (pdk == NULL) + return (NULL); + + _prop_object_init(&pdk->pdk_obj, &_prop_object_type_dict_keysym); + + strcpy(pdk->pdk_key, key); + pdk->pdk_size = size; + + /* + * We dropped the mutex when we allocated the new object, so + * we have to check again if it is in the tree. + */ + _PROP_MUTEX_LOCK(_prop_dict_keysym_tree_mutex); + n = _prop_rb_tree_find(&_prop_dict_keysym_tree, key); + if (n != NULL) { + opdk = RBNODE_TO_PDK(n); + prop_object_retain(opdk); + _PROP_MUTEX_UNLOCK(_prop_dict_keysym_tree_mutex); + _prop_dict_keysym_put(pdk); + return (opdk); + } + rv = _prop_rb_tree_insert_node(&_prop_dict_keysym_tree, &pdk->pdk_link); + _PROP_ASSERT(rv == true); + _PROP_MUTEX_UNLOCK(_prop_dict_keysym_tree_mutex); + return (pdk); +} + +static _prop_object_free_rv_t +_prop_dictionary_free(prop_stack_t stack, prop_object_t *obj) +{ + prop_dictionary_t pd = *obj; + prop_dictionary_keysym_t pdk; + prop_object_t po; + + _PROP_ASSERT(pd->pd_count <= pd->pd_capacity); + _PROP_ASSERT((pd->pd_capacity == 0 && pd->pd_array == NULL) || + (pd->pd_capacity != 0 && pd->pd_array != NULL)); + + /* The empty dictorinary is easy, handle that first. */ + if (pd->pd_count == 0) { + if (pd->pd_array != NULL) + _PROP_FREE(pd->pd_array, M_PROP_DICT); + + _PROP_RWLOCK_DESTROY(pd->pd_rwlock); + + _PROP_POOL_PUT(_prop_dictionary_pool, pd); + + return (_PROP_OBJECT_FREE_DONE); + } + + po = pd->pd_array[pd->pd_count - 1].pde_objref; + _PROP_ASSERT(po != NULL); + + if (stack == NULL) { + /* + * If we are in emergency release mode, + * just let caller recurse down. + */ + *obj = po; + return (_PROP_OBJECT_FREE_FAILED); + } + + /* Otherwise, try to push the current object on the stack. */ + if (!_prop_stack_push(stack, pd, NULL, NULL, NULL)) { + /* Push failed, entering emergency release mode. */ + return (_PROP_OBJECT_FREE_FAILED); + } + /* Object pushed on stack, caller will release it. */ + --pd->pd_count; + pdk = pd->pd_array[pd->pd_count].pde_key; + _PROP_ASSERT(pdk != NULL); + + prop_object_release(pdk); + + *obj = po; + return (_PROP_OBJECT_FREE_RECURSE); +} + + +static void +_prop_dictionary_lock(void) +{ + _PROP_MUTEX_LOCK(_prop_dict_keysym_tree_mutex); +} + +static void +_prop_dictionary_unlock(void) +{ + _PROP_MUTEX_UNLOCK(_prop_dict_keysym_tree_mutex); +} + +static void +_prop_dictionary_emergency_free(prop_object_t obj) +{ + prop_dictionary_t pd = obj; + prop_dictionary_keysym_t pdk; + + _PROP_ASSERT(pd->pd_count != 0); + --pd->pd_count; + + pdk = pd->pd_array[pd->pd_count].pde_key; + _PROP_ASSERT(pdk != NULL); + prop_object_release(pdk); +} + +static bool +_prop_dictionary_externalize(struct _prop_object_externalize_context *ctx, + void *v) +{ + prop_dictionary_t pd = v; + prop_dictionary_keysym_t pdk; + struct _prop_object *po; + prop_object_iterator_t pi; + unsigned int i; + bool rv = false; + + _PROP_RWLOCK_RDLOCK(pd->pd_rwlock); + + if (pd->pd_count == 0) { + _PROP_RWLOCK_UNLOCK(pd->pd_rwlock); + return (_prop_object_externalize_empty_tag(ctx, "dict")); + } + + if (_prop_object_externalize_start_tag(ctx, "dict") == false || + _prop_object_externalize_append_char(ctx, '\n') == false) + goto out; + + pi = _prop_dictionary_iterator_locked(pd); + if (pi == NULL) + goto out; + + ctx->poec_depth++; + _PROP_ASSERT(ctx->poec_depth != 0); + + while ((pdk = _prop_dictionary_iterator_next_object_locked(pi)) + != NULL) { + po = _prop_dictionary_get_keysym(pd, pdk, true); + if (po == NULL || + _prop_object_externalize_start_tag(ctx, "key") == false || + _prop_object_externalize_append_encoded_cstring(ctx, + pdk->pdk_key) == false || + _prop_object_externalize_end_tag(ctx, "key") == false || + (*po->po_type->pot_extern)(ctx, po) == false) { + prop_object_iterator_release(pi); + goto out; + } + } + + prop_object_iterator_release(pi); + + ctx->poec_depth--; + for (i = 0; i < ctx->poec_depth; i++) { + if (_prop_object_externalize_append_char(ctx, '\t') == false) + goto out; + } + if (_prop_object_externalize_end_tag(ctx, "dict") == false) + goto out; + + rv = true; + + out: + _PROP_RWLOCK_UNLOCK(pd->pd_rwlock); + return (rv); +} + +/* ARGSUSED */ +static _prop_object_equals_rv_t +_prop_dictionary_equals(prop_object_t v1, prop_object_t v2, + void **stored_pointer1, void **stored_pointer2, + prop_object_t *next_obj1, prop_object_t *next_obj2) +{ + prop_dictionary_t dict1 = v1; + prop_dictionary_t dict2 = v2; + uintptr_t idx; + _prop_object_equals_rv_t rv = _PROP_OBJECT_EQUALS_FALSE; + + if (dict1 == dict2) + return (_PROP_OBJECT_EQUALS_TRUE); + + _PROP_ASSERT(*stored_pointer1 == *stored_pointer2); + + idx = (uintptr_t)*stored_pointer1; + + if (idx == 0) { + if ((uintptr_t)dict1 < (uintptr_t)dict2) { + _PROP_RWLOCK_RDLOCK(dict1->pd_rwlock); + _PROP_RWLOCK_RDLOCK(dict2->pd_rwlock); + } else { + _PROP_RWLOCK_RDLOCK(dict2->pd_rwlock); + _PROP_RWLOCK_RDLOCK(dict1->pd_rwlock); + } + } + + if (dict1->pd_count != dict2->pd_count) + goto out; + + if (idx == dict1->pd_count) { + rv = _PROP_OBJECT_EQUALS_TRUE; + goto out; + } + + _PROP_ASSERT(idx < dict1->pd_count); + + *stored_pointer1 = (void *)(idx + 1); + *stored_pointer2 = (void *)(idx + 1); + + *next_obj1 = &dict1->pd_array[idx].pde_objref; + *next_obj2 = &dict2->pd_array[idx].pde_objref; + + if (!prop_dictionary_keysym_equals(dict1->pd_array[idx].pde_key, + dict2->pd_array[idx].pde_key)) + goto out; + + return (_PROP_OBJECT_EQUALS_RECURSE); + + out: + _PROP_RWLOCK_UNLOCK(dict1->pd_rwlock); + _PROP_RWLOCK_UNLOCK(dict2->pd_rwlock); + return (rv); +} + +static void +_prop_dictionary_equals_finish(prop_object_t v1, prop_object_t v2) +{ + _PROP_RWLOCK_UNLOCK(((prop_dictionary_t)v1)->pd_rwlock); + _PROP_RWLOCK_UNLOCK(((prop_dictionary_t)v2)->pd_rwlock); +} + +static prop_dictionary_t +_prop_dictionary_alloc(unsigned int capacity) +{ + prop_dictionary_t pd; + struct _prop_dict_entry *array; + + if (capacity != 0) { + array = _PROP_CALLOC(capacity * sizeof(*array), M_PROP_DICT); + if (array == NULL) + return (NULL); + } else + array = NULL; + + pd = _PROP_POOL_GET(_prop_dictionary_pool); + if (pd != NULL) { + _prop_object_init(&pd->pd_obj, &_prop_object_type_dictionary); + + _PROP_RWLOCK_INIT(pd->pd_rwlock); + pd->pd_array = array; + pd->pd_capacity = capacity; + pd->pd_count = 0; + pd->pd_flags = 0; + + pd->pd_version = 0; + } else if (array != NULL) + _PROP_FREE(array, M_PROP_DICT); + + return (pd); +} + +static bool +_prop_dictionary_expand(prop_dictionary_t pd, unsigned int capacity) +{ + struct _prop_dict_entry *array, *oarray; + + /* + * Dictionary must be WRITE-LOCKED. + */ + + oarray = pd->pd_array; + + array = _PROP_CALLOC(capacity * sizeof(*array), M_PROP_DICT); + if (array == NULL) + return (false); + if (oarray != NULL) + memcpy(array, oarray, pd->pd_capacity * sizeof(*array)); + pd->pd_array = array; + pd->pd_capacity = capacity; + + if (oarray != NULL) + _PROP_FREE(oarray, M_PROP_DICT); + + return (true); +} + +static prop_object_t +_prop_dictionary_iterator_next_object_locked(void *v) +{ + struct _prop_dictionary_iterator *pdi = v; + prop_dictionary_t pd = pdi->pdi_base.pi_obj; + prop_dictionary_keysym_t pdk = NULL; + + _PROP_ASSERT(prop_object_is_dictionary(pd)); + + if (pd->pd_version != pdi->pdi_base.pi_version) + goto out; /* dictionary changed during iteration */ + + _PROP_ASSERT(pdi->pdi_index <= pd->pd_count); + + if (pdi->pdi_index == pd->pd_count) + goto out; /* we've iterated all objects */ + + pdk = pd->pd_array[pdi->pdi_index].pde_key; + pdi->pdi_index++; + + out: + return (pdk); +} + +static prop_object_t +_prop_dictionary_iterator_next_object(void *v) +{ + struct _prop_dictionary_iterator *pdi = v; + prop_dictionary_t pd = pdi->pdi_base.pi_obj; + prop_dictionary_keysym_t pdk; + + _PROP_ASSERT(prop_object_is_dictionary(pd)); + + _PROP_RWLOCK_RDLOCK(pd->pd_rwlock); + pdk = _prop_dictionary_iterator_next_object_locked(pdi); + _PROP_RWLOCK_UNLOCK(pd->pd_rwlock); + return (pdk); +} + +static void +_prop_dictionary_iterator_reset_locked(void *v) +{ + struct _prop_dictionary_iterator *pdi = v; + prop_dictionary_t pd = pdi->pdi_base.pi_obj; + + _PROP_ASSERT(prop_object_is_dictionary(pd)); + + pdi->pdi_index = 0; + pdi->pdi_base.pi_version = pd->pd_version; +} + +static void +_prop_dictionary_iterator_reset(void *v) +{ + struct _prop_dictionary_iterator *pdi = v; + prop_dictionary_t pd = pdi->pdi_base.pi_obj; + + _PROP_RWLOCK_RDLOCK(pd->pd_rwlock); + _prop_dictionary_iterator_reset_locked(pdi); + _PROP_RWLOCK_UNLOCK(pd->pd_rwlock); +} + +/* + * prop_dictionary_create -- + * Create a dictionary. + */ +prop_dictionary_t +prop_dictionary_create(void) +{ + + return (_prop_dictionary_alloc(0)); +} + +/* + * prop_dictionary_create_with_capacity -- + * Create a dictionary with the capacity to store N objects. + */ +prop_dictionary_t +prop_dictionary_create_with_capacity(unsigned int capacity) +{ + + return (_prop_dictionary_alloc(capacity)); +} + +/* + * prop_dictionary_copy -- + * Copy a dictionary. The new dictionary has an initial capacity equal + * to the number of objects stored int the original dictionary. The new + * dictionary contains refrences to the original dictionary's objects, + * not copies of those objects (i.e. a shallow copy). + */ +prop_dictionary_t +prop_dictionary_copy(prop_dictionary_t opd) +{ + prop_dictionary_t pd; + prop_dictionary_keysym_t pdk; + prop_object_t po; + unsigned int idx; + + if (! prop_object_is_dictionary(opd)) + return (NULL); + + _PROP_RWLOCK_RDLOCK(opd->pd_rwlock); + + pd = _prop_dictionary_alloc(opd->pd_count); + if (pd != NULL) { + for (idx = 0; idx < opd->pd_count; idx++) { + pdk = opd->pd_array[idx].pde_key; + po = opd->pd_array[idx].pde_objref; + + prop_object_retain(pdk); + prop_object_retain(po); + + pd->pd_array[idx].pde_key = pdk; + pd->pd_array[idx].pde_objref = po; + } + pd->pd_count = opd->pd_count; + pd->pd_flags = opd->pd_flags; + } + _PROP_RWLOCK_UNLOCK(opd->pd_rwlock); + return (pd); +} + +/* + * prop_dictionary_copy_mutable -- + * Like prop_dictionary_copy(), but the resulting dictionary is + * mutable. + */ +prop_dictionary_t +prop_dictionary_copy_mutable(prop_dictionary_t opd) +{ + prop_dictionary_t pd; + + if (! prop_object_is_dictionary(opd)) + return (NULL); + + pd = prop_dictionary_copy(opd); + if (pd != NULL) + pd->pd_flags &= ~PD_F_IMMUTABLE; + + return (pd); +} + +/* + * prop_dictionary_make_immutable -- + * Set the immutable flag on that dictionary. + */ +void +prop_dictionary_make_immutable(prop_dictionary_t pd) +{ + + _PROP_RWLOCK_WRLOCK(pd->pd_rwlock); + if (prop_dictionary_is_immutable(pd) == false) + pd->pd_flags |= PD_F_IMMUTABLE; + _PROP_RWLOCK_UNLOCK(pd->pd_rwlock); +} + +/* + * prop_dictionary_count -- + * Return the number of objects stored in the dictionary. + */ +unsigned int +prop_dictionary_count(prop_dictionary_t pd) +{ + unsigned int rv; + + if (! prop_object_is_dictionary(pd)) + return (0); + + _PROP_RWLOCK_RDLOCK(pd->pd_rwlock); + rv = pd->pd_count; + _PROP_RWLOCK_UNLOCK(pd->pd_rwlock); + + return (rv); +} + +/* + * prop_dictionary_ensure_capacity -- + * Ensure that the dictionary has the capacity to store the specified + * total number of objects (including the objects already stored in + * the dictionary). + */ +bool +prop_dictionary_ensure_capacity(prop_dictionary_t pd, unsigned int capacity) +{ + bool rv; + + if (! prop_object_is_dictionary(pd)) + return (false); + + _PROP_RWLOCK_WRLOCK(pd->pd_rwlock); + if (capacity > pd->pd_capacity) + rv = _prop_dictionary_expand(pd, capacity); + else + rv = true; + _PROP_RWLOCK_UNLOCK(pd->pd_rwlock); + return (rv); +} + +static prop_object_iterator_t +_prop_dictionary_iterator_locked(prop_dictionary_t pd) +{ + struct _prop_dictionary_iterator *pdi; + + if (! prop_object_is_dictionary(pd)) + return (NULL); + + pdi = _PROP_CALLOC(sizeof(*pdi), M_TEMP); + if (pdi == NULL) + return (NULL); + pdi->pdi_base.pi_next_object = _prop_dictionary_iterator_next_object; + pdi->pdi_base.pi_reset = _prop_dictionary_iterator_reset; + prop_object_retain(pd); + pdi->pdi_base.pi_obj = pd; + _prop_dictionary_iterator_reset_locked(pdi); + + return (&pdi->pdi_base); +} + +/* + * prop_dictionary_iterator -- + * Return an iterator for the dictionary. The dictionary is retained by + * the iterator. + */ +prop_object_iterator_t +prop_dictionary_iterator(prop_dictionary_t pd) +{ + prop_object_iterator_t pi; + + _PROP_RWLOCK_RDLOCK(pd->pd_rwlock); + pi = _prop_dictionary_iterator_locked(pd); + _PROP_RWLOCK_UNLOCK(pd->pd_rwlock); + return (pi); +} + +/* + * prop_dictionary_all_keys -- + * Return an array containing a snapshot of all of the keys + * in the dictionary. + */ +prop_array_t +prop_dictionary_all_keys(prop_dictionary_t pd) +{ + prop_array_t array; + unsigned int idx; + bool rv = true; + + if (! prop_object_is_dictionary(pd)) + return (NULL); + + /* There is no pressing need to lock the dictionary for this. */ + array = prop_array_create_with_capacity(pd->pd_count); + + _PROP_RWLOCK_RDLOCK(pd->pd_rwlock); + + for (idx = 0; idx < pd->pd_count; idx++) { + rv = prop_array_add(array, pd->pd_array[idx].pde_key); + if (rv == false) + break; + } + + _PROP_RWLOCK_UNLOCK(pd->pd_rwlock); + + if (rv == false) { + prop_object_release(array); + array = NULL; + } + return (array); +} + +static struct _prop_dict_entry * +_prop_dict_lookup(prop_dictionary_t pd, const char *key, + unsigned int *idxp) +{ + struct _prop_dict_entry *pde; + unsigned int base, idx, distance; + int res; + + /* + * Dictionary must be READ-LOCKED or WRITE-LOCKED. + */ + + for (idx = 0, base = 0, distance = pd->pd_count; distance != 0; + distance >>= 1) { + idx = base + (distance >> 1); + pde = &pd->pd_array[idx]; + _PROP_ASSERT(pde->pde_key != NULL); + res = strcmp(key, pde->pde_key->pdk_key); + if (res == 0) { + if (idxp != NULL) + *idxp = idx; + return (pde); + } + if (res > 0) { /* key > pdk_key: move right */ + base = idx + 1; + distance--; + } /* else move left */ + } + + /* idx points to the slot we looked at last. */ + if (idxp != NULL) + *idxp = idx; + return (NULL); +} + +static prop_object_t +_prop_dictionary_get(prop_dictionary_t pd, const char *key, bool locked) +{ + const struct _prop_dict_entry *pde; + prop_object_t po = NULL; + + if (! prop_object_is_dictionary(pd)) + return (NULL); + + if (!locked) + _PROP_RWLOCK_RDLOCK(pd->pd_rwlock); + pde = _prop_dict_lookup(pd, key, NULL); + if (pde != NULL) { + _PROP_ASSERT(pde->pde_objref != NULL); + po = pde->pde_objref; + } + if (!locked) + _PROP_RWLOCK_UNLOCK(pd->pd_rwlock); + return (po); +} +/* + * prop_dictionary_get -- + * Return the object stored with specified key. + */ +prop_object_t +prop_dictionary_get(prop_dictionary_t pd, const char *key) +{ + prop_object_t po; + + _PROP_RWLOCK_RDLOCK(pd->pd_rwlock); + po = _prop_dictionary_get(pd, key, true); + _PROP_RWLOCK_UNLOCK(pd->pd_rwlock); + return (po); +} + +static prop_object_t +_prop_dictionary_get_keysym(prop_dictionary_t pd, prop_dictionary_keysym_t pdk, + bool locked) +{ + + if (! (prop_object_is_dictionary(pd) && + prop_object_is_dictionary_keysym(pdk))) + return (NULL); + + return (_prop_dictionary_get(pd, pdk->pdk_key, locked)); +} + +/* + * prop_dictionary_get_keysym -- + * Return the object stored at the location encoded by the keysym. + */ +prop_object_t +prop_dictionary_get_keysym(prop_dictionary_t pd, prop_dictionary_keysym_t pdk) +{ + + return (_prop_dictionary_get_keysym(pd, pdk, false)); +} + +/* + * prop_dictionary_set -- + * Store a reference to an object at with the specified key. + * If the key already exisit, the original object is released. + */ +bool +prop_dictionary_set(prop_dictionary_t pd, const char *key, prop_object_t po) +{ + struct _prop_dict_entry *pde; + prop_dictionary_keysym_t pdk; + unsigned int idx; + bool rv = false; + + if (! prop_object_is_dictionary(pd)) + return (false); + + _PROP_ASSERT(pd->pd_count <= pd->pd_capacity); + + if (prop_dictionary_is_immutable(pd)) + return (false); + + _PROP_RWLOCK_WRLOCK(pd->pd_rwlock); + + pde = _prop_dict_lookup(pd, key, &idx); + if (pde != NULL) { + prop_object_t opo = pde->pde_objref; + prop_object_retain(po); + pde->pde_objref = po; + prop_object_release(opo); + rv = true; + goto out; + } + + pdk = _prop_dict_keysym_alloc(key); + if (pdk == NULL) + goto out; + + if (pd->pd_count == pd->pd_capacity && + _prop_dictionary_expand(pd, + pd->pd_capacity + EXPAND_STEP) == false) { + prop_object_release(pdk); + goto out; + } + + /* At this point, the store will succeed. */ + prop_object_retain(po); + + if (pd->pd_count == 0) { + pd->pd_array[0].pde_key = pdk; + pd->pd_array[0].pde_objref = po; + pd->pd_count++; + pd->pd_version++; + rv = true; + goto out; + } + + pde = &pd->pd_array[idx]; + _PROP_ASSERT(pde->pde_key != NULL); + + if (strcmp(key, pde->pde_key->pdk_key) < 0) { + /* + * key < pdk_key: insert to the left. This is the same as + * inserting to the right, except we decrement the current + * index first. + * + * Because we're unsigned, we have to special case 0 + * (grumble). + */ + if (idx == 0) { + memmove(&pd->pd_array[1], &pd->pd_array[0], + pd->pd_count * sizeof(*pde)); + pd->pd_array[0].pde_key = pdk; + pd->pd_array[0].pde_objref = po; + pd->pd_count++; + pd->pd_version++; + rv = true; + goto out; + } + idx--; + } + + memmove(&pd->pd_array[idx + 2], &pd->pd_array[idx + 1], + (pd->pd_count - (idx + 1)) * sizeof(*pde)); + pd->pd_array[idx + 1].pde_key = pdk; + pd->pd_array[idx + 1].pde_objref = po; + pd->pd_count++; + + pd->pd_version++; + + rv = true; + + out: + _PROP_RWLOCK_UNLOCK(pd->pd_rwlock); + return (rv); +} + +/* + * prop_dictionary_set_keysym -- + * Replace the object in the dictionary at the location encoded by + * the keysym. + */ +bool +prop_dictionary_set_keysym(prop_dictionary_t pd, prop_dictionary_keysym_t pdk, + prop_object_t po) +{ + + if (! (prop_object_is_dictionary(pd) && + prop_object_is_dictionary_keysym(pdk))) + return (false); + + return (prop_dictionary_set(pd, pdk->pdk_key, po)); +} + +static void +_prop_dictionary_remove(prop_dictionary_t pd, struct _prop_dict_entry *pde, + unsigned int idx) +{ + prop_dictionary_keysym_t pdk = pde->pde_key; + prop_object_t po = pde->pde_objref; + + /* + * Dictionary must be WRITE-LOCKED. + */ + + _PROP_ASSERT(pd->pd_count != 0); + _PROP_ASSERT(idx < pd->pd_count); + _PROP_ASSERT(pde == &pd->pd_array[idx]); + + idx++; + memmove(&pd->pd_array[idx - 1], &pd->pd_array[idx], + (pd->pd_count - idx) * sizeof(*pde)); + pd->pd_count--; + pd->pd_version++; + + + prop_object_release(pdk); + + prop_object_release(po); +} + +/* + * prop_dictionary_remove -- + * Remove the reference to an object with the specified key from + * the dictionary. + */ +void +prop_dictionary_remove(prop_dictionary_t pd, const char *key) +{ + struct _prop_dict_entry *pde; + unsigned int idx; + + if (! prop_object_is_dictionary(pd)) + return; + + _PROP_RWLOCK_WRLOCK(pd->pd_rwlock); + + /* XXX Should this be a _PROP_ASSERT()? */ + if (prop_dictionary_is_immutable(pd)) + goto out; + + pde = _prop_dict_lookup(pd, key, &idx); + /* XXX Should this be a _PROP_ASSERT()? */ + if (pde == NULL) + goto out; + + _prop_dictionary_remove(pd, pde, idx); + out: + _PROP_RWLOCK_UNLOCK(pd->pd_rwlock); +} + +/* + * prop_dictionary_remove_keysym -- + * Remove a reference to an object stored in the dictionary at the + * location encoded by the keysym. + */ +void +prop_dictionary_remove_keysym(prop_dictionary_t pd, + prop_dictionary_keysym_t pdk) +{ + + if (! (prop_object_is_dictionary(pd) && + prop_object_is_dictionary_keysym(pdk))) + return; + + prop_dictionary_remove(pd, pdk->pdk_key); +} + +/* + * prop_dictionary_equals -- + * Return true if the two dictionaries are equivalent. Note we do a + * by-value comparison of the objects in the dictionary. + */ +bool +prop_dictionary_equals(prop_dictionary_t dict1, prop_dictionary_t dict2) +{ + if (!prop_object_is_dictionary(dict1) || + !prop_object_is_dictionary(dict2)) + return (false); + + return (prop_object_equals(dict1, dict2)); +} + +/* + * prop_dictionary_keysym_cstring_nocopy -- + * Return an immutable reference to the keysym's value. + */ +const char * +prop_dictionary_keysym_cstring_nocopy(prop_dictionary_keysym_t pdk) +{ + + if (! prop_object_is_dictionary_keysym(pdk)) + return (NULL); + + return (pdk->pdk_key); +} + +/* + * prop_dictionary_keysym_equals -- + * Return true if the two dictionary key symbols are equivalent. + * Note: We do not compare the object references. + */ +bool +prop_dictionary_keysym_equals(prop_dictionary_keysym_t pdk1, + prop_dictionary_keysym_t pdk2) +{ + if (!prop_object_is_dictionary_keysym(pdk1) || + !prop_object_is_dictionary_keysym(pdk2)) + return (false); + + return (prop_object_equals(pdk1, pdk2)); +} + +/* + * prop_dictionary_externalize -- + * Externalize a dictionary, returning a NUL-terminated buffer + * containing the XML-style representation. The buffer is allocated + * with the M_TEMP memory type. + */ +char * +prop_dictionary_externalize(prop_dictionary_t pd) +{ + struct _prop_object_externalize_context *ctx; + char *cp; + + ctx = _prop_object_externalize_context_alloc(); + if (ctx == NULL) + return (NULL); + + if (_prop_object_externalize_header(ctx) == false || + (*pd->pd_obj.po_type->pot_extern)(ctx, pd) == false || + _prop_object_externalize_footer(ctx) == false) { + /* We are responsible for releasing the buffer. */ + _PROP_FREE(ctx->poec_buf, M_TEMP); + _prop_object_externalize_context_free(ctx); + return (NULL); + } + + cp = ctx->poec_buf; + _prop_object_externalize_context_free(ctx); + + return (cp); +} + +/* + * _prop_dictionary_internalize -- + * Parse a ... and return the object created from the + * external representation. + * + * Internal state in via rec_data is the storage area for the last processed + * key. + * _prop_dictionary_internalize_body is the upper half of the parse loop. + * It is responsible for parsing the key directly and storing it in the area + * referenced by rec_data. + * _prop_dictionary_internalize_cont is the lower half and called with the value + * associated with the key. + */ +static bool _prop_dictionary_internalize_body(prop_stack_t, + prop_object_t *, struct _prop_object_internalize_context *, char *); + +bool +_prop_dictionary_internalize(prop_stack_t stack, prop_object_t *obj, + struct _prop_object_internalize_context *ctx) +{ + prop_dictionary_t dict; + char *tmpkey; + + /* We don't currently understand any attributes. */ + if (ctx->poic_tagattr != NULL) + return (true); + + dict = prop_dictionary_create(); + if (dict == NULL) + return (true); + + if (ctx->poic_is_empty_element) { + *obj = dict; + return (true); + } + + tmpkey = _PROP_MALLOC(PDK_MAXKEY + 1, M_TEMP); + if (tmpkey == NULL) { + prop_object_release(dict); + return (true); + } + + *obj = dict; + /* + * Opening tag is found, storage for key allocated and + * now continue to the first element. + */ + return _prop_dictionary_internalize_body(stack, obj, ctx, tmpkey); +} + +static bool +_prop_dictionary_internalize_continue(prop_stack_t stack, prop_object_t *obj, + struct _prop_object_internalize_context *ctx, void *data, prop_object_t child) +{ + prop_dictionary_t dict = *obj; + char *tmpkey = data; + + _PROP_ASSERT(tmpkey != NULL); + + if (child == NULL || + prop_dictionary_set(dict, tmpkey, child) == false) { + _PROP_FREE(tmpkey, M_TEMP); + if (child != NULL) + prop_object_release(child); + prop_object_release(dict); + *obj = NULL; + return (true); + } + + prop_object_release(child); + + /* + * key, value was added, now continue looking for the next key + * or the closing tag. + */ + return _prop_dictionary_internalize_body(stack, obj, ctx, tmpkey); +} + +static bool +_prop_dictionary_internalize_body(prop_stack_t stack, prop_object_t *obj, + struct _prop_object_internalize_context *ctx, char *tmpkey) +{ + prop_dictionary_t dict = *obj; + size_t keylen; + + /* Fetch the next tag. */ + if (_prop_object_internalize_find_tag(ctx, NULL, _PROP_TAG_TYPE_EITHER) == false) + goto bad; + + /* Check to see if this is the end of the dictionary. */ + if (_PROP_TAG_MATCH(ctx, "dict") && + ctx->poic_tag_type == _PROP_TAG_TYPE_END) { + _PROP_FREE(tmpkey, M_TEMP); + return (true); + } + + /* Ok, it must be a non-empty key start tag. */ + if (!_PROP_TAG_MATCH(ctx, "key") || + ctx->poic_tag_type != _PROP_TAG_TYPE_START || + ctx->poic_is_empty_element) + goto bad; + + if (_prop_object_internalize_decode_string(ctx, + tmpkey, PDK_MAXKEY, &keylen, + &ctx->poic_cp) == false) + goto bad; + + _PROP_ASSERT(keylen <= PDK_MAXKEY); + tmpkey[keylen] = '\0'; + + if (_prop_object_internalize_find_tag(ctx, "key", + _PROP_TAG_TYPE_END) == false) + goto bad; + + /* ..and now the beginning of the value. */ + if (_prop_object_internalize_find_tag(ctx, NULL, + _PROP_TAG_TYPE_START) == false) + goto bad; + + /* + * Key is found, now wait for value to be parsed. + */ + if (_prop_stack_push(stack, *obj, + _prop_dictionary_internalize_continue, + tmpkey, NULL)) + return (false); + + bad: + _PROP_FREE(tmpkey, M_TEMP); + prop_object_release(dict); + *obj = NULL; + return (true); +} + +/* + * prop_dictionary_internalize -- + * Create a dictionary by parsing the NUL-terminated XML-style + * representation. + */ +prop_dictionary_t +prop_dictionary_internalize(const char *xml) +{ + return _prop_generic_internalize(xml, "dict"); +} + +/* + * prop_dictionary_externalize_to_file -- + * Externalize a dictionary to the specified file. + */ +bool +prop_dictionary_externalize_to_file(prop_dictionary_t dict, const char *fname) +{ + char *xml; + bool rv; + int save_errno = 0; /* XXXGCC -Wuninitialized [mips, ...] */ + + xml = prop_dictionary_externalize(dict); + if (xml == NULL) + return (false); + rv = _prop_object_externalize_write_file(fname, xml, + strlen(xml), false); + if (rv == false) + save_errno = errno; + _PROP_FREE(xml, M_TEMP); + if (rv == false) + errno = save_errno; + + return (rv); +} + +/* + * prop_dictionary_externalize_to_zfile --- + * Externalize a dictionary to the specified file and on the fly + * compressing the result with gzip (via zlib). + */ +bool +prop_dictionary_externalize_to_zfile(prop_dictionary_t dict, const char *fname) +{ + char *xml; + bool rv; + int save_errno = 0; + + xml = prop_dictionary_externalize(dict); + if (xml == NULL) + return false; + rv = _prop_object_externalize_write_file(fname, xml, strlen(xml), true); + if (rv == false) + save_errno = errno; + _PROP_FREE(xml, M_TEMP); + if (rv == false) + errno = save_errno; + + return rv; +} + +/* + * prop_dictionary_internalize_from_file -- + * Internalize a dictionary from a file. + */ +prop_dictionary_t +prop_dictionary_internalize_from_file(const char *fname) +{ + struct _prop_object_internalize_mapped_file *mf; + prop_dictionary_t dict; + + mf = _prop_object_internalize_map_file(fname); + if (mf == NULL) + return (NULL); + dict = prop_dictionary_internalize(mf->poimf_xml); + _prop_object_internalize_unmap_file(mf); + + return (dict); +} + +#define _READ_CHUNK 512 +/* + * prop_dictionary_internalize_from_zfile --- + * Internalize a dictionary from a compressed gzip file. + */ +prop_dictionary_t +prop_dictionary_internalize_from_zfile(const char *fname) +{ + struct _prop_object_internalize_mapped_file *mf; + prop_dictionary_t dict; + z_stream strm; + unsigned char out[_READ_CHUNK]; + char *uncomp_xml = NULL; + size_t have; + ssize_t totalsize = 0; + int rv = 0; + + mf = _prop_object_internalize_map_file(fname); + if (mf == NULL) + return NULL; + + /* Decompress the mmap'ed buffer with zlib */ + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + strm.avail_in = 0; + strm.next_in = Z_NULL; + + /* 15+16 to use gzip method */ + if (inflateInit2(&strm, 15+16) != Z_OK) { + _prop_object_internalize_unmap_file(mf); + return NULL; + } + strm.avail_in = mf->poimf_mapsize; + strm.next_in = mf->poimf_xml; + + /* Output buffer (uncompressed) */ + uncomp_xml = _PROP_MALLOC(_READ_CHUNK, M_TEMP); + if (uncomp_xml == NULL) { + (void)inflateEnd(&strm); + _prop_object_internalize_unmap_file(mf); + return NULL; + } + + /* Inflate the input buffer and copy into 'uncomp_xml' */ + do { + strm.avail_out = _READ_CHUNK; + strm.next_out = out; + rv = inflate(&strm, Z_NO_FLUSH); + switch (rv) { + case Z_DATA_ERROR: + /* + * Wrong compressed data or uncompressed, try + * normal method as last resort. + */ + (void)inflateEnd(&strm); + _PROP_FREE(uncomp_xml, M_TEMP); + dict = prop_dictionary_internalize(mf->poimf_xml); + _prop_object_internalize_unmap_file(mf); + return dict; + case Z_STREAM_ERROR: + case Z_NEED_DICT: + case Z_MEM_ERROR: + (void)inflateEnd(&strm); + _PROP_FREE(uncomp_xml, M_TEMP); + _prop_object_internalize_unmap_file(mf); + errno = rv; + return NULL; + } + have = _READ_CHUNK - strm.avail_out; + totalsize += have; + uncomp_xml = _PROP_REALLOC(uncomp_xml, totalsize, M_TEMP); + memcpy(uncomp_xml + totalsize - have, out, have); + } while (strm.avail_out == 0); + + /* we are done */ + (void)inflateEnd(&strm); + dict = prop_dictionary_internalize(uncomp_xml); + _PROP_FREE(uncomp_xml, M_TEMP); + _prop_object_internalize_unmap_file(mf); + + return dict; +} diff --git a/lib/portableproplib/prop_dictionary_util.c b/lib/portableproplib/prop_dictionary_util.c new file mode 100644 index 00000000..a26443e4 --- /dev/null +++ b/lib/portableproplib/prop_dictionary_util.c @@ -0,0 +1,208 @@ +/* $NetBSD: prop_dictionary_util.c,v 1.3 2008/04/28 20:22:53 martin Exp $ */ + +/*- + * Copyright (c) 2006 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Utility routines to make it more convenient to work with values + * stored in dictionaries. + * + * Note: There is no special magic going on here. We use the standard + * proplib(3) APIs to do all of this work. Any application could do + * exactly what we're doing here. + */ + +#include +#include "prop_object_impl.h" /* only to hide kernel vs. not-kernel */ + +bool +prop_dictionary_get_bool(prop_dictionary_t dict, + const char *key, + bool *valp) +{ + prop_bool_t b; + + b = prop_dictionary_get(dict, key); + if (prop_object_type(b) != PROP_TYPE_BOOL) + return (false); + + *valp = prop_bool_true(b); + + return (true); +} + +bool +prop_dictionary_set_bool(prop_dictionary_t dict, + const char *key, + bool val) +{ + prop_bool_t b; + int rv; + + b = prop_bool_create(val); + if (b == NULL) + return (false); + rv = prop_dictionary_set(dict, key, b); + prop_object_release(b); + + return (rv); +} + +#define TEMPLATE(size) \ +bool \ +prop_dictionary_get_int ## size (prop_dictionary_t dict, \ + const char *key, \ + int ## size ## _t *valp) \ +{ \ + prop_number_t num; \ + \ + num = prop_dictionary_get(dict, key); \ + if (prop_object_type(num) != PROP_TYPE_NUMBER) \ + return (false); \ + \ + if (prop_number_unsigned(num) && \ + prop_number_unsigned_integer_value(num) > \ + /*CONSTCOND*/((size) == 8 ? INT8_MAX : \ + (size) == 16 ? INT16_MAX : \ + (size) == 32 ? INT32_MAX : INT64_MAX)) { \ + return (false); \ + } \ + \ + if (prop_number_size(num) > (size)) \ + return (false); \ + \ + *valp = (int ## size ## _t) prop_number_integer_value(num); \ + \ + return (true); \ +} \ + \ +bool \ +prop_dictionary_get_uint ## size (prop_dictionary_t dict, \ + const char *key, \ + uint ## size ## _t *valp) \ +{ \ + prop_number_t num; \ + \ + num = prop_dictionary_get(dict, key); \ + if (prop_object_type(num) != PROP_TYPE_NUMBER) \ + return (false); \ + \ + if (prop_number_unsigned(num) == false && \ + prop_number_integer_value(num) < 0) { \ + return (false); \ + } \ + \ + if (prop_number_size(num) > (size)) \ + return (false); \ + \ + *valp = (uint ## size ## _t) \ + prop_number_unsigned_integer_value(num); \ + \ + return (true); \ +} \ + \ +bool \ +prop_dictionary_set_int ## size (prop_dictionary_t dict, \ + const char *key, \ + int ## size ## _t val) \ +{ \ + prop_number_t num; \ + int rv; \ + \ + num = prop_number_create_integer((int64_t) val); \ + if (num == NULL) \ + return (false); \ + rv = prop_dictionary_set(dict, key, num); \ + prop_object_release(num); \ + \ + return (rv); \ +} \ + \ +bool \ +prop_dictionary_set_uint ## size (prop_dictionary_t dict, \ + const char *key, \ + uint ## size ## _t val) \ +{ \ + prop_number_t num; \ + int rv; \ + \ + num = prop_number_create_unsigned_integer((uint64_t) val); \ + if (num == NULL) \ + return (false); \ + rv = prop_dictionary_set(dict, key, num); \ + prop_object_release(num); \ + \ + return (rv); \ +} + +TEMPLATE(8) +TEMPLATE(16) +TEMPLATE(32) +TEMPLATE(64) + +#undef TEMPLATE + +#define TEMPLATE(variant, qualifier) \ +bool \ +prop_dictionary_get_cstring ## variant (prop_dictionary_t dict, \ + const char *key, \ + qualifier char **cpp) \ +{ \ + prop_string_t str; \ + \ + str = prop_dictionary_get(dict, key); \ + if (prop_object_type(str) != PROP_TYPE_STRING) \ + return (false); \ + \ + *cpp = prop_string_cstring ## variant (str); \ + \ + return (*cpp == NULL ? false : true); \ +} \ + \ +bool \ +prop_dictionary_set_cstring ## variant (prop_dictionary_t dict, \ + const char *key, \ + const char *cp) \ +{ \ + prop_string_t str; \ + int rv; \ + \ + str = prop_string_create_cstring ## variant (cp); \ + if (str == NULL) \ + return (false); \ + rv = prop_dictionary_set(dict, key, str); \ + prop_object_release(str); \ + \ + return (rv); \ +} + +TEMPLATE(,) +TEMPLATE(_nocopy,const) + +#undef TEMPLATE diff --git a/lib/portableproplib/prop_ingest.c b/lib/portableproplib/prop_ingest.c new file mode 100644 index 00000000..823d2c6c --- /dev/null +++ b/lib/portableproplib/prop_ingest.c @@ -0,0 +1,159 @@ +/* $NetBSD: prop_ingest.c,v 1.3 2008/04/28 20:22:53 martin Exp $ */ + +/*- + * Copyright (c) 2006 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include "prop_object_impl.h" + +struct _prop_ingest_context { + prop_ingest_error_t pic_error; + prop_type_t pic_type; + const char * pic_key; + void * pic_private; +}; + +/* + * prop_ingest_context_alloc -- + * Allocate and initialize an ingest context. + */ +prop_ingest_context_t +prop_ingest_context_alloc(void *private) +{ + prop_ingest_context_t ctx; + + ctx = _PROP_MALLOC(sizeof(*ctx), M_TEMP); + if (ctx != NULL) { + ctx->pic_error = PROP_INGEST_ERROR_NO_ERROR; + ctx->pic_type = PROP_TYPE_UNKNOWN; + ctx->pic_key = NULL; + ctx->pic_private = private; + } + return (ctx); +} + +/* + * prop_ingest_context_free -- + * Free an ingest context. + */ +void +prop_ingest_context_free(prop_ingest_context_t ctx) +{ + + _PROP_FREE(ctx, M_TEMP); +} + +/* + * prop_ingest_context_error -- + * Get the error code from an ingest context. + */ +prop_ingest_error_t +prop_ingest_context_error(prop_ingest_context_t ctx) +{ + + return (ctx->pic_error); +} + +/* + * prop_ingest_context_type -- + * Return the type of last object visisted by an ingest context. + */ +prop_type_t +prop_ingest_context_type(prop_ingest_context_t ctx) +{ + + return (ctx->pic_type); +} + +/* + * prop_ingest_context_key -- + * Return the last key looked up by an ingest context. + */ +const char * +prop_ingest_context_key(prop_ingest_context_t ctx) +{ + + return (ctx->pic_key); +} + +/* + * prop_ingest_context_private -- + * Return the caller-private data associated with an ingest context. + */ +void * +prop_ingest_context_private(prop_ingest_context_t ctx) +{ + + return (ctx->pic_private); +} + +/* + * prop_dictionary_ingest -- + * Ingest a dictionary using handlers for each object to translate + * into an arbitrary binary format. + */ +bool +prop_dictionary_ingest(prop_dictionary_t dict, + const prop_ingest_table_entry rules[], + prop_ingest_context_t ctx) +{ + const prop_ingest_table_entry *pite; + prop_object_t obj; + + ctx->pic_error = PROP_INGEST_ERROR_NO_ERROR; + + for (pite = rules; pite->pite_key != NULL; pite++) { + ctx->pic_key = pite->pite_key; + obj = prop_dictionary_get(dict, pite->pite_key); + ctx->pic_type = prop_object_type(obj); + if (obj == NULL) { + if (pite->pite_flags & PROP_INGEST_FLAG_OPTIONAL) { + if ((*pite->pite_handler)(ctx, NULL) == false) { + ctx->pic_error = + PROP_INGEST_ERROR_HANDLER_FAILED; + return (false); + } + continue; + } + ctx->pic_error = PROP_INGEST_ERROR_NO_KEY; + return (false); + } + if (ctx->pic_type != pite->pite_type && + pite->pite_type != PROP_TYPE_UNKNOWN) { + ctx->pic_error = PROP_INGEST_ERROR_WRONG_TYPE; + return (false); + } + if ((*pite->pite_handler)(ctx, obj) == false) { + ctx->pic_error = PROP_INGEST_ERROR_HANDLER_FAILED; + return (false); + } + } + + return (true); +} diff --git a/lib/portableproplib/prop_number.c b/lib/portableproplib/prop_number.c new file mode 100644 index 00000000..059d8e38 --- /dev/null +++ b/lib/portableproplib/prop_number.c @@ -0,0 +1,576 @@ +/* $NetBSD: prop_number.c,v 1.20 2008/11/30 00:17:07 haad Exp $ */ + +/*- + * Copyright (c) 2006 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include "prop_object_impl.h" +#include "prop_rb_impl.h" + +#include +#include + +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; +}; + +#define RBNODE_TO_PN(n) \ + ((struct _prop_number *) \ + ((uintptr_t)n - offsetof(struct _prop_number, pn_link))) + +_PROP_POOL_INIT(_prop_number_pool, sizeof(struct _prop_number), "propnmbr") + +static _prop_object_free_rv_t + _prop_number_free(prop_stack_t, prop_object_t *); +static bool _prop_number_externalize( + struct _prop_object_externalize_context *, + void *); +static _prop_object_equals_rv_t + _prop_number_equals(prop_object_t, prop_object_t, + void **, void **, + prop_object_t *, prop_object_t *); + +static void _prop_number_lock(void); +static void _prop_number_unlock(void); + +static const struct _prop_object_type _prop_object_type_number = { + .pot_type = PROP_TYPE_NUMBER, + .pot_free = _prop_number_free, + .pot_extern = _prop_number_externalize, + .pot_equals = _prop_number_equals, + .pot_lock = _prop_number_lock, + .pot_unlock = _prop_number_unlock, +}; + +#define prop_object_is_number(x) \ + ((x) != NULL && (x)->pn_obj.po_type == &_prop_object_type_number) + +/* + * Number objects are immutable, and we are likely to have many number + * objects that have the same value. So, to save memory, we unique'ify + * numbers so we only have one copy of each. + */ + +static int +_prop_number_compare_values(const struct _prop_number_value *pnv1, + const struct _prop_number_value *pnv2) +{ + + /* Signed numbers are sorted before unsigned numbers. */ + + if (pnv1->pnv_is_unsigned) { + if (! pnv2->pnv_is_unsigned) + return (1); + if (pnv1->pnv_unsigned < pnv2->pnv_unsigned) + return (-1); + if (pnv1->pnv_unsigned > pnv2->pnv_unsigned) + return (1); + return (0); + } + + if (pnv2->pnv_is_unsigned) + return (-1); + if (pnv1->pnv_signed < pnv2->pnv_signed) + return (-1); + if (pnv1->pnv_signed > pnv2->pnv_signed) + return (1); + return (0); +} + +static int +_prop_number_rb_compare_nodes(const struct rb_node *n1, + const struct rb_node *n2) +{ + const prop_number_t pn1 = RBNODE_TO_PN(n1); + const prop_number_t pn2 = RBNODE_TO_PN(n2); + + return (_prop_number_compare_values(&pn1->pn_value, &pn2->pn_value)); +} + +static int +_prop_number_rb_compare_key(const struct rb_node *n, + const void *v) +{ + const prop_number_t pn = RBNODE_TO_PN(n); + const struct _prop_number_value *pnv = v; + + return (_prop_number_compare_values(&pn->pn_value, pnv)); +} + +static const struct rb_tree_ops _prop_number_rb_tree_ops = { + .rbto_compare_nodes = _prop_number_rb_compare_nodes, + .rbto_compare_key = _prop_number_rb_compare_key, +}; + +static struct rb_tree _prop_number_tree; +static bool _prop_number_tree_initialized; + +_PROP_MUTEX_DECL_STATIC(_prop_number_tree_mutex) + +/* ARGSUSED */ +static _prop_object_free_rv_t +_prop_number_free(prop_stack_t stack, prop_object_t *obj) +{ + prop_number_t pn = *obj; + + _prop_rb_tree_remove_node(&_prop_number_tree, &pn->pn_link); + + _PROP_POOL_PUT(_prop_number_pool, pn); + + return (_PROP_OBJECT_FREE_DONE); +} + +static void +_prop_number_lock() +{ + _PROP_MUTEX_LOCK(_prop_number_tree_mutex); +} + +static void +_prop_number_unlock() +{ + _PROP_MUTEX_UNLOCK(_prop_number_tree_mutex); +} + +static bool +_prop_number_externalize(struct _prop_object_externalize_context *ctx, + void *v) +{ + prop_number_t pn = v; + char tmpstr[32]; + + /* + * For the record: + * The original implementation used hex for signed numbers, + * but we changed it to be human readable. + */ + if (pn->pn_value.pnv_is_unsigned) + sprintf(tmpstr, "%" PRIu64, pn->pn_value.pnv_unsigned); + else + sprintf(tmpstr, "%" PRIi64, pn->pn_value.pnv_signed); + + if (_prop_object_externalize_start_tag(ctx, "integer") == false || + _prop_object_externalize_append_cstring(ctx, tmpstr) == false || + _prop_object_externalize_end_tag(ctx, "integer") == false) + return (false); + + return (true); +} + +/* ARGSUSED */ +static _prop_object_equals_rv_t +_prop_number_equals(prop_object_t v1, prop_object_t v2, + void **stored_pointer1, void **stored_pointer2, + prop_object_t *next_obj1, prop_object_t *next_obj2) +{ + prop_number_t num1 = v1; + prop_number_t num2 = v2; + + /* + * There is only ever one copy of a number object at any given + * time, so we can reduce this to a simple pointer equality check + * in the common case. + */ + if (num1 == num2) + return (_PROP_OBJECT_EQUALS_TRUE); + + /* + * If the numbers are the same signed-ness, then we know they + * cannot be equal because they would have had pointer equality. + */ + if (num1->pn_value.pnv_is_unsigned == num2->pn_value.pnv_is_unsigned) + return (_PROP_OBJECT_EQUALS_FALSE); + + /* + * We now have one signed value and one unsigned value. We can + * compare them iff: + * - The unsigned value is not larger than the signed value + * can represent. + * - The signed value is not smaller than the unsigned value + * can represent. + */ + if (num1->pn_value.pnv_is_unsigned) { + /* + * num1 is unsigned and num2 is signed. + */ + if (num1->pn_value.pnv_unsigned > INT64_MAX) + return (_PROP_OBJECT_EQUALS_FALSE); + if (num2->pn_value.pnv_signed < 0) + return (_PROP_OBJECT_EQUALS_FALSE); + } else { + /* + * num1 is signed and num2 is unsigned. + */ + if (num1->pn_value.pnv_signed < 0) + return (_PROP_OBJECT_EQUALS_FALSE); + if (num2->pn_value.pnv_unsigned > INT64_MAX) + return (_PROP_OBJECT_EQUALS_FALSE); + } + + if (num1->pn_value.pnv_signed == num2->pn_value.pnv_signed) + return _PROP_OBJECT_EQUALS_TRUE; + else + return _PROP_OBJECT_EQUALS_FALSE; +} + +static prop_number_t +_prop_number_alloc(const struct _prop_number_value *pnv) +{ + prop_number_t opn, pn; + struct rb_node *n; + bool rv; + + /* + * Check to see if this already exists in the tree. If it does, + * we just retain it and return it. + */ + _PROP_MUTEX_LOCK(_prop_number_tree_mutex); + if (! _prop_number_tree_initialized) { + _prop_rb_tree_init(&_prop_number_tree, + &_prop_number_rb_tree_ops); + _prop_number_tree_initialized = true; + } else { + n = _prop_rb_tree_find(&_prop_number_tree, pnv); + if (n != NULL) { + opn = RBNODE_TO_PN(n); + prop_object_retain(opn); + _PROP_MUTEX_UNLOCK(_prop_number_tree_mutex); + return (opn); + } + } + _PROP_MUTEX_UNLOCK(_prop_number_tree_mutex); + + /* + * Not in the tree. Create it now. + */ + + pn = _PROP_POOL_GET(_prop_number_pool); + if (pn == NULL) + return (NULL); + + _prop_object_init(&pn->pn_obj, &_prop_object_type_number); + + pn->pn_value = *pnv; + + /* + * We dropped the mutex when we allocated the new object, so + * we have to check again if it is in the tree. + */ + _PROP_MUTEX_LOCK(_prop_number_tree_mutex); + n = _prop_rb_tree_find(&_prop_number_tree, pnv); + if (n != NULL) { + opn = RBNODE_TO_PN(n); + prop_object_retain(opn); + _PROP_MUTEX_UNLOCK(_prop_number_tree_mutex); + _PROP_POOL_PUT(_prop_number_pool, pn); + return (opn); + } + rv = _prop_rb_tree_insert_node(&_prop_number_tree, &pn->pn_link); + _PROP_ASSERT(rv == true); + _PROP_MUTEX_UNLOCK(_prop_number_tree_mutex); + return (pn); +} + +/* + * prop_number_create_integer -- + * Create a prop_number_t and initialize it with the + * provided integer value. + */ +prop_number_t +prop_number_create_integer(int64_t val) +{ + struct _prop_number_value pnv; + + memset(&pnv, 0, sizeof(pnv)); + pnv.pnv_signed = val; + pnv.pnv_is_unsigned = false; + + return (_prop_number_alloc(&pnv)); +} + +/* + * prop_number_create_unsigned_integer -- + * Create a prop_number_t and initialize it with the + * provided unsigned integer value. + */ +prop_number_t +prop_number_create_unsigned_integer(uint64_t val) +{ + struct _prop_number_value pnv; + + memset(&pnv, 0, sizeof(pnv)); + pnv.pnv_unsigned = val; + pnv.pnv_is_unsigned = true; + + return (_prop_number_alloc(&pnv)); +} + +/* + * prop_number_copy -- + * Copy a prop_number_t. + */ +prop_number_t +prop_number_copy(prop_number_t opn) +{ + + if (! prop_object_is_number(opn)) + return (NULL); + + /* + * Because we only ever allocate one object for any given + * value, this can be reduced to a simple retain operation. + */ + prop_object_retain(opn); + return (opn); +} + +/* + * prop_number_unsigned -- + * Returns true if the prop_number_t has an unsigned value. + */ +bool +prop_number_unsigned(prop_number_t pn) +{ + + return (pn->pn_value.pnv_is_unsigned); +} + +/* + * prop_number_size -- + * Return the size, in bits, required to hold the value of + * the specified number. + */ +int +prop_number_size(prop_number_t pn) +{ + struct _prop_number_value *pnv; + + if (! prop_object_is_number(pn)) + return (0); + + pnv = &pn->pn_value; + + if (pnv->pnv_is_unsigned) { + if (pnv->pnv_unsigned > UINT32_MAX) + return (64); + if (pnv->pnv_unsigned > UINT16_MAX) + return (32); + if (pnv->pnv_unsigned > UINT8_MAX) + return (16); + return (8); + } + + if (pnv->pnv_signed > INT32_MAX || pnv->pnv_signed < INT32_MIN) + return (64); + if (pnv->pnv_signed > INT16_MAX || pnv->pnv_signed < INT16_MIN) + return (32); + if (pnv->pnv_signed > INT8_MAX || pnv->pnv_signed < INT8_MIN) + return (16); + return (8); +} + +/* + * prop_number_integer_value -- + * Get the integer value of a prop_number_t. + */ +int64_t +prop_number_integer_value(prop_number_t pn) +{ + + /* + * XXX Impossible to distinguish between "not a prop_number_t" + * XXX and "prop_number_t has a value of 0". + */ + if (! prop_object_is_number(pn)) + return (0); + + return (pn->pn_value.pnv_signed); +} + +/* + * prop_number_unsigned_integer_value -- + * Get the unsigned integer value of a prop_number_t. + */ +uint64_t +prop_number_unsigned_integer_value(prop_number_t pn) +{ + + /* + * XXX Impossible to distinguish between "not a prop_number_t" + * XXX and "prop_number_t has a value of 0". + */ + if (! prop_object_is_number(pn)) + return (0); + + return (pn->pn_value.pnv_unsigned); +} + +/* + * prop_number_equals -- + * Return true if two numbers are equivalent. + */ +bool +prop_number_equals(prop_number_t num1, prop_number_t num2) +{ + if (!prop_object_is_number(num1) || !prop_object_is_number(num2)) + return (false); + + return (prop_object_equals(num1, num2)); +} + +/* + * prop_number_equals_integer -- + * Return true if the number is equivalent to the specified integer. + */ +bool +prop_number_equals_integer(prop_number_t pn, int64_t val) +{ + + if (! prop_object_is_number(pn)) + return (false); + + if (pn->pn_value.pnv_is_unsigned && + (pn->pn_value.pnv_unsigned > INT64_MAX || val < 0)) + return (false); + + return (pn->pn_value.pnv_signed == val); +} + +/* + * prop_number_equals_unsigned_integer -- + * Return true if the number is equivalent to the specified + * unsigned integer. + */ +bool +prop_number_equals_unsigned_integer(prop_number_t pn, uint64_t val) +{ + + if (! prop_object_is_number(pn)) + return (false); + + if (! pn->pn_value.pnv_is_unsigned && + (pn->pn_value.pnv_signed < 0 || val > INT64_MAX)) + return (false); + + return (pn->pn_value.pnv_unsigned == val); +} + +static bool +_prop_number_internalize_unsigned(struct _prop_object_internalize_context *ctx, + struct _prop_number_value *pnv) +{ + char *cp; + + _PROP_ASSERT(/*CONSTCOND*/sizeof(unsigned long long) == + sizeof(uint64_t)); + + errno = 0; + pnv->pnv_unsigned = (uint64_t) strtoull(ctx->poic_cp, &cp, 0); + if (pnv->pnv_unsigned == UINT64_MAX && errno == ERANGE) + return (false); + pnv->pnv_is_unsigned = true; + ctx->poic_cp = cp; + + return (true); +} + +static bool +_prop_number_internalize_signed(struct _prop_object_internalize_context *ctx, + struct _prop_number_value *pnv) +{ + char *cp; + + _PROP_ASSERT(/*CONSTCOND*/sizeof(long long) == sizeof(int64_t)); + + errno = 0; + pnv->pnv_signed = (int64_t) strtoll(ctx->poic_cp, &cp, 0); + if ((pnv->pnv_signed == INT64_MAX || pnv->pnv_signed == INT64_MIN) && + errno == ERANGE) + return (false); + 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. + */ +/* ARGSUSED */ +bool +_prop_number_internalize(prop_stack_t stack, prop_object_t *obj, + struct _prop_object_internalize_context *ctx) +{ + struct _prop_number_value pnv; + + memset(&pnv, 0, sizeof(pnv)); + + /* No attributes, no empty elements. */ + if (ctx->poic_tagattr != NULL || ctx->poic_is_empty_element) + return (true); + + /* + * If the first character is '-', then we treat as signed. + * If the first two characters are "0x" (i.e. the number is + * in hex), then we treat as unsigned. Otherwise, we try + * signed first, and if that fails (presumably due to ERANGE), + * then we switch to unsigned. + */ + if (ctx->poic_cp[0] == '-') { + if (_prop_number_internalize_signed(ctx, &pnv) == false) + return (true); + } else if (ctx->poic_cp[0] == '0' && ctx->poic_cp[1] == 'x') { + if (_prop_number_internalize_unsigned(ctx, &pnv) == false) + return (true); + } else { + if (_prop_number_internalize_signed(ctx, &pnv) == false && + _prop_number_internalize_unsigned(ctx, &pnv) == false) + return (true); + } + + if (_prop_object_internalize_find_tag(ctx, "integer", + _PROP_TAG_TYPE_END) == false) + return (true); + + *obj = _prop_number_alloc(&pnv); + return (true); +} diff --git a/lib/portableproplib/prop_object.c b/lib/portableproplib/prop_object.c new file mode 100644 index 00000000..7a2c55af --- /dev/null +++ b/lib/portableproplib/prop_object.c @@ -0,0 +1,1241 @@ +/* $NetBSD: prop_object.c,v 1.23 2008/11/30 00:17:07 haad Exp $ */ + +/*- + * Copyright (c) 2006, 2007 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include "prop_object_impl.h" + +#include +#include +#include +#include +#include +#include + +#include + +/* + * _prop_object_init -- + * Initialize an object. Called when sub-classes create + * an instance. + */ +void +_prop_object_init(struct _prop_object *po, const struct _prop_object_type *pot) +{ + + po->po_type = pot; + po->po_refcnt = 1; +} + +/* + * _prop_object_fini -- + * Finalize an object. Called when sub-classes destroy + * an instance. + */ +/*ARGSUSED*/ +void +_prop_object_fini(struct _prop_object *po _PROP_ARG_UNUSED) +{ + /* Nothing to do, currently. */ +} + +/* + * _prop_object_externalize_start_tag -- + * Append an XML-style start tag to the externalize buffer. + */ +bool +_prop_object_externalize_start_tag( + struct _prop_object_externalize_context *ctx, const char *tag) +{ + unsigned int i; + + for (i = 0; i < ctx->poec_depth; i++) { + if (_prop_object_externalize_append_char(ctx, '\t') == false) + return (false); + } + if (_prop_object_externalize_append_char(ctx, '<') == false || + _prop_object_externalize_append_cstring(ctx, tag) == false || + _prop_object_externalize_append_char(ctx, '>') == false) + return (false); + + return (true); +} + +/* + * _prop_object_externalize_end_tag -- + * Append an XML-style end tag to the externalize buffer. + */ +bool +_prop_object_externalize_end_tag( + struct _prop_object_externalize_context *ctx, const char *tag) +{ + + if (_prop_object_externalize_append_char(ctx, '<') == false || + _prop_object_externalize_append_char(ctx, '/') == false || + _prop_object_externalize_append_cstring(ctx, tag) == false || + _prop_object_externalize_append_char(ctx, '>') == false || + _prop_object_externalize_append_char(ctx, '\n') == false) + return (false); + + return (true); +} + +/* + * _prop_object_externalize_empty_tag -- + * Append an XML-style empty tag to the externalize buffer. + */ +bool +_prop_object_externalize_empty_tag( + struct _prop_object_externalize_context *ctx, const char *tag) +{ + unsigned int i; + + for (i = 0; i < ctx->poec_depth; i++) { + if (_prop_object_externalize_append_char(ctx, '\t') == false) + return (false); + } + + if (_prop_object_externalize_append_char(ctx, '<') == false || + _prop_object_externalize_append_cstring(ctx, tag) == false || + _prop_object_externalize_append_char(ctx, '/') == false || + _prop_object_externalize_append_char(ctx, '>') == false || + _prop_object_externalize_append_char(ctx, '\n') == false) + return (false); + + return (true); +} + +/* + * _prop_object_externalize_append_cstring -- + * Append a C string to the externalize buffer. + */ +bool +_prop_object_externalize_append_cstring( + struct _prop_object_externalize_context *ctx, const char *cp) +{ + + while (*cp != '\0') { + if (_prop_object_externalize_append_char(ctx, + (unsigned char) *cp) == false) + return (false); + cp++; + } + + return (true); +} + +/* + * _prop_object_externalize_append_encoded_cstring -- + * Append an encoded C string to the externalize buffer. + */ +bool +_prop_object_externalize_append_encoded_cstring( + struct _prop_object_externalize_context *ctx, const char *cp) +{ + + while (*cp != '\0') { + switch (*cp) { + case '<': + if (_prop_object_externalize_append_cstring(ctx, + "<") == 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; + } + cp++; + } + + return (true); +} + +#define BUF_EXPAND 256 + +/* + * _prop_object_externalize_append_char -- + * Append a single character to the externalize buffer. + */ +bool +_prop_object_externalize_append_char( + struct _prop_object_externalize_context *ctx, unsigned char c) +{ + + _PROP_ASSERT(ctx->poec_capacity != 0); + _PROP_ASSERT(ctx->poec_buf != NULL); + _PROP_ASSERT(ctx->poec_len <= ctx->poec_capacity); + + if (ctx->poec_len == ctx->poec_capacity) { + char *cp = _PROP_REALLOC(ctx->poec_buf, + ctx->poec_capacity + BUF_EXPAND, + M_TEMP); + if (cp == NULL) + return (false); + ctx->poec_capacity = ctx->poec_capacity + BUF_EXPAND; + ctx->poec_buf = cp; + } + + ctx->poec_buf[ctx->poec_len++] = c; + + return (true); +} + +/* + * _prop_object_externalize_header -- + * Append the standard XML header to the externalize buffer. + */ +bool +_prop_object_externalize_header(struct _prop_object_externalize_context *ctx) +{ + static const char _plist_xml_header[] = +"\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. + */ +bool +_prop_object_externalize_footer(struct _prop_object_externalize_context *ctx) +{ + + if (_prop_object_externalize_end_tag(ctx, "plist") == false || + _prop_object_externalize_append_char(ctx, '\0') == false) + return (false); + + return (true); +} + +/* + * _prop_object_externalize_context_alloc -- + * Allocate an externalize context. + */ +struct _prop_object_externalize_context * +_prop_object_externalize_context_alloc(void) +{ + struct _prop_object_externalize_context *ctx; + + ctx = _PROP_MALLOC(sizeof(*ctx), M_TEMP); + if (ctx != NULL) { + ctx->poec_buf = _PROP_MALLOC(BUF_EXPAND, M_TEMP); + if (ctx->poec_buf == NULL) { + _PROP_FREE(ctx, M_TEMP); + return (NULL); + } + ctx->poec_len = 0; + ctx->poec_capacity = BUF_EXPAND; + ctx->poec_depth = 0; + } + return (ctx); +} + +/* + * _prop_object_externalize_context_free -- + * Free an externalize context. + */ +void +_prop_object_externalize_context_free( + struct _prop_object_externalize_context *ctx) +{ + + /* Buffer is always freed by the caller. */ + _PROP_FREE(ctx, M_TEMP); +} + +/* + * _prop_object_internalize_skip_comment -- + * Skip the body and end tag of a comment. + */ +static bool +_prop_object_internalize_skip_comment( + struct _prop_object_internalize_context *ctx) +{ + const char *cp = ctx->poic_cp; + + while (!_PROP_EOF(*cp)) { + if (cp[0] == '-' && + cp[1] == '-' && + cp[2] == '>') { + ctx->poic_cp = cp + 3; + return (true); + } + cp++; + } + + return (false); /* ran out of buffer */ +} + +/* + * _prop_object_internalize_find_tag -- + * Find the next tag in an XML stream. Optionally compare the found + * tag to an expected tag name. State of the context is undefined + * if this routine returns false. Upon success, the context points + * to the first octet after the tag. + */ +bool +_prop_object_internalize_find_tag(struct _prop_object_internalize_context *ctx, + const char *tag, _prop_tag_type_t type) +{ + const char *cp; + size_t taglen; + + if (tag != NULL) + taglen = strlen(tag); + else + taglen = 0; + + start_over: + cp = ctx->poic_cp; + + /* + * Find the start of the tag. + */ + while (_PROP_ISSPACE(*cp)) + cp++; + if (_PROP_EOF(*cp)) + return (false); + + if (*cp != '<') + return (false); + + ctx->poic_tag_start = cp++; + if (_PROP_EOF(*cp)) + return (false); + + if (*cp == '!') { + if (cp[1] != '-' || cp[2] != '-') + return (false); + /* + * Comment block -- only allowed if we are allowed to + * return a start tag. + */ + if (type == _PROP_TAG_TYPE_END) + return (false); + ctx->poic_cp = cp + 3; + if (_prop_object_internalize_skip_comment(ctx) == false) + return (false); + goto start_over; + } + + if (*cp == '/') { + if (type != _PROP_TAG_TYPE_END && + type != _PROP_TAG_TYPE_EITHER) + return (false); + cp++; + if (_PROP_EOF(*cp)) + return (false); + ctx->poic_tag_type = _PROP_TAG_TYPE_END; + } else { + if (type != _PROP_TAG_TYPE_START && + type != _PROP_TAG_TYPE_EITHER) + return (false); + ctx->poic_tag_type = _PROP_TAG_TYPE_START; + } + + ctx->poic_tagname = cp; + + while (!_PROP_ISSPACE(*cp) && *cp != '/' && *cp != '>') + cp++; + if (_PROP_EOF(*cp)) + return (false); + + ctx->poic_tagname_len = cp - ctx->poic_tagname; + + /* Make sure this is the tag we're looking for. */ + if (tag != NULL && + (taglen != ctx->poic_tagname_len || + memcmp(tag, ctx->poic_tagname, taglen) != 0)) + return (false); + + /* Check for empty tag. */ + if (*cp == '/') { + if (ctx->poic_tag_type != _PROP_TAG_TYPE_START) + return(false); /* only valid on start tags */ + ctx->poic_is_empty_element = true; + cp++; + if (_PROP_EOF(*cp) || *cp != '>') + return (false); + } else + ctx->poic_is_empty_element = false; + + /* Easy case of no arguments. */ + if (*cp == '>') { + ctx->poic_tagattr = NULL; + ctx->poic_tagattr_len = 0; + ctx->poic_tagattrval = NULL; + ctx->poic_tagattrval_len = 0; + ctx->poic_cp = cp + 1; + return (true); + } + + _PROP_ASSERT(!_PROP_EOF(*cp)); + cp++; + if (_PROP_EOF(*cp)) + return (false); + + while (_PROP_ISSPACE(*cp)) + cp++; + if (_PROP_EOF(*cp)) + return (false); + + ctx->poic_tagattr = cp; + + while (!_PROP_ISSPACE(*cp) && *cp != '=') + cp++; + if (_PROP_EOF(*cp)) + return (false); + + ctx->poic_tagattr_len = cp - ctx->poic_tagattr; + + cp++; + if (*cp != '\"') + return (false); + cp++; + if (_PROP_EOF(*cp)) + return (false); + + ctx->poic_tagattrval = cp; + while (*cp != '\"') + cp++; + if (_PROP_EOF(*cp)) + return (false); + ctx->poic_tagattrval_len = cp - ctx->poic_tagattrval; + + cp++; + if (*cp != '>') + return (false); + + ctx->poic_cp = cp + 1; + return (true); +} + +/* + * _prop_object_internalize_decode_string -- + * Decode an encoded string. + */ +bool +_prop_object_internalize_decode_string( + struct _prop_object_internalize_context *ctx, + char *target, size_t targsize, size_t *sizep, + const char **cpp) +{ + const char *src; + size_t tarindex; + char c; + + tarindex = 0; + src = ctx->poic_cp; + + for (;;) { + if (_PROP_EOF(*src)) + return (false); + if (*src == '<') { + break; + } + + if ((c = *src) == '&') { + if (src[1] == 'a' && + src[2] == 'm' && + src[3] == 'p' && + src[4] == ';') { + c = '&'; + src += 5; + } else if (src[1] == 'l' && + src[2] == 't' && + src[3] == ';') { + c = '<'; + src += 4; + } else if (src[1] == 'g' && + src[2] == 't' && + src[3] == ';') { + c = '>'; + src += 4; + } else if (src[1] == 'a' && + src[2] == 'p' && + src[3] == 'o' && + src[4] == 's' && + src[5] == ';') { + c = '\''; + src += 6; + } else if (src[1] == 'q' && + src[2] == 'u' && + src[3] == 'o' && + src[4] == 't' && + src[5] == ';') { + c = '\"'; + src += 6; + } else + return (false); + } else + src++; + if (target) { + if (tarindex >= targsize) + return (false); + target[tarindex] = c; + } + tarindex++; + } + + _PROP_ASSERT(*src == '<'); + if (sizep != NULL) + *sizep = tarindex; + if (cpp != NULL) + *cpp = src; + + return (true); +} + +/* + * _prop_object_internalize_match -- + * Returns true if the two character streams match. + */ +bool +_prop_object_internalize_match(const char *str1, size_t len1, + const char *str2, size_t len2) +{ + + return (len1 == len2 && memcmp(str1, str2, len1) == 0); +} + +#define INTERNALIZER(t, f) \ +{ t, sizeof(t) - 1, f } + +static const struct _prop_object_internalizer { + const char *poi_tag; + size_t poi_taglen; + prop_object_internalizer_t poi_intern; +} _prop_object_internalizer_table[] = { + INTERNALIZER("array", _prop_array_internalize), + + INTERNALIZER("true", _prop_bool_internalize), + INTERNALIZER("false", _prop_bool_internalize), + + INTERNALIZER("data", _prop_data_internalize), + + INTERNALIZER("dict", _prop_dictionary_internalize), + + INTERNALIZER("integer", _prop_number_internalize), + + INTERNALIZER("string", _prop_string_internalize), + + { 0, 0, NULL } +}; + +#undef INTERNALIZER + +/* + * _prop_object_internalize_by_tag -- + * Determine the object type from the tag in the context and + * internalize it. + */ +prop_object_t +_prop_object_internalize_by_tag(struct _prop_object_internalize_context *ctx) +{ + const struct _prop_object_internalizer *poi; + prop_object_t obj, parent_obj; + void *data, *iter; + prop_object_internalizer_continue_t iter_func; + struct _prop_stack stack; + + _prop_stack_init(&stack); + +match_start: + for (poi = _prop_object_internalizer_table; + poi->poi_tag != NULL; poi++) { + if (_prop_object_internalize_match(ctx->poic_tagname, + ctx->poic_tagname_len, + poi->poi_tag, + poi->poi_taglen)) + break; + } + if (poi == NULL) { + while (_prop_stack_pop(&stack, &obj, &iter, &data, NULL)) { + iter_func = (prop_object_internalizer_continue_t)iter; + (*iter_func)(&stack, &obj, ctx, data, NULL); + } + + return (NULL); + } + + obj = NULL; + if (!(*poi->poi_intern)(&stack, &obj, ctx)) + goto match_start; + + parent_obj = obj; + while (_prop_stack_pop(&stack, &parent_obj, &iter, &data, NULL)) { + iter_func = (prop_object_internalizer_continue_t)iter; + if (!(*iter_func)(&stack, &parent_obj, ctx, data, obj)) + goto match_start; + obj = parent_obj; + } + + return (parent_obj); +} + +prop_object_t +_prop_generic_internalize(const char *xml, const char *master_tag) +{ + prop_object_t obj = NULL; + struct _prop_object_internalize_context *ctx; + + ctx = _prop_object_internalize_context_alloc(xml); + if (ctx == NULL) + return (NULL); + + /* We start with a tag. */ + if (_prop_object_internalize_find_tag(ctx, "plist", + _PROP_TAG_TYPE_START) == false) + goto out; + + /* Plist elements cannot be empty. */ + if (ctx->poic_is_empty_element) + goto out; + + /* + * We don't understand any plist attributes, but Apple XML + * property lists often have a "version" attribute. If we + * see that one, we simply ignore it. + */ + if (ctx->poic_tagattr != NULL && + !_PROP_TAGATTR_MATCH(ctx, "version")) + goto out; + + /* Next we expect to see opening master_tag. */ + if (_prop_object_internalize_find_tag(ctx, master_tag, + _PROP_TAG_TYPE_START) == false) + goto out; + + obj = _prop_object_internalize_by_tag(ctx); + if (obj == NULL) + goto out; + + /* + * We've advanced past the closing master_tag. + * Now we want . + */ + if (_prop_object_internalize_find_tag(ctx, "plist", + _PROP_TAG_TYPE_END) == false) { + prop_object_release(obj); + obj = NULL; + } + + out: + _prop_object_internalize_context_free(ctx); + return (obj); +} + +/* + * _prop_object_internalize_context_alloc -- + * Allocate an internalize context. + */ +struct _prop_object_internalize_context * +_prop_object_internalize_context_alloc(const char *xml) +{ + struct _prop_object_internalize_context *ctx; + + ctx = _PROP_MALLOC(sizeof(struct _prop_object_internalize_context), + M_TEMP); + if (ctx == NULL) + return (NULL); + + ctx->poic_xml = ctx->poic_cp = xml; + + /* + * Skip any whitespace and XML preamble stuff that we don't + * know about / care about. + */ + for (;;) { + while (_PROP_ISSPACE(*xml)) + xml++; + if (_PROP_EOF(*xml) || *xml != '<') + goto bad; + +#define MATCH(str) (memcmp(&xml[1], str, sizeof(str) - 1) == 0) + + /* + * Skip over the XML preamble that Apple XML property + * lists usually include at the top of the file. + */ + if (MATCH("?xml ") || + MATCH("!DOCTYPE plist")) { + while (*xml != '>' && !_PROP_EOF(*xml)) + xml++; + if (_PROP_EOF(*xml)) + goto bad; + xml++; /* advance past the '>' */ + continue; + } + + if (MATCH(" S | + * | Q S --> Q * | + * | --> | + */ + standin_father = standin; + KASSERT(RB_SENTINEL_P(standin->rb_nodes[standin_other])); + KASSERT(!RB_SENTINEL_P(self->rb_nodes[standin_other])); + KASSERT(self->rb_nodes[standin_which] == standin); + /* + * Make our brother our son. + */ + standin->rb_nodes[standin_other] = self->rb_nodes[standin_other]; + standin->rb_nodes[standin_other]->rb_parent = standin; + KASSERT(standin->rb_nodes[standin_other]->rb_position == standin_other); + } else { + /* + * | P --> P | + * | S --> Q | + * | Q --> | + */ + standin_father = standin->rb_parent; + standin_father->rb_nodes[standin_which] = + standin->rb_nodes[standin_which]; + standin->rb_left = self->rb_left; + standin->rb_right = self->rb_right; + standin->rb_left->rb_parent = standin; + standin->rb_right->rb_parent = standin; + } + + /* + * Now copy the result of self to standin and then replace + * self with standin in the tree. + */ + standin->rb_parent = self->rb_parent; + standin->rb_properties = self->rb_properties; + standin->rb_parent->rb_nodes[standin->rb_position] = standin; + + /* + * Remove ourselves from the node list and decrement the count. + */ + RB_TAILQ_REMOVE(&rbt->rbt_nodes, self, rb_link); + RBT_COUNT_DECR(rbt); + + KASSERT(rb_tree_check_node(rbt, standin, NULL, false)); + KASSERT(rb_tree_check_node(rbt, standin_father, NULL, false)); + + if (!rebalance) + return; + + rb_tree_removal_rebalance(rbt, standin_father, standin_which); + KASSERT(rb_tree_check_node(rbt, standin, NULL, true)); +} + +/* + * We could do this by doing + * rb_tree_node_swap(rbt, self, which); + * rb_tree_prune_node(rbt, self, false); + * + * But it's more efficient to just evalate and recolor the child. + */ +/*ARGSUSED*/ +static void +rb_tree_prune_blackred_branch(struct rb_tree *rbt _PROP_ARG_UNUSED, + struct rb_node *self, unsigned int which) +{ + struct rb_node *parent = self->rb_parent; + struct rb_node *child = self->rb_nodes[which]; + + KASSERT(which == RB_NODE_LEFT || which == RB_NODE_RIGHT); + KASSERT(RB_BLACK_P(self) && RB_RED_P(child)); + KASSERT(!RB_TWOCHILDREN_P(child)); + KASSERT(RB_CHILDLESS_P(child)); + KASSERT(rb_tree_check_node(rbt, self, NULL, false)); + KASSERT(rb_tree_check_node(rbt, child, NULL, false)); + + /* + * Remove ourselves from the tree and give our former child our + * properties (position, color, root). + */ + parent->rb_nodes[self->rb_position] = child; + child->rb_parent = parent; + child->rb_properties = self->rb_properties; + + /* + * Remove ourselves from the node list and decrement the count. + */ + RB_TAILQ_REMOVE(&rbt->rbt_nodes, self, rb_link); + RBT_COUNT_DECR(rbt); + + KASSERT(RB_ROOT_P(self) || rb_tree_check_node(rbt, parent, NULL, true)); + KASSERT(rb_tree_check_node(rbt, child, NULL, true)); +} +/* + * + */ +void +_prop_rb_tree_remove_node(struct rb_tree *rbt, struct rb_node *self) +{ + struct rb_node *standin; + unsigned int which; + /* + * In the following diagrams, we (the node to be removed) are S. Red + * nodes are lowercase. T could be either red or black. + * + * Remember the major axiom of the red-black tree: the number of + * black nodes from the root to each leaf is constant across all + * leaves, only the number of red nodes varies. + * + * Thus removing a red leaf doesn't require any other changes to a + * red-black tree. So if we must remove a node, attempt to rearrange + * the tree so we can remove a red node. + * + * The simpliest case is a childless red node or a childless root node: + * + * | T --> T | or | R --> * | + * | s --> * | + */ + if (RB_CHILDLESS_P(self)) { + if (RB_RED_P(self) || RB_ROOT_P(self)) { + rb_tree_prune_node(rbt, self, false); + return; + } + rb_tree_prune_node(rbt, self, true); + return; + } + KASSERT(!RB_CHILDLESS_P(self)); + if (!RB_TWOCHILDREN_P(self)) { + /* + * The next simpliest case is the node we are deleting is + * black and has one red child. + * + * | T --> T --> T | + * | S --> R --> R | + * | r --> s --> * | + */ + which = RB_LEFT_SENTINEL_P(self) ? RB_NODE_RIGHT : RB_NODE_LEFT; + KASSERT(RB_BLACK_P(self)); + KASSERT(RB_RED_P(self->rb_nodes[which])); + KASSERT(RB_CHILDLESS_P(self->rb_nodes[which])); + rb_tree_prune_blackred_branch(rbt, self, which); + return; + } + KASSERT(RB_TWOCHILDREN_P(self)); + + /* + * We invert these because we prefer to remove from the inside of + * the tree. + */ + which = self->rb_position ^ RB_NODE_OTHER; + + /* + * Let's find the node closes to us opposite of our parent + * Now swap it with ourself, "prune" it, and rebalance, if needed. + */ + standin = _prop_rb_tree_iterate(rbt, self, which); + rb_tree_swap_prune_and_rebalance(rbt, self, standin); +} + +static void +rb_tree_removal_rebalance(struct rb_tree *rbt, struct rb_node *parent, + unsigned int which) +{ + KASSERT(!RB_SENTINEL_P(parent)); + KASSERT(RB_SENTINEL_P(parent->rb_nodes[which])); + KASSERT(which == RB_NODE_LEFT || which == RB_NODE_RIGHT); + + while (RB_BLACK_P(parent->rb_nodes[which])) { + unsigned int other = which ^ RB_NODE_OTHER; + struct rb_node *brother = parent->rb_nodes[other]; + + KASSERT(!RB_SENTINEL_P(brother)); + /* + * For cases 1, 2a, and 2b, our brother's children must + * be black and our father must be black + */ + if (RB_BLACK_P(parent) + && RB_BLACK_P(brother->rb_left) + && RB_BLACK_P(brother->rb_right)) { + /* + * Case 1: Our brother is red, swap its position + * (and colors) with our parent. This is now case 2b. + * + * B -> D + * x d -> b E + * C E -> x C + */ + if (RB_RED_P(brother)) { + KASSERT(RB_BLACK_P(parent)); + rb_tree_reparent_nodes(rbt, parent, other); + brother = parent->rb_nodes[other]; + KASSERT(!RB_SENTINEL_P(brother)); + KASSERT(RB_BLACK_P(brother)); + KASSERT(RB_RED_P(parent)); + KASSERT(rb_tree_check_node(rbt, brother, NULL, false)); + KASSERT(rb_tree_check_node(rbt, parent, NULL, false)); + } else { + /* + * Both our parent and brother are black. + * Change our brother to red, advance up rank + * and go through the loop again. + * + * B -> B + * A D -> A d + * C E -> C E + */ + RB_MARK_RED(brother); + KASSERT(RB_BLACK_P(brother->rb_left)); + KASSERT(RB_BLACK_P(brother->rb_right)); + if (RB_ROOT_P(parent)) + return; + KASSERT(rb_tree_check_node(rbt, brother, NULL, false)); + KASSERT(rb_tree_check_node(rbt, parent, NULL, false)); + which = parent->rb_position; + parent = parent->rb_parent; + } + } else if (RB_RED_P(parent) + && RB_BLACK_P(brother) + && RB_BLACK_P(brother->rb_left) + && RB_BLACK_P(brother->rb_right)) { + KASSERT(RB_BLACK_P(brother)); + KASSERT(RB_BLACK_P(brother->rb_left)); + KASSERT(RB_BLACK_P(brother->rb_right)); + RB_MARK_BLACK(parent); + RB_MARK_RED(brother); + KASSERT(rb_tree_check_node(rbt, brother, NULL, true)); + break; /* We're done! */ + } else { + KASSERT(RB_BLACK_P(brother)); + KASSERT(!RB_CHILDLESS_P(brother)); + /* + * Case 3: our brother is black, our left nephew is + * red, and our right nephew is black. Swap our + * brother with our left nephew. This result in a + * tree that matches case 4. + * + * B -> D + * A D -> B E + * c e -> A C + */ + if (RB_BLACK_P(brother->rb_nodes[other])) { + KASSERT(RB_RED_P(brother->rb_nodes[which])); + rb_tree_reparent_nodes(rbt, brother, which); + KASSERT(brother->rb_parent == parent->rb_nodes[other]); + brother = parent->rb_nodes[other]; + KASSERT(RB_RED_P(brother->rb_nodes[other])); + } + /* + * Case 4: our brother is black and our right nephew + * is red. Swap our parent and brother locations and + * change our right nephew to black. (these can be + * done in either order so we change the color first). + * The result is a valid red-black tree and is a + * terminal case. + * + * B -> D + * A D -> B E + * c e -> A C + */ + RB_MARK_BLACK(brother->rb_nodes[other]); + rb_tree_reparent_nodes(rbt, parent, other); + break; /* We're done! */ + } + } + KASSERT(rb_tree_check_node(rbt, parent, NULL, true)); +} + +struct rb_node * +_prop_rb_tree_iterate(struct rb_tree *rbt, struct rb_node *self, + unsigned int direction) +{ + const unsigned int other = direction ^ RB_NODE_OTHER; + KASSERT(direction == RB_NODE_LEFT || direction == RB_NODE_RIGHT); + + if (self == NULL) { + self = rbt->rbt_root; + if (RB_SENTINEL_P(self)) + return NULL; + while (!RB_SENTINEL_P(self->rb_nodes[other])) + self = self->rb_nodes[other]; + return self; + } + KASSERT(!RB_SENTINEL_P(self)); + /* + * We can't go any further in this direction. We proceed up in the + * opposite direction until our parent is in direction we want to go. + */ + if (RB_SENTINEL_P(self->rb_nodes[direction])) { + while (!RB_ROOT_P(self)) { + if (other == self->rb_position) + return self->rb_parent; + self = self->rb_parent; + } + return NULL; + } + + /* + * Advance down one in current direction and go down as far as possible + * in the opposite direction. + */ + self = self->rb_nodes[direction]; + KASSERT(!RB_SENTINEL_P(self)); + while (!RB_SENTINEL_P(self->rb_nodes[other])) + self = self->rb_nodes[other]; + return self; +} + +#ifdef RBDEBUG +static const struct rb_node * +rb_tree_iterate_const(const struct rb_tree *rbt, const struct rb_node *self, + unsigned int direction) +{ + const unsigned int other = direction ^ RB_NODE_OTHER; + KASSERT(direction == RB_NODE_LEFT || direction == RB_NODE_RIGHT); + + if (self == NULL) { + self = rbt->rbt_root; + if (RB_SENTINEL_P(self)) + return NULL; + while (!RB_SENTINEL_P(self->rb_nodes[other])) + self = self->rb_nodes[other]; + return self; + } + KASSERT(!RB_SENTINEL_P(self)); + /* + * We can't go any further in this direction. We proceed up in the + * opposite direction until our parent is in direction we want to go. + */ + if (RB_SENTINEL_P(self->rb_nodes[direction])) { + while (!RB_ROOT_P(self)) { + if (other == self->rb_position) + return self->rb_parent; + self = self->rb_parent; + } + return NULL; + } + + /* + * Advance down one in current direction and go down as far as possible + * in the opposite direction. + */ + self = self->rb_nodes[direction]; + KASSERT(!RB_SENTINEL_P(self)); + while (!RB_SENTINEL_P(self->rb_nodes[other])) + self = self->rb_nodes[other]; + return self; +} + +static bool +rb_tree_check_node(const struct rb_tree *rbt, const struct rb_node *self, + const struct rb_node *prev, bool red_check) +{ + KASSERT(!self->rb_sentinel); + KASSERT(self->rb_left); + KASSERT(self->rb_right); + KASSERT(prev == NULL || + (*rbt->rbt_ops->rbto_compare_nodes)(prev, self) > 0); + + /* + * Verify our relationship to our parent. + */ + if (RB_ROOT_P(self)) { + KASSERT(self == rbt->rbt_root); + KASSERT(self->rb_position == RB_NODE_LEFT); + KASSERT(self->rb_parent->rb_nodes[RB_NODE_LEFT] == self); + KASSERT(self->rb_parent == (const struct rb_node *) &rbt->rbt_root); + } else { + KASSERT(self != rbt->rbt_root); + KASSERT(!RB_PARENT_SENTINEL_P(self)); + if (self->rb_position == RB_NODE_LEFT) { + KASSERT((*rbt->rbt_ops->rbto_compare_nodes)(self, self->rb_parent) > 0); + KASSERT(self->rb_parent->rb_nodes[RB_NODE_LEFT] == self); + } else { + KASSERT((*rbt->rbt_ops->rbto_compare_nodes)(self, self->rb_parent) < 0); + KASSERT(self->rb_parent->rb_nodes[RB_NODE_RIGHT] == self); + } + } + + /* + * Verify our position in the linked list against the tree itself. + */ + { + const struct rb_node *prev0 = rb_tree_iterate_const(rbt, self, RB_NODE_LEFT); + const struct rb_node *next0 = rb_tree_iterate_const(rbt, self, RB_NODE_RIGHT); + KASSERT(prev0 == TAILQ_PREV(self, rb_node_qh, rb_link)); + if (next0 != TAILQ_NEXT(self, rb_link)) + next0 = rb_tree_iterate_const(rbt, self, RB_NODE_RIGHT); + KASSERT(next0 == TAILQ_NEXT(self, rb_link)); + } + + /* + * The root must be black. + * There can never be two adjacent red nodes. + */ + if (red_check) { + KASSERT(!RB_ROOT_P(self) || RB_BLACK_P(self)); + if (RB_RED_P(self)) { + const struct rb_node *brother; + KASSERT(!RB_ROOT_P(self)); + brother = self->rb_parent->rb_nodes[self->rb_position ^ RB_NODE_OTHER]; + KASSERT(RB_BLACK_P(self->rb_parent)); + /* + * I'm red and have no children, then I must either + * have no brother or my brother also be red and + * also have no children. (black count == 0) + */ + KASSERT(!RB_CHILDLESS_P(self) + || RB_SENTINEL_P(brother) + || RB_RED_P(brother) + || RB_CHILDLESS_P(brother)); + /* + * If I'm not childless, I must have two children + * and they must be both be black. + */ + KASSERT(RB_CHILDLESS_P(self) + || (RB_TWOCHILDREN_P(self) + && RB_BLACK_P(self->rb_left) + && RB_BLACK_P(self->rb_right))); + /* + * If I'm not childless, thus I have black children, + * then my brother must either be black or have two + * black children. + */ + KASSERT(RB_CHILDLESS_P(self) + || RB_BLACK_P(brother) + || (RB_TWOCHILDREN_P(brother) + && RB_BLACK_P(brother->rb_left) + && RB_BLACK_P(brother->rb_right))); + } else { + /* + * If I'm black and have one child, that child must + * be red and childless. + */ + KASSERT(RB_CHILDLESS_P(self) + || RB_TWOCHILDREN_P(self) + || (!RB_LEFT_SENTINEL_P(self) + && RB_RIGHT_SENTINEL_P(self) + && RB_RED_P(self->rb_left) + && RB_CHILDLESS_P(self->rb_left)) + || (!RB_RIGHT_SENTINEL_P(self) + && RB_LEFT_SENTINEL_P(self) + && RB_RED_P(self->rb_right) + && RB_CHILDLESS_P(self->rb_right))); + + /* + * If I'm a childless black node and my parent is + * black, my 2nd closet relative away from my parent + * is either red or has a red parent or red children. + */ + if (!RB_ROOT_P(self) + && RB_CHILDLESS_P(self) + && RB_BLACK_P(self->rb_parent)) { + const unsigned int which = self->rb_position; + const unsigned int other = which ^ RB_NODE_OTHER; + const struct rb_node *relative0, *relative; + + relative0 = rb_tree_iterate_const(rbt, + self, other); + KASSERT(relative0 != NULL); + relative = rb_tree_iterate_const(rbt, + relative0, other); + KASSERT(relative != NULL); + KASSERT(RB_SENTINEL_P(relative->rb_nodes[which])); +#if 0 + KASSERT(RB_RED_P(relative) + || RB_RED_P(relative->rb_left) + || RB_RED_P(relative->rb_right) + || RB_RED_P(relative->rb_parent)); +#endif + } + } + /* + * A grandparent's children must be real nodes and not + * sentinels. First check out grandparent. + */ + KASSERT(RB_ROOT_P(self) + || RB_ROOT_P(self->rb_parent) + || RB_TWOCHILDREN_P(self->rb_parent->rb_parent)); + /* + * If we are have grandchildren on our left, then + * we must have a child on our right. + */ + KASSERT(RB_LEFT_SENTINEL_P(self) + || RB_CHILDLESS_P(self->rb_left) + || !RB_RIGHT_SENTINEL_P(self)); + /* + * If we are have grandchildren on our right, then + * we must have a child on our left. + */ + KASSERT(RB_RIGHT_SENTINEL_P(self) + || RB_CHILDLESS_P(self->rb_right) + || !RB_LEFT_SENTINEL_P(self)); + + /* + * If we have a child on the left and it doesn't have two + * children make sure we don't have great-great-grandchildren on + * the right. + */ + KASSERT(RB_TWOCHILDREN_P(self->rb_left) + || RB_CHILDLESS_P(self->rb_right) + || RB_CHILDLESS_P(self->rb_right->rb_left) + || RB_CHILDLESS_P(self->rb_right->rb_left->rb_left) + || RB_CHILDLESS_P(self->rb_right->rb_left->rb_right) + || RB_CHILDLESS_P(self->rb_right->rb_right) + || RB_CHILDLESS_P(self->rb_right->rb_right->rb_left) + || RB_CHILDLESS_P(self->rb_right->rb_right->rb_right)); + + /* + * If we have a child on the right and it doesn't have two + * children make sure we don't have great-great-grandchildren on + * the left. + */ + KASSERT(RB_TWOCHILDREN_P(self->rb_right) + || RB_CHILDLESS_P(self->rb_left) + || RB_CHILDLESS_P(self->rb_left->rb_left) + || RB_CHILDLESS_P(self->rb_left->rb_left->rb_left) + || RB_CHILDLESS_P(self->rb_left->rb_left->rb_right) + || RB_CHILDLESS_P(self->rb_left->rb_right) + || RB_CHILDLESS_P(self->rb_left->rb_right->rb_left) + || RB_CHILDLESS_P(self->rb_left->rb_right->rb_right)); + + /* + * If we are fully interior node, then our predecessors and + * successors must have no children in our direction. + */ + if (RB_TWOCHILDREN_P(self)) { + const struct rb_node *prev0; + const struct rb_node *next0; + + prev0 = rb_tree_iterate_const(rbt, self, RB_NODE_LEFT); + KASSERT(prev0 != NULL); + KASSERT(RB_RIGHT_SENTINEL_P(prev0)); + + next0 = rb_tree_iterate_const(rbt, self, RB_NODE_RIGHT); + KASSERT(next0 != NULL); + KASSERT(RB_LEFT_SENTINEL_P(next0)); + } + } + + return true; +} + +static unsigned int +rb_tree_count_black(const struct rb_node *self) +{ + unsigned int left, right; + + if (RB_SENTINEL_P(self)) + return 0; + + left = rb_tree_count_black(self->rb_left); + right = rb_tree_count_black(self->rb_right); + + KASSERT(left == right); + + return left + RB_BLACK_P(self); +} + +void +_prop_rb_tree_check(const struct rb_tree *rbt, bool red_check) +{ + const struct rb_node *self; + const struct rb_node *prev; + unsigned int count; + + KASSERT(rbt->rbt_root == NULL || rbt->rbt_root->rb_position == RB_NODE_LEFT); + + prev = NULL; + count = 0; + TAILQ_FOREACH(self, &rbt->rbt_nodes, rb_link) { + rb_tree_check_node(rbt, self, prev, false); + count++; + } + KASSERT(rbt->rbt_count == count); + KASSERT(RB_SENTINEL_P(rbt->rbt_root) + || rb_tree_count_black(rbt->rbt_root)); + + /* + * The root must be black. + * There can never be two adjacent red nodes. + */ + if (red_check) { + KASSERT(rbt->rbt_root == NULL || RB_BLACK_P(rbt->rbt_root)); + TAILQ_FOREACH(self, &rbt->rbt_nodes, rb_link) { + rb_tree_check_node(rbt, self, NULL, true); + } + } +} +#endif /* RBDEBUG */ diff --git a/lib/portableproplib/prop_rb_impl.h b/lib/portableproplib/prop_rb_impl.h new file mode 100644 index 00000000..4dd6801c --- /dev/null +++ b/lib/portableproplib/prop_rb_impl.h @@ -0,0 +1,137 @@ +/* $NetBSD: prop_rb_impl.h,v 1.7 2008/06/30 20:14:09 matt Exp $ */ + +/*- + * Copyright (c) 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Matt Thomas . + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _PROP_RB_IMPL_H_ +#define _PROP_RB_IMPL_H_ + +#include +#include "queue.h" + +struct rb_node { + struct rb_node *rb_nodes[3]; +#define RB_NODE_LEFT 0 +#define RB_NODE_RIGHT 1 +#define RB_NODE_OTHER 1 +#define RB_NODE_PARENT 2 +#define rb_left rb_nodes[RB_NODE_LEFT] +#define rb_right rb_nodes[RB_NODE_RIGHT] +#define rb_parent rb_nodes[RB_NODE_PARENT] + union { + struct { +#if BYTE_ORDER == LITTLE_ENDIAN + unsigned int : 28; + unsigned int s_root : 1; + unsigned int s_position : 1; + unsigned int s_color : 1; + unsigned int s_sentinel : 1; +#endif +#if BYTE_ORDER == BIG_ENDIAN + unsigned int s_sentinel : 1; + unsigned int s_color : 1; + unsigned int s_position : 1; + unsigned int s_root : 1; + unsigned int : 28; +#endif + } u_s; + unsigned int u_i; + } rb_u; +#define rb_root rb_u.u_s.s_root +#define rb_position rb_u.u_s.s_position +#define rb_color rb_u.u_s.s_color +#define rb_sentinel rb_u.u_s.s_sentinel +#define rb_properties rb_u.u_i +#define RB_SENTINEL_P(rb) ((rb)->rb_sentinel + 0) +#define RB_LEFT_SENTINEL_P(rb) ((rb)->rb_left->rb_sentinel + 0) +#define RB_RIGHT_SENTINEL_P(rb) ((rb)->rb_right->rb_sentinel + 0) +#define RB_PARENT_SENTINEL_P(rb) ((rb)->rb_parent->rb_sentinel + 0) +#define RB_CHILDLESS_P(rb) (RB_LEFT_SENTINEL_P(rb) \ + && RB_RIGHT_SENTINEL_P(rb)) +#define RB_TWOCHILDREN_P(rb) (!RB_LEFT_SENTINEL_P(rb) \ + && !RB_RIGHT_SENTINEL_P(rb)) +#define RB_ROOT_P(rb) ((rb)->rb_root != false) +#define RB_RED_P(rb) ((rb)->rb_color + 0) +#define RB_BLACK_P(rb) (!(rb)->rb_color) +#define RB_MARK_RED(rb) ((void)((rb)->rb_color = 1)) +#define RB_MARK_BLACK(rb) ((void)((rb)->rb_color = 0)) +#define RB_MARK_ROOT(rb) ((void)((rb)->rb_root = 1)) +#ifdef RBDEBUG + TAILQ_ENTRY(rb_node) rb_link; +#endif +}; + +#ifdef RBDEBUG +TAILQ_HEAD(rb_node_qh, rb_node); + +#define RB_TAILQ_REMOVE TAILQ_REMOVE +#define RB_TAILQ_INIT TAILQ_INIT +#define RB_TAILQ_INSERT_HEAD(a, b, c) TAILQ_INSERT_HEAD +#define RB_TAILQ_INSERT_BEFORE(a, b, c) TAILQ_INSERT_BEFORE +#define RB_TAILQ_INSERT_AFTER(a, b, c, d) TAILQ_INSERT_AFTER +#else +#define RB_TAILQ_REMOVE(a, b, c) do { } while (/*CONSTCOND*/0) +#define RB_TAILQ_INIT(a) do { } while (/*CONSTCOND*/0) +#define RB_TAILQ_INSERT_HEAD(a, b, c) do { } while (/*CONSTCOND*/0) +#define RB_TAILQ_INSERT_BEFORE(a, b, c) do { } while (/*CONSTCOND*/0) +#define RB_TAILQ_INSERT_AFTER(a, b, c, d) do { } while (/*CONSTCOND*/0) +#endif + +typedef int (*rb_compare_nodes_fn)(const struct rb_node *, + const struct rb_node *); +typedef int (*rb_compare_key_fn)(const struct rb_node *, const void *); + +struct rb_tree_ops { + rb_compare_nodes_fn rbto_compare_nodes; + rb_compare_key_fn rbto_compare_key; +}; + +struct rb_tree { + struct rb_node *rbt_root; +#ifdef RBDEBUG + struct rb_node_qh rbt_nodes; +#endif + const struct rb_tree_ops *rbt_ops; +#ifdef RBDEBUG + unsigned int rbt_count; +#endif +}; + +void _prop_rb_tree_init(struct rb_tree *, const struct rb_tree_ops *); +bool _prop_rb_tree_insert_node(struct rb_tree *, struct rb_node *); +struct rb_node * + _prop_rb_tree_find(struct rb_tree *, const void *); +void _prop_rb_tree_remove_node(struct rb_tree *, struct rb_node *); +#ifdef RBDEBUG +void _prop_rb_tree_check(const struct rb_tree *, bool); +#endif +struct rb_node * + _prop_rb_tree_iterate(struct rb_tree *, struct rb_node *, unsigned int); + +#endif /* _PROP_RB_IMPL_H_*/ diff --git a/lib/portableproplib/prop_stack.c b/lib/portableproplib/prop_stack.c new file mode 100644 index 00000000..a08118c2 --- /dev/null +++ b/lib/portableproplib/prop_stack.c @@ -0,0 +1,118 @@ +/* $NetBSD: prop_stack.c,v 1.2 2007/08/30 12:23:54 joerg Exp $ */ + +/*- + * Copyright (c) 2007 Joerg Sonnenberger . + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "prop_stack.h" +#include "prop_object_impl.h" + +void +_prop_stack_init(prop_stack_t stack) +{ + stack->used_intern_elems = 0; + SLIST_INIT(&stack->extern_elems); +} + +bool +_prop_stack_push(prop_stack_t stack, prop_object_t obj, void *data1, + void *data2, void *data3) +{ + struct _prop_stack_extern_elem *eelem; + struct _prop_stack_intern_elem *ielem; + + if (stack->used_intern_elems == PROP_STACK_INTERN_ELEMS) { + eelem = _PROP_MALLOC(sizeof(*eelem), M_TEMP); + + if (eelem == NULL) + return false; + + eelem->object = obj; + eelem->object_data[0] = data1; + eelem->object_data[1] = data2; + eelem->object_data[2] = data3; + + SLIST_INSERT_HEAD(&stack->extern_elems, eelem, stack_link); + + return true; + } + + _PROP_ASSERT(stack->used_intern_elems < PROP_STACK_INTERN_ELEMS); + _PROP_ASSERT(SLIST_EMPTY(&stack->extern_elems)); + + ielem = &stack->intern_elems[stack->used_intern_elems]; + ielem->object = obj; + ielem->object_data[0] = data1; + ielem->object_data[1] = data2; + ielem->object_data[2] = data3; + + ++stack->used_intern_elems; + + return true; +} + +bool +_prop_stack_pop(prop_stack_t stack, prop_object_t *obj, void **data1, + void **data2, void **data3) +{ + struct _prop_stack_extern_elem *eelem; + struct _prop_stack_intern_elem *ielem; + + if (stack->used_intern_elems == 0) + return false; + + if ((eelem = SLIST_FIRST(&stack->extern_elems)) != NULL) { + _PROP_ASSERT(stack->used_intern_elems == PROP_STACK_INTERN_ELEMS); + + SLIST_REMOVE_HEAD(&stack->extern_elems, stack_link); + if (obj) + *obj = eelem->object; + if (data1) + *data1 = eelem->object_data[0]; + if (data2) + *data2 = eelem->object_data[1]; + if (data3) + *data3 = eelem->object_data[2]; + _PROP_FREE(eelem, M_TEMP); + return true; + } + + --stack->used_intern_elems; + ielem = &stack->intern_elems[stack->used_intern_elems]; + + if (obj) + *obj = ielem->object; + if (data1) + *data1 = ielem->object_data[0]; + if (data2) + *data2 = ielem->object_data[1]; + if (data3) + *data3 = ielem->object_data[2]; + + return true; +} diff --git a/lib/portableproplib/prop_stack.h b/lib/portableproplib/prop_stack.h new file mode 100644 index 00000000..524adce8 --- /dev/null +++ b/lib/portableproplib/prop_stack.h @@ -0,0 +1,64 @@ +/* $NetBSD: prop_stack.h,v 1.2 2007/08/30 12:23:54 joerg Exp $ */ + +/*- + * Copyright (c) 2007 Joerg Sonnenberger . + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _PROP_STACK_H +#define _PROP_STACK_H + +#include +#include +#include + +struct _prop_stack_intern_elem { + prop_object_t object; + void *object_data[3]; +}; + +struct _prop_stack_extern_elem { + SLIST_ENTRY(_prop_stack_extern_elem) stack_link; + prop_object_t object; + void *object_data[3]; +}; + +#define PROP_STACK_INTERN_ELEMS 16 + +struct _prop_stack { + struct _prop_stack_intern_elem intern_elems[PROP_STACK_INTERN_ELEMS]; + size_t used_intern_elems; + SLIST_HEAD(, _prop_stack_extern_elem) extern_elems; +}; + +typedef struct _prop_stack *prop_stack_t; + +void _prop_stack_init(prop_stack_t); +bool _prop_stack_push(prop_stack_t, prop_object_t, void *, void *, void *); +bool _prop_stack_pop(prop_stack_t, prop_object_t *, void **, void **, void **); + +#endif diff --git a/lib/portableproplib/prop_string.c b/lib/portableproplib/prop_string.c new file mode 100644 index 00000000..4f9ed886 --- /dev/null +++ b/lib/portableproplib/prop_string.c @@ -0,0 +1,471 @@ +/* $NetBSD: prop_string.c,v 1.11 2008/08/03 04:00:12 thorpej Exp $ */ + +/*- + * Copyright (c) 2006 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include "prop_object_impl.h" + +struct _prop_string { + struct _prop_object ps_obj; + union { + char * psu_mutable; + const char * psu_immutable; + } ps_un; +#define ps_mutable ps_un.psu_mutable +#define ps_immutable ps_un.psu_immutable + size_t ps_size; /* not including \0 */ + int ps_flags; +}; + +#define PS_F_NOCOPY 0x01 + +_PROP_POOL_INIT(_prop_string_pool, sizeof(struct _prop_string), "propstng") + +_PROP_MALLOC_DEFINE(M_PROP_STRING, "prop string", + "property string container object") + +static _prop_object_free_rv_t + _prop_string_free(prop_stack_t, prop_object_t *); +static bool _prop_string_externalize( + struct _prop_object_externalize_context *, + void *); +static _prop_object_equals_rv_t + _prop_string_equals(prop_object_t, prop_object_t, + void **, void **, + prop_object_t *, prop_object_t *); + +static const struct _prop_object_type _prop_object_type_string = { + .pot_type = PROP_TYPE_STRING, + .pot_free = _prop_string_free, + .pot_extern = _prop_string_externalize, + .pot_equals = _prop_string_equals, +}; + +#define prop_object_is_string(x) \ + ((x) != NULL && (x)->ps_obj.po_type == &_prop_object_type_string) +#define prop_string_contents(x) ((x)->ps_immutable ? (x)->ps_immutable : "") + +/* ARGSUSED */ +static _prop_object_free_rv_t +_prop_string_free(prop_stack_t stack, prop_object_t *obj) +{ + prop_string_t ps = *obj; + + if ((ps->ps_flags & PS_F_NOCOPY) == 0 && ps->ps_mutable != NULL) + _PROP_FREE(ps->ps_mutable, M_PROP_STRING); + _PROP_POOL_PUT(_prop_string_pool, ps); + + return (_PROP_OBJECT_FREE_DONE); +} + +static bool +_prop_string_externalize(struct _prop_object_externalize_context *ctx, + void *v) +{ + prop_string_t ps = v; + + if (ps->ps_size == 0) + return (_prop_object_externalize_empty_tag(ctx, "string")); + + if (_prop_object_externalize_start_tag(ctx, "string") == false || + _prop_object_externalize_append_encoded_cstring(ctx, + ps->ps_immutable) == false || + _prop_object_externalize_end_tag(ctx, "string") == false) + return (false); + + return (true); +} + +/* ARGSUSED */ +static _prop_object_equals_rv_t +_prop_string_equals(prop_object_t v1, prop_object_t v2, + void **stored_pointer1, void **stored_pointer2, + prop_object_t *next_obj1, prop_object_t *next_obj2) +{ + prop_string_t str1 = v1; + prop_string_t str2 = v2; + + if (str1 == str2) + return (_PROP_OBJECT_EQUALS_TRUE); + if (str1->ps_size != str2->ps_size) + return (_PROP_OBJECT_EQUALS_FALSE); + if (strcmp(prop_string_contents(str1), prop_string_contents(str2))) + return (_PROP_OBJECT_EQUALS_FALSE); + else + return (_PROP_OBJECT_EQUALS_TRUE); +} + +static prop_string_t +_prop_string_alloc(void) +{ + prop_string_t ps; + + ps = _PROP_POOL_GET(_prop_string_pool); + if (ps != NULL) { + _prop_object_init(&ps->ps_obj, &_prop_object_type_string); + + ps->ps_mutable = NULL; + ps->ps_size = 0; + ps->ps_flags = 0; + } + + return (ps); +} + +/* + * prop_string_create -- + * Create an empty mutable string. + */ +prop_string_t +prop_string_create(void) +{ + + return (_prop_string_alloc()); +} + +/* + * prop_string_create_cstring -- + * Create a string that contains a copy of the provided C string. + */ +prop_string_t +prop_string_create_cstring(const char *str) +{ + prop_string_t ps; + char *cp; + size_t len; + + ps = _prop_string_alloc(); + if (ps != NULL) { + len = strlen(str); + cp = _PROP_MALLOC(len + 1, M_PROP_STRING); + if (cp == NULL) { + prop_object_release(ps); + return (NULL); + } + strcpy(cp, str); + ps->ps_mutable = cp; + ps->ps_size = len; + } + return (ps); +} + +/* + * prop_string_create_cstring_nocopy -- + * Create an immutable string that contains a refrence to the + * provided C string. + */ +prop_string_t +prop_string_create_cstring_nocopy(const char *str) +{ + prop_string_t ps; + + ps = _prop_string_alloc(); + if (ps != NULL) { + ps->ps_immutable = str; + ps->ps_size = strlen(str); + ps->ps_flags |= PS_F_NOCOPY; + } + return (ps); +} + +/* + * prop_string_copy -- + * Copy a string. If the original string is immutable, then the + * copy is also immutable and references the same external data. + */ +prop_string_t +prop_string_copy(prop_string_t ops) +{ + prop_string_t ps; + + if (! prop_object_is_string(ops)) + return (NULL); + + ps = _prop_string_alloc(); + if (ps != NULL) { + ps->ps_size = ops->ps_size; + ps->ps_flags = ops->ps_flags; + if (ops->ps_flags & PS_F_NOCOPY) + ps->ps_immutable = ops->ps_immutable; + else { + char *cp = _PROP_MALLOC(ps->ps_size + 1, M_PROP_STRING); + if (cp == NULL) { + prop_object_release(ps); + return (NULL); + } + strcpy(cp, prop_string_contents(ops)); + ps->ps_mutable = cp; + } + } + return (ps); +} + +/* + * prop_string_copy_mutable -- + * Copy a string, always returning a mutable copy. + */ +prop_string_t +prop_string_copy_mutable(prop_string_t ops) +{ + prop_string_t ps; + char *cp; + + if (! prop_object_is_string(ops)) + return (NULL); + + ps = _prop_string_alloc(); + if (ps != NULL) { + ps->ps_size = ops->ps_size; + cp = _PROP_MALLOC(ps->ps_size + 1, M_PROP_STRING); + if (cp == NULL) { + prop_object_release(ps); + return (NULL); + } + strcpy(cp, prop_string_contents(ops)); + ps->ps_mutable = cp; + } + return (ps); +} + +/* + * prop_string_size -- + * Return the size of the string, not including the terminating NUL. + */ +size_t +prop_string_size(prop_string_t ps) +{ + + if (! prop_object_is_string(ps)) + return (0); + + return (ps->ps_size); +} + +/* + * prop_string_mutable -- + * Return true if the string is a mutable string. + */ +bool +prop_string_mutable(prop_string_t ps) +{ + + if (! prop_object_is_string(ps)) + return (false); + + return ((ps->ps_flags & PS_F_NOCOPY) == 0); +} + +/* + * prop_string_cstring -- + * Return a copy of the contents of the string as a C string. + * The string is allocated with the M_TEMP malloc type. + */ +char * +prop_string_cstring(prop_string_t ps) +{ + char *cp; + + if (! prop_object_is_string(ps)) + return (NULL); + + cp = _PROP_MALLOC(ps->ps_size + 1, M_TEMP); + if (cp != NULL) + strcpy(cp, prop_string_contents(ps)); + + return (cp); +} + +/* + * prop_string_cstring_nocopy -- + * Return an immutable reference to the contents of the string + * as a C string. + */ +const char * +prop_string_cstring_nocopy(prop_string_t ps) +{ + + if (! prop_object_is_string(ps)) + return (NULL); + + return (prop_string_contents(ps)); +} + +/* + * prop_string_append -- + * Append the contents of one string to another. Returns true + * upon success. The destination string must be mutable. + */ +bool +prop_string_append(prop_string_t dst, prop_string_t src) +{ + char *ocp, *cp; + size_t len; + + if (! (prop_object_is_string(dst) && + prop_object_is_string(src))) + return (false); + + if (dst->ps_flags & PS_F_NOCOPY) + return (false); + + len = dst->ps_size + src->ps_size; + cp = _PROP_MALLOC(len + 1, M_PROP_STRING); + if (cp == NULL) + return (false); + sprintf(cp, "%s%s", prop_string_contents(dst), + prop_string_contents(src)); + ocp = dst->ps_mutable; + dst->ps_mutable = cp; + dst->ps_size = len; + if (ocp != NULL) + _PROP_FREE(ocp, M_PROP_STRING); + + return (true); +} + +/* + * prop_string_append_cstring -- + * Append a C string to a string. Returns true upon success. + * The destination string must be mutable. + */ +bool +prop_string_append_cstring(prop_string_t dst, const char *src) +{ + char *ocp, *cp; + size_t len; + + if (! prop_object_is_string(dst)) + return (false); + + _PROP_ASSERT(src != NULL); + + if (dst->ps_flags & PS_F_NOCOPY) + return (false); + + len = dst->ps_size + strlen(src); + cp = _PROP_MALLOC(len + 1, M_PROP_STRING); + if (cp == NULL) + return (false); + sprintf(cp, "%s%s", prop_string_contents(dst), src); + ocp = dst->ps_mutable; + dst->ps_mutable = cp; + dst->ps_size = len; + if (ocp != NULL) + _PROP_FREE(ocp, M_PROP_STRING); + + return (true); +} + +/* + * prop_string_equals -- + * Return true if two strings are equivalent. + */ +bool +prop_string_equals(prop_string_t str1, prop_string_t str2) +{ + if (!prop_object_is_string(str1) || !prop_object_is_string(str2)) + return (false); + + return prop_object_equals(str1, str2); +} + +/* + * prop_string_equals_cstring -- + * Return true if the string is equivalent to the specified + * C string. + */ +bool +prop_string_equals_cstring(prop_string_t ps, const char *cp) +{ + + if (! prop_object_is_string(ps)) + return (false); + + return (strcmp(prop_string_contents(ps), cp) == 0); +} + +/* + * _prop_string_internalize -- + * Parse a ... and return the object created from the + * external representation. + */ +/* ARGSUSED */ +bool +_prop_string_internalize(prop_stack_t stack, prop_object_t *obj, + struct _prop_object_internalize_context *ctx) +{ + prop_string_t string; + char *str; + size_t len, alen; + + if (ctx->poic_is_empty_element) { + *obj = prop_string_create(); + return (true); + } + + /* No attributes recognized here. */ + if (ctx->poic_tagattr != NULL) + return (true); + + /* Compute the length of the result. */ + if (_prop_object_internalize_decode_string(ctx, NULL, 0, &len, + NULL) == false) + return (true); + + str = _PROP_MALLOC(len + 1, M_PROP_STRING); + if (str == NULL) + return (true); + + if (_prop_object_internalize_decode_string(ctx, str, len, &alen, + &ctx->poic_cp) == false || + alen != len) { + _PROP_FREE(str, M_PROP_STRING); + return (true); + } + str[len] = '\0'; + + if (_prop_object_internalize_find_tag(ctx, "string", + _PROP_TAG_TYPE_END) == false) { + _PROP_FREE(str, M_PROP_STRING); + return (true); + } + + string = _prop_string_alloc(); + if (string == NULL) { + _PROP_FREE(str, M_PROP_STRING); + return (true); + } + + string->ps_mutable = str; + string->ps_size = len; + *obj = string; + + return (true); +} diff --git a/lib/purge.c b/lib/purge.c index ed7e77c6..db983218 100644 --- a/lib/purge.c +++ b/lib/purge.c @@ -167,7 +167,7 @@ xbps_purge_pkg(const char *pkgname, bool check_state) if (path == NULL) return errno; - dict = prop_dictionary_internalize_from_file(path); + dict = prop_dictionary_internalize_from_zfile(path); if (dict == NULL) { free(path); return errno; diff --git a/lib/register.c b/lib/register.c index ae207ea2..09429477 100644 --- a/lib/register.c +++ b/lib/register.c @@ -71,7 +71,7 @@ xbps_register_pkg(prop_dictionary_t pkgrd, bool automatic) return EINVAL; } - if ((dict = prop_dictionary_internalize_from_file(plist)) != NULL) { + if ((dict = prop_dictionary_internalize_from_zfile(plist)) != NULL) { pkgd = xbps_find_pkg_in_dict_by_name(dict, "packages", pkgname); if (pkgd == NULL) { @@ -136,7 +136,7 @@ xbps_register_pkg(prop_dictionary_t pkgrd, bool automatic) /* * Write plist file to storage. */ - if (!prop_dictionary_externalize_to_file(dict, plist)) + if (!prop_dictionary_externalize_to_zfile(dict, plist)) rv = errno; } else { free(plist); diff --git a/lib/regpkgs_dictionary.c b/lib/regpkgs_dictionary.c index 2837aeeb..6c75797a 100644 --- a/lib/regpkgs_dictionary.c +++ b/lib/regpkgs_dictionary.c @@ -71,7 +71,7 @@ xbps_regpkgs_dictionary_init(void) if (plist == NULL) return NULL; - regpkgs_dict = prop_dictionary_internalize_from_file(plist); + regpkgs_dict = prop_dictionary_internalize_from_zfile(plist); if (regpkgs_dict == NULL) { free(plist); return NULL; diff --git a/lib/remove.c b/lib/remove.c index 1e4f68fb..489faddb 100644 --- a/lib/remove.c +++ b/lib/remove.c @@ -251,7 +251,7 @@ xbps_remove_pkg(const char *pkgname, const char *version, bool update) return errno; } - dict = prop_dictionary_internalize_from_file(path); + dict = prop_dictionary_internalize_from_zfile(path); if (dict == NULL) { free(buf); free(path); diff --git a/lib/repository.c b/lib/repository.c index 0a52de84..92e187f1 100644 --- a/lib/repository.c +++ b/lib/repository.c @@ -53,7 +53,7 @@ xbps_repository_register(const char *uri) return errno; /* First check if we have the repository plist file. */ - dict = prop_dictionary_internalize_from_file(plist); + dict = prop_dictionary_internalize_from_zfile(plist); if (dict == NULL) { /* Looks like not, create it. */ dict = prop_dictionary_create(); @@ -98,7 +98,7 @@ xbps_repository_register(const char *uri) } /* Write dictionary into plist file. */ - if (!prop_dictionary_externalize_to_file(dict, plist)) { + if (!prop_dictionary_externalize_to_zfile(dict, plist)) { if (obj) prop_object_release(obj); rv = errno; @@ -127,7 +127,7 @@ xbps_repository_unregister(const char *uri) if (plist == NULL) return errno; - dict = prop_dictionary_internalize_from_file(plist); + dict = prop_dictionary_internalize_from_zfile(plist); if (dict == NULL) { free(plist); return errno; @@ -142,7 +142,7 @@ xbps_repository_unregister(const char *uri) rv = xbps_remove_string_from_array(array, uri); if (rv == 0) { /* Update plist file. */ - if (!prop_dictionary_externalize_to_file(dict, plist)) + if (!prop_dictionary_externalize_to_zfile(dict, plist)) rv = errno; } diff --git a/lib/repository_pool.c b/lib/repository_pool.c index 0340db39..5ff4d104 100644 --- a/lib/repository_pool.c +++ b/lib/repository_pool.c @@ -66,7 +66,7 @@ xbps_repository_pool_init(void) goto out; } - dict = prop_dictionary_internalize_from_file(plist); + dict = prop_dictionary_internalize_from_zfile(plist); if (dict == NULL) { free(plist); rv = errno; @@ -112,7 +112,7 @@ xbps_repository_pool_init(void) rv = errno; goto out; } - rpool->rp_repod = prop_dictionary_internalize_from_file(plist); + rpool->rp_repod = prop_dictionary_internalize_from_zfile(plist); if (rpool->rp_repod == NULL) { free(rpool->rp_uri); free(rpool); diff --git a/lib/requiredby.c b/lib/requiredby.c index 8ca1816a..52d29c2c 100644 --- a/lib/requiredby.c +++ b/lib/requiredby.c @@ -133,7 +133,7 @@ xbps_requiredby_pkg_remove(const char *pkgname) if (plist == NULL) return EINVAL; - if ((dict = prop_dictionary_internalize_from_file(plist)) == NULL) { + if ((dict = prop_dictionary_internalize_from_zfile(plist)) == NULL) { free(plist); return errno; } @@ -141,7 +141,7 @@ xbps_requiredby_pkg_remove(const char *pkgname) rv = xbps_callback_array_iter_in_dict(dict, "packages", remove_pkg_from_reqby, __UNCONST(pkgname)); if (rv == 0) { - if (!prop_dictionary_externalize_to_file(dict, plist)) + if (!prop_dictionary_externalize_to_zfile(dict, plist)) rv = errno; } diff --git a/lib/state.c b/lib/state.c index 66551297..b1a15b95 100644 --- a/lib/state.c +++ b/lib/state.c @@ -153,7 +153,7 @@ xbps_set_pkg_state_installed(const char *pkgname, pkg_state_t state) if (plist == NULL) return EINVAL; - if ((dict = prop_dictionary_internalize_from_file(plist)) == NULL) { + if ((dict = prop_dictionary_internalize_from_zfile(plist)) == NULL) { dict = prop_dictionary_create(); if (dict == NULL) { rv = errno; @@ -230,7 +230,7 @@ xbps_set_pkg_state_installed(const char *pkgname, pkg_state_t state) } } - if (!prop_dictionary_externalize_to_file(dict, plist)) + if (!prop_dictionary_externalize_to_zfile(dict, plist)) rv = errno; out: diff --git a/lib/unpack.c b/lib/unpack.c index ce0b824c..631a18b9 100644 --- a/lib/unpack.c +++ b/lib/unpack.c @@ -320,7 +320,7 @@ unpack_archive_fini(struct archive *ar, prop_dictionary_t pkg) */ if (!preserve && (access(buf, R_OK) == 0)) { old_filesd = - prop_dictionary_internalize_from_file(buf); + prop_dictionary_internalize_from_zfile(buf); if (old_filesd == NULL) { prop_object_release(filesd); free(buf); @@ -340,7 +340,7 @@ unpack_archive_fini(struct archive *ar, prop_dictionary_t pkg) * can safely externalize files.plist because the path * is reachable. */ - if (!prop_dictionary_externalize_to_file(filesd, buf)) { + if (!prop_dictionary_externalize_to_zfile(filesd, buf)) { prop_object_release(filesd); free(buf); return errno; diff --git a/vars.mk b/vars.mk index 634c084e..6730f7a6 100644 --- a/vars.mk +++ b/vars.mk @@ -12,23 +12,32 @@ INSTALL_STRIPPED ?= -s # Uncomment this to enable. #BUILD_API_DOCS = 1 +# Uncomment this to build and link from an external proplib. +#USE_EXTERNAL_PROPLIB = 1 + LDFLAGS = -L$(TOPDIR)/lib CPPFLAGS = -I$(TOPDIR)/include -D_XOPEN_SOURCE=600 -D_GNU_SOURCE CPPFLAGS += -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGE_FILES +ifndef USE_EXTERNAL_PROPLIB +CPPFLAGS += -I$(TOPDIR)/lib/portableproplib +else +STATIC_PROPLIB = -lprop +endif + ifdef DEBUG INSTALL_STRIPPED = DEBUG_FLAGS = -g CPPFLAGS += -DDEBUG endif -WARNFLAGS = -pedantic -std=c99 -Wall -Wextra -Werror -Wshadow -Wformat=2 +WARNFLAGS = -std=c99 -Wall -Wextra -Werror -Wshadow -Wformat=2 WARNFLAGS += -Wmissing-declarations -Wcomment -Wunused-macros -Wendif-labels WARNFLAGS += -Wcast-qual -Wcast-align -Wstack-protector CFLAGS = $(DEBUG_FLAGS) $(WARNFLAGS) -fPIC -DPIC -fstack-protector-all SHAREDLIB_CFLAGS = -fvisibility=default # Grr, hate the static libs! -STATIC_LIBS = -lprop -lpthread +STATIC_LIBS = -lz $(STATIC_PROPLIB) -lpthread STATIC_LIBS += `pkg-config openssl --libs --static` STATIC_LIBS += `pkg-config libarchive --libs --static`