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`