From 044ded722887357e4c9fff845e87c6db7e08f736 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Tue, 30 Jun 2020 11:42:25 +0300 Subject: Upgrade to 1.7.3 --- .gitattributes | 2 +- libpkgconf/README-DEV | 15 +- libpkgconf/build/bootstrap.build | 7 +- libpkgconf/dont-merge-fragments.patch | 26 - libpkgconf/fix-demunging-windows-paths.patch | 13 + libpkgconf/libpkgconf/fragment.c | 698 +---------- libpkgconf/libpkgconf/fragment.c.orig | 1 - libpkgconf/libpkgconf/libpkgconf.h | 389 +----- libpkgconf/libpkgconf/libpkgconf.h.orig | 1 - libpkgconf/libpkgconf/pkg.c | 1677 +++++++++++++++++++++++++- libpkgconf/libpkgconf/pkg.c.orig | 1 + libpkgconf/manifest | 2 +- upstream | 2 +- 13 files changed, 1705 insertions(+), 1129 deletions(-) delete mode 100644 libpkgconf/dont-merge-fragments.patch create mode 100644 libpkgconf/fix-demunging-windows-paths.patch mode change 100644 => 120000 libpkgconf/libpkgconf/fragment.c delete mode 120000 libpkgconf/libpkgconf/fragment.c.orig mode change 100644 => 120000 libpkgconf/libpkgconf/libpkgconf.h delete mode 120000 libpkgconf/libpkgconf/libpkgconf.h.orig mode change 120000 => 100644 libpkgconf/libpkgconf/pkg.c create mode 120000 libpkgconf/libpkgconf/pkg.c.orig diff --git a/.gitattributes b/.gitattributes index 9fce1b0..1631641 100644 --- a/.gitattributes +++ b/.gitattributes @@ -10,7 +10,7 @@ #*.bat text eol=crlf # Use `eol=lf` for files that should have the LF line ending both in the -# working tree (even on Windows) in the repository. +# working tree (even on Windows) and in the repository. # #*.sh text eol=lf diff --git a/libpkgconf/README-DEV b/libpkgconf/README-DEV index 37a1317..3d7618e 100644 --- a/libpkgconf/README-DEV +++ b/libpkgconf/README-DEV @@ -30,17 +30,14 @@ $ cat libpkgconf/config.h.in | $ diff defined-macros used-macros -We also extend the upstream package functionality allowing to prevent merging -of "special" fragments, such as `-framework `, into a single fragment. +We also fix the bug in demunging windows paths (issue #17 is reported): -$ mv libpkgconf.h libpkgconf.h.orig -$ cp libpkgconf.h.orig libpkgconf.h +$ mv pkg.c pkg.c.orig +$ cp pkg.c.orig pkg.c -$ mv fragment.c fragment.c.orig -$ cp fragment.c.orig fragment.c - -$ git apply dont-merge-fragments.patch +$ cd .. +$ git apply fix-demunging-windows-paths.patch Note that the patch is produces by the following command: -$ git diff >dont-merge-fragments.patch +$ git diff >fix-demunging-windows-paths.patch diff --git a/libpkgconf/build/bootstrap.build b/libpkgconf/build/bootstrap.build index 6599b62..aedf2a6 100644 --- a/libpkgconf/build/bootstrap.build +++ b/libpkgconf/build/bootstrap.build @@ -15,15 +15,16 @@ using dist # 2 to 3 (libpkgconf.so.2.0.0 -> libpkgconf.so.3.0.0). This probably means # that the first two release version components constitute a major version, # and the release number increments each time this version changes. So we just -# need to watch their Makefile.am for any changes. +# need to watch their Makefile.am for any changes of the libpkgconf_la_LDFLAGS +# variable value. # # See also: http://kaniini.dereferenced.org/2015/07/20/pkgconf-0-9-12-and-future.html # # Note that the upstream project didn't increment the release number (3) for # the 1.5 library version despite the ABI-breaking changes (issue #15 is -# reported). +# reported), so we increment it ourselves. # -if ($version.major == 1 && $version.minor == 6) +if ($version.major == 1 && $version.minor == 7) release_num = 4 else fail "increment the release number?" diff --git a/libpkgconf/dont-merge-fragments.patch b/libpkgconf/dont-merge-fragments.patch deleted file mode 100644 index d8e5eae..0000000 --- a/libpkgconf/dont-merge-fragments.patch +++ /dev/null @@ -1,26 +0,0 @@ -diff --git a/libpkgconf/libpkgconf/fragment.c b/libpkgconf/libpkgconf/fragment.c -index b431694..37830c8 100644 ---- a/libpkgconf/libpkgconf/fragment.c -+++ b/libpkgconf/libpkgconf/fragment.c -@@ -150,7 +150,8 @@ pkgconf_fragment_add(const pkgconf_client_t *client, pkgconf_list_t *list, const - { - char mungebuf[PKGCONF_ITEM_SIZE]; - -- if (list->tail != NULL && list->tail->data != NULL) -+ if (list->tail != NULL && list->tail->data != NULL && -+ !(client->flags & PKGCONF_PKG_PKGF_DONT_MERGE_SPECIAL_FRAGMENTS)) - { - pkgconf_fragment_t *parent = list->tail->data; - -diff --git a/libpkgconf/libpkgconf/libpkgconf.h b/libpkgconf/libpkgconf/libpkgconf.h -index 44a9e7f..6f4c8b5 100644 ---- a/libpkgconf/libpkgconf/libpkgconf.h -+++ b/libpkgconf/libpkgconf/libpkgconf.h -@@ -247,6 +247,7 @@ PKGCONF_API pkgconf_cross_personality_t *pkgconf_cross_personality_find(const ch - #define PKGCONF_PKG_PKGF_DONT_RELOCATE_PATHS 0x0800 - #define PKGCONF_PKG_PKGF_SIMPLIFY_ERRORS 0x1000 - #define PKGCONF_PKG_PKGF_DONT_FILTER_INTERNAL_CFLAGS 0x2000 -+#define PKGCONF_PKG_PKGF_DONT_MERGE_SPECIAL_FRAGMENTS 0x4000 - - #define PKGCONF_PKG_DEPF_INTERNAL 0x1 - diff --git a/libpkgconf/fix-demunging-windows-paths.patch b/libpkgconf/fix-demunging-windows-paths.patch new file mode 100644 index 0000000..c9f11f6 --- /dev/null +++ b/libpkgconf/fix-demunging-windows-paths.patch @@ -0,0 +1,13 @@ +diff --git a/libpkgconf/libpkgconf/pkg.c b/libpkgconf/libpkgconf/pkg.c +index 214f544..ba4fb5f 100644 +--- a/libpkgconf/libpkgconf/pkg.c ++++ b/libpkgconf/libpkgconf/pkg.c +@@ -391,7 +391,7 @@ pkgconf_pkg_new_from_file(pkgconf_client_t *client, const char *filename, FILE * + */ + char *mungeptr; + if ((mungeptr = strrchr(idptr, '/')) != NULL) +- idptr = mungeptr++; ++ idptr = ++mungeptr; + #endif + + pkg->id = strdup(idptr); diff --git a/libpkgconf/libpkgconf/fragment.c b/libpkgconf/libpkgconf/fragment.c deleted file mode 100644 index 37830c8..0000000 --- a/libpkgconf/libpkgconf/fragment.c +++ /dev/null @@ -1,697 +0,0 @@ -/* - * fragment.c - * Management of fragment lists. - * - * Copyright (c) 2012, 2013, 2014 pkgconf authors (see AUTHORS). - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * This software is provided 'as is' and without any warranty, express or - * implied. In no event shall the authors be liable for any damages arising - * from the use of this software. - */ - -#include -#include - -/* - * !doc - * - * libpkgconf `fragment` module - * ============================ - * - * The `fragment` module provides low-level management and rendering of fragment lists. A - * `fragment list` contains various `fragments` of text (such as ``-I /usr/include``) in a matter - * which is composable, mergeable and reorderable. - */ - -struct pkgconf_fragment_check { - char *token; - size_t len; -}; - -static inline bool -pkgconf_fragment_is_unmergeable(const char *string) -{ - static const struct pkgconf_fragment_check check_fragments[] = { - {"-framework", 10}, - {"-isystem", 8}, - {"-idirafter", 10}, - {"-pthread", 8}, - {"-Wa,", 4}, - {"-Wl,", 4}, - {"-Wp,", 4}, - {"-trigraphs", 10}, - {"-pedantic", 9}, - {"-ansi", 5}, - {"-std=", 5}, - {"-stdlib=", 8}, - {"-include", 8}, - {"-nostdinc", 9}, - {"-nostdlibinc", 12}, - {"-nobuiltininc", 13}, - }; - - if (*string != '-') - return true; - - for (size_t i = 0; i < PKGCONF_ARRAY_SIZE(check_fragments); i++) - if (!strncmp(string, check_fragments[i].token, check_fragments[i].len)) - return true; - - /* only one pair of {-flag, arg} may be merged together */ - if (strchr(string, ' ') != NULL) - return false; - - return false; -} - -static inline bool -pkgconf_fragment_should_munge(const char *string, const char *sysroot_dir) -{ - if (*string != '/') - return false; - - if (sysroot_dir != NULL && strncmp(sysroot_dir, string, strlen(sysroot_dir))) - return true; - - return false; -} - -static inline bool -pkgconf_fragment_is_special(const char *string) -{ - if (*string != '-') - return true; - - if (!strncmp(string, "-lib:", 5)) - return true; - - return pkgconf_fragment_is_unmergeable(string); -} - -static inline void -pkgconf_fragment_munge(const pkgconf_client_t *client, char *buf, size_t buflen, const char *source, const char *sysroot_dir) -{ - *buf = '\0'; - - if (sysroot_dir == NULL) - sysroot_dir = pkgconf_tuple_find_global(client, "pc_sysrootdir"); - - if (sysroot_dir != NULL && pkgconf_fragment_should_munge(source, sysroot_dir)) - pkgconf_strlcat(buf, sysroot_dir, buflen); - - pkgconf_strlcat(buf, source, buflen); - - if (*buf == '/' && !(client->flags & PKGCONF_PKG_PKGF_DONT_RELOCATE_PATHS)) - pkgconf_path_relocate(buf, buflen); -} - -static inline char * -pkgconf_fragment_copy_munged(const pkgconf_client_t *client, const char *source) -{ - char mungebuf[PKGCONF_ITEM_SIZE]; - pkgconf_fragment_munge(client, mungebuf, sizeof mungebuf, source, client->sysroot_dir); - return strdup(mungebuf); -} - -/* - * !doc - * - * .. c:function:: void pkgconf_fragment_add(const pkgconf_client_t *client, pkgconf_list_t *list, const char *string) - * - * Adds a `fragment` of text to a `fragment list`, possibly modifying the fragment if a sysroot is set. - * - * :param pkgconf_client_t* client: The pkgconf client being accessed. - * :param pkgconf_list_t* list: The fragment list. - * :param char* string: The string of text to add as a fragment to the fragment list. - * :return: nothing - */ -void -pkgconf_fragment_add(const pkgconf_client_t *client, pkgconf_list_t *list, const char *string) -{ - pkgconf_fragment_t *frag; - - if (*string == '\0') - return; - - if (!pkgconf_fragment_is_special(string)) - { - frag = calloc(sizeof(pkgconf_fragment_t), 1); - - frag->type = *(string + 1); - frag->data = pkgconf_fragment_copy_munged(client, string + 2); - - PKGCONF_TRACE(client, "added fragment {%c, '%s'} to list @%p", frag->type, frag->data, list); - } - else - { - char mungebuf[PKGCONF_ITEM_SIZE]; - - if (list->tail != NULL && list->tail->data != NULL && - !(client->flags & PKGCONF_PKG_PKGF_DONT_MERGE_SPECIAL_FRAGMENTS)) - { - pkgconf_fragment_t *parent = list->tail->data; - - /* only attempt to merge 'special' fragments together */ - if (!parent->type && pkgconf_fragment_is_unmergeable(parent->data)) - { - size_t len; - char *newdata; - - pkgconf_fragment_munge(client, mungebuf, sizeof mungebuf, string, NULL); - - len = strlen(parent->data) + strlen(mungebuf) + 2; - newdata = malloc(len); - - pkgconf_strlcpy(newdata, parent->data, len); - pkgconf_strlcat(newdata, " ", len); - pkgconf_strlcat(newdata, mungebuf, len); - - PKGCONF_TRACE(client, "merging '%s' to '%s' to form fragment {'%s'} in list @%p", mungebuf, parent->data, newdata, list); - - free(parent->data); - parent->data = newdata; - parent->merged = true; - - /* use a copy operation to force a dedup */ - pkgconf_node_delete(&parent->iter, list); - pkgconf_fragment_copy(client, list, parent, false); - - /* the fragment list now (maybe) has the copied node, so free the original */ - free(parent->data); - free(parent); - - return; - } - } - - frag = calloc(sizeof(pkgconf_fragment_t), 1); - - frag->type = 0; - frag->data = strdup(string); - - PKGCONF_TRACE(client, "created special fragment {'%s'} in list @%p", frag->data, list); - } - - pkgconf_node_insert_tail(&frag->iter, frag, list); -} - -static inline pkgconf_fragment_t * -pkgconf_fragment_lookup(pkgconf_list_t *list, const pkgconf_fragment_t *base) -{ - pkgconf_node_t *node; - - PKGCONF_FOREACH_LIST_ENTRY_REVERSE(list->tail, node) - { - pkgconf_fragment_t *frag = node->data; - - if (base->type != frag->type) - continue; - - if (!strcmp(base->data, frag->data)) - return frag; - } - - return NULL; -} - -static inline bool -pkgconf_fragment_can_merge_back(const pkgconf_fragment_t *base, unsigned int flags, bool is_private) -{ - (void) flags; - - if (base->type == 'l') - { - if (is_private) - return false; - - return true; - } - - if (base->type == 'F') - return false; - if (base->type == 'L') - return false; - if (base->type == 'I') - return false; - - return true; -} - -static inline bool -pkgconf_fragment_can_merge(const pkgconf_fragment_t *base, unsigned int flags, bool is_private) -{ - (void) flags; - - if (is_private) - return false; - - return pkgconf_fragment_is_unmergeable(base->data); -} - -static inline pkgconf_fragment_t * -pkgconf_fragment_exists(pkgconf_list_t *list, const pkgconf_fragment_t *base, unsigned int flags, bool is_private) -{ - if (!pkgconf_fragment_can_merge_back(base, flags, is_private)) - return NULL; - - if (!pkgconf_fragment_can_merge(base, flags, is_private)) - return NULL; - - return pkgconf_fragment_lookup(list, base); -} - -static inline bool -pkgconf_fragment_should_merge(const pkgconf_fragment_t *base) -{ - const pkgconf_fragment_t *parent; - - /* if we are the first fragment, that means the next fragment is the same, so it's always safe. */ - if (base->iter.prev == NULL) - return true; - - /* this really shouldn't ever happen, but handle it */ - parent = base->iter.prev->data; - if (parent == NULL) - return true; - - switch (parent->type) - { - case 'l': - case 'L': - case 'I': - return true; - default: - return !base->type || parent->type == base->type; - } -} - -/* - * !doc - * - * .. c:function:: bool pkgconf_fragment_has_system_dir(const pkgconf_client_t *client, const pkgconf_fragment_t *frag) - * - * Checks if a `fragment` contains a `system path`. System paths are detected at compile time and optionally overridden by - * the ``PKG_CONFIG_SYSTEM_INCLUDE_PATH`` and ``PKG_CONFIG_SYSTEM_LIBRARY_PATH`` environment variables. - * - * :param pkgconf_client_t* client: The pkgconf client object the fragment belongs to. - * :param pkgconf_fragment_t* frag: The fragment being checked. - * :return: true if the fragment contains a system path, else false - * :rtype: bool - */ -bool -pkgconf_fragment_has_system_dir(const pkgconf_client_t *client, const pkgconf_fragment_t *frag) -{ - const pkgconf_list_t *check_paths = NULL; - - switch (frag->type) - { - case 'L': - check_paths = &client->filter_libdirs; - break; - case 'I': - check_paths = &client->filter_includedirs; - break; - default: - return false; - } - - return pkgconf_path_match_list(frag->data, check_paths); -} - -/* - * !doc - * - * .. c:function:: void pkgconf_fragment_copy(const pkgconf_client_t *client, pkgconf_list_t *list, const pkgconf_fragment_t *base, bool is_private) - * - * Copies a `fragment` to another `fragment list`, possibly removing a previous copy of the `fragment` - * in a process known as `mergeback`. - * - * :param pkgconf_client_t* client: The pkgconf client being accessed. - * :param pkgconf_list_t* list: The list the fragment is being added to. - * :param pkgconf_fragment_t* base: The fragment being copied. - * :param bool is_private: Whether the fragment list is a `private` fragment list (static linking). - * :return: nothing - */ -void -pkgconf_fragment_copy(const pkgconf_client_t *client, pkgconf_list_t *list, const pkgconf_fragment_t *base, bool is_private) -{ - pkgconf_fragment_t *frag; - - if ((frag = pkgconf_fragment_exists(list, base, client->flags, is_private)) != NULL) - { - if (pkgconf_fragment_should_merge(frag)) - pkgconf_fragment_delete(list, frag); - } - else if (!is_private && !pkgconf_fragment_can_merge_back(base, client->flags, is_private) && (pkgconf_fragment_lookup(list, base) != NULL)) - return; - - frag = calloc(sizeof(pkgconf_fragment_t), 1); - - frag->type = base->type; - frag->merged = base->merged; - if (base->data != NULL) - frag->data = strdup(base->data); - - pkgconf_node_insert_tail(&frag->iter, frag, list); -} - -/* - * !doc - * - * .. c:function:: void pkgconf_fragment_copy_list(const pkgconf_client_t *client, pkgconf_list_t *list, const pkgconf_list_t *base) - * - * Copies a `fragment list` to another `fragment list`, possibly removing a previous copy of the fragments - * in a process known as `mergeback`. - * - * :param pkgconf_client_t* client: The pkgconf client being accessed. - * :param pkgconf_list_t* list: The list the fragments are being added to. - * :param pkgconf_list_t* base: The list the fragments are being copied from. - * :return: nothing - */ -void -pkgconf_fragment_copy_list(const pkgconf_client_t *client, pkgconf_list_t *list, const pkgconf_list_t *base) -{ - pkgconf_node_t *node; - - PKGCONF_FOREACH_LIST_ENTRY(base->head, node) - { - pkgconf_fragment_t *frag = node->data; - - pkgconf_fragment_copy(client, list, frag, true); - } -} - -/* - * !doc - * - * .. c:function:: void pkgconf_fragment_filter(const pkgconf_client_t *client, pkgconf_list_t *dest, pkgconf_list_t *src, pkgconf_fragment_filter_func_t filter_func) - * - * Copies a `fragment list` to another `fragment list` which match a user-specified filtering function. - * - * :param pkgconf_client_t* client: The pkgconf client being accessed. - * :param pkgconf_list_t* dest: The destination list. - * :param pkgconf_list_t* src: The source list. - * :param pkgconf_fragment_filter_func_t filter_func: The filter function to use. - * :param void* data: Optional data to pass to the filter function. - * :return: nothing - */ -void -pkgconf_fragment_filter(const pkgconf_client_t *client, pkgconf_list_t *dest, pkgconf_list_t *src, pkgconf_fragment_filter_func_t filter_func, void *data) -{ - pkgconf_node_t *node; - - PKGCONF_FOREACH_LIST_ENTRY(src->head, node) - { - pkgconf_fragment_t *frag = node->data; - - if (filter_func(client, frag, data)) - pkgconf_fragment_copy(client, dest, frag, true); - } -} - -static inline char * -fragment_quote(const pkgconf_fragment_t *frag) -{ - const char *src = frag->data; - ssize_t outlen = strlen(src) + 10; - char *out, *dst; - - if (frag->data == NULL) - return NULL; - - out = dst = calloc(outlen, 1); - - for (; *src; src++) - { - if (((*src < ' ') || - (*src >= (' ' + (frag->merged ? 1 : 0)) && *src < '$') || - (*src > '$' && *src < '(') || - (*src > ')' && *src < '+') || - (*src > ':' && *src < '=') || - (*src > '=' && *src < '@') || - (*src > 'Z' && *src < '^') || - (*src == '`') || - (*src > 'z' && *src < '~') || - (*src > '~'))) - *dst++ = '\\'; - - *dst++ = *src; - - if ((ptrdiff_t)(dst - out) + 2 > outlen) - { - outlen *= 2; - out = realloc(out, outlen); - } - } - - *dst = 0; - return out; -} - -static inline size_t -pkgconf_fragment_len(const pkgconf_fragment_t *frag) -{ - size_t len = 1; - - if (frag->type) - len += 2; - - if (frag->data != NULL) - { - char *quoted = fragment_quote(frag); - len += strlen(quoted); - free(quoted); - } - - return len; -} - -static size_t -fragment_render_len(const pkgconf_list_t *list, bool escape) -{ - (void) escape; - - size_t out = 1; /* trailing nul */ - pkgconf_node_t *node; - - PKGCONF_FOREACH_LIST_ENTRY(list->head, node) - { - const pkgconf_fragment_t *frag = node->data; - out += pkgconf_fragment_len(frag); - } - - return out; -} - -static void -fragment_render_buf(const pkgconf_list_t *list, char *buf, size_t buflen, bool escape) -{ - (void) escape; - - pkgconf_node_t *node; - char *bptr = buf; - - memset(buf, 0, buflen); - - PKGCONF_FOREACH_LIST_ENTRY(list->head, node) - { - const pkgconf_fragment_t *frag = node->data; - size_t buf_remaining = buflen - (bptr - buf); - char *quoted = fragment_quote(frag); - - if (strlen(quoted) > buf_remaining) - { - free(quoted); - break; - } - - if (frag->type) - { - *bptr++ = '-'; - *bptr++ = frag->type; - } - - if (quoted != NULL) - { - bptr += pkgconf_strlcpy(bptr, quoted, buf_remaining); - free(quoted); - } - - *bptr++ = ' '; - } - - *bptr = '\0'; -} - -static const pkgconf_fragment_render_ops_t default_render_ops = { - .render_len = fragment_render_len, - .render_buf = fragment_render_buf -}; - -/* - * !doc - * - * .. c:function:: size_t pkgconf_fragment_render_len(const pkgconf_list_t *list, bool escape, const pkgconf_fragment_render_ops_t *ops) - * - * Calculates the required memory to store a `fragment list` when rendered as a string. - * - * :param pkgconf_list_t* list: The `fragment list` being rendered. - * :param bool escape: Whether or not to escape special shell characters (deprecated). - * :param pkgconf_fragment_render_ops_t* ops: An optional ops structure to use for custom renderers, else ``NULL``. - * :return: the amount of bytes required to represent the `fragment list` when rendered - * :rtype: size_t - */ -size_t -pkgconf_fragment_render_len(const pkgconf_list_t *list, bool escape, const pkgconf_fragment_render_ops_t *ops) -{ - (void) escape; - - ops = ops != NULL ? ops : &default_render_ops; - return ops->render_len(list, true); -} - -/* - * !doc - * - * .. c:function:: void pkgconf_fragment_render_buf(const pkgconf_list_t *list, char *buf, size_t buflen, bool escape, const pkgconf_fragment_render_ops_t *ops) - * - * Renders a `fragment list` into a buffer. - * - * :param pkgconf_list_t* list: The `fragment list` being rendered. - * :param char* buf: The buffer to render the fragment list into. - * :param size_t buflen: The length of the buffer. - * :param bool escape: Whether or not to escape special shell characters (deprecated). - * :param pkgconf_fragment_render_ops_t* ops: An optional ops structure to use for custom renderers, else ``NULL``. - * :return: nothing - */ -void -pkgconf_fragment_render_buf(const pkgconf_list_t *list, char *buf, size_t buflen, bool escape, const pkgconf_fragment_render_ops_t *ops) -{ - (void) escape; - - ops = ops != NULL ? ops : &default_render_ops; - ops->render_buf(list, buf, buflen, true); -} - -/* - * !doc - * - * .. c:function:: char *pkgconf_fragment_render(const pkgconf_list_t *list) - * - * Allocate memory and render a `fragment list` into it. - * - * :param pkgconf_list_t* list: The `fragment list` being rendered. - * :param bool escape: Whether or not to escape special shell characters (deprecated). - * :param pkgconf_fragment_render_ops_t* ops: An optional ops structure to use for custom renderers, else ``NULL``. - * :return: An allocated string containing the rendered `fragment list`. - * :rtype: char * - */ -char * -pkgconf_fragment_render(const pkgconf_list_t *list, bool escape, const pkgconf_fragment_render_ops_t *ops) -{ - (void) escape; - - size_t buflen = pkgconf_fragment_render_len(list, true, ops); - char *buf = calloc(1, buflen); - - pkgconf_fragment_render_buf(list, buf, buflen, true, ops); - - return buf; -} - -/* - * !doc - * - * .. c:function:: void pkgconf_fragment_delete(pkgconf_list_t *list, pkgconf_fragment_t *node) - * - * Delete a `fragment node` from a `fragment list`. - * - * :param pkgconf_list_t* list: The `fragment list` to delete from. - * :param pkgconf_fragment_t* node: The `fragment node` to delete. - * :return: nothing - */ -void -pkgconf_fragment_delete(pkgconf_list_t *list, pkgconf_fragment_t *node) -{ - pkgconf_node_delete(&node->iter, list); - - free(node->data); - free(node); -} - -/* - * !doc - * - * .. c:function:: void pkgconf_fragment_free(pkgconf_list_t *list) - * - * Delete an entire `fragment list`. - * - * :param pkgconf_list_t* list: The `fragment list` to delete. - * :return: nothing - */ -void -pkgconf_fragment_free(pkgconf_list_t *list) -{ - pkgconf_node_t *node, *next; - - PKGCONF_FOREACH_LIST_ENTRY_SAFE(list->head, next, node) - { - pkgconf_fragment_t *frag = node->data; - - free(frag->data); - free(frag); - } -} - -/* - * !doc - * - * .. c:function:: bool pkgconf_fragment_parse(const pkgconf_client_t *client, pkgconf_list_t *list, pkgconf_list_t *vars, const char *value) - * - * Parse a string into a `fragment list`. - * - * :param pkgconf_client_t* client: The pkgconf client being accessed. - * :param pkgconf_list_t* list: The `fragment list` to add the fragment entries to. - * :param pkgconf_list_t* vars: A list of variables to use for variable substitution. - * :param char* value: The string to parse into fragments. - * :return: true on success, false on parse error - */ -bool -pkgconf_fragment_parse(const pkgconf_client_t *client, pkgconf_list_t *list, pkgconf_list_t *vars, const char *value) -{ - int i, ret, argc; - char **argv; - char *repstr = pkgconf_tuple_parse(client, vars, value); - - PKGCONF_TRACE(client, "post-subst: [%s] -> [%s]", value, repstr); - - ret = pkgconf_argv_split(repstr, &argc, &argv); - if (ret < 0) - { - PKGCONF_TRACE(client, "unable to parse fragment string [%s]", repstr); - free(repstr); - return false; - } - - for (i = 0; i < argc; i++) - { - if (argv[i] == NULL) - { - PKGCONF_TRACE(client, "parsed fragment string is inconsistent: argc = %d while argv[%d] == NULL", argc, i); - pkgconf_argv_free(argv); - free(repstr); - return false; - } - - pkgconf_fragment_add(client, list, argv[i]); - } - - pkgconf_argv_free(argv); - free(repstr); - - return true; -} diff --git a/libpkgconf/libpkgconf/fragment.c b/libpkgconf/libpkgconf/fragment.c new file mode 120000 index 0000000..ccec352 --- /dev/null +++ b/libpkgconf/libpkgconf/fragment.c @@ -0,0 +1 @@ +../../upstream/libpkgconf/fragment.c \ No newline at end of file diff --git a/libpkgconf/libpkgconf/fragment.c.orig b/libpkgconf/libpkgconf/fragment.c.orig deleted file mode 120000 index ccec352..0000000 --- a/libpkgconf/libpkgconf/fragment.c.orig +++ /dev/null @@ -1 +0,0 @@ -../../upstream/libpkgconf/fragment.c \ No newline at end of file diff --git a/libpkgconf/libpkgconf/libpkgconf.h b/libpkgconf/libpkgconf/libpkgconf.h deleted file mode 100644 index 6f4c8b5..0000000 --- a/libpkgconf/libpkgconf/libpkgconf.h +++ /dev/null @@ -1,388 +0,0 @@ -/* - * libpkgconf.h - * Global include file for everything in libpkgconf. - * - * Copyright (c) 2011, 2015 pkgconf authors (see AUTHORS). - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * This software is provided 'as is' and without any warranty, express or - * implied. In no event shall the authors be liable for any damages arising - * from the use of this software. - */ - -#ifndef LIBPKGCONF__LIBPKGCONF_H -#define LIBPKGCONF__LIBPKGCONF_H - -#include -#include -#include -#include -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/* pkg-config uses ';' on win32 as ':' is part of path */ -#ifdef _WIN32 -#define PKG_CONFIG_PATH_SEP_S ";" -#else -#define PKG_CONFIG_PATH_SEP_S ":" -#endif - -#ifdef _WIN32 -#define PKG_DIR_SEP_S '\\' -#else -#define PKG_DIR_SEP_S '/' -#endif - -#ifdef _WIN32 -#define realpath(N,R) _fullpath((R),(N),_MAX_PATH) -#endif - -#define PKGCONF_BUFSIZE (65535) - -typedef enum { - PKGCONF_CMP_NOT_EQUAL, - PKGCONF_CMP_ANY, - PKGCONF_CMP_LESS_THAN, - PKGCONF_CMP_LESS_THAN_EQUAL, - PKGCONF_CMP_EQUAL, - PKGCONF_CMP_GREATER_THAN, - PKGCONF_CMP_GREATER_THAN_EQUAL -} pkgconf_pkg_comparator_t; - -#define PKGCONF_CMP_COUNT 7 - -typedef struct pkgconf_pkg_ pkgconf_pkg_t; -typedef struct pkgconf_dependency_ pkgconf_dependency_t; -typedef struct pkgconf_tuple_ pkgconf_tuple_t; -typedef struct pkgconf_fragment_ pkgconf_fragment_t; -typedef struct pkgconf_path_ pkgconf_path_t; -typedef struct pkgconf_client_ pkgconf_client_t; -typedef struct pkgconf_cross_personality_ pkgconf_cross_personality_t; - -#define PKGCONF_ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x))) - -#define PKGCONF_FOREACH_LIST_ENTRY(head, value) \ - for ((value) = (head); (value) != NULL; (value) = (value)->next) - -#define PKGCONF_FOREACH_LIST_ENTRY_SAFE(head, nextiter, value) \ - for ((value) = (head), (nextiter) = (head) != NULL ? (head)->next : NULL; (value) != NULL; (value) = (nextiter), (nextiter) = (nextiter) != NULL ? (nextiter)->next : NULL) - -#define PKGCONF_FOREACH_LIST_ENTRY_REVERSE(tail, value) \ - for ((value) = (tail); (value) != NULL; (value) = (value)->prev) - -#define LIBPKGCONF_VERSION 10603 -#define LIBPKGCONF_VERSION_STR "1.6.3" - -struct pkgconf_fragment_ { - pkgconf_node_t iter; - - char type; - char *data; - - bool merged; -}; - -struct pkgconf_dependency_ { - pkgconf_node_t iter; - - char *package; - pkgconf_pkg_comparator_t compare; - char *version; - pkgconf_pkg_t *parent; - pkgconf_pkg_t *match; - - unsigned int flags; -}; - -struct pkgconf_tuple_ { - pkgconf_node_t iter; - - char *key; - char *value; -}; - -struct pkgconf_path_ { - pkgconf_node_t lnode; - - char *path; - void *handle_path; - void *handle_device; -}; - -#define PKGCONF_PKG_PROPF_NONE 0x00 -#define PKGCONF_PKG_PROPF_STATIC 0x01 -#define PKGCONF_PKG_PROPF_CACHED 0x02 -#define PKGCONF_PKG_PROPF_SEEN 0x04 -#define PKGCONF_PKG_PROPF_UNINSTALLED 0x08 -#define PKGCONF_PKG_PROPF_VIRTUAL 0x10 - -struct pkgconf_pkg_ { - pkgconf_node_t cache_iter; - - int refcount; - char *id; - char *filename; - char *realname; - char *version; - char *description; - char *url; - char *pc_filedir; - - pkgconf_list_t libs; - pkgconf_list_t libs_private; - pkgconf_list_t cflags; - pkgconf_list_t cflags_private; - - pkgconf_list_t required; /* this used to be requires but that is now a reserved keyword */ - pkgconf_list_t requires_private; - pkgconf_list_t conflicts; - pkgconf_list_t provides; - - pkgconf_list_t vars; - - unsigned int flags; - - pkgconf_client_t *owner; - - /* these resources are owned by the package and do not need special management, - * under no circumstance attempt to allocate or free objects belonging to these pointers - */ - pkgconf_tuple_t *orig_prefix; - pkgconf_tuple_t *prefix; -}; - -typedef bool (*pkgconf_pkg_iteration_func_t)(const pkgconf_pkg_t *pkg, void *data); -typedef void (*pkgconf_pkg_traverse_func_t)(pkgconf_client_t *client, pkgconf_pkg_t *pkg, void *data); -typedef bool (*pkgconf_queue_apply_func_t)(pkgconf_client_t *client, pkgconf_pkg_t *world, void *data, int maxdepth); -typedef bool (*pkgconf_error_handler_func_t)(const char *msg, const pkgconf_client_t *client, const void *data); - -struct pkgconf_client_ { - pkgconf_list_t dir_list; - pkgconf_list_t pkg_cache; - - pkgconf_list_t filter_libdirs; - pkgconf_list_t filter_includedirs; - - pkgconf_list_t global_vars; - - void *error_handler_data; - void *warn_handler_data; - void *trace_handler_data; - - pkgconf_error_handler_func_t error_handler; - pkgconf_error_handler_func_t warn_handler; - pkgconf_error_handler_func_t trace_handler; - - FILE *auditf; - - char *sysroot_dir; - char *buildroot_dir; - - unsigned int flags; - - char *prefix_varname; - - bool already_sent_notice; -}; - -struct pkgconf_cross_personality_ { - const char *name; - - pkgconf_list_t dir_list; - - pkgconf_list_t filter_libdirs; - pkgconf_list_t filter_includedirs; - - char *sysroot_dir; -}; - -/* client.c */ -PKGCONF_API void pkgconf_client_init(pkgconf_client_t *client, pkgconf_error_handler_func_t error_handler, void *error_handler_data, const pkgconf_cross_personality_t *personality); -PKGCONF_API pkgconf_client_t * pkgconf_client_new(pkgconf_error_handler_func_t error_handler, void *error_handler_data, const pkgconf_cross_personality_t *personality); -PKGCONF_API void pkgconf_client_deinit(pkgconf_client_t *client); -PKGCONF_API void pkgconf_client_free(pkgconf_client_t *client); -PKGCONF_API const char *pkgconf_client_get_sysroot_dir(const pkgconf_client_t *client); -PKGCONF_API void pkgconf_client_set_sysroot_dir(pkgconf_client_t *client, const char *sysroot_dir); -PKGCONF_API const char *pkgconf_client_get_buildroot_dir(const pkgconf_client_t *client); -PKGCONF_API void pkgconf_client_set_buildroot_dir(pkgconf_client_t *client, const char *buildroot_dir); -PKGCONF_API unsigned int pkgconf_client_get_flags(const pkgconf_client_t *client); -PKGCONF_API void pkgconf_client_set_flags(pkgconf_client_t *client, unsigned int flags); -PKGCONF_API const char *pkgconf_client_get_prefix_varname(const pkgconf_client_t *client); -PKGCONF_API void pkgconf_client_set_prefix_varname(pkgconf_client_t *client, const char *prefix_varname); -PKGCONF_API pkgconf_error_handler_func_t pkgconf_client_get_warn_handler(const pkgconf_client_t *client); -PKGCONF_API void pkgconf_client_set_warn_handler(pkgconf_client_t *client, pkgconf_error_handler_func_t warn_handler, void *warn_handler_data); -PKGCONF_API pkgconf_error_handler_func_t pkgconf_client_get_error_handler(const pkgconf_client_t *client); -PKGCONF_API void pkgconf_client_set_error_handler(pkgconf_client_t *client, pkgconf_error_handler_func_t error_handler, void *error_handler_data); -PKGCONF_API pkgconf_error_handler_func_t pkgconf_client_get_trace_handler(const pkgconf_client_t *client); -PKGCONF_API void pkgconf_client_set_trace_handler(pkgconf_client_t *client, pkgconf_error_handler_func_t trace_handler, void *trace_handler_data); -PKGCONF_API void pkgconf_client_dir_list_build(pkgconf_client_t *client, const pkgconf_cross_personality_t *personality); - -/* personality.c */ -PKGCONF_API pkgconf_cross_personality_t *pkgconf_cross_personality_default(void); -PKGCONF_API pkgconf_cross_personality_t *pkgconf_cross_personality_find(const char *triplet); - -#define PKGCONF_IS_MODULE_SEPARATOR(c) ((c) == ',' || isspace ((unsigned int)(c))) -#define PKGCONF_IS_OPERATOR_CHAR(c) ((c) == '<' || (c) == '>' || (c) == '!' || (c) == '=') - -#define PKGCONF_PKG_PKGF_NONE 0x0000 -#define PKGCONF_PKG_PKGF_SEARCH_PRIVATE 0x0001 -#define PKGCONF_PKG_PKGF_ENV_ONLY 0x0002 -#define PKGCONF_PKG_PKGF_NO_UNINSTALLED 0x0004 -#define PKGCONF_PKG_PKGF_SKIP_ROOT_VIRTUAL 0x0008 -#define PKGCONF_PKG_PKGF_MERGE_PRIVATE_FRAGMENTS 0x0010 -#define PKGCONF_PKG_PKGF_SKIP_CONFLICTS 0x0020 -#define PKGCONF_PKG_PKGF_NO_CACHE 0x0040 -#define PKGCONF_PKG_PKGF_SKIP_ERRORS 0x0080 -#define PKGCONF_PKG_PKGF_ITER_PKG_IS_PRIVATE 0x0100 -#define PKGCONF_PKG_PKGF_SKIP_PROVIDES 0x0200 -#define PKGCONF_PKG_PKGF_REDEFINE_PREFIX 0x0400 -#define PKGCONF_PKG_PKGF_DONT_RELOCATE_PATHS 0x0800 -#define PKGCONF_PKG_PKGF_SIMPLIFY_ERRORS 0x1000 -#define PKGCONF_PKG_PKGF_DONT_FILTER_INTERNAL_CFLAGS 0x2000 -#define PKGCONF_PKG_PKGF_DONT_MERGE_SPECIAL_FRAGMENTS 0x4000 - -#define PKGCONF_PKG_DEPF_INTERNAL 0x1 - -#define PKGCONF_PKG_ERRF_OK 0x0 -#define PKGCONF_PKG_ERRF_PACKAGE_NOT_FOUND 0x1 -#define PKGCONF_PKG_ERRF_PACKAGE_VER_MISMATCH 0x2 -#define PKGCONF_PKG_ERRF_PACKAGE_CONFLICT 0x4 -#define PKGCONF_PKG_ERRF_DEPGRAPH_BREAK 0x8 - -#if defined(__GNUC__) || defined(__INTEL_COMPILER) -#define PRINTFLIKE(fmtarg, firstvararg) \ - __attribute__((__format__ (__printf__, fmtarg, firstvararg))) -#define DEPRECATED \ - __attribute__((deprecated)) -#else -#define PRINTFLIKE(fmtarg, firstvararg) -#define DEPRECATED -#endif /* defined(__INTEL_COMPILER) || defined(__GNUC__) */ - -/* parser.c */ -typedef void (*pkgconf_parser_operand_func_t)(void *data, const size_t lineno, const char *key, const char *value); -typedef void (*pkgconf_parser_warn_func_t)(void *data, const char *fmt, ...); - -PKGCONF_API void pkgconf_parser_parse(FILE *f, void *data, const pkgconf_parser_operand_func_t *ops, const pkgconf_parser_warn_func_t warnfunc, const char *filename); - -/* pkg.c */ -PKGCONF_API bool pkgconf_error(const pkgconf_client_t *client, const char *format, ...) PRINTFLIKE(2, 3); -PKGCONF_API bool pkgconf_warn(const pkgconf_client_t *client, const char *format, ...) PRINTFLIKE(2, 3); -PKGCONF_API bool pkgconf_trace(const pkgconf_client_t *client, const char *filename, size_t lineno, const char *funcname, const char *format, ...) PRINTFLIKE(5, 6); -PKGCONF_API bool pkgconf_default_error_handler(const char *msg, const pkgconf_client_t *client, const void *data); - -#ifndef PKGCONF_LITE -#if defined(__GNUC__) || defined(__INTEL_COMPILER) -#define PKGCONF_TRACE(client, ...) do { \ - pkgconf_trace(client, __FILE__, __LINE__, __PRETTY_FUNCTION__, __VA_ARGS__); \ - } while (0); -#else -#define PKGCONF_TRACE(client, ...) do { \ - pkgconf_trace(client, __FILE__, __LINE__, __func__, __VA_ARGS__); \ - } while (0); -#endif -#else -#define PKGCONF_TRACE(client, ...) -#endif - -PKGCONF_API pkgconf_pkg_t *pkgconf_pkg_ref(pkgconf_client_t *client, pkgconf_pkg_t *pkg); -PKGCONF_API void pkgconf_pkg_unref(pkgconf_client_t *client, pkgconf_pkg_t *pkg); -PKGCONF_API void pkgconf_pkg_free(pkgconf_client_t *client, pkgconf_pkg_t *pkg); -PKGCONF_API pkgconf_pkg_t *pkgconf_pkg_find(pkgconf_client_t *client, const char *name); -PKGCONF_API unsigned int pkgconf_pkg_traverse(pkgconf_client_t *client, pkgconf_pkg_t *root, pkgconf_pkg_traverse_func_t func, void *data, int maxdepth, unsigned int skip_flags); -PKGCONF_API unsigned int pkgconf_pkg_verify_graph(pkgconf_client_t *client, pkgconf_pkg_t *root, int depth); -PKGCONF_API pkgconf_pkg_t *pkgconf_pkg_verify_dependency(pkgconf_client_t *client, pkgconf_dependency_t *pkgdep, unsigned int *eflags); -PKGCONF_API const char *pkgconf_pkg_get_comparator(const pkgconf_dependency_t *pkgdep); -PKGCONF_API unsigned int pkgconf_pkg_cflags(pkgconf_client_t *client, pkgconf_pkg_t *root, pkgconf_list_t *list, int maxdepth); -PKGCONF_API unsigned int pkgconf_pkg_libs(pkgconf_client_t *client, pkgconf_pkg_t *root, pkgconf_list_t *list, int maxdepth); -PKGCONF_API pkgconf_pkg_comparator_t pkgconf_pkg_comparator_lookup_by_name(const char *name); -PKGCONF_API pkgconf_pkg_t *pkgconf_builtin_pkg_get(const char *name); - -PKGCONF_API int pkgconf_compare_version(const char *a, const char *b); -PKGCONF_API pkgconf_pkg_t *pkgconf_scan_all(pkgconf_client_t *client, void *ptr, pkgconf_pkg_iteration_func_t func); - -/* parse.c */ -PKGCONF_API pkgconf_pkg_t *pkgconf_pkg_new_from_file(pkgconf_client_t *client, const char *path, FILE *f); -PKGCONF_API void pkgconf_dependency_parse_str(const pkgconf_client_t *client, pkgconf_list_t *deplist_head, const char *depends, unsigned int flags); -PKGCONF_API void pkgconf_dependency_parse(const pkgconf_client_t *client, pkgconf_pkg_t *pkg, pkgconf_list_t *deplist_head, const char *depends, unsigned int flags); -PKGCONF_API void pkgconf_dependency_append(pkgconf_list_t *list, pkgconf_dependency_t *tail); -PKGCONF_API void pkgconf_dependency_free(pkgconf_list_t *list); -PKGCONF_API pkgconf_dependency_t *pkgconf_dependency_add(const pkgconf_client_t *client, pkgconf_list_t *list, const char *package, const char *version, pkgconf_pkg_comparator_t compare, unsigned int flags); - -/* argvsplit.c */ -PKGCONF_API int pkgconf_argv_split(const char *src, int *argc, char ***argv); -PKGCONF_API void pkgconf_argv_free(char **argv); - -/* fragment.c */ -typedef struct pkgconf_fragment_render_ops_ { - size_t (*render_len)(const pkgconf_list_t *list, bool escape); - void (*render_buf)(const pkgconf_list_t *list, char *buf, size_t len, bool escape); -} pkgconf_fragment_render_ops_t; - -typedef bool (*pkgconf_fragment_filter_func_t)(const pkgconf_client_t *client, const pkgconf_fragment_t *frag, void *data); -PKGCONF_API bool pkgconf_fragment_parse(const pkgconf_client_t *client, pkgconf_list_t *list, pkgconf_list_t *vars, const char *value); -PKGCONF_API void pkgconf_fragment_add(const pkgconf_client_t *client, pkgconf_list_t *list, const char *string); -PKGCONF_API void pkgconf_fragment_copy(const pkgconf_client_t *client, pkgconf_list_t *list, const pkgconf_fragment_t *base, bool is_private); -PKGCONF_API void pkgconf_fragment_copy_list(const pkgconf_client_t *client, pkgconf_list_t *list, const pkgconf_list_t *base); -PKGCONF_API void pkgconf_fragment_delete(pkgconf_list_t *list, pkgconf_fragment_t *node); -PKGCONF_API void pkgconf_fragment_free(pkgconf_list_t *list); -PKGCONF_API void pkgconf_fragment_filter(const pkgconf_client_t *client, pkgconf_list_t *dest, pkgconf_list_t *src, pkgconf_fragment_filter_func_t filter_func, void *data); -PKGCONF_API size_t pkgconf_fragment_render_len(const pkgconf_list_t *list, bool escape, const pkgconf_fragment_render_ops_t *ops); -PKGCONF_API void pkgconf_fragment_render_buf(const pkgconf_list_t *list, char *buf, size_t len, bool escape, const pkgconf_fragment_render_ops_t *ops); -PKGCONF_API char *pkgconf_fragment_render(const pkgconf_list_t *list, bool escape, const pkgconf_fragment_render_ops_t *ops); -PKGCONF_API bool pkgconf_fragment_has_system_dir(const pkgconf_client_t *client, const pkgconf_fragment_t *frag); - -/* fileio.c */ -PKGCONF_API char *pkgconf_fgetline(char *line, size_t size, FILE *stream); - -/* tuple.c */ -PKGCONF_API pkgconf_tuple_t *pkgconf_tuple_add(const pkgconf_client_t *client, pkgconf_list_t *parent, const char *key, const char *value, bool parse); -PKGCONF_API char *pkgconf_tuple_find(const pkgconf_client_t *client, pkgconf_list_t *list, const char *key); -PKGCONF_API char *pkgconf_tuple_parse(const pkgconf_client_t *client, pkgconf_list_t *list, const char *value); -PKGCONF_API void pkgconf_tuple_free(pkgconf_list_t *list); -PKGCONF_API void pkgconf_tuple_free_entry(pkgconf_tuple_t *tuple, pkgconf_list_t *list); -PKGCONF_API void pkgconf_tuple_add_global(pkgconf_client_t *client, const char *key, const char *value); -PKGCONF_API char *pkgconf_tuple_find_global(const pkgconf_client_t *client, const char *key); -PKGCONF_API void pkgconf_tuple_free_global(pkgconf_client_t *client); -PKGCONF_API void pkgconf_tuple_define_global(pkgconf_client_t *client, const char *kv); - -/* queue.c */ -PKGCONF_API void pkgconf_queue_push(pkgconf_list_t *list, const char *package); -PKGCONF_API bool pkgconf_queue_compile(pkgconf_client_t *client, pkgconf_pkg_t *world, pkgconf_list_t *list); -PKGCONF_API void pkgconf_queue_free(pkgconf_list_t *list); -PKGCONF_API bool pkgconf_queue_apply(pkgconf_client_t *client, pkgconf_list_t *list, pkgconf_queue_apply_func_t func, int maxdepth, void *data); -PKGCONF_API bool pkgconf_queue_validate(pkgconf_client_t *client, pkgconf_list_t *list, int maxdepth); - -/* cache.c */ -PKGCONF_API pkgconf_pkg_t *pkgconf_cache_lookup(pkgconf_client_t *client, const char *id); -PKGCONF_API void pkgconf_cache_add(pkgconf_client_t *client, pkgconf_pkg_t *pkg); -PKGCONF_API void pkgconf_cache_remove(pkgconf_client_t *client, pkgconf_pkg_t *pkg); -PKGCONF_API void pkgconf_cache_free(pkgconf_client_t *client); - -/* audit.c */ -PKGCONF_API void pkgconf_audit_set_log(pkgconf_client_t *client, FILE *auditf); -PKGCONF_API void pkgconf_audit_log(pkgconf_client_t *client, const char *format, ...) PRINTFLIKE(2, 3); -PKGCONF_API void pkgconf_audit_log_dependency(pkgconf_client_t *client, const pkgconf_pkg_t *dep, const pkgconf_dependency_t *depnode); - -/* path.c */ -PKGCONF_API void pkgconf_path_add(const char *text, pkgconf_list_t *dirlist, bool filter); -PKGCONF_API size_t pkgconf_path_split(const char *text, pkgconf_list_t *dirlist, bool filter); -PKGCONF_API size_t pkgconf_path_build_from_environ(const char *envvarname, const char *fallback, pkgconf_list_t *dirlist, bool filter); -PKGCONF_API bool pkgconf_path_match_list(const char *path, const pkgconf_list_t *dirlist); -PKGCONF_API void pkgconf_path_free(pkgconf_list_t *dirlist); -PKGCONF_API bool pkgconf_path_relocate(char *buf, size_t buflen); -PKGCONF_API void pkgconf_path_copy_list(pkgconf_list_t *dst, const pkgconf_list_t *src); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/libpkgconf/libpkgconf/libpkgconf.h b/libpkgconf/libpkgconf/libpkgconf.h new file mode 120000 index 0000000..b885e88 --- /dev/null +++ b/libpkgconf/libpkgconf/libpkgconf.h @@ -0,0 +1 @@ +../../upstream/libpkgconf/libpkgconf.h \ No newline at end of file diff --git a/libpkgconf/libpkgconf/libpkgconf.h.orig b/libpkgconf/libpkgconf/libpkgconf.h.orig deleted file mode 120000 index b885e88..0000000 --- a/libpkgconf/libpkgconf/libpkgconf.h.orig +++ /dev/null @@ -1 +0,0 @@ -../../upstream/libpkgconf/libpkgconf.h \ No newline at end of file diff --git a/libpkgconf/libpkgconf/pkg.c b/libpkgconf/libpkgconf/pkg.c deleted file mode 120000 index ea9e3fd..0000000 --- a/libpkgconf/libpkgconf/pkg.c +++ /dev/null @@ -1 +0,0 @@ -../../upstream/libpkgconf/pkg.c \ No newline at end of file diff --git a/libpkgconf/libpkgconf/pkg.c b/libpkgconf/libpkgconf/pkg.c new file mode 100644 index 0000000..ba4fb5f --- /dev/null +++ b/libpkgconf/libpkgconf/pkg.c @@ -0,0 +1,1676 @@ +/* + * pkg.c + * higher-level dependency graph compilation, management and manipulation + * + * Copyright (c) 2011, 2012, 2013 pkgconf authors (see AUTHORS). + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * This software is provided 'as is' and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising + * from the use of this software. + */ + +#include +#include +#include + +/* + * !doc + * + * libpkgconf `pkg` module + * ======================= + * + * The `pkg` module provides dependency resolution services and the overall `.pc` file parsing + * routines. + */ + +#ifdef _WIN32 +# define PKG_CONFIG_REG_KEY "Software\\pkgconfig\\PKG_CONFIG_PATH" +# undef PKG_DEFAULT_PATH +# define PKG_DEFAULT_PATH "../lib/pkgconfig;../share/pkgconfig" +# define strncasecmp _strnicmp +# define strcasecmp _stricmp +#endif + +#define PKG_CONFIG_EXT ".pc" + +static inline bool +str_has_suffix(const char *str, const char *suffix) +{ + size_t str_len = strlen(str); + size_t suf_len = strlen(suffix); + + if (str_len < suf_len) + return false; + + return !strncasecmp(str + str_len - suf_len, suffix, suf_len); +} + +static char * +pkg_get_parent_dir(pkgconf_pkg_t *pkg) +{ + char buf[PKGCONF_ITEM_SIZE], *pathbuf; + + pkgconf_strlcpy(buf, pkg->filename, sizeof buf); + pathbuf = strrchr(buf, PKG_DIR_SEP_S); + if (pathbuf == NULL) + pathbuf = strrchr(buf, '/'); + if (pathbuf != NULL) + pathbuf[0] = '\0'; + + return strdup(buf); +} + +typedef void (*pkgconf_pkg_parser_keyword_func_t)(const pkgconf_client_t *client, pkgconf_pkg_t *pkg, const char *keyword, const size_t lineno, const ptrdiff_t offset, const char *value); +typedef struct { + const char *keyword; + const pkgconf_pkg_parser_keyword_func_t func; + const ptrdiff_t offset; +} pkgconf_pkg_parser_keyword_pair_t; + +static int pkgconf_pkg_parser_keyword_pair_cmp(const void *key, const void *ptr) +{ + const pkgconf_pkg_parser_keyword_pair_t *pair = ptr; + return strcasecmp(key, pair->keyword); +} + +static void +pkgconf_pkg_parser_tuple_func(const pkgconf_client_t *client, pkgconf_pkg_t *pkg, const char *keyword, const size_t lineno, const ptrdiff_t offset, const char *value) +{ + (void) keyword; + (void) lineno; + + char **dest = (char **)((char *) pkg + offset); + *dest = pkgconf_tuple_parse(client, &pkg->vars, value); +} + +static void +pkgconf_pkg_parser_version_func(const pkgconf_client_t *client, pkgconf_pkg_t *pkg, const char *keyword, const size_t lineno, const ptrdiff_t offset, const char *value) +{ + (void) keyword; + (void) lineno; + char *p, *i; + size_t len; + char **dest = (char **)((char *) pkg + offset); + + /* cut at any detected whitespace */ + p = pkgconf_tuple_parse(client, &pkg->vars, value); + + len = strcspn(p, " \t"); + if (len) + { + i = p + (ptrdiff_t) len; + *i = '\0'; + + pkgconf_warn(client, "%s:" SIZE_FMT_SPECIFIER ": warning: malformed version field with whitespace, trimming to [%s]\n", pkg->filename, + lineno, p); + } + + *dest = p; +} + +static void +pkgconf_pkg_parser_fragment_func(const pkgconf_client_t *client, pkgconf_pkg_t *pkg, const char *keyword, const size_t lineno, const ptrdiff_t offset, const char *value) +{ + pkgconf_list_t *dest = (pkgconf_list_t *)((char *) pkg + offset); + bool ret = pkgconf_fragment_parse(client, dest, &pkg->vars, value); + + if (!ret) + { + pkgconf_warn(client, "%s:" SIZE_FMT_SPECIFIER ": warning: unable to parse field '%s' into an argument vector, value [%s]\n", pkg->filename, + lineno, keyword, value); + } +} + +static void +pkgconf_pkg_parser_dependency_func(const pkgconf_client_t *client, pkgconf_pkg_t *pkg, const char *keyword, const size_t lineno, const ptrdiff_t offset, const char *value) +{ + (void) keyword; + (void) lineno; + + pkgconf_list_t *dest = (pkgconf_list_t *)((char *) pkg + offset); + pkgconf_dependency_parse(client, pkg, dest, value, 0); +} + +/* a variant of pkgconf_pkg_parser_dependency_func which colors the dependency node as an "internal" dependency. */ +static void +pkgconf_pkg_parser_internal_dependency_func(const pkgconf_client_t *client, pkgconf_pkg_t *pkg, const char *keyword, const size_t lineno, const ptrdiff_t offset, const char *value) +{ + (void) keyword; + (void) lineno; + + pkgconf_list_t *dest = (pkgconf_list_t *)((char *) pkg + offset); + pkgconf_dependency_parse(client, pkg, dest, value, PKGCONF_PKG_DEPF_INTERNAL); +} + +/* keep this in alphabetical order */ +static const pkgconf_pkg_parser_keyword_pair_t pkgconf_pkg_parser_keyword_funcs[] = { + {"CFLAGS", pkgconf_pkg_parser_fragment_func, offsetof(pkgconf_pkg_t, cflags)}, + {"CFLAGS.private", pkgconf_pkg_parser_fragment_func, offsetof(pkgconf_pkg_t, cflags_private)}, + {"Conflicts", pkgconf_pkg_parser_dependency_func, offsetof(pkgconf_pkg_t, conflicts)}, + {"Description", pkgconf_pkg_parser_tuple_func, offsetof(pkgconf_pkg_t, description)}, + {"LIBS", pkgconf_pkg_parser_fragment_func, offsetof(pkgconf_pkg_t, libs)}, + {"LIBS.private", pkgconf_pkg_parser_fragment_func, offsetof(pkgconf_pkg_t, libs_private)}, + {"Name", pkgconf_pkg_parser_tuple_func, offsetof(pkgconf_pkg_t, realname)}, + {"Provides", pkgconf_pkg_parser_dependency_func, offsetof(pkgconf_pkg_t, provides)}, + {"Requires", pkgconf_pkg_parser_dependency_func, offsetof(pkgconf_pkg_t, required)}, + {"Requires.internal", pkgconf_pkg_parser_internal_dependency_func, offsetof(pkgconf_pkg_t, requires_private)}, + {"Requires.private", pkgconf_pkg_parser_dependency_func, offsetof(pkgconf_pkg_t, requires_private)}, + {"Version", pkgconf_pkg_parser_version_func, offsetof(pkgconf_pkg_t, version)}, +}; + +static void +pkgconf_pkg_parser_keyword_set(void *opaque, const size_t lineno, const char *keyword, const char *value) +{ + pkgconf_pkg_t *pkg = opaque; + + const pkgconf_pkg_parser_keyword_pair_t *pair = bsearch(keyword, + pkgconf_pkg_parser_keyword_funcs, PKGCONF_ARRAY_SIZE(pkgconf_pkg_parser_keyword_funcs), + sizeof(pkgconf_pkg_parser_keyword_pair_t), pkgconf_pkg_parser_keyword_pair_cmp); + + if (pair == NULL || pair->func == NULL) + return; + + pair->func(pkg->owner, pkg, keyword, lineno, pair->offset, value); +} + +static const char * +determine_prefix(const pkgconf_pkg_t *pkg, char *buf, size_t buflen) +{ + char *pathiter; + + pkgconf_strlcpy(buf, pkg->filename, buflen); + pkgconf_path_relocate(buf, buflen); + + pathiter = strrchr(buf, PKG_DIR_SEP_S); + if (pathiter == NULL) + pathiter = strrchr(buf, '/'); + if (pathiter != NULL) + pathiter[0] = '\0'; + + pathiter = strrchr(buf, PKG_DIR_SEP_S); + if (pathiter == NULL) + pathiter = strrchr(buf, '/'); + if (pathiter == NULL) + return NULL; + + /* parent dir is not pkgconfig, can't relocate then */ + if (strcmp(pathiter + 1, "pkgconfig")) + return NULL; + + /* okay, work backwards and do it again. */ + pathiter[0] = '\0'; + pathiter = strrchr(buf, PKG_DIR_SEP_S); + if (pathiter == NULL) + pathiter = strrchr(buf, '/'); + if (pathiter == NULL) + return NULL; + + pathiter[0] = '\0'; + + return buf; +} + +static void +remove_additional_separators(char *buf) +{ + char *p = buf; + + while (*p) { + if (*p == '/') { + char *q; + + q = ++p; + while (*q && *q == '/') + q++; + + if (p != q) + memmove (p, q, strlen (q) + 1); + } else { + p++; + } + } +} + +static void +canonicalize_path(char *buf) +{ +#ifdef _WIN32 + char *p = buf; + + while (*p) { + if (*p == '\\') + *p = '/'; + p++; + } +#endif + + remove_additional_separators(buf); +} + +static bool +is_path_prefix_equal(const char *path1, const char *path2, size_t path2_len) +{ +#ifdef _WIN32 + return !_strnicmp(path1, path2, path2_len); +#else + return !strncmp(path1, path2, path2_len); +#endif +} + +static void +pkgconf_pkg_parser_value_set(void *opaque, const size_t lineno, const char *keyword, const char *value) +{ + char canonicalized_value[PKGCONF_ITEM_SIZE]; + pkgconf_pkg_t *pkg = opaque; + + (void) lineno; + + pkgconf_strlcpy(canonicalized_value, value, sizeof canonicalized_value); + canonicalize_path(canonicalized_value); + + /* Some pc files will use absolute paths for all of their directories + * which is broken when redefining the prefix. We try to outsmart the + * file and rewrite any directory that starts with the same prefix. + */ + if (pkg->owner->flags & PKGCONF_PKG_PKGF_REDEFINE_PREFIX && pkg->orig_prefix + && is_path_prefix_equal(canonicalized_value, pkg->orig_prefix->value, strlen(pkg->orig_prefix->value))) + { + char newvalue[PKGCONF_ITEM_SIZE]; + + pkgconf_strlcpy(newvalue, pkg->prefix->value, sizeof newvalue); + pkgconf_strlcat(newvalue, canonicalized_value + strlen(pkg->orig_prefix->value), sizeof newvalue); + pkgconf_tuple_add(pkg->owner, &pkg->vars, keyword, newvalue, false); + } + else if (strcmp(keyword, pkg->owner->prefix_varname) || !(pkg->owner->flags & PKGCONF_PKG_PKGF_REDEFINE_PREFIX)) + pkgconf_tuple_add(pkg->owner, &pkg->vars, keyword, value, true); + else + { + char pathbuf[PKGCONF_ITEM_SIZE]; + const char *relvalue = determine_prefix(pkg, pathbuf, sizeof pathbuf); + + if (relvalue != NULL) + { + pkg->orig_prefix = pkgconf_tuple_add(pkg->owner, &pkg->vars, "orig_prefix", canonicalized_value, true); + pkg->prefix = pkgconf_tuple_add(pkg->owner, &pkg->vars, keyword, relvalue, false); + } + else + pkgconf_tuple_add(pkg->owner, &pkg->vars, keyword, value, true); + } +} + +typedef struct { + const char *field; + const ptrdiff_t offset; +} pkgconf_pkg_validity_check_t; + +static const pkgconf_pkg_validity_check_t pkgconf_pkg_validations[] = { + {"Name", offsetof(pkgconf_pkg_t, realname)}, + {"Description", offsetof(pkgconf_pkg_t, description)}, + {"Version", offsetof(pkgconf_pkg_t, version)}, +}; + +static const pkgconf_parser_operand_func_t pkg_parser_funcs[256] = { + [':'] = pkgconf_pkg_parser_keyword_set, + ['='] = pkgconf_pkg_parser_value_set +}; + +static void pkg_warn_func(pkgconf_pkg_t *pkg, const char *fmt, ...) PRINTFLIKE(2, 3); + +static void +pkg_warn_func(pkgconf_pkg_t *pkg, const char *fmt, ...) +{ + char buf[PKGCONF_ITEM_SIZE]; + va_list va; + + va_start(va, fmt); + vsnprintf(buf, sizeof buf, fmt, va); + va_end(va); + + pkgconf_warn(pkg->owner, "%s", buf); +} + +static bool +pkgconf_pkg_validate(const pkgconf_client_t *client, const pkgconf_pkg_t *pkg) +{ + size_t i; + bool valid = true; + + for (i = 0; i < PKGCONF_ARRAY_SIZE(pkgconf_pkg_validations); i++) + { + char **p = (char **)((char *) pkg + pkgconf_pkg_validations[i].offset); + + if (*p != NULL) + continue; + + pkgconf_warn(client, "%s: warning: file does not declare a `%s' field\n", pkg->filename, pkgconf_pkg_validations[i].field); + valid = false; + } + + return valid; +} + +/* + * !doc + * + * .. c:function:: pkgconf_pkg_t *pkgconf_pkg_new_from_file(const pkgconf_client_t *client, const char *filename, FILE *f) + * + * Parse a .pc file into a pkgconf_pkg_t object structure. + * + * :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution. + * :param char* filename: The filename of the package file (including full path). + * :param FILE* f: The file object to read from. + * :returns: A ``pkgconf_pkg_t`` object which contains the package data. + * :rtype: pkgconf_pkg_t * + */ +pkgconf_pkg_t * +pkgconf_pkg_new_from_file(pkgconf_client_t *client, const char *filename, FILE *f) +{ + pkgconf_pkg_t *pkg; + char *idptr; + + pkg = calloc(sizeof(pkgconf_pkg_t), 1); + pkg->owner = client; + pkg->filename = strdup(filename); + pkg->pc_filedir = pkg_get_parent_dir(pkg); + pkgconf_tuple_add(client, &pkg->vars, "pcfiledir", pkg->pc_filedir, true); + + /* make module id */ + if ((idptr = strrchr(pkg->filename, PKG_DIR_SEP_S)) != NULL) + idptr++; + else + idptr = pkg->filename; + +#ifdef _WIN32 + /* On Windows, both \ and / are allowed in paths, so we have to chop both. + * strrchr() took us to the last \ in that case, so we just have to see if + * it is followed by a /. If so, lop it off. + */ + char *mungeptr; + if ((mungeptr = strrchr(idptr, '/')) != NULL) + idptr = ++mungeptr; +#endif + + pkg->id = strdup(idptr); + idptr = strrchr(pkg->id, '.'); + if (idptr) + *idptr = '\0'; + + pkgconf_parser_parse(f, pkg, pkg_parser_funcs, (pkgconf_parser_warn_func_t) pkg_warn_func, pkg->filename); + + if (!pkgconf_pkg_validate(client, pkg)) + { + pkgconf_warn(client, "%s: warning: skipping invalid file\n", pkg->filename); + pkgconf_pkg_free(client, pkg); + return NULL; + } + + pkgconf_dependency_add(client, &pkg->provides, pkg->id, pkg->version, PKGCONF_CMP_EQUAL, 0); + + return pkgconf_pkg_ref(client, pkg); +} + +/* + * !doc + * + * .. c:function:: void pkgconf_pkg_free(pkgconf_client_t *client, pkgconf_pkg_t *pkg) + * + * Releases all releases for a given ``pkgconf_pkg_t`` object. + * + * :param pkgconf_client_t* client: The client which owns the ``pkgconf_pkg_t`` object, `pkg`. + * :param pkgconf_pkg_t* pkg: The package to free. + * :return: nothing + */ +void +pkgconf_pkg_free(pkgconf_client_t *client, pkgconf_pkg_t *pkg) +{ + if (pkg == NULL) + return; + + if (pkg->flags & PKGCONF_PKG_PROPF_STATIC && !(pkg->flags & PKGCONF_PKG_PROPF_VIRTUAL)) + return; + + pkgconf_cache_remove(client, pkg); + + pkgconf_dependency_free(&pkg->required); + pkgconf_dependency_free(&pkg->requires_private); + pkgconf_dependency_free(&pkg->conflicts); + pkgconf_dependency_free(&pkg->provides); + + pkgconf_fragment_free(&pkg->cflags); + pkgconf_fragment_free(&pkg->cflags_private); + pkgconf_fragment_free(&pkg->libs); + pkgconf_fragment_free(&pkg->libs_private); + + pkgconf_tuple_free(&pkg->vars); + + if (pkg->flags & PKGCONF_PKG_PROPF_VIRTUAL) + return; + + if (pkg->id != NULL) + free(pkg->id); + + if (pkg->filename != NULL) + free(pkg->filename); + + if (pkg->realname != NULL) + free(pkg->realname); + + if (pkg->version != NULL) + free(pkg->version); + + if (pkg->description != NULL) + free(pkg->description); + + if (pkg->url != NULL) + free(pkg->url); + + if (pkg->pc_filedir != NULL) + free(pkg->pc_filedir); + + free(pkg); +} + +/* + * !doc + * + * .. c:function:: pkgconf_pkg_t *pkgconf_pkg_ref(const pkgconf_client_t *client, pkgconf_pkg_t *pkg) + * + * Adds an additional reference to the package object. + * + * :param pkgconf_client_t* client: The pkgconf client object which owns the package being referenced. + * :param pkgconf_pkg_t* pkg: The package object being referenced. + * :return: The package itself with an incremented reference count. + * :rtype: pkgconf_pkg_t * + */ +pkgconf_pkg_t * +pkgconf_pkg_ref(pkgconf_client_t *client, pkgconf_pkg_t *pkg) +{ + if (pkg->owner != NULL && pkg->owner != client) + PKGCONF_TRACE(client, "WTF: client %p refers to package %p owned by other client %p", client, pkg, pkg->owner); + + pkg->refcount++; + PKGCONF_TRACE(client, "refcount@%p: %d", pkg, pkg->refcount); + + return pkg; +} + +/* + * !doc + * + * .. c:function:: void pkgconf_pkg_unref(pkgconf_client_t *client, pkgconf_pkg_t *pkg) + * + * Releases a reference on the package object. If the reference count is 0, then also free the package. + * + * :param pkgconf_client_t* client: The pkgconf client object which owns the package being dereferenced. + * :param pkgconf_pkg_t* pkg: The package object being dereferenced. + * :return: nothing + */ +void +pkgconf_pkg_unref(pkgconf_client_t *client, pkgconf_pkg_t *pkg) +{ + if (pkg->owner != NULL && pkg->owner != client) + PKGCONF_TRACE(client, "WTF: client %p unrefs package %p owned by other client %p", client, pkg, pkg->owner); + + pkg->refcount--; + PKGCONF_TRACE(pkg->owner, "refcount@%p: %d", pkg, pkg->refcount); + + if (pkg->refcount <= 0) + pkgconf_pkg_free(pkg->owner, pkg); +} + +static inline pkgconf_pkg_t * +pkgconf_pkg_try_specific_path(pkgconf_client_t *client, const char *path, const char *name) +{ + pkgconf_pkg_t *pkg = NULL; + FILE *f; + char locbuf[PKGCONF_ITEM_SIZE]; + char uninst_locbuf[PKGCONF_ITEM_SIZE]; + + PKGCONF_TRACE(client, "trying path: %s for %s", path, name); + + snprintf(locbuf, sizeof locbuf, "%s%c%s" PKG_CONFIG_EXT, path, PKG_DIR_SEP_S, name); + snprintf(uninst_locbuf, sizeof uninst_locbuf, "%s%c%s-uninstalled" PKG_CONFIG_EXT, path, PKG_DIR_SEP_S, name); + + if (!(client->flags & PKGCONF_PKG_PKGF_NO_UNINSTALLED) && (f = fopen(uninst_locbuf, "r")) != NULL) + { + PKGCONF_TRACE(client, "found (uninstalled): %s", uninst_locbuf); + pkg = pkgconf_pkg_new_from_file(client, uninst_locbuf, f); + pkg->flags |= PKGCONF_PKG_PROPF_UNINSTALLED; + } + else if ((f = fopen(locbuf, "r")) != NULL) + { + PKGCONF_TRACE(client, "found: %s", locbuf); + pkg = pkgconf_pkg_new_from_file(client, locbuf, f); + } + + return pkg; +} + +static pkgconf_pkg_t * +pkgconf_pkg_scan_dir(pkgconf_client_t *client, const char *path, void *data, pkgconf_pkg_iteration_func_t func) +{ + DIR *dir; + struct dirent *dirent; + pkgconf_pkg_t *outpkg = NULL; + + dir = opendir(path); + if (dir == NULL) + return NULL; + + PKGCONF_TRACE(client, "scanning dir [%s]", path); + + for (dirent = readdir(dir); dirent != NULL; dirent = readdir(dir)) + { + char filebuf[PKGCONF_ITEM_SIZE]; + pkgconf_pkg_t *pkg; + FILE *f; + + pkgconf_strlcpy(filebuf, path, sizeof filebuf); + pkgconf_strlcat(filebuf, "/", sizeof filebuf); + pkgconf_strlcat(filebuf, dirent->d_name, sizeof filebuf); + + if (!str_has_suffix(filebuf, PKG_CONFIG_EXT)) + continue; + + PKGCONF_TRACE(client, "trying file [%s]", filebuf); + + f = fopen(filebuf, "r"); + if (f == NULL) + continue; + + pkg = pkgconf_pkg_new_from_file(client, filebuf, f); + if (pkg != NULL) + { + if (func(pkg, data)) + { + outpkg = pkg; + goto out; + } + + pkgconf_pkg_unref(client, pkg); + } + } + +out: + closedir(dir); + return outpkg; +} + +/* + * !doc + * + * .. c:function:: pkgconf_pkg_t *pkgconf_scan_all(pkgconf_client_t *client, void *data, pkgconf_pkg_iteration_func_t func) + * + * Iterates over all packages found in the `package directory list`, running ``func`` on them. If ``func`` returns true, + * then stop iteration and return the last iterated package. + * + * :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution. + * :param void* data: An opaque pointer to data to provide the iteration function with. + * :param pkgconf_pkg_iteration_func_t func: A function which is called for each package to determine if the package matches, + * always return ``false`` to iterate over all packages. + * :return: A package object reference if one is found by the scan function, else ``NULL``. + * :rtype: pkgconf_pkg_t * + */ +pkgconf_pkg_t * +pkgconf_scan_all(pkgconf_client_t *client, void *data, pkgconf_pkg_iteration_func_t func) +{ + pkgconf_node_t *n; + pkgconf_pkg_t *pkg; + + PKGCONF_FOREACH_LIST_ENTRY(client->dir_list.head, n) + { + pkgconf_path_t *pnode = n->data; + + PKGCONF_TRACE(client, "scanning directory: %s", pnode->path); + + if ((pkg = pkgconf_pkg_scan_dir(client, pnode->path, data, func)) != NULL) + return pkg; + } + + return NULL; +} + +#ifdef _WIN32 +static pkgconf_pkg_t * +pkgconf_pkg_find_in_registry_key(pkgconf_client_t *client, HKEY hkey, const char *name) +{ + pkgconf_pkg_t *pkg = NULL; + + HKEY key; + int i = 0; + + char buf[16384]; /* per registry limits */ + DWORD bufsize = sizeof buf; + if (RegOpenKeyEx(hkey, PKG_CONFIG_REG_KEY, + 0, KEY_READ, &key) != ERROR_SUCCESS) + return NULL; + + while (RegEnumValue(key, i++, buf, &bufsize, NULL, NULL, NULL, NULL) + == ERROR_SUCCESS) + { + char pathbuf[PKGCONF_ITEM_SIZE]; + DWORD type; + DWORD pathbuflen = sizeof pathbuf; + + if (RegQueryValueEx(key, buf, NULL, &type, (LPBYTE) pathbuf, &pathbuflen) + == ERROR_SUCCESS && type == REG_SZ) + { + pkg = pkgconf_pkg_try_specific_path(client, pathbuf, name); + if (pkg != NULL) + break; + } + + bufsize = sizeof buf; + } + + RegCloseKey(key); + return pkg; +} +#endif + +/* + * !doc + * + * .. c:function:: pkgconf_pkg_t *pkgconf_pkg_find(pkgconf_client_t *client, const char *name) + * + * Search for a package. + * + * :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution. + * :param char* name: The name of the package `atom` to use for searching. + * :return: A package object reference if the package was found, else ``NULL``. + * :rtype: pkgconf_pkg_t * + */ +pkgconf_pkg_t * +pkgconf_pkg_find(pkgconf_client_t *client, const char *name) +{ + pkgconf_pkg_t *pkg = NULL; + pkgconf_node_t *n; + FILE *f; + + PKGCONF_TRACE(client, "looking for: %s", name); + + /* name might actually be a filename. */ + if (str_has_suffix(name, PKG_CONFIG_EXT)) + { + if ((f = fopen(name, "r")) != NULL) + { + pkgconf_pkg_t *pkg; + + PKGCONF_TRACE(client, "%s is a file", name); + + pkg = pkgconf_pkg_new_from_file(client, name, f); + if (pkg != NULL) + { + pkgconf_path_add(pkg->pc_filedir, &client->dir_list, true); + return pkg; + } + } + } + + /* check builtins */ + if ((pkg = pkgconf_builtin_pkg_get(name)) != NULL) + { + PKGCONF_TRACE(client, "%s is a builtin", name); + return pkg; + } + + /* check cache */ + if (!(client->flags & PKGCONF_PKG_PKGF_NO_CACHE)) + { + if ((pkg = pkgconf_cache_lookup(client, name)) != NULL) + { + PKGCONF_TRACE(client, "%s is cached", name); + return pkg; + } + } + + PKGCONF_FOREACH_LIST_ENTRY(client->dir_list.head, n) + { + pkgconf_path_t *pnode = n->data; + + pkg = pkgconf_pkg_try_specific_path(client, pnode->path, name); + if (pkg != NULL) + goto out; + } + +#ifdef _WIN32 + /* support getting PKG_CONFIG_PATH from registry */ + pkg = pkgconf_pkg_find_in_registry_key(client, HKEY_CURRENT_USER, name); + if (!pkg) + pkg = pkgconf_pkg_find_in_registry_key(client, HKEY_LOCAL_MACHINE, name); +#endif + +out: + pkgconf_cache_add(client, pkg); + + return pkg; +} + +/* + * !doc + * + * .. c:function:: int pkgconf_compare_version(const char *a, const char *b) + * + * Compare versions using RPM version comparison rules as described in the LSB. + * + * :param char* a: The first version to compare in the pair. + * :param char* b: The second version to compare in the pair. + * :return: -1 if the first version is greater, 0 if both versions are equal, 1 if the second version is greater. + * :rtype: int + */ +int +pkgconf_compare_version(const char *a, const char *b) +{ + char oldch1, oldch2; + char buf1[PKGCONF_ITEM_SIZE], buf2[PKGCONF_ITEM_SIZE]; + char *str1, *str2; + char *one, *two; + int ret; + bool isnum; + + /* optimization: if version matches then it's the same version. */ + if (a == NULL) + return 1; + + if (b == NULL) + return -1; + + if (!strcasecmp(a, b)) + return 0; + + pkgconf_strlcpy(buf1, a, sizeof buf1); + pkgconf_strlcpy(buf2, b, sizeof buf2); + + one = str1 = buf1; + two = str2 = buf2; + + while (*one || *two) + { + while (*one && !isalnum((unsigned int)*one) && *one != '~') + one++; + while (*two && !isalnum((unsigned int)*two) && *two != '~') + two++; + + if (*one == '~' || *two == '~') + { + if (*one != '~') + return -1; + if (*two != '~') + return 1; + + one++; + two++; + continue; + } + + if (!(*one && *two)) + break; + + str1 = one; + str2 = two; + + if (isdigit((unsigned int)*str1)) + { + while (*str1 && isdigit((unsigned int)*str1)) + str1++; + + while (*str2 && isdigit((unsigned int)*str2)) + str2++; + + isnum = true; + } + else + { + while (*str1 && isalpha((unsigned int)*str1)) + str1++; + + while (*str2 && isalpha((unsigned int)*str2)) + str2++; + + isnum = false; + } + + oldch1 = *str1; + oldch2 = *str2; + + *str1 = '\0'; + *str2 = '\0'; + + if (one == str1) + return -1; + + if (two == str2) + return (isnum ? 1 : -1); + + if (isnum) + { + int onelen, twolen; + + while (*one == '0') + one++; + + while (*two == '0') + two++; + + onelen = strlen(one); + twolen = strlen(two); + + if (onelen > twolen) + return 1; + else if (twolen > onelen) + return -1; + } + + ret = strcmp(one, two); + if (ret != 0) + return ret < 0 ? -1 : 1; + + *str1 = oldch1; + *str2 = oldch2; + + one = str1; + two = str2; + } + + if ((!*one) && (!*two)) + return 0; + + if (!*one) + return -1; + + return 1; +} + +static pkgconf_pkg_t pkg_config_virtual = { + .id = "pkg-config", + .realname = "pkg-config", + .description = "virtual package defining pkg-config API version supported", + .url = PACKAGE_BUGREPORT, + .version = PACKAGE_VERSION, + .flags = PKGCONF_PKG_PROPF_STATIC, + .vars = { + .head = &(pkgconf_node_t){ + .next = &(pkgconf_node_t){ + .next = &(pkgconf_node_t){ + .data = &(pkgconf_tuple_t){ + .key = "pc_system_libdirs", + .value = SYSTEM_LIBDIR, + } + }, + .data = &(pkgconf_tuple_t){ + .key = "pc_system_includedirs", + .value = SYSTEM_INCLUDEDIR, + } + }, + .data = &(pkgconf_tuple_t){ + .key = "pc_path", + .value = PKG_DEFAULT_PATH, + }, + }, + .tail = NULL, + } +}; + +static pkgconf_pkg_t pkgconf_virtual = { + .id = "pkgconf", + .realname = "pkgconf", + .description = "virtual package defining pkgconf API version supported", + .url = PACKAGE_BUGREPORT, + .version = PACKAGE_VERSION, + .flags = PKGCONF_PKG_PROPF_STATIC, + .vars = { + .head = &(pkgconf_node_t){ + .next = &(pkgconf_node_t){ + .next = &(pkgconf_node_t){ + .data = &(pkgconf_tuple_t){ + .key = "pc_system_libdirs", + .value = SYSTEM_LIBDIR, + } + }, + .data = &(pkgconf_tuple_t){ + .key = "pc_system_includedirs", + .value = SYSTEM_INCLUDEDIR, + } + }, + .data = &(pkgconf_tuple_t){ + .key = "pc_path", + .value = PKG_DEFAULT_PATH, + }, + }, + .tail = NULL, + }, +}; + +typedef struct { + const char *name; + pkgconf_pkg_t *pkg; +} pkgconf_builtin_pkg_pair_t; + +/* keep these in alphabetical order */ +static const pkgconf_builtin_pkg_pair_t pkgconf_builtin_pkg_pair_set[] = { + {"pkg-config", &pkg_config_virtual}, + {"pkgconf", &pkgconf_virtual}, +}; + +static int pkgconf_builtin_pkg_pair_cmp(const void *key, const void *ptr) +{ + const pkgconf_builtin_pkg_pair_t *pair = ptr; + return strcasecmp(key, pair->name); +} + +/* + * !doc + * + * .. c:function:: pkgconf_pkg_t *pkgconf_builtin_pkg_get(const char *name) + * + * Looks up a built-in package. The package should not be freed or dereferenced. + * + * :param char* name: An atom corresponding to a built-in package to search for. + * :return: the built-in package if present, else ``NULL``. + * :rtype: pkgconf_pkg_t * + */ +pkgconf_pkg_t * +pkgconf_builtin_pkg_get(const char *name) +{ + const pkgconf_builtin_pkg_pair_t *pair = bsearch(name, pkgconf_builtin_pkg_pair_set, + PKGCONF_ARRAY_SIZE(pkgconf_builtin_pkg_pair_set), sizeof(pkgconf_builtin_pkg_pair_t), + pkgconf_builtin_pkg_pair_cmp); + + return (pair != NULL) ? pair->pkg : NULL; +} + +typedef bool (*pkgconf_vercmp_res_func_t)(const char *a, const char *b); + +typedef struct { + const char *name; + pkgconf_pkg_comparator_t compare; +} pkgconf_pkg_comparator_pair_t; + +static const pkgconf_pkg_comparator_pair_t pkgconf_pkg_comparator_names[] = { + {"!=", PKGCONF_CMP_NOT_EQUAL}, + {"(any)", PKGCONF_CMP_ANY}, + {"<", PKGCONF_CMP_LESS_THAN}, + {"<=", PKGCONF_CMP_LESS_THAN_EQUAL}, + {"=", PKGCONF_CMP_EQUAL}, + {">", PKGCONF_CMP_GREATER_THAN}, + {">=", PKGCONF_CMP_GREATER_THAN_EQUAL}, +}; + +static int pkgconf_pkg_comparator_pair_namecmp(const void *key, const void *ptr) +{ + const pkgconf_pkg_comparator_pair_t *pair = ptr; + return strcmp(key, pair->name); +} + +static bool pkgconf_pkg_comparator_lt(const char *a, const char *b) +{ + return (pkgconf_compare_version(a, b) < 0); +} + +static bool pkgconf_pkg_comparator_gt(const char *a, const char *b) +{ + return (pkgconf_compare_version(a, b) > 0); +} + +static bool pkgconf_pkg_comparator_lte(const char *a, const char *b) +{ + return (pkgconf_compare_version(a, b) <= 0); +} + +static bool pkgconf_pkg_comparator_gte(const char *a, const char *b) +{ + return (pkgconf_compare_version(a, b) >= 0); +} + +static bool pkgconf_pkg_comparator_eq(const char *a, const char *b) +{ + return (pkgconf_compare_version(a, b) == 0); +} + +static bool pkgconf_pkg_comparator_ne(const char *a, const char *b) +{ + return (pkgconf_compare_version(a, b) != 0); +} + +static bool pkgconf_pkg_comparator_any(const char *a, const char *b) +{ + (void) a; + (void) b; + + return true; +} + +static bool pkgconf_pkg_comparator_none(const char *a, const char *b) +{ + (void) a; + (void) b; + + return false; +} + +static const pkgconf_vercmp_res_func_t pkgconf_pkg_comparator_impls[] = { + [PKGCONF_CMP_ANY] = pkgconf_pkg_comparator_any, + [PKGCONF_CMP_LESS_THAN] = pkgconf_pkg_comparator_lt, + [PKGCONF_CMP_GREATER_THAN] = pkgconf_pkg_comparator_gt, + [PKGCONF_CMP_LESS_THAN_EQUAL] = pkgconf_pkg_comparator_lte, + [PKGCONF_CMP_GREATER_THAN_EQUAL] = pkgconf_pkg_comparator_gte, + [PKGCONF_CMP_EQUAL] = pkgconf_pkg_comparator_eq, + [PKGCONF_CMP_NOT_EQUAL] = pkgconf_pkg_comparator_ne, +}; + +/* + * !doc + * + * .. c:function:: const char *pkgconf_pkg_get_comparator(const pkgconf_dependency_t *pkgdep) + * + * Returns the comparator used in a depgraph dependency node as a string. + * + * :param pkgconf_dependency_t* pkgdep: The depgraph dependency node to return the comparator for. + * :return: A string matching the comparator or ``"???"``. + * :rtype: char * + */ +const char * +pkgconf_pkg_get_comparator(const pkgconf_dependency_t *pkgdep) +{ + if (pkgdep->compare >= PKGCONF_ARRAY_SIZE(pkgconf_pkg_comparator_names)) + return "???"; + + return pkgconf_pkg_comparator_names[pkgdep->compare].name; +} + +/* + * !doc + * + * .. c:function:: pkgconf_pkg_comparator_t pkgconf_pkg_comparator_lookup_by_name(const char *name) + * + * Look up the appropriate comparator bytecode in the comparator set (defined + * in ``pkg.c``, see ``pkgconf_pkg_comparator_names`` and ``pkgconf_pkg_comparator_impls``). + * + * :param char* name: The comparator to look up by `name`. + * :return: The comparator bytecode if found, else ``PKGCONF_CMP_ANY``. + * :rtype: pkgconf_pkg_comparator_t + */ +pkgconf_pkg_comparator_t +pkgconf_pkg_comparator_lookup_by_name(const char *name) +{ + const pkgconf_pkg_comparator_pair_t *p = bsearch(name, pkgconf_pkg_comparator_names, + PKGCONF_ARRAY_SIZE(pkgconf_pkg_comparator_names), sizeof(pkgconf_pkg_comparator_pair_t), + pkgconf_pkg_comparator_pair_namecmp); + + return (p != NULL) ? p->compare : PKGCONF_CMP_ANY; +} + +typedef struct { + pkgconf_dependency_t *pkgdep; +} pkgconf_pkg_scan_providers_ctx_t; + +typedef struct { + const pkgconf_vercmp_res_func_t rulecmp[PKGCONF_CMP_COUNT]; + const pkgconf_vercmp_res_func_t depcmp[PKGCONF_CMP_COUNT]; +} pkgconf_pkg_provides_vermatch_rule_t; + +static const pkgconf_pkg_provides_vermatch_rule_t pkgconf_pkg_provides_vermatch_rules[] = { + [PKGCONF_CMP_ANY] = { + .rulecmp = { + [PKGCONF_CMP_ANY] = pkgconf_pkg_comparator_none, + }, + .depcmp = { + [PKGCONF_CMP_ANY] = pkgconf_pkg_comparator_none, + }, + }, + [PKGCONF_CMP_LESS_THAN] = { + .rulecmp = { + [PKGCONF_CMP_ANY] = pkgconf_pkg_comparator_none, + [PKGCONF_CMP_LESS_THAN] = pkgconf_pkg_comparator_lt, + [PKGCONF_CMP_GREATER_THAN] = pkgconf_pkg_comparator_gt, + [PKGCONF_CMP_LESS_THAN_EQUAL] = pkgconf_pkg_comparator_lte, + [PKGCONF_CMP_GREATER_THAN_EQUAL] = pkgconf_pkg_comparator_gte, + }, + .depcmp = { + [PKGCONF_CMP_GREATER_THAN] = pkgconf_pkg_comparator_lt, + [PKGCONF_CMP_GREATER_THAN_EQUAL] = pkgconf_pkg_comparator_lt, + [PKGCONF_CMP_EQUAL] = pkgconf_pkg_comparator_lt, + [PKGCONF_CMP_NOT_EQUAL] = pkgconf_pkg_comparator_gte, + }, + }, + [PKGCONF_CMP_GREATER_THAN] = { + .rulecmp = { + [PKGCONF_CMP_ANY] = pkgconf_pkg_comparator_none, + [PKGCONF_CMP_LESS_THAN] = pkgconf_pkg_comparator_lt, + [PKGCONF_CMP_GREATER_THAN] = pkgconf_pkg_comparator_gt, + [PKGCONF_CMP_LESS_THAN_EQUAL] = pkgconf_pkg_comparator_lte, + [PKGCONF_CMP_GREATER_THAN_EQUAL] = pkgconf_pkg_comparator_gte, + }, + .depcmp = { + [PKGCONF_CMP_LESS_THAN] = pkgconf_pkg_comparator_gt, + [PKGCONF_CMP_LESS_THAN_EQUAL] = pkgconf_pkg_comparator_gt, + [PKGCONF_CMP_EQUAL] = pkgconf_pkg_comparator_gt, + [PKGCONF_CMP_NOT_EQUAL] = pkgconf_pkg_comparator_lte, + }, + }, + [PKGCONF_CMP_LESS_THAN_EQUAL] = { + .rulecmp = { + [PKGCONF_CMP_ANY] = pkgconf_pkg_comparator_none, + [PKGCONF_CMP_LESS_THAN] = pkgconf_pkg_comparator_lt, + [PKGCONF_CMP_GREATER_THAN] = pkgconf_pkg_comparator_gt, + [PKGCONF_CMP_LESS_THAN_EQUAL] = pkgconf_pkg_comparator_lte, + [PKGCONF_CMP_GREATER_THAN_EQUAL] = pkgconf_pkg_comparator_gte, + }, + .depcmp = { + [PKGCONF_CMP_GREATER_THAN] = pkgconf_pkg_comparator_lte, + [PKGCONF_CMP_GREATER_THAN_EQUAL] = pkgconf_pkg_comparator_lte, + [PKGCONF_CMP_EQUAL] = pkgconf_pkg_comparator_lte, + [PKGCONF_CMP_NOT_EQUAL] = pkgconf_pkg_comparator_gt, + }, + }, + [PKGCONF_CMP_GREATER_THAN_EQUAL] = { + .rulecmp = { + [PKGCONF_CMP_ANY] = pkgconf_pkg_comparator_none, + [PKGCONF_CMP_LESS_THAN] = pkgconf_pkg_comparator_lt, + [PKGCONF_CMP_GREATER_THAN] = pkgconf_pkg_comparator_gt, + [PKGCONF_CMP_LESS_THAN_EQUAL] = pkgconf_pkg_comparator_lte, + [PKGCONF_CMP_GREATER_THAN_EQUAL] = pkgconf_pkg_comparator_gte, + }, + .depcmp = { + [PKGCONF_CMP_LESS_THAN] = pkgconf_pkg_comparator_gte, + [PKGCONF_CMP_LESS_THAN_EQUAL] = pkgconf_pkg_comparator_gte, + [PKGCONF_CMP_EQUAL] = pkgconf_pkg_comparator_gte, + [PKGCONF_CMP_NOT_EQUAL] = pkgconf_pkg_comparator_lt, + }, + }, + [PKGCONF_CMP_EQUAL] = { + .rulecmp = { + [PKGCONF_CMP_ANY] = pkgconf_pkg_comparator_none, + [PKGCONF_CMP_LESS_THAN] = pkgconf_pkg_comparator_lt, + [PKGCONF_CMP_GREATER_THAN] = pkgconf_pkg_comparator_gt, + [PKGCONF_CMP_LESS_THAN_EQUAL] = pkgconf_pkg_comparator_lte, + [PKGCONF_CMP_GREATER_THAN_EQUAL] = pkgconf_pkg_comparator_gte, + [PKGCONF_CMP_EQUAL] = pkgconf_pkg_comparator_eq, + [PKGCONF_CMP_NOT_EQUAL] = pkgconf_pkg_comparator_ne + }, + .depcmp = { + [PKGCONF_CMP_ANY] = pkgconf_pkg_comparator_none, + }, + }, + [PKGCONF_CMP_NOT_EQUAL] = { + .rulecmp = { + [PKGCONF_CMP_ANY] = pkgconf_pkg_comparator_none, + [PKGCONF_CMP_LESS_THAN] = pkgconf_pkg_comparator_gte, + [PKGCONF_CMP_GREATER_THAN] = pkgconf_pkg_comparator_lte, + [PKGCONF_CMP_LESS_THAN_EQUAL] = pkgconf_pkg_comparator_gt, + [PKGCONF_CMP_GREATER_THAN_EQUAL] = pkgconf_pkg_comparator_lt, + [PKGCONF_CMP_EQUAL] = pkgconf_pkg_comparator_ne, + [PKGCONF_CMP_NOT_EQUAL] = pkgconf_pkg_comparator_eq + }, + .depcmp = { + [PKGCONF_CMP_ANY] = pkgconf_pkg_comparator_none, + }, + }, +}; + +/* + * pkgconf_pkg_scan_provides_vercmp(pkgdep, provider) + * + * compare a provides node against the requested dependency node. + * + * XXX: maybe handle PKGCONF_CMP_ANY in a versioned comparison + */ +static bool +pkgconf_pkg_scan_provides_vercmp(const pkgconf_dependency_t *pkgdep, const pkgconf_dependency_t *provider) +{ + const pkgconf_pkg_provides_vermatch_rule_t *rule = &pkgconf_pkg_provides_vermatch_rules[pkgdep->compare]; + + if (rule->depcmp[provider->compare] != NULL && + !rule->depcmp[provider->compare](provider->version, pkgdep->version)) + return false; + + if (rule->rulecmp[provider->compare] != NULL && + !rule->rulecmp[provider->compare](pkgdep->version, provider->version)) + return false; + + return true; +} + +/* + * pkgconf_pkg_scan_provides_entry(pkg, ctx) + * + * attempt to match a single package's Provides rules against the requested dependency node. + */ +static bool +pkgconf_pkg_scan_provides_entry(const pkgconf_pkg_t *pkg, const pkgconf_pkg_scan_providers_ctx_t *ctx) +{ + const pkgconf_dependency_t *pkgdep = ctx->pkgdep; + pkgconf_node_t *node; + + PKGCONF_FOREACH_LIST_ENTRY(pkg->provides.head, node) + { + const pkgconf_dependency_t *provider = node->data; + if (!strcmp(provider->package, pkgdep->package)) + return pkgconf_pkg_scan_provides_vercmp(pkgdep, provider); + } + + return false; +} + +/* + * pkgconf_pkg_scan_providers(client, pkgdep, eflags) + * + * scan all available packages to see if a Provides rule matches the pkgdep. + */ +static pkgconf_pkg_t * +pkgconf_pkg_scan_providers(pkgconf_client_t *client, pkgconf_dependency_t *pkgdep, unsigned int *eflags) +{ + pkgconf_pkg_t *pkg; + pkgconf_pkg_scan_providers_ctx_t ctx = { + .pkgdep = pkgdep, + }; + + pkg = pkgconf_scan_all(client, &ctx, (pkgconf_pkg_iteration_func_t) pkgconf_pkg_scan_provides_entry); + if (pkg != NULL) + { + pkgdep->match = pkgconf_pkg_ref(client, pkg); + return pkg; + } + + if (eflags != NULL) + *eflags |= PKGCONF_PKG_ERRF_PACKAGE_NOT_FOUND; + + return NULL; +} + +/* + * !doc + * + * .. c:function:: pkgconf_pkg_t *pkgconf_pkg_verify_dependency(pkgconf_client_t *client, pkgconf_dependency_t *pkgdep, unsigned int *eflags) + * + * Verify a pkgconf_dependency_t node in the depgraph. If the dependency is solvable, + * return the appropriate ``pkgconf_pkg_t`` object, else ``NULL``. + * + * :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution. + * :param pkgconf_dependency_t* pkgdep: The dependency graph node to solve. + * :param uint* eflags: An optional pointer that, if set, will be populated with an error code from the resolver. + * :return: On success, the appropriate ``pkgconf_pkg_t`` object to solve the dependency, else ``NULL``. + * :rtype: pkgconf_pkg_t * + */ +pkgconf_pkg_t * +pkgconf_pkg_verify_dependency(pkgconf_client_t *client, pkgconf_dependency_t *pkgdep, unsigned int *eflags) +{ + pkgconf_pkg_t *pkg = NULL; + + if (eflags != NULL) + *eflags = PKGCONF_PKG_ERRF_OK; + + PKGCONF_TRACE(client, "trying to verify dependency: %s", pkgdep->package); + + if (pkgdep->match != NULL) + { + PKGCONF_TRACE(client, "cached dependency: %s -> %s@%p", pkgdep->package, pkgdep->match->id, pkgdep->match); + return pkgconf_pkg_ref(client, pkgdep->match); + } + + pkg = pkgconf_pkg_find(client, pkgdep->package); + if (pkg == NULL) + { + if (client->flags & PKGCONF_PKG_PKGF_SKIP_PROVIDES) + { + if (eflags != NULL) + *eflags |= PKGCONF_PKG_ERRF_PACKAGE_NOT_FOUND; + + return NULL; + } + + return pkgconf_pkg_scan_providers(client, pkgdep, eflags); + } + + if (pkg->id == NULL) + pkg->id = strdup(pkgdep->package); + + if (pkgconf_pkg_comparator_impls[pkgdep->compare](pkg->version, pkgdep->version) != true) + { + if (eflags != NULL) + *eflags |= PKGCONF_PKG_ERRF_PACKAGE_VER_MISMATCH; + } + else + pkgdep->match = pkgconf_pkg_ref(client, pkg); + + return pkg; +} + +/* + * !doc + * + * .. c:function:: unsigned int pkgconf_pkg_verify_graph(pkgconf_client_t *client, pkgconf_pkg_t *root, int depth) + * + * Verify the graph dependency nodes are satisfiable by walking the tree using + * ``pkgconf_pkg_traverse()``. + * + * :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution. + * :param pkgconf_pkg_t* root: The root entry in the package dependency graph which should contain the top-level dependencies to resolve. + * :param int depth: The maximum allowed depth for dependency resolution. + * :return: On success, ``PKGCONF_PKG_ERRF_OK`` (0), else an error code. + * :rtype: unsigned int + */ +unsigned int +pkgconf_pkg_verify_graph(pkgconf_client_t *client, pkgconf_pkg_t *root, int depth) +{ + return pkgconf_pkg_traverse(client, root, NULL, NULL, depth, 0); +} + +static unsigned int +pkgconf_pkg_report_graph_error(pkgconf_client_t *client, pkgconf_pkg_t *parent, pkgconf_pkg_t *pkg, pkgconf_dependency_t *node, unsigned int eflags) +{ + if (eflags & PKGCONF_PKG_ERRF_PACKAGE_NOT_FOUND) + { + if (!(client->flags & PKGCONF_PKG_PKGF_SIMPLIFY_ERRORS) & !client->already_sent_notice) + { + pkgconf_error(client, "Package %s was not found in the pkg-config search path.\n", node->package); + pkgconf_error(client, "Perhaps you should add the directory containing `%s.pc'\n", node->package); + pkgconf_error(client, "to the PKG_CONFIG_PATH environment variable\n"); + client->already_sent_notice = true; + } + + pkgconf_error(client, "Package '%s', required by '%s', not found\n", node->package, parent->id); + pkgconf_audit_log(client, "%s NOT-FOUND\n", node->package); + } + else if (eflags & PKGCONF_PKG_ERRF_PACKAGE_VER_MISMATCH) + { + pkgconf_error(client, "Package dependency requirement '%s %s %s' could not be satisfied.\n", + node->package, pkgconf_pkg_get_comparator(node), node->version); + + if (pkg != NULL) + pkgconf_error(client, "Package '%s' has version '%s', required version is '%s %s'\n", + node->package, pkg->version, pkgconf_pkg_get_comparator(node), node->version); + } + + if (pkg != NULL) + pkgconf_pkg_unref(client, pkg); + + return eflags; +} + +static inline unsigned int +pkgconf_pkg_walk_list(pkgconf_client_t *client, + pkgconf_pkg_t *parent, + pkgconf_list_t *deplist, + pkgconf_pkg_traverse_func_t func, + void *data, + int depth, + unsigned int skip_flags) +{ + unsigned int eflags = PKGCONF_PKG_ERRF_OK; + pkgconf_node_t *node; + + PKGCONF_FOREACH_LIST_ENTRY(deplist->head, node) + { + unsigned int eflags_local = PKGCONF_PKG_ERRF_OK; + pkgconf_dependency_t *depnode = node->data; + pkgconf_pkg_t *pkgdep; + + if (*depnode->package == '\0') + continue; + + pkgdep = pkgconf_pkg_verify_dependency(client, depnode, &eflags_local); + + eflags |= eflags_local; + if (eflags_local != PKGCONF_PKG_ERRF_OK && !(client->flags & PKGCONF_PKG_PKGF_SKIP_ERRORS)) + { + pkgconf_pkg_report_graph_error(client, parent, pkgdep, depnode, eflags_local); + continue; + } + if (pkgdep == NULL) + continue; + + if (pkgdep->flags & PKGCONF_PKG_PROPF_SEEN) + { + pkgconf_pkg_unref(client, pkgdep); + continue; + } + + if (skip_flags && (depnode->flags & skip_flags) == skip_flags) + { + pkgconf_pkg_unref(client, pkgdep); + continue; + } + + pkgconf_audit_log_dependency(client, pkgdep, depnode); + + pkgdep->flags |= PKGCONF_PKG_PROPF_SEEN; + eflags |= pkgconf_pkg_traverse(client, pkgdep, func, data, depth - 1, skip_flags); + pkgdep->flags &= ~PKGCONF_PKG_PROPF_SEEN; + pkgconf_pkg_unref(client, pkgdep); + } + + return eflags; +} + +static inline unsigned int +pkgconf_pkg_walk_conflicts_list(pkgconf_client_t *client, + pkgconf_pkg_t *root, pkgconf_list_t *deplist) +{ + unsigned int eflags; + pkgconf_node_t *node, *childnode; + + PKGCONF_FOREACH_LIST_ENTRY(deplist->head, node) + { + pkgconf_dependency_t *parentnode = node->data; + + if (*parentnode->package == '\0') + continue; + + PKGCONF_FOREACH_LIST_ENTRY(root->required.head, childnode) + { + pkgconf_pkg_t *pkgdep; + pkgconf_dependency_t *depnode = childnode->data; + + if (*depnode->package == '\0' || strcmp(depnode->package, parentnode->package)) + continue; + + pkgdep = pkgconf_pkg_verify_dependency(client, parentnode, &eflags); + if (eflags == PKGCONF_PKG_ERRF_OK) + { + pkgconf_error(client, "Version '%s' of '%s' conflicts with '%s' due to satisfying conflict rule '%s %s%s%s'.\n", + pkgdep->version, pkgdep->realname, root->realname, parentnode->package, pkgconf_pkg_get_comparator(parentnode), + parentnode->version != NULL ? " " : "", parentnode->version != NULL ? parentnode->version : ""); + + if (!(client->flags & PKGCONF_PKG_PKGF_SIMPLIFY_ERRORS)) + { + pkgconf_error(client, "It may be possible to ignore this conflict and continue, try the\n"); + pkgconf_error(client, "PKG_CONFIG_IGNORE_CONFLICTS environment variable.\n"); + } + + pkgconf_pkg_unref(client, pkgdep); + + return PKGCONF_PKG_ERRF_PACKAGE_CONFLICT; + } + + pkgconf_pkg_unref(client, pkgdep); + } + } + + return PKGCONF_PKG_ERRF_OK; +} + +/* + * !doc + * + * .. c:function:: unsigned int pkgconf_pkg_traverse(pkgconf_client_t *client, pkgconf_pkg_t *root, pkgconf_pkg_traverse_func_t func, void *data, int maxdepth, unsigned int skip_flags) + * + * Walk and resolve the dependency graph up to `maxdepth` levels. + * + * :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution. + * :param pkgconf_pkg_t* root: The root of the dependency graph. + * :param pkgconf_pkg_traverse_func_t func: A traversal function to call for each resolved node in the dependency graph. + * :param void* data: An opaque pointer to data to be passed to the traversal function. + * :param int maxdepth: The maximum depth to walk the dependency graph for. -1 means infinite recursion. + * :param uint skip_flags: Skip over dependency nodes containing the specified flags. A setting of 0 skips no dependency nodes. + * :return: ``PKGCONF_PKG_ERRF_OK`` on success, else an error code. + * :rtype: unsigned int + */ +unsigned int +pkgconf_pkg_traverse(pkgconf_client_t *client, + pkgconf_pkg_t *root, + pkgconf_pkg_traverse_func_t func, + void *data, + int maxdepth, + unsigned int skip_flags) +{ + unsigned int eflags = PKGCONF_PKG_ERRF_OK; + + if (maxdepth == 0) + return eflags; + + PKGCONF_TRACE(client, "%s: level %d", root->id, maxdepth); + + if ((root->flags & PKGCONF_PKG_PROPF_VIRTUAL) != PKGCONF_PKG_PROPF_VIRTUAL || (client->flags & PKGCONF_PKG_PKGF_SKIP_ROOT_VIRTUAL) != PKGCONF_PKG_PKGF_SKIP_ROOT_VIRTUAL) + { + if (func != NULL) + func(client, root, data); + } + + if (!(client->flags & PKGCONF_PKG_PKGF_SKIP_CONFLICTS)) + { + eflags = pkgconf_pkg_walk_conflicts_list(client, root, &root->conflicts); + if (eflags != PKGCONF_PKG_ERRF_OK) + return eflags; + } + + PKGCONF_TRACE(client, "%s: walking requires list", root->id); + eflags = pkgconf_pkg_walk_list(client, root, &root->required, func, data, maxdepth, skip_flags); + if (eflags != PKGCONF_PKG_ERRF_OK) + return eflags; + + if (client->flags & PKGCONF_PKG_PKGF_SEARCH_PRIVATE) + { + PKGCONF_TRACE(client, "%s: walking requires.private list", root->id); + + /* XXX: ugly */ + client->flags |= PKGCONF_PKG_PKGF_ITER_PKG_IS_PRIVATE; + eflags = pkgconf_pkg_walk_list(client, root, &root->requires_private, func, data, maxdepth, skip_flags); + client->flags &= ~PKGCONF_PKG_PKGF_ITER_PKG_IS_PRIVATE; + + if (eflags != PKGCONF_PKG_ERRF_OK) + return eflags; + } + + return eflags; +} + +static void +pkgconf_pkg_cflags_collect(pkgconf_client_t *client, pkgconf_pkg_t *pkg, void *data) +{ + pkgconf_list_t *list = data; + pkgconf_node_t *node; + + PKGCONF_FOREACH_LIST_ENTRY(pkg->cflags.head, node) + { + pkgconf_fragment_t *frag = node->data; + pkgconf_fragment_copy(client, list, frag, false); + } +} + +static void +pkgconf_pkg_cflags_private_collect(pkgconf_client_t *client, pkgconf_pkg_t *pkg, void *data) +{ + pkgconf_list_t *list = data; + pkgconf_node_t *node; + + PKGCONF_FOREACH_LIST_ENTRY(pkg->cflags_private.head, node) + { + pkgconf_fragment_t *frag = node->data; + pkgconf_fragment_copy(client, list, frag, true); + } +} + +/* + * !doc + * + * .. c:function:: int pkgconf_pkg_cflags(pkgconf_client_t *client, pkgconf_pkg_t *root, pkgconf_list_t *list, int maxdepth) + * + * Walks a dependency graph and extracts relevant ``CFLAGS`` fragments. + * + * :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution. + * :param pkgconf_pkg_t* root: The root of the dependency graph. + * :param pkgconf_list_t* list: The fragment list to add the extracted ``CFLAGS`` fragments to. + * :param int maxdepth: The maximum allowed depth for dependency resolution. -1 means infinite recursion. + * :return: ``PKGCONF_PKG_ERRF_OK`` if successful, otherwise an error code. + * :rtype: unsigned int + */ +unsigned int +pkgconf_pkg_cflags(pkgconf_client_t *client, pkgconf_pkg_t *root, pkgconf_list_t *list, int maxdepth) +{ + unsigned int eflag; + unsigned int skip_flags = (client->flags & PKGCONF_PKG_PKGF_DONT_FILTER_INTERNAL_CFLAGS) == 0 ? PKGCONF_PKG_DEPF_INTERNAL : 0; + pkgconf_list_t frags = PKGCONF_LIST_INITIALIZER; + + eflag = pkgconf_pkg_traverse(client, root, pkgconf_pkg_cflags_collect, &frags, maxdepth, skip_flags); + + if (eflag == PKGCONF_PKG_ERRF_OK && client->flags & PKGCONF_PKG_PKGF_MERGE_PRIVATE_FRAGMENTS) + eflag = pkgconf_pkg_traverse(client, root, pkgconf_pkg_cflags_private_collect, &frags, maxdepth, skip_flags); + + if (eflag != PKGCONF_PKG_ERRF_OK) + { + pkgconf_fragment_free(&frags); + return eflag; + } + + pkgconf_fragment_copy_list(client, list, &frags); + pkgconf_fragment_free(&frags); + + return eflag; +} + +static void +pkgconf_pkg_libs_collect(pkgconf_client_t *client, pkgconf_pkg_t *pkg, void *data) +{ + pkgconf_list_t *list = data; + pkgconf_node_t *node; + + PKGCONF_FOREACH_LIST_ENTRY(pkg->libs.head, node) + { + pkgconf_fragment_t *frag = node->data; + pkgconf_fragment_copy(client, list, frag, (client->flags & PKGCONF_PKG_PKGF_ITER_PKG_IS_PRIVATE) != 0); + } + + if (client->flags & PKGCONF_PKG_PKGF_MERGE_PRIVATE_FRAGMENTS) + { + PKGCONF_FOREACH_LIST_ENTRY(pkg->libs_private.head, node) + { + pkgconf_fragment_t *frag = node->data; + pkgconf_fragment_copy(client, list, frag, true); + } + } +} + +/* + * !doc + * + * .. c:function:: int pkgconf_pkg_libs(pkgconf_client_t *client, pkgconf_pkg_t *root, pkgconf_list_t *list, int maxdepth) + * + * Walks a dependency graph and extracts relevant ``LIBS`` fragments. + * + * :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution. + * :param pkgconf_pkg_t* root: The root of the dependency graph. + * :param pkgconf_list_t* list: The fragment list to add the extracted ``LIBS`` fragments to. + * :param int maxdepth: The maximum allowed depth for dependency resolution. -1 means infinite recursion. + * :return: ``PKGCONF_PKG_ERRF_OK`` if successful, otherwise an error code. + * :rtype: unsigned int + */ +unsigned int +pkgconf_pkg_libs(pkgconf_client_t *client, pkgconf_pkg_t *root, pkgconf_list_t *list, int maxdepth) +{ + unsigned int eflag; + + eflag = pkgconf_pkg_traverse(client, root, pkgconf_pkg_libs_collect, list, maxdepth, 0); + + if (eflag != PKGCONF_PKG_ERRF_OK) + { + pkgconf_fragment_free(list); + return eflag; + } + + return eflag; +} diff --git a/libpkgconf/libpkgconf/pkg.c.orig b/libpkgconf/libpkgconf/pkg.c.orig new file mode 120000 index 0000000..ea9e3fd --- /dev/null +++ b/libpkgconf/libpkgconf/pkg.c.orig @@ -0,0 +1 @@ +../../upstream/libpkgconf/pkg.c \ No newline at end of file diff --git a/libpkgconf/manifest b/libpkgconf/manifest index 6099c0f..d40c3fe 100644 --- a/libpkgconf/manifest +++ b/libpkgconf/manifest @@ -1,6 +1,6 @@ : 1 name: libpkgconf -version: 1.6.3+3 +version: 1.7.3-a.0.z project: pkgconf summary: C library for retriving pkg-config compiler and linker flags license: ISC, MIT ; ISC for the most of original files. diff --git a/upstream b/upstream index c862e03..aca0674 160000 --- a/upstream +++ b/upstream @@ -1 +1 @@ -Subproject commit c862e030cf83447f679e4f49876f5298f0fc9691 +Subproject commit aca0674837cb6df1b29faddb8afe6b2f39733f6b -- cgit v1.1