diff options
author | Karen Arutyunov <karen@codesynthesis.com> | 2017-12-12 16:04:02 +0300 |
---|---|---|
committer | Karen Arutyunov <karen@codesynthesis.com> | 2017-12-12 16:04:02 +0300 |
commit | 429162ba11e58758e5e4ac4f03239891fc3e189c (patch) | |
tree | a530c0621a5cb3fe18c8d85392658458d02d6ea1 | |
parent | a18413342bc088e10a56ff70dca8f3007a83085d (diff) |
Merge with latest upstream package version (master branch)
-rw-r--r-- | libpkgconf/argvsplit.c | 12 | ||||
-rw-r--r-- | libpkgconf/argvsplit.c.orig | 157 | ||||
-rw-r--r-- | libpkgconf/cache.c | 4 | ||||
-rw-r--r-- | libpkgconf/client.c | 3 | ||||
-rw-r--r-- | libpkgconf/dependency.c | 3 | ||||
-rw-r--r-- | libpkgconf/fragment.c | 171 | ||||
-rw-r--r-- | libpkgconf/fragment.c.orig | 658 | ||||
-rw-r--r-- | libpkgconf/libpkgconf.h | 22 | ||||
-rw-r--r-- | libpkgconf/libpkgconf.h.orig | 331 | ||||
-rw-r--r-- | libpkgconf/pkg.c | 57 | ||||
-rw-r--r-- | libpkgconf/pkg.c.orig | 1608 | ||||
-rw-r--r-- | libpkgconf/queue.c | 4 | ||||
-rw-r--r-- | libpkgconf/queue.c.orig | 195 | ||||
-rw-r--r-- | libpkgconf/tuple.c | 2 | ||||
-rw-r--r-- | tests/basic/driver.c | 6 |
15 files changed, 822 insertions, 2411 deletions
diff --git a/libpkgconf/argvsplit.c b/libpkgconf/argvsplit.c index 4e40327..4553154 100644 --- a/libpkgconf/argvsplit.c +++ b/libpkgconf/argvsplit.c @@ -89,18 +89,8 @@ pkgconf_argv_split(const char *src, int *argc, char ***argv) } else { - /* - * There is no reason to keep space character escaped in fragment - * objects, especially given that other characters are unescaped (issue - * #139 is reported). - * - * Update: as a part of the issue #140 fix the backslash is now - * escaped as well. - */ - /* - if (isspace((unsigned int) *src_iter) || *src_iter == '\\') + if (*src_iter == '\\') *dst_iter++ = '\\'; - */ *dst_iter++ = *src_iter; } diff --git a/libpkgconf/argvsplit.c.orig b/libpkgconf/argvsplit.c.orig deleted file mode 100644 index a9e8efa..0000000 --- a/libpkgconf/argvsplit.c.orig +++ /dev/null @@ -1,157 +0,0 @@ -/* - * argvsplit.c - * argv_split() routine - * - * Copyright (c) 2012, 2017 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 <libpkgconf/stdinc.h> -#include <libpkgconf/libpkgconf.h> - -/* - * !doc - * - * libpkgconf `argvsplit` module - * ============================= - * - * This is a lowlevel module which provides parsing of strings into argument vectors, - * similar to what a shell would do. - */ - -/* - * !doc - * - * .. c:function:: void pkgconf_argv_free(char **argv) - * - * Frees an argument vector. - * - * :param char** argv: The argument vector to free. - * :return: nothing - */ -void -pkgconf_argv_free(char **argv) -{ - free(argv[0]); - free(argv); -} - -/* - * !doc - * - * .. c:function:: int pkgconf_argv_split(const char *src, int *argc, char ***argv) - * - * Splits a string into an argument vector. - * - * :param char* src: The string to split. - * :param int* argc: A pointer to an integer to store the argument count. - * :param char*** argv: A pointer to a pointer for an argument vector. - * :return: 0 on success, -1 on error. - * :rtype: int - */ -int -pkgconf_argv_split(const char *src, int *argc, char ***argv) -{ - char *buf = malloc(strlen(src) + 1); - const char *src_iter; - char *dst_iter; - int argc_count = 0; - int argv_size = 5; - char quote = 0; - bool escaped = false; - - src_iter = src; - dst_iter = buf; - - memset(buf, 0, strlen(src) + 1); - - *argv = calloc(sizeof (void *), argv_size); - (*argv)[argc_count] = dst_iter; - - while (*src_iter) - { - if (escaped) - { - /* POSIX: only \CHAR is special inside a double quote if CHAR is {$, `, ", \, newline}. */ - if (quote == '\"') - { - if (!(*src_iter == '$' || *src_iter == '`' || *src_iter == '"' || *src_iter == '\\')) - *dst_iter++ = '\\'; - - *dst_iter++ = *src_iter; - } - else - { - if (isspace((unsigned int) *src_iter) || *src_iter == '\\') - *dst_iter++ = '\\'; - - *dst_iter++ = *src_iter; - } - - escaped = false; - } - else if (quote) - { - if (*src_iter == quote) - quote = 0; - else if (*src_iter == '\\') - escaped = true; - else - *dst_iter++ = *src_iter; - } - else if (isspace((unsigned int)*src_iter)) - { - if ((*argv)[argc_count] != NULL) - { - argc_count++, dst_iter++; - - if (argc_count == argv_size) - { - argv_size += 5; - *argv = realloc(*argv, sizeof(void *) * argv_size); - } - - (*argv)[argc_count] = dst_iter; - } - } - else switch(*src_iter) - { - case '\\': - escaped = true; - break; - - case '\"': - case '\'': - quote = *src_iter; - break; - - default: - *dst_iter++ = *src_iter; - break; - } - - src_iter++; - } - - if (escaped || quote) - { - free(*argv); - free(buf); - return -1; - } - - if (strlen((*argv)[argc_count])) - { - argc_count++; - } - - *argc = argc_count; - return 0; -} diff --git a/libpkgconf/cache.c b/libpkgconf/cache.c index 1662ce5..7715e33 100644 --- a/libpkgconf/cache.c +++ b/libpkgconf/cache.c @@ -44,7 +44,7 @@ * :rtype: pkgconf_pkg_t * */ pkgconf_pkg_t * -pkgconf_cache_lookup(const pkgconf_client_t *client, const char *id) +pkgconf_cache_lookup(pkgconf_client_t *client, const char *id) { pkgconf_node_t *node; @@ -134,7 +134,7 @@ pkgconf_cache_free(pkgconf_client_t *client) PKGCONF_FOREACH_LIST_ENTRY_SAFE(client->pkg_cache.head, iter2, iter) { pkgconf_pkg_t *pkg = iter->data; - pkgconf_pkg_free(client, pkg); + pkgconf_pkg_unref(client, pkg); } memset(&client->pkg_cache, 0, sizeof client->pkg_cache); diff --git a/libpkgconf/client.c b/libpkgconf/client.c index f5f4717..811e043 100644 --- a/libpkgconf/client.c +++ b/libpkgconf/client.c @@ -324,6 +324,9 @@ pkgconf_trace(const pkgconf_client_t *client, const char *filename, size_t linen size_t len; va_list va; + if (client == NULL) + return false; + len = snprintf(errbuf, sizeof errbuf, "%s:" SIZE_FMT_SPECIFIER " [%s]: ", filename, lineno, funcname); va_start(va, format); diff --git a/libpkgconf/dependency.c b/libpkgconf/dependency.c index 1a4ec68..cd01a06 100644 --- a/libpkgconf/dependency.c +++ b/libpkgconf/dependency.c @@ -132,6 +132,9 @@ pkgconf_dependency_free(pkgconf_list_t *list) { pkgconf_dependency_t *dep = node->data; + if (dep->match != NULL) + pkgconf_pkg_unref(NULL, dep->match); + if (dep->package != NULL) free(dep->package); diff --git a/libpkgconf/fragment.c b/libpkgconf/fragment.c index 7576db7..78a3463 100644 --- a/libpkgconf/fragment.c +++ b/libpkgconf/fragment.c @@ -173,6 +173,7 @@ pkgconf_fragment_add(const pkgconf_client_t *client, pkgconf_list_t *list, const free(parent->data); parent->data = newdata; + parent->merged = true; /* use a copy operation to force a dedup */ pkgconf_node_delete(&parent->iter, list); @@ -350,7 +351,9 @@ pkgconf_fragment_copy(const pkgconf_client_t *client, pkgconf_list_t *list, cons frag = calloc(sizeof(pkgconf_fragment_t), 1); frag->type = base->type; - frag->data = strdup(base->data); + frag->merged = base->merged; + if (base->data != NULL) + frag->data = strdup(base->data); pkgconf_node_insert_tail(&frag->iter, frag, list); } @@ -383,17 +386,18 @@ pkgconf_fragment_filter(const pkgconf_client_t *client, pkgconf_list_t *dest, pk } } -static inline char * -fragment_escape(const char *src) +static inline bool +fragment_should_quote(const pkgconf_fragment_t *frag) { - ssize_t outlen = strlen(src) + 10; - char *out = calloc(outlen, 1); - char *dst = out; + const char *src; - while (*src) + if (frag->data == NULL) + return false; + + for (src = frag->data; *src; src++) { if (((*src < ' ') || - (*src > ' ' && *src < '$') || + (*src >= (' ' + (frag->merged ? 1 : 0)) && *src < '$') || (*src > '$' && *src < '(') || (*src > ')' && *src < '+') || (*src > ':' && *src < '=') || @@ -402,23 +406,14 @@ fragment_escape(const char *src) (*src == '`') || (*src > 'z' && *src < '~') || (*src > '~')) && *src != '\\') - *dst++ = '\\'; - - *dst++ = *src++; - - if ((ptrdiff_t)(dst - out) + 2 > outlen) - { - outlen *= 2; - out = realloc(out, outlen); - } + return true; } - *dst = 0; - return out; + return false; } static inline size_t -pkgconf_fragment_len(const pkgconf_fragment_t *frag, bool escape) +pkgconf_fragment_len(const pkgconf_fragment_t *frag) { size_t len = 1; @@ -427,62 +422,37 @@ pkgconf_fragment_len(const pkgconf_fragment_t *frag, bool escape) if (frag->data != NULL) { - if (!escape) - len += strlen(frag->data); - else - { - char *tmp = fragment_escape(frag->data); - len += strlen(tmp); - free(tmp); - } + len += strlen(frag->data); + + if (fragment_should_quote(frag)) + len += 2; } return len; } -/* - * !doc - * - * .. c:function:: size_t pkgconf_fragment_render_len(const pkgconf_list_t *list) - * - * 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. - * :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) +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, escape); + out += pkgconf_fragment_len(frag); } return out; } -/* - * !doc - * - * .. c:function:: void pkgconf_fragment_render_buf(const pkgconf_list_t *list, char *buf, size_t buflen) - * - * 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. - * :return: nothing - */ -void -pkgconf_fragment_render_buf(const pkgconf_list_t *list, char *buf, size_t buflen, bool escape) +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; @@ -492,10 +462,14 @@ pkgconf_fragment_render_buf(const pkgconf_list_t *list, char *buf, size_t buflen { const pkgconf_fragment_t *frag = node->data; size_t buf_remaining = buflen - (bptr - buf); + bool should_quote = fragment_should_quote(frag); - if (pkgconf_fragment_len(frag, escape) > buf_remaining) + if (pkgconf_fragment_len(frag) > buf_remaining) break; + if (should_quote) + *bptr++ = '\''; + if (frag->type) { *bptr++ = '-'; @@ -503,16 +477,10 @@ pkgconf_fragment_render_buf(const pkgconf_list_t *list, char *buf, size_t buflen } if (frag->data) - { - if (!escape) - bptr += pkgconf_strlcpy(bptr, frag->data, buf_remaining); - else - { - char *tmp = fragment_escape(frag->data); - bptr += pkgconf_strlcpy(bptr, tmp, buf_remaining); - free(tmp); - } - } + bptr += pkgconf_strlcpy(bptr, frag->data, buf_remaining); + + if (should_quote) + *bptr++ = '\''; *bptr++ = ' '; } @@ -520,6 +488,60 @@ pkgconf_fragment_render_buf(const pkgconf_list_t *list, char *buf, size_t buflen *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; + + /* + * The function must not return a value (issue #162 is reported). + */ + ops->render_buf(list, buf, buflen, true); +} + /* * !doc * @@ -528,17 +550,20 @@ pkgconf_fragment_render_buf(const pkgconf_list_t *list, char *buf, size_t buflen * 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. + * :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) +pkgconf_fragment_render(const pkgconf_list_t *list, bool escape, const pkgconf_fragment_render_ops_t *ops) { - size_t buflen = pkgconf_fragment_render_len(list, escape); + (void) escape; + + size_t buflen = pkgconf_fragment_render_len(list, true, ops); char *buf = calloc(1, buflen); - pkgconf_fragment_render_buf(list, buf, buflen, escape); + pkgconf_fragment_render_buf(list, buf, buflen, true, ops); return buf; } diff --git a/libpkgconf/fragment.c.orig b/libpkgconf/fragment.c.orig new file mode 100644 index 0000000..feb12f9 --- /dev/null +++ b/libpkgconf/fragment.c.orig @@ -0,0 +1,658 @@ +/* + * 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 <libpkgconf/stdinc.h> +#include <libpkgconf/libpkgconf.h> + +/* + * !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) + { + 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_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 bool +fragment_should_quote(const pkgconf_fragment_t *frag) +{ + const char *src; + + if (frag->data == NULL) + return false; + + for (src = frag->data; *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 > '~')) && *src != '\\') + return true; + } + + return false; +} + +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) + { + len += strlen(frag->data); + + if (fragment_should_quote(frag)) + len += 2; + } + + 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); + bool should_quote = fragment_should_quote(frag); + + if (pkgconf_fragment_len(frag) > buf_remaining) + break; + + if (should_quote) + *bptr++ = '\''; + + if (frag->type) + { + *bptr++ = '-'; + *bptr++ = frag->type; + } + + if (frag->data) + bptr += pkgconf_strlcpy(bptr, frag->data, buf_remaining); + + if (should_quote) + *bptr++ = '\''; + + *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; + return 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.h b/libpkgconf/libpkgconf.h index b07ec68..0e8d83e 100644 --- a/libpkgconf/libpkgconf.h +++ b/libpkgconf/libpkgconf.h @@ -78,6 +78,8 @@ struct pkgconf_fragment_ { char type; char *data; + + bool merged; }; struct pkgconf_dependency_ { @@ -87,6 +89,7 @@ struct pkgconf_dependency_ { pkgconf_pkg_comparator_t compare; char *version; pkgconf_pkg_t *parent; + pkgconf_pkg_t *match; }; struct pkgconf_tuple_ { @@ -128,7 +131,7 @@ struct pkgconf_pkg_ { pkgconf_list_t cflags; pkgconf_list_t cflags_private; - pkgconf_list_t requires_; /* Keyword in C++20. */ + 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; @@ -136,6 +139,8 @@ struct pkgconf_pkg_ { pkgconf_list_t vars; unsigned int flags; + + pkgconf_client_t *owner; }; typedef bool (*pkgconf_pkg_iteration_func_t)(const pkgconf_pkg_t *pkg, void *data); @@ -242,7 +247,7 @@ PKGCONF_API bool pkgconf_default_error_handler(const char *msg, const pkgconf_cl } while (0); #endif -PKGCONF_API pkgconf_pkg_t *pkgconf_pkg_ref(const pkgconf_client_t *client, pkgconf_pkg_t *pkg); +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); @@ -272,6 +277,11 @@ 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); @@ -279,9 +289,9 @@ PKGCONF_API void pkgconf_fragment_copy(const pkgconf_client_t *client, pkgconf_l 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); -PKGCONF_API void pkgconf_fragment_render_buf(const pkgconf_list_t *list, char *buf, size_t len, bool escape); -PKGCONF_API char *pkgconf_fragment_render(const pkgconf_list_t *list, bool escape); +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 */ @@ -306,7 +316,7 @@ PKGCONF_API bool pkgconf_queue_apply(pkgconf_client_t *client, pkgconf_list_t *l 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(const pkgconf_client_t *client, const char *id); +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); diff --git a/libpkgconf/libpkgconf.h.orig b/libpkgconf/libpkgconf.h.orig deleted file mode 100644 index ac873bf..0000000 --- a/libpkgconf/libpkgconf.h.orig +++ /dev/null @@ -1,331 +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 <stdio.h> -#include <stdarg.h> -#include <stddef.h> -#include <stdbool.h> -#include <libpkgconf/iter.h> -#include <libpkgconf/bsdstubs.h> -#include <libpkgconf/libpkgconf-api.h> - -#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 - -#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; - -#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) - -struct pkgconf_fragment_ { - pkgconf_node_t iter; - - char type; - char *data; -}; - -struct pkgconf_dependency_ { - pkgconf_node_t iter; - - char *package; - pkgconf_pkg_comparator_t compare; - char *version; - pkgconf_pkg_t *parent; -}; - -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 requires; - pkgconf_list_t requires_private; - pkgconf_list_t conflicts; - pkgconf_list_t provides; - - pkgconf_list_t vars; - - unsigned int flags; -}; - -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; -}; - -/* client.c */ -PKGCONF_API void pkgconf_client_init(pkgconf_client_t *client, pkgconf_error_handler_func_t error_handler, void *error_handler_data); -PKGCONF_API pkgconf_client_t * pkgconf_client_new(pkgconf_error_handler_func_t error_handler, void *error_handler_data); -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); - -#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_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 - -/* pkg.c */ -#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__) */ - -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); - -#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 - -PKGCONF_API pkgconf_pkg_t *pkgconf_pkg_ref(const 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); -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); -PKGCONF_API void pkgconf_pkg_dir_list_build(pkgconf_client_t *client); - -/* 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); -PKGCONF_API void pkgconf_dependency_parse(const pkgconf_client_t *client, pkgconf_pkg_t *pkg, pkgconf_list_t *deplist_head, const char *depends); -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); - -/* 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 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_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); -PKGCONF_API void pkgconf_fragment_render_buf(const pkgconf_list_t *list, char *buf, size_t len, bool escape); -PKGCONF_API char *pkgconf_fragment_render(const pkgconf_list_t *list, bool escape); -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(const 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); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/libpkgconf/pkg.c b/libpkgconf/pkg.c index 3e3268a..6918d6d 100644 --- a/libpkgconf/pkg.c +++ b/libpkgconf/pkg.c @@ -181,7 +181,7 @@ static const pkgconf_pkg_parser_keyword_pair_t pkgconf_pkg_parser_keyword_funcs[ {"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, requires_)}, + {"Requires", pkgconf_pkg_parser_dependency_func, offsetof(pkgconf_pkg_t, required)}, {"Requires.private", pkgconf_pkg_parser_dependency_func, offsetof(pkgconf_pkg_t, requires_private)}, {"Version", pkgconf_pkg_parser_tuple_func, offsetof(pkgconf_pkg_t, version)}, }; @@ -423,7 +423,7 @@ pkgconf_pkg_free(pkgconf_client_t *client, pkgconf_pkg_t *pkg) pkgconf_cache_remove(client, pkg); - pkgconf_dependency_free(&pkg->requires_); + pkgconf_dependency_free(&pkg->required); pkgconf_dependency_free(&pkg->requires_private); pkgconf_dependency_free(&pkg->conflicts); pkgconf_dependency_free(&pkg->provides); @@ -475,11 +475,15 @@ pkgconf_pkg_free(pkgconf_client_t *client, pkgconf_pkg_t *pkg) * :rtype: pkgconf_pkg_t * */ pkgconf_pkg_t * -pkgconf_pkg_ref(const pkgconf_client_t *client, pkgconf_pkg_t *pkg) +pkgconf_pkg_ref(pkgconf_client_t *client, pkgconf_pkg_t *pkg) { - (void) client; + 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->owner = client; pkg->refcount++; + PKGCONF_TRACE(client, "refcount@%p: %d", pkg, pkg->refcount); + return pkg; } @@ -497,9 +501,14 @@ pkgconf_pkg_ref(const pkgconf_client_t *client, pkgconf_pkg_t *pkg) 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(client, pkg); + pkgconf_pkg_free(pkg->owner, pkg); } static inline pkgconf_pkg_t * @@ -1264,6 +1273,12 @@ pkgconf_pkg_verify_dependency(pkgconf_client_t *client, pkgconf_dependency_t *pk 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) { @@ -1281,11 +1296,13 @@ pkgconf_pkg_verify_dependency(pkgconf_client_t *client, pkgconf_dependency_t *pk if (pkg->id == NULL) pkg->id = strdup(pkgdep->package); - if (pkgconf_pkg_comparator_impls[pkgdep->compare](pkg->version, pkgdep->version) == true) - return pkg; - - if (eflags != NULL) - *eflags |= PKGCONF_PKG_ERRF_PACKAGE_VER_MISMATCH; + 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; } @@ -1331,11 +1348,7 @@ pkgconf_pkg_report_graph_error(pkgconf_client_t *client, pkgconf_pkg_t *parent, pkgconf_error(client, "Package dependency requirement '%s %s %s' could not be satisfied.\n", node->package, pkgconf_pkg_get_comparator(node), node->version); - /* - * Add support for PKGCONF_PKG_PKGF_SIMPLIFY_ERRORS flag (issue #134 is - * reported). - */ - if (pkg != NULL && !(client->flags & PKGCONF_PKG_PKGF_SIMPLIFY_ERRORS)) + 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); } @@ -1408,7 +1421,7 @@ pkgconf_pkg_walk_conflicts_list(pkgconf_client_t *client, if (*parentnode->package == '\0') continue; - PKGCONF_FOREACH_LIST_ENTRY(root->requires_.head, childnode) + PKGCONF_FOREACH_LIST_ENTRY(root->required.head, childnode) { pkgconf_pkg_t *pkgdep; pkgconf_dependency_t *depnode = childnode->data; @@ -1423,15 +1436,11 @@ pkgconf_pkg_walk_conflicts_list(pkgconf_client_t *client, pkgdep->version, pkgdep->realname, root->realname, parentnode->package, pkgconf_pkg_get_comparator(parentnode), parentnode->version != NULL ? " " : "", parentnode->version != NULL ? parentnode->version : ""); - /* - * Add support for PKGCONF_PKG_PKGF_SIMPLIFY_ERRORS flag (issue #134 is - * reported). - */ - if (!(client->flags & PKGCONF_PKG_PKGF_SIMPLIFY_ERRORS)) - { + 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); @@ -1488,7 +1497,7 @@ pkgconf_pkg_traverse(pkgconf_client_t *client, } PKGCONF_TRACE(client, "%s: walking requires list", root->id); - eflags = pkgconf_pkg_walk_list(client, root, &root->requires_, func, data, maxdepth); + eflags = pkgconf_pkg_walk_list(client, root, &root->required, func, data, maxdepth); if (eflags != PKGCONF_PKG_ERRF_OK) return eflags; diff --git a/libpkgconf/pkg.c.orig b/libpkgconf/pkg.c.orig deleted file mode 100644 index ea970b8..0000000 --- a/libpkgconf/pkg.c.orig +++ /dev/null @@ -1,1608 +0,0 @@ -/* - * 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 <libpkgconf/stdinc.h> -#include <libpkgconf/config.h> -#include <libpkgconf/libpkgconf.h> - -/* - * !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 inline const char * -get_default_pkgconfig_path(char *outbuf, size_t outlen) -{ -#ifdef _WIN32 - char namebuf[MAX_PATH]; - char *p; - - int sizepath = GetModuleFileName(NULL, namebuf, sizeof namebuf); - char * winslash; - namebuf[sizepath] = '\0'; - while ((winslash = strchr (namebuf, '\\')) != NULL) - { - *winslash = '/'; - } - p = strrchr(namebuf, '/'); - if (p == NULL) - return PKG_DEFAULT_PATH; - - *p = '\0'; - pkgconf_strlcpy(outbuf, namebuf, outlen); - pkgconf_strlcat(outbuf, "/", outlen); - pkgconf_strlcat(outbuf, "../lib/pkgconfig", outlen); - pkgconf_strlcat(outbuf, ";", outlen); - pkgconf_strlcat(outbuf, namebuf, outlen); - pkgconf_strlcat(outbuf, "/", outlen); - pkgconf_strlcat(outbuf, "../share/pkgconfig", outlen); - - return outbuf; -#else - (void) outbuf; - (void) outlen; -#endif - - return PKG_DEFAULT_PATH; -} - -static const char * -pkg_get_parent_dir(pkgconf_pkg_t *pkg, char *buf, size_t buflen) -{ - char *pathbuf; - - pkgconf_strlcpy(buf, pkg->filename, buflen); - pathbuf = strrchr(buf, PKG_DIR_SEP_S); - if (pathbuf == NULL) - pathbuf = strrchr(buf, '/'); - if (pathbuf != NULL) - pathbuf[0] = '\0'; - - return buf; -} - -/* - * !doc - * - * .. c:function:: void pkgconf_pkg_dir_list_build(pkgconf_client_t *client) - * - * Bootstraps the package search paths. If the ``PKGCONF_PKG_PKGF_ENV_ONLY`` `flag` is set on the client, - * then only the ``PKG_CONFIG_PATH`` environment variable will be used, otherwise both the - * ``PKG_CONFIG_PATH`` and ``PKG_CONFIG_LIBDIR`` environment variables will be used. - * - * :param pkgconf_client_t* client: The pkgconf client object to bootstrap. - * :return: nothing - */ -void -pkgconf_pkg_dir_list_build(pkgconf_client_t *client) -{ - pkgconf_path_build_from_environ("PKG_CONFIG_PATH", NULL, &client->dir_list, true); - - if (!(client->flags & PKGCONF_PKG_PKGF_ENV_ONLY)) - { - char pathbuf[PKGCONF_BUFSIZE]; - - pkgconf_path_build_from_environ("PKG_CONFIG_LIBDIR", get_default_pkgconfig_path(pathbuf, sizeof pathbuf), &client->dir_list, true); - } -} - -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, 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, 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_fragment_func(const pkgconf_client_t *client, pkgconf_pkg_t *pkg, const char *keyword, const size_t lineno, const ptrdiff_t offset, 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, char *value) -{ - (void) keyword; - (void) lineno; - - pkgconf_list_t *dest = (pkgconf_list_t *)((char *) pkg + offset); - pkgconf_dependency_parse(client, pkg, dest, value); -} - -/* 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, requires)}, - {"Requires.private", pkgconf_pkg_parser_dependency_func, offsetof(pkgconf_pkg_t, requires_private)}, - {"Version", pkgconf_pkg_parser_tuple_func, offsetof(pkgconf_pkg_t, version)}, -}; - -static bool -pkgconf_pkg_parser_keyword_set(const pkgconf_client_t *client, pkgconf_pkg_t *pkg, const size_t lineno, const char *keyword, char *value) -{ - 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 false; - - pair->func(client, pkg, keyword, lineno, pair->offset, value); - return true; -} - -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; -} - -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 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 readbuf[PKGCONF_BUFSIZE]; - char pathbuf[PKGCONF_ITEM_SIZE]; - char *idptr; - size_t lineno = 0; - - pkg = calloc(sizeof(pkgconf_pkg_t), 1); - pkg->filename = strdup(filename); - pkgconf_tuple_add(client, &pkg->vars, "pcfiledir", pkg_get_parent_dir(pkg, pathbuf, sizeof pathbuf), true); - - /* make module id */ - if ((idptr = strrchr(pkg->filename, PKG_DIR_SEP_S)) != NULL) - idptr++; -#ifdef _WIN32 - else if ((idptr = strrchr(pkg->filename, '/')) != NULL) - idptr++; -#endif - else - idptr = pkg->filename; - - pkg->id = strdup(idptr); - idptr = strrchr(pkg->id, '.'); - if (idptr) - *idptr = '\0'; - - while (pkgconf_fgetline(readbuf, PKGCONF_BUFSIZE, f) != NULL) - { - char op, *p, *key, *value; - bool warned_key_whitespace = false, warned_value_whitespace = false; - - lineno++; - - PKGCONF_TRACE(client, "%s:" SIZE_FMT_SPECIFIER " > [%s]", filename, lineno, readbuf); - - p = readbuf; - while (*p && (isalpha((unsigned int)*p) || isdigit((unsigned int)*p) || *p == '_' || *p == '.')) - p++; - - key = readbuf; - if (!isalpha((unsigned int)*key) && !isdigit((unsigned int)*p)) - continue; - - while (*p && isspace((unsigned int)*p)) - { - if (!warned_key_whitespace) - { - pkgconf_warn(client, "%s:" SIZE_FMT_SPECIFIER ": warning: whitespace encountered while parsing key section\n", - pkg->filename, lineno); - warned_key_whitespace = true; - } - - /* set to null to avoid trailing spaces in key */ - *p = '\0'; - p++; - } - - op = *p; - *p = '\0'; - p++; - - while (*p && isspace((unsigned int)*p)) - p++; - - value = p; - p = value + (strlen(value) - 1); - while (*p && isspace((unsigned int) *p) && p > value) - { - if (!warned_value_whitespace && op == '=') - { - pkgconf_warn(client, "%s:" SIZE_FMT_SPECIFIER ": warning: trailing whitespace encountered while parsing value section\n", - pkg->filename, lineno); - warned_value_whitespace = true; - } - - *p = '\0'; - p--; - } - - switch (op) - { - case ':': - pkgconf_pkg_parser_keyword_set(client, pkg, lineno, key, value); - break; - case '=': - if (strcmp(key, client->prefix_varname) || !(client->flags & PKGCONF_PKG_PKGF_REDEFINE_PREFIX)) - pkgconf_tuple_add(client, &pkg->vars, key, value, true); - else - { - const char *relvalue = determine_prefix(pkg, pathbuf, sizeof pathbuf); - if (relvalue != NULL) - { - pkgconf_tuple_add(client, &pkg->vars, "orig_prefix", value, true); - pkgconf_tuple_add(client, &pkg->vars, key, relvalue, false); - } - else - pkgconf_tuple_add(client, &pkg->vars, key, value, true); - } - break; - default: - break; - } - } - - fclose(f); - - 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); - - 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->requires); - 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(const pkgconf_client_t *client, pkgconf_pkg_t *pkg) -{ - (void) client; - - 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) -{ - pkg->refcount--; - if (pkg->refcount <= 0) - pkgconf_pkg_free(client, 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/%s" PKG_CONFIG_EXT, path, name); - snprintf(uninst_locbuf, sizeof uninst_locbuf, "%s/%s-uninstalled" PKG_CONFIG_EXT, path, 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) -{ - char pathbuf[PKGCONF_ITEM_SIZE]; - 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_get_parent_dir(pkg, pathbuf, sizeof pathbuf), &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) - return ret; - - *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){ - .prev = NULL, - .next = NULL, - .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){ - .prev = NULL, - .next = NULL, - .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) - 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); - - 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) - return pkg; - - if (eflags != NULL) - *eflags |= PKGCONF_PKG_ERRF_PACKAGE_VER_MISMATCH; - - 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); -} - -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 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; - } - - pkgconf_audit_log_dependency(client, pkgdep, depnode); - - pkgdep->flags |= PKGCONF_PKG_PROPF_SEEN; - eflags |= pkgconf_pkg_traverse(client, pkgdep, func, data, depth - 1); - 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->requires.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 : ""); - 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) - * - * 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. - * :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 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->requires, func, data, maxdepth); - 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); - 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; - - eflag = pkgconf_pkg_traverse(client, root, pkgconf_pkg_cflags_collect, list, maxdepth); - if (eflag != PKGCONF_PKG_ERRF_OK) - pkgconf_fragment_free(list); - - if (client->flags & PKGCONF_PKG_PKGF_MERGE_PRIVATE_FRAGMENTS) - { - eflag = pkgconf_pkg_traverse(client, root, pkgconf_pkg_cflags_private_collect, list, maxdepth); - if (eflag != PKGCONF_PKG_ERRF_OK) - pkgconf_fragment_free(list); - } - - 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); - - if (eflag != PKGCONF_PKG_ERRF_OK) - { - pkgconf_fragment_free(list); - return eflag; - } - - return eflag; -} diff --git a/libpkgconf/queue.c b/libpkgconf/queue.c index fb6eefd..1bfcb30 100644 --- a/libpkgconf/queue.c +++ b/libpkgconf/queue.c @@ -77,10 +77,10 @@ pkgconf_queue_compile(pkgconf_client_t *client, pkgconf_pkg_t *world, pkgconf_li pkgconf_queue_t *pkgq; pkgq = iter->data; - pkgconf_dependency_parse(client, world, &world->requires_, pkgq->package); + pkgconf_dependency_parse(client, world, &world->required, pkgq->package); } - return (world->requires_.head != NULL); + return (world->required.head != NULL); } /* diff --git a/libpkgconf/queue.c.orig b/libpkgconf/queue.c.orig deleted file mode 100644 index 73507fa..0000000 --- a/libpkgconf/queue.c.orig +++ /dev/null @@ -1,195 +0,0 @@ -/* - * queue.c - * compilation of a list of packages into a world dependency set - * - * Copyright (c) 2012 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 <libpkgconf/stdinc.h> -#include <libpkgconf/libpkgconf.h> - -/* - * !doc - * - * libpkgconf `queue` module - * ========================= - * - * The `queue` module provides an interface that allows easily building a dependency graph from an - * arbitrary set of dependencies. It also provides support for doing "preflight" checks on the entire - * dependency graph prior to working with it. - * - * Using the `queue` module functions is the recommended way of working with dependency graphs. - */ - -typedef struct { - pkgconf_node_t iter; - char *package; -} pkgconf_queue_t; - -/* - * !doc - * - * .. c:function:: void pkgconf_queue_push(pkgconf_list_t *list, const char *package) - * - * Pushes a requested dependency onto the dependency resolver's queue. - * - * :param pkgconf_list_t* list: the dependency resolution queue to add the package request to. - * :param char* package: the dependency atom requested - * :return: nothing - */ -void -pkgconf_queue_push(pkgconf_list_t *list, const char *package) -{ - pkgconf_queue_t *pkgq = calloc(sizeof(pkgconf_queue_t), 1); - - pkgq->package = strdup(package); - pkgconf_node_insert_tail(&pkgq->iter, pkgq, list); -} - -/* - * !doc - * - * .. c:function:: bool pkgconf_queue_compile(pkgconf_client_t *client, pkgconf_pkg_t *world, pkgconf_list_t *list) - * - * Compile a dependency resolution queue into a dependency resolution problem if possible, otherwise report an error. - * - * :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution. - * :param pkgconf_pkg_t* world: The designated root of the dependency graph. - * :param pkgconf_list_t* list: The list of dependency requests to consider. - * :return: true if the built dependency resolution problem is consistent, else false - * :rtype: bool - */ -bool -pkgconf_queue_compile(pkgconf_client_t *client, pkgconf_pkg_t *world, pkgconf_list_t *list) -{ - pkgconf_node_t *iter; - - PKGCONF_FOREACH_LIST_ENTRY(list->head, iter) - { - pkgconf_queue_t *pkgq; - - pkgq = iter->data; - pkgconf_dependency_parse(client, world, &world->requires, pkgq->package); - } - - return (world->requires.head != NULL); -} - -/* - * !doc - * - * .. c:function:: void pkgconf_queue_free(pkgconf_list_t *list) - * - * Release any memory related to a dependency resolution queue. - * - * :param pkgconf_list_t* list: The dependency resolution queue to release. - * :return: nothing - */ -void -pkgconf_queue_free(pkgconf_list_t *list) -{ - pkgconf_node_t *node, *tnode; - - PKGCONF_FOREACH_LIST_ENTRY_SAFE(list->head, tnode, node) - { - pkgconf_queue_t *pkgq = node->data; - - free(pkgq->package); - free(pkgq); - } -} - -static inline unsigned int -pkgconf_queue_verify(pkgconf_client_t *client, pkgconf_pkg_t *world, pkgconf_list_t *list, int maxdepth) -{ - if (!pkgconf_queue_compile(client, world, list)) - return PKGCONF_PKG_ERRF_DEPGRAPH_BREAK; - - return pkgconf_pkg_verify_graph(client, world, maxdepth); -} - -/* - * !doc - * - * .. c:function:: void pkgconf_queue_apply(pkgconf_client_t *client, pkgconf_list_t *list, pkgconf_queue_apply_func_t func, int maxdepth, void *data) - * - * Attempt to compile a dependency resolution queue into a dependency resolution problem, then attempt to solve the problem and - * feed the solution to a callback function if a complete dependency graph is found. - * - * :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution. - * :param pkgconf_list_t* list: The list of dependency requests to consider. - * :param pkgconf_queue_apply_func_t func: The callback function to call if a solution is found by the dependency resolver. - * :param int maxdepth: The maximum allowed depth for the dependency resolver. A depth of -1 means unlimited. - * :param void* data: An opaque pointer which is passed to the callback function. - * :returns: true if the dependency resolver found a solution, otherwise false. - * :rtype: bool - */ -bool -pkgconf_queue_apply(pkgconf_client_t *client, pkgconf_list_t *list, pkgconf_queue_apply_func_t func, int maxdepth, void *data) -{ - pkgconf_pkg_t world = { - .id = "virtual:world", - .realname = "virtual world package", - .flags = PKGCONF_PKG_PROPF_STATIC | PKGCONF_PKG_PROPF_VIRTUAL, - }; - - /* if maxdepth is one, then we will not traverse deeper than our virtual package. */ - if (!maxdepth) - maxdepth = -1; - - if (pkgconf_queue_verify(client, &world, list, maxdepth) != PKGCONF_PKG_ERRF_OK) - return false; - - if (!func(client, &world, data, maxdepth)) - { - pkgconf_pkg_free(client, &world); - return false; - } - - pkgconf_pkg_free(client, &world); - - return true; -} - -/* - * !doc - * - * .. c:function:: void pkgconf_queue_validate(pkgconf_client_t *client, pkgconf_list_t *list, pkgconf_queue_apply_func_t func, int maxdepth, void *data) - * - * Attempt to compile a dependency resolution queue into a dependency resolution problem, then attempt to solve the problem. - * - * :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution. - * :param pkgconf_list_t* list: The list of dependency requests to consider. - * :param int maxdepth: The maximum allowed depth for the dependency resolver. A depth of -1 means unlimited. - * :returns: true if the dependency resolver found a solution, otherwise false. - * :rtype: bool - */ -bool -pkgconf_queue_validate(pkgconf_client_t *client, pkgconf_list_t *list, int maxdepth) -{ - bool retval = true; - pkgconf_pkg_t world = { - .id = "virtual:world", - .realname = "virtual world package", - .flags = PKGCONF_PKG_PROPF_STATIC | PKGCONF_PKG_PROPF_VIRTUAL, - }; - - /* if maxdepth is one, then we will not traverse deeper than our virtual package. */ - if (!maxdepth) - maxdepth = -1; - - if (pkgconf_queue_verify(client, &world, list, maxdepth) != PKGCONF_PKG_ERRF_OK) - retval = false; - - pkgconf_pkg_free(client, &world); - - return retval; -} diff --git a/libpkgconf/tuple.c b/libpkgconf/tuple.c index 7fbc71b..a4b4250 100644 --- a/libpkgconf/tuple.c +++ b/libpkgconf/tuple.c @@ -158,6 +158,8 @@ pkgconf_tuple_add(const pkgconf_client_t *client, pkgconf_list_t *list, const ch pkgconf_tuple_find_delete(list, key); + PKGCONF_TRACE(client, "adding tuple to @%p: %s => %s (parsed? %d)", list, key, value, parse); + tuple->key = strdup(key); if (parse) tuple->value = pkgconf_tuple_parse(client, list, value); diff --git a/tests/basic/driver.c b/tests/basic/driver.c index 1f861de..c9810ce 100644 --- a/tests/basic/driver.c +++ b/tests/basic/driver.c @@ -37,7 +37,9 @@ error_handler (const char* msg, const pkgconf_client_t* c, const void* d) static void print_and_free (pkgconf_list_t* list) { - char* buf = pkgconf_fragment_render (list, /* escape */ true); + char* buf = pkgconf_fragment_render (list, + true /* escape */, + NULL /* options */); printf("%s", buf); free (buf); @@ -65,7 +67,7 @@ int main (int argc, const char* argv[]) { pkgconf_client_t* c = - pkgconf_client_new (error_handler, /* error_handler_data */ NULL); + pkgconf_client_new (error_handler, NULL /* error_handler_data */); assert (c != NULL); |