From d227a477fd9f81b409298e4981b419430695c402 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Fri, 13 Oct 2017 02:57:39 +0300 Subject: Relax thread stack size requirements --- libpkgconf/buildfile | 31 ++- libpkgconf/dependency.c | 4 +- libpkgconf/dependency.c.orig | 309 +++++++++++++++++++++ libpkgconf/fragment.c | 4 +- libpkgconf/fragment.c.orig | 627 +++++++++++++++++++++++++++++++++++++++++++ libpkgconf/libpkgconf.h | 2 - libpkgconf/libpkgconf.h.orig | 331 +++++++++++++++++++++++ libpkgconf/path.c | 6 +- libpkgconf/path.c.orig | 327 ++++++++++++++++++++++ libpkgconf/pkg.c | 18 +- libpkgconf/stdinc.h | 38 +++ libpkgconf/tuple.c | 4 +- libpkgconf/tuple.c.orig | 348 ++++++++++++++++++++++++ 13 files changed, 2023 insertions(+), 26 deletions(-) create mode 100644 libpkgconf/dependency.c.orig create mode 100644 libpkgconf/fragment.c.orig create mode 100644 libpkgconf/libpkgconf.h.orig create mode 100644 libpkgconf/path.c.orig create mode 100644 libpkgconf/tuple.c.orig diff --git a/libpkgconf/buildfile b/libpkgconf/buildfile index c344ec7..a2f9427 100644 --- a/libpkgconf/buildfile +++ b/libpkgconf/buildfile @@ -16,6 +16,9 @@ else # h{version}: in{version} $src_root/file{manifest} +tclass = $c.target.class +tsys = $c.target.system + # Define the PKG_DEFAULT_PATH, SYSTEM_INCLUDEDIR and SYSTEM_LIBDIR macros. # The whole idea feels utterly broken (hello cross-compilation) so we will # just do bare minimum and wait and see. @@ -23,7 +26,7 @@ h{version}: in{version} $src_root/file{manifest} # @@ We should probably allow to configure these macros via configuration # variables config.pkgconfig.pkg_default_path and alike. # -if ($c.target.class == "windows") +if ($tclass == "windows") { inc_dirs = "" lib_dirs = "" @@ -55,17 +58,17 @@ c.poptions += -DPKG_DEFAULT_PATH=\"$def_dirs\" \ # Disable warnings. # -if ($c.target.system == "win32-msvc") +if ($tsys == "win32-msvc") c.coptions += /wd4996 /wd4267 -if ($c.target.class == "windows") +if ($tclass == "windows") { # See libpkgconf/libpkgconf-api.h for details. # objs{*}: c.poptions += -DLIBPKGCONF_EXPORT obja{*}: c.poptions += -DPKGCONFIG_IS_STATIC - if ($c.target.system == "mingw32") + if ($tsys == "mingw32") c.libs += -ladvapi32 else c.libs += advapi32.lib @@ -81,8 +84,26 @@ liba{pkgconf}: cc.export.poptions += -DPKGCONFIG_IS_STATIC # -std=c9x option (is implied by c.std=99 in root.build) and define _GNU_SOURCE # for the source files that require such deviations. # -if ($c.target.class != "windows") +# We will also re-enable definitions of POSIX macros (specifically PATH_MAX) +# that are disabled by -std=c9x. +# +if ($tclass != "windows") +{ + # On FreeBSD and MacOS -D_POSIX_C_SOURCE disables declarations of strndup(), + # strlcpy() and alike, that are not POSIX. Not very consistent given that + # they are also not C99. Luckily, -std=c9x doesn't disable PATH_MAX on these + # OSes, so we just don't define the macro for them. + # + # @@ It all becomes a bit hairy. Should we just undefine the corresponding + # HAVE_* macros in config.h, so the custom function implementations are + # picked up? Alternatively, can we stop specifying the C standard and + # defining _POSIX_C_SOURCE macro altogether? + # + if ($tclass != "bsd" && $tclass != "macos") + c.poptions += -D_POSIX_C_SOURCE=200112L + obj{client fragment path pkg queue tuple}: c.poptions += -D_GNU_SOURCE +} # Install into the pkgconf/libpkgconf/ subdirectory of, say, /usr/include/. # Also make sure Cflags is properly set in .pc files to pkgconfig/. diff --git a/libpkgconf/dependency.c b/libpkgconf/dependency.c index 3078102..6f72dbb 100644 --- a/libpkgconf/dependency.c +++ b/libpkgconf/dependency.c @@ -56,7 +56,7 @@ static inline pkgconf_dependency_t * pkgconf_dependency_addraw(const pkgconf_client_t *client, pkgconf_list_t *list, const char *package, size_t package_sz, const char *version, size_t version_sz, pkgconf_pkg_comparator_t compare) { pkgconf_dependency_t *dep; - char depbuf[PKGCONF_BUFSIZE]; + char depbuf[PKGCONF_SBUFSIZE]; dep = calloc(sizeof(pkgconf_dependency_t), 1); dep->package = pkgconf_strndup(package, package_sz); @@ -161,7 +161,7 @@ pkgconf_dependency_parse_str(const pkgconf_client_t *client, pkgconf_list_t *dep { parse_state_t state = OUTSIDE_MODULE; pkgconf_pkg_comparator_t compare = PKGCONF_CMP_ANY; - char cmpname[PKGCONF_BUFSIZE]; + char cmpname[PKGCONF_SBUFSIZE]; char buf[PKGCONF_BUFSIZE]; size_t package_sz = 0, version_sz = 0; char *start = buf; diff --git a/libpkgconf/dependency.c.orig b/libpkgconf/dependency.c.orig new file mode 100644 index 0000000..3078102 --- /dev/null +++ b/libpkgconf/dependency.c.orig @@ -0,0 +1,309 @@ +/* + * dependency.c + * dependency parsing and management + * + * Copyright (c) 2011, 2012, 2013 pkgconf authors (see AUTHORS). + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * This software is provided 'as is' and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising + * from the use of this software. + */ + +#include +#include + +/* + * !doc + * + * libpkgconf `dependency` module + * ============================== + * + * The `dependency` module provides support for building `dependency lists` (the basic component of the overall `dependency graph`) and + * `dependency nodes` which store dependency information. + */ + +typedef enum { + OUTSIDE_MODULE = 0, + INSIDE_MODULE_NAME = 1, + BEFORE_OPERATOR = 2, + INSIDE_OPERATOR = 3, + AFTER_OPERATOR = 4, + INSIDE_VERSION = 5 +} parse_state_t; + +#define DEBUG_PARSE 0 + +static const char * +dependency_to_str(const pkgconf_dependency_t *dep, char *buf, size_t buflen) +{ + pkgconf_strlcpy(buf, dep->package, buflen); + if (dep->version != NULL) + { + pkgconf_strlcat(buf, " ", buflen); + pkgconf_strlcat(buf, pkgconf_pkg_get_comparator(dep), buflen); + pkgconf_strlcat(buf, " ", buflen); + pkgconf_strlcat(buf, dep->version, buflen); + } + + return buf; +} + +static inline pkgconf_dependency_t * +pkgconf_dependency_addraw(const pkgconf_client_t *client, pkgconf_list_t *list, const char *package, size_t package_sz, const char *version, size_t version_sz, pkgconf_pkg_comparator_t compare) +{ + pkgconf_dependency_t *dep; + char depbuf[PKGCONF_BUFSIZE]; + + dep = calloc(sizeof(pkgconf_dependency_t), 1); + dep->package = pkgconf_strndup(package, package_sz); + + if (version_sz != 0) + dep->version = pkgconf_strndup(version, version_sz); + + dep->compare = compare; + + PKGCONF_TRACE(client, "added dependency [%s] to list @%p", dependency_to_str(dep, depbuf, sizeof depbuf), list); + pkgconf_node_insert_tail(&dep->iter, dep, list); + + return dep; +} + +/* + * !doc + * + * .. c:function:: pkgconf_dependency_t *pkgconf_dependency_add(pkgconf_list_t *list, const char *package, const char *version, pkgconf_pkg_comparator_t compare) + * + * Adds a parsed dependency to a dependency list as a dependency node. + * + * :param pkgconf_client_t* client: The client object that owns the package this dependency list belongs to. + * :param pkgconf_list_t* list: The dependency list to add a dependency node to. + * :param char* package: The package `atom` to set on the dependency node. + * :param char* version: The package `version` to set on the dependency node. + * :param pkgconf_pkg_comparator_t compare: The comparison operator to set on the dependency node. + * :return: A dependency node. + * :rtype: pkgconf_dependency_t * + */ +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) +{ + if (version != NULL) + return pkgconf_dependency_addraw(client, list, package, strlen(package), version, strlen(version), compare); + + return pkgconf_dependency_addraw(client, list, package, strlen(package), NULL, 0, compare); +} + +/* + * !doc + * + * .. c:function:: void pkgconf_dependency_append(pkgconf_list_t *list, pkgconf_dependency_t *tail) + * + * Adds a dependency node to a pre-existing dependency list. + * + * :param pkgconf_list_t* list: The dependency list to add a dependency node to. + * :param pkgconf_dependency_t* tail: The dependency node to add to the tail of the dependency list. + * :return: nothing + */ +void +pkgconf_dependency_append(pkgconf_list_t *list, pkgconf_dependency_t *tail) +{ + pkgconf_node_insert_tail(&tail->iter, tail, list); +} + +/* + * !doc + * + * .. c:function:: void pkgconf_dependency_free(pkgconf_list_t *list) + * + * Release a dependency list and it's child dependency nodes. + * + * :param pkgconf_list_t* list: The dependency list to release. + * :return: nothing + */ +void +pkgconf_dependency_free(pkgconf_list_t *list) +{ + pkgconf_node_t *node, *next; + + PKGCONF_FOREACH_LIST_ENTRY_SAFE(list->head, next, node) + { + pkgconf_dependency_t *dep = node->data; + + if (dep->package != NULL) + free(dep->package); + + if (dep->version != NULL) + free(dep->version); + + free(dep); + } +} + +/* + * !doc + * + * .. c:function:: void pkgconf_dependency_parse_str(pkgconf_list_t *deplist_head, const char *depends) + * + * Parse a dependency declaration into a dependency list. + * Commas are counted as whitespace to allow for constructs such as ``@SUBSTVAR@, zlib`` being processed + * into ``, zlib``. + * + * :param pkgconf_client_t* client: The client object that owns the package this dependency list belongs to. + * :param pkgconf_list_t* deplist_head: The dependency list to populate with dependency nodes. + * :param char* depends: The dependency data to parse. + * :return: nothing + */ +void +pkgconf_dependency_parse_str(const pkgconf_client_t *client, pkgconf_list_t *deplist_head, const char *depends) +{ + parse_state_t state = OUTSIDE_MODULE; + pkgconf_pkg_comparator_t compare = PKGCONF_CMP_ANY; + char cmpname[PKGCONF_BUFSIZE]; + char buf[PKGCONF_BUFSIZE]; + size_t package_sz = 0, version_sz = 0; + char *start = buf; + char *ptr = buf; + char *vstart = NULL; + char *package = NULL, *version = NULL; + char *cnameptr = cmpname; + + memset(cmpname, '\0', sizeof cmpname); + + pkgconf_strlcpy(buf, depends, sizeof buf); + pkgconf_strlcat(buf, " ", sizeof buf); + + while (*ptr) + { + switch (state) + { + case OUTSIDE_MODULE: + if (!PKGCONF_IS_MODULE_SEPARATOR(*ptr)) + state = INSIDE_MODULE_NAME; + + break; + + case INSIDE_MODULE_NAME: + if (isspace((unsigned int)*ptr)) + { + const char *sptr = ptr; + + while (*sptr && isspace((unsigned int)*sptr)) + sptr++; + + if (*sptr == '\0') + state = OUTSIDE_MODULE; + else if (PKGCONF_IS_MODULE_SEPARATOR(*sptr)) + state = OUTSIDE_MODULE; + else if (PKGCONF_IS_OPERATOR_CHAR(*sptr)) + state = BEFORE_OPERATOR; + else + state = OUTSIDE_MODULE; + } + else if (PKGCONF_IS_MODULE_SEPARATOR(*ptr)) + state = OUTSIDE_MODULE; + else if (*(ptr + 1) == '\0') + { + ptr++; + state = OUTSIDE_MODULE; + } + + if (state != INSIDE_MODULE_NAME && start != ptr) + { + char *iter = start; + + while (PKGCONF_IS_MODULE_SEPARATOR(*iter)) + iter++; + + package = iter; + package_sz = ptr - iter; + start = ptr; + } + + if (state == OUTSIDE_MODULE) + { + pkgconf_dependency_addraw(client, deplist_head, package, package_sz, NULL, 0, compare); + + compare = PKGCONF_CMP_ANY; + package_sz = 0; + } + + break; + + case BEFORE_OPERATOR: + if (PKGCONF_IS_OPERATOR_CHAR(*ptr)) + { + state = INSIDE_OPERATOR; + *cnameptr++ = *ptr; + } + + break; + + case INSIDE_OPERATOR: + if (!PKGCONF_IS_OPERATOR_CHAR(*ptr)) + { + state = AFTER_OPERATOR; + compare = pkgconf_pkg_comparator_lookup_by_name(cmpname); + } + else + *cnameptr++ = *ptr; + + break; + + case AFTER_OPERATOR: + if (!isspace((unsigned int)*ptr)) + { + vstart = ptr; + state = INSIDE_VERSION; + } + break; + + case INSIDE_VERSION: + if (PKGCONF_IS_MODULE_SEPARATOR(*ptr) || *(ptr + 1) == '\0') + { + version = vstart; + version_sz = ptr - vstart; + state = OUTSIDE_MODULE; + + pkgconf_dependency_addraw(client, deplist_head, package, package_sz, version, version_sz, compare); + + compare = PKGCONF_CMP_ANY; + cnameptr = cmpname; + memset(cmpname, 0, sizeof cmpname); + package_sz = 0; + } + + if (state == OUTSIDE_MODULE) + start = ptr; + break; + } + + ptr++; + } +} + +/* + * !doc + * + * .. c:function:: void pkgconf_dependency_parse(const pkgconf_client_t *client, pkgconf_pkg_t *pkg, pkgconf_list_t *deplist, const char *depends) + * + * Preprocess dependency data and then process that dependency declaration into a dependency list. + * Commas are counted as whitespace to allow for constructs such as ``@SUBSTVAR@, zlib`` being processed + * into ``, zlib``. + * + * :param pkgconf_client_t* client: The client object that owns the package this dependency list belongs to. + * :param pkgconf_pkg_t* pkg: The package object that owns this dependency list. + * :param pkgconf_list_t* deplist: The dependency list to populate with dependency nodes. + * :param char* depends: The dependency data to parse. + * :return: nothing + */ +void +pkgconf_dependency_parse(const pkgconf_client_t *client, pkgconf_pkg_t *pkg, pkgconf_list_t *deplist, const char *depends) +{ + char *kvdepends = pkgconf_tuple_parse(client, &pkg->vars, depends); + + pkgconf_dependency_parse_str(client, deplist, kvdepends); + free(kvdepends); +} diff --git a/libpkgconf/fragment.c b/libpkgconf/fragment.c index b405631..22d6d04 100644 --- a/libpkgconf/fragment.c +++ b/libpkgconf/fragment.c @@ -122,7 +122,7 @@ pkgconf_fragment_munge(const pkgconf_client_t *client, char *buf, size_t buflen, static inline char * pkgconf_fragment_copy_munged(const pkgconf_client_t *client, const char *source) { - char mungebuf[PKGCONF_BUFSIZE]; + char mungebuf[PKGCONF_SBUFSIZE]; pkgconf_fragment_munge(client, mungebuf, sizeof mungebuf, source, client->sysroot_dir); return strdup(mungebuf); } @@ -158,7 +158,7 @@ pkgconf_fragment_add(const pkgconf_client_t *client, pkgconf_list_t *list, const } else { - char mungebuf[PKGCONF_BUFSIZE]; + char mungebuf[PKGCONF_SBUFSIZE]; if (list->tail != NULL && list->tail->data != NULL) { diff --git a/libpkgconf/fragment.c.orig b/libpkgconf/fragment.c.orig new file mode 100644 index 0000000..b405631 --- /dev/null +++ b/libpkgconf/fragment.c.orig @@ -0,0 +1,627 @@ +/* + * fragment.c + * Management of fragment lists. + * + * Copyright (c) 2012, 2013, 2014 pkgconf authors (see AUTHORS). + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * This software is provided 'as is' and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising + * from the use of this software. + */ + +#include +#include + +/* + * !doc + * + * libpkgconf `fragment` module + * ============================ + * + * The `fragment` module provides low-level management and rendering of fragment lists. A + * `fragment list` contains various `fragments` of text (such as ``-I /usr/include``) in a matter + * which is composable, mergeable and reorderable. + */ + +struct pkgconf_fragment_check { + char *token; + size_t len; +}; + +static inline bool +pkgconf_fragment_is_unmergeable(const char *string) +{ + static const struct pkgconf_fragment_check check_fragments[] = { + {"-framework", 10}, + {"-isystem", 8}, + {"-idirafter", 10}, + {"-pthread", 8}, + {"-Wa,", 4}, + {"-Wl,", 4}, + {"-Wp,", 4}, + {"-trigraphs", 10}, + {"-pedantic", 9}, + {"-ansi", 5}, + {"-std=", 5}, + {"-stdlib=", 8}, + {"-include", 8}, + {"-nostdinc", 9}, + {"-nostdlibinc", 12}, + {"-nobuiltininc", 13}, + }; + + if (*string != '-') + return true; + + for (size_t i = 0; i < PKGCONF_ARRAY_SIZE(check_fragments); i++) + if (!strncmp(string, check_fragments[i].token, check_fragments[i].len)) + return true; + + /* only one pair of {-flag, arg} may be merged together */ + if (strchr(string, ' ') != NULL) + return false; + + return false; +} + +static inline bool +pkgconf_fragment_should_munge(const char *string, const char *sysroot_dir) +{ + static const struct pkgconf_fragment_check check_fragments[] = { + {"-isystem", 8}, + {"-idirafter", 10}, + {"-include", 8}, + }; + + if (*string != '/') + return false; + + if (sysroot_dir != NULL && strncmp(sysroot_dir, string, strlen(sysroot_dir))) + 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; + + 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_BUFSIZE]; + 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_BUFSIZE]; + + 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; + + /* 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->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 char * +fragment_escape(const char *src) +{ + ssize_t outlen = strlen(src) + 10; + char *out = calloc(outlen, 1); + char *dst = out; + + while (*src) + { + if (((*src < ' ') || + (*src > ' ' && *src < '$') || + (*src > '$' && *src < '(') || + (*src > ')' && *src < '+') || + (*src > ':' && *src < '=') || + (*src > '=' && *src < '@') || + (*src > 'Z' && *src < '^') || + (*src == '`') || + (*src > 'z' && *src < '~') || + (*src > '~')) && *src != '\\') + *dst++ = '\\'; + + *dst++ = *src++; + + if ((ptrdiff_t)(dst - out) + 2 > outlen) + { + outlen *= 2; + out = realloc(out, outlen); + } + } + + *dst = 0; + return out; +} + +static inline size_t +pkgconf_fragment_len(const pkgconf_fragment_t *frag, bool escape) +{ + size_t len = 1; + + if (frag->type) + len += 2; + + if (frag->data != NULL) + { + if (!escape) + len += strlen(frag->data); + else + { + char *tmp = fragment_escape(frag->data); + len += strlen(tmp); + free(tmp); + } + } + + 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) +{ + 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); + } + + 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) +{ + 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); + + if (pkgconf_fragment_len(frag, escape) > buf_remaining) + break; + + if (frag->type) + { + *bptr++ = '-'; + *bptr++ = frag->type; + } + + 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++ = ' '; + } + + *bptr = '\0'; +} + +/* + * !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. + * :return: An allocated string containing the rendered `fragment list`. + * :rtype: char * + */ +char * +pkgconf_fragment_render(const pkgconf_list_t *list, bool escape) +{ + size_t buflen = pkgconf_fragment_render_len(list, escape); + char *buf = calloc(1, buflen); + + pkgconf_fragment_render_buf(list, buf, buflen, escape); + + 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:: void 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: nothing + */ +void +pkgconf_fragment_parse(const pkgconf_client_t *client, pkgconf_list_t *list, pkgconf_list_t *vars, const char *value) +{ + int i, argc; + char **argv; + char *repstr = pkgconf_tuple_parse(client, vars, value); + + pkgconf_argv_split(repstr, &argc, &argv); + + for (i = 0; i < argc; i++) + pkgconf_fragment_add(client, list, argv[i]); + + pkgconf_argv_free(argv); + free(repstr); +} diff --git a/libpkgconf/libpkgconf.h b/libpkgconf/libpkgconf.h index d7fe911..66b4f54 100644 --- a/libpkgconf/libpkgconf.h +++ b/libpkgconf/libpkgconf.h @@ -41,8 +41,6 @@ extern "C" { #define PKG_DIR_SEP_S '/' #endif -#define PKGCONF_BUFSIZE (65535) - typedef enum { PKGCONF_CMP_NOT_EQUAL, PKGCONF_CMP_ANY, diff --git a/libpkgconf/libpkgconf.h.orig b/libpkgconf/libpkgconf.h.orig new file mode 100644 index 0000000..d7fe911 --- /dev/null +++ b/libpkgconf/libpkgconf.h.orig @@ -0,0 +1,331 @@ +/* + * libpkgconf.h + * Global include file for everything in libpkgconf. + * + * Copyright (c) 2011, 2015 pkgconf authors (see AUTHORS). + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * This software is provided 'as is' and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising + * from the use of this software. + */ + +#ifndef LIBPKGCONF__LIBPKGCONF_H +#define LIBPKGCONF__LIBPKGCONF_H + +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* pkg-config uses ';' on win32 as ':' is part of path */ +#ifdef _WIN32 +#define PKG_CONFIG_PATH_SEP_S ";" +#else +#define PKG_CONFIG_PATH_SEP_S ":" +#endif + +#ifdef _WIN32 +#define PKG_DIR_SEP_S '\\' +#else +#define PKG_DIR_SEP_S '/' +#endif + +#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 void 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/path.c b/libpkgconf/path.c index 5b7aa1c..7f90928 100644 --- a/libpkgconf/path.c +++ b/libpkgconf/path.c @@ -78,7 +78,7 @@ void pkgconf_path_add(const char *text, pkgconf_list_t *dirlist, bool filter) { pkgconf_path_t *node; - char path[PKGCONF_BUFSIZE]; + char path[PKGCONF_SBUFSIZE]; #ifdef PKGCONF_CACHE_INODES struct stat st; @@ -89,7 +89,7 @@ pkgconf_path_add(const char *text, pkgconf_list_t *dirlist, bool filter) return; if (S_ISLNK(st.st_mode)) { - char linkdest[PKGCONF_BUFSIZE]; + char linkdest[PKGCONF_SBUFSIZE]; ssize_t len = readlink(text, linkdest, sizeof(linkdest)); if (len != -1 && (size_t)len < sizeof(linkdest) && @@ -201,7 +201,7 @@ bool pkgconf_path_match_list(const char *path, const pkgconf_list_t *dirlist) { pkgconf_node_t *n = NULL; - char relocated[PKGCONF_BUFSIZE]; + char relocated[PKGCONF_SBUFSIZE]; const char *cpath = path; pkgconf_strlcpy(relocated, path, sizeof relocated); diff --git a/libpkgconf/path.c.orig b/libpkgconf/path.c.orig new file mode 100644 index 0000000..5b7aa1c --- /dev/null +++ b/libpkgconf/path.c.orig @@ -0,0 +1,327 @@ +/* + * path.c + * filesystem path management + * + * Copyright (c) 2016 pkgconf authors (see AUTHORS). + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * This software is provided 'as is' and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising + * from the use of this software. + */ + +#include +#include +#include + +#if defined(HAVE_CYGWIN_CONV_PATH) && defined(__MSYS__) +# include +#endif + +#if defined(HAVE_SYS_STAT_H) && ! defined(_WIN32) +# include +# define PKGCONF_CACHE_INODES +#endif + +static bool +#ifdef PKGCONF_CACHE_INODES +path_list_contains_entry(const char *text, pkgconf_list_t *dirlist, struct stat *st) +#else +path_list_contains_entry(const char *text, pkgconf_list_t *dirlist) +#endif +{ + pkgconf_node_t *n; + + PKGCONF_FOREACH_LIST_ENTRY(dirlist->head, n) + { + pkgconf_path_t *pn = n->data; + +#ifdef PKGCONF_CACHE_INODES + if (pn->handle_device == (void *)(intptr_t)st->st_dev && pn->handle_path == (void *)(intptr_t)st->st_ino) + return true; +#endif + + if (!strcmp(text, pn->path)) + return true; + } + + return false; +} + +/* + * !doc + * + * libpkgconf `path` module + * ======================== + * + * The `path` module provides functions for manipulating lists of paths in a cross-platform manner. Notably, + * it is used by the `pkgconf client` to parse the ``PKG_CONFIG_PATH``, ``PKG_CONFIG_LIBDIR`` and related environment + * variables. + */ + +/* + * !doc + * + * .. c:function:: void pkgconf_path_add(const char *text, pkgconf_list_t *dirlist) + * + * Adds a path node to a path list. If the path is already in the list, do nothing. + * + * :param char* text: The path text to add as a path node. + * :param pkgconf_list_t* dirlist: The path list to add the path node to. + * :param bool filter: Whether to perform duplicate filtering. + * :return: nothing + */ +void +pkgconf_path_add(const char *text, pkgconf_list_t *dirlist, bool filter) +{ + pkgconf_path_t *node; + char path[PKGCONF_BUFSIZE]; + +#ifdef PKGCONF_CACHE_INODES + struct stat st; + + if (filter) + { + if (lstat(text, &st) == -1) + return; + if (S_ISLNK(st.st_mode)) + { + char linkdest[PKGCONF_BUFSIZE]; + ssize_t len = readlink(text, linkdest, sizeof(linkdest)); + + if (len != -1 && (size_t)len < sizeof(linkdest) && + stat(linkdest, &st) == -1) + return; + } + if (path_list_contains_entry(text, dirlist, &st)) + return; + } +#else + if (filter && path_list_contains_entry(text, dirlist)) + return; +#endif + + pkgconf_strlcpy(path, text, sizeof path); + pkgconf_path_relocate(path, sizeof path); + + node = calloc(sizeof(pkgconf_path_t), 1); + node->path = strdup(path); + +#ifdef PKGCONF_CACHE_INODES + if (filter) { + node->handle_path = (void *)(intptr_t) st.st_ino; + node->handle_device = (void *)(intptr_t) st.st_dev; + } +#endif + + pkgconf_node_insert_tail(&node->lnode, node, dirlist); +} + +/* + * !doc + * + * .. c:function:: size_t pkgconf_path_split(const char *text, pkgconf_list_t *dirlist) + * + * Splits a given text input and inserts paths into a path list. + * + * :param char* text: The path text to split and add as path nodes. + * :param pkgconf_list_t* dirlist: The path list to have the path nodes added to. + * :param bool filter: Whether to perform duplicate filtering. + * :return: number of path nodes added to the path list + * :rtype: size_t + */ +size_t +pkgconf_path_split(const char *text, pkgconf_list_t *dirlist, bool filter) +{ + size_t count = 0; + char *workbuf, *p, *iter; + + if (text == NULL) + return 0; + + iter = workbuf = strdup(text); + while ((p = strtok(iter, PKG_CONFIG_PATH_SEP_S)) != NULL) + { + pkgconf_path_add(p, dirlist, filter); + + count++, iter = NULL; + } + free(workbuf); + + return count; +} + +/* + * !doc + * + * .. c:function:: size_t pkgconf_path_build_from_environ(const char *envvarname, const char *fallback, pkgconf_list_t *dirlist) + * + * Adds the paths specified in an environment variable to a path list. If the environment variable is not set, + * an optional default set of paths is added. + * + * :param char* envvarname: The environment variable to look up. + * :param char* fallback: The fallback paths to use if the environment variable is not set. + * :param pkgconf_list_t* dirlist: The path list to add the path nodes to. + * :param bool filter: Whether to perform duplicate filtering. + * :return: number of path nodes added to the path list + * :rtype: size_t + */ +size_t +pkgconf_path_build_from_environ(const char *envvarname, const char *fallback, pkgconf_list_t *dirlist, bool filter) +{ + const char *data; + + data = getenv(envvarname); + if (data != NULL) + return pkgconf_path_split(data, dirlist, filter); + + if (fallback != NULL) + return pkgconf_path_split(fallback, dirlist, filter); + + /* no fallback and no environment variable, thusly no nodes added */ + return 0; +} + +/* + * !doc + * + * .. c:function:: bool pkgconf_path_match_list(const char *path, const pkgconf_list_t *dirlist) + * + * Checks whether a path has a matching prefix in a path list. + * + * :param char* path: The path to check against a path list. + * :param pkgconf_list_t* dirlist: The path list to check the path against. + * :return: true if the path list has a matching prefix, otherwise false + * :rtype: bool + */ +bool +pkgconf_path_match_list(const char *path, const pkgconf_list_t *dirlist) +{ + pkgconf_node_t *n = NULL; + char relocated[PKGCONF_BUFSIZE]; + const char *cpath = path; + + pkgconf_strlcpy(relocated, path, sizeof relocated); + if (pkgconf_path_relocate(relocated, sizeof relocated)) + cpath = relocated; + + PKGCONF_FOREACH_LIST_ENTRY(dirlist->head, n) + { + pkgconf_path_t *pnode = n->data; + + if (!strcmp(pnode->path, cpath)) + return true; + } + + return false; +} + +/* + * !doc + * + * .. c:function:: void pkgconf_path_free(pkgconf_list_t *dirlist) + * + * Releases any path nodes attached to the given path list. + * + * :param pkgconf_list_t* dirlist: The path list to clean up. + * :return: nothing + */ +void +pkgconf_path_free(pkgconf_list_t *dirlist) +{ + pkgconf_node_t *n, *tn; + + PKGCONF_FOREACH_LIST_ENTRY_SAFE(dirlist->head, tn, n) + { + pkgconf_path_t *pnode = n->data; + + free(pnode->path); + free(pnode); + } +} + +static char * +normpath(const char *path) +{ + if (!path) + return NULL; + + char *copy = strdup(path); + if (NULL == copy) + return NULL; + char *ptr = copy; + + for (int ii = 0; copy[ii]; ii++) + { + *ptr++ = path[ii]; + if ('/' == path[ii]) + { + ii++; + while ('/' == path[ii]) + ii++; + ii--; + } + } + *ptr = '\0'; + + return copy; +} + +/* + * !doc + * + * .. c:function:: bool pkgconf_path_relocate(char *buf, size_t buflen) + * + * Relocates a path, possibly calling normpath() or cygwin_conv_path() on it. + * + * :param char* buf: The path to relocate. + * :param size_t buflen: The buffer length the path is contained in. + * :return: true on success, false on error + * :rtype: bool + */ +bool +pkgconf_path_relocate(char *buf, size_t buflen) +{ +#if defined(HAVE_CYGWIN_CONV_PATH) && defined(__MSYS__) + ssize_t size; + char *tmpbuf, *ti; + + size = cygwin_conv_path(CCP_POSIX_TO_WIN_A, buf, NULL, 0); + if (size < 0 || (size_t) size > buflen) + return false; + + tmpbuf = malloc(size); + if (cygwin_conv_path(CCP_POSIX_TO_WIN_A, buf, tmpbuf, size)) + return false; + + pkgconf_strlcpy(buf, tmpbuf, buflen); + free(tmpbuf); + + /* rewrite any backslash arguments for best compatibility */ + for (ti = buf; *ti != '\0'; ti++) + { + if (*ti == '\\') + *ti = '/'; + } +#else + char *tmpbuf; + + if ((tmpbuf = normpath(buf)) != NULL) + { + size_t tmpbuflen = strlen(tmpbuf); + if (tmpbuflen > buflen) + { + free(tmpbuf); + return false; + } + + pkgconf_strlcpy(buf, tmpbuf, buflen); + free(tmpbuf); + } +#endif + + return true; +} diff --git a/libpkgconf/pkg.c b/libpkgconf/pkg.c index 92acdb2..9056965 100644 --- a/libpkgconf/pkg.c +++ b/libpkgconf/pkg.c @@ -35,8 +35,7 @@ # define strcasecmp _stricmp #endif -#define PKG_CONFIG_EXT ".pc" -#define PKG_CONFIG_PATH_SZ (65535) +#define PKG_CONFIG_EXT ".pc" static inline bool str_has_suffix(const char *str, const char *suffix) @@ -275,8 +274,7 @@ pkgconf_pkg_new_from_file(pkgconf_client_t *client, const char *filename, FILE * { pkgconf_pkg_t *pkg; char readbuf[PKGCONF_BUFSIZE]; - char pathbuf[PKGCONF_BUFSIZE]; - char prefixbuf[PKGCONF_BUFSIZE]; + char pathbuf[PKGCONF_SBUFSIZE]; char *idptr; size_t lineno = 0; @@ -362,7 +360,7 @@ pkgconf_pkg_new_from_file(pkgconf_client_t *client, const char *filename, FILE * pkgconf_tuple_add(client, &pkg->vars, key, value, true); else { - const char *relvalue = determine_prefix(pkg, prefixbuf, sizeof prefixbuf); + const char *relvalue = determine_prefix(pkg, pathbuf, sizeof pathbuf); if (relvalue != NULL) { pkgconf_tuple_add(client, &pkg->vars, "orig_prefix", value, true); @@ -497,8 +495,8 @@ pkgconf_pkg_try_specific_path(pkgconf_client_t *client, const char *path, const { pkgconf_pkg_t *pkg = NULL; FILE *f; - char locbuf[PKG_CONFIG_PATH_SZ]; - char uninst_locbuf[PKG_CONFIG_PATH_SZ]; + char locbuf[PKGCONF_SBUFSIZE]; + char uninst_locbuf[PKGCONF_SBUFSIZE]; PKGCONF_TRACE(client, "trying path: %s for %s", path, name); @@ -535,7 +533,7 @@ pkgconf_pkg_scan_dir(pkgconf_client_t *client, const char *path, void *data, pkg for (dirent = readdir(dir); dirent != NULL; dirent = readdir(dir)) { - char filebuf[PKGCONF_BUFSIZE]; + char filebuf[PKGCONF_SBUFSIZE]; pkgconf_pkg_t *pkg; FILE *f; @@ -657,7 +655,7 @@ pkgconf_pkg_find_in_registry_key(pkgconf_client_t *client, HKEY hkey, const char pkgconf_pkg_t * pkgconf_pkg_find(pkgconf_client_t *client, const char *name) { - char pathbuf[PKGCONF_BUFSIZE]; + char pathbuf[PKGCONF_SBUFSIZE]; pkgconf_pkg_t *pkg = NULL; pkgconf_node_t *n; FILE *f; @@ -737,7 +735,7 @@ int pkgconf_compare_version(const char *a, const char *b) { char oldch1, oldch2; - char buf1[PKGCONF_BUFSIZE], buf2[PKGCONF_BUFSIZE]; + char buf1[PKGCONF_SBUFSIZE], buf2[PKGCONF_SBUFSIZE]; char *str1, *str2; char *one, *two; int ret; diff --git a/libpkgconf/stdinc.h b/libpkgconf/stdinc.h index a4c7c84..abebe03 100644 --- a/libpkgconf/stdinc.h +++ b/libpkgconf/stdinc.h @@ -59,4 +59,42 @@ # include #endif +/* + * Stack-allocated buffer sizes. + * + * Original libpkgconf package uses PKGCONF_BUFSIZE size for all + * stack-allocated buffers, imposing high requirements for the thread stack + * size. This make it unusable on MacOS (as of 10.12) for non-main threads that + * are created with the default 512K stack size. In particular that make it + * impossible to use libpkgconf API in threads created with C++11 std::thread + * class, that doesn't allow stack size customization. + * + * As an example, using pkgconf_pkg_find() for quite a simple .pc file consumes + * at least 460K. The measurment was made as a differece between addresses of 2 + * stack-allocated variables: one was defined right before the function call, + * another right after the buffer definition in pkgconf_tuple_parse() function. + * + * To relax the stack size requirements we will minimize the usage of + * PKGCONF_BUFSIZE, using the smaller PKGCONF_SBUFSIZE instead, wherever it is + * possible. The PKGCONF_SBUFSIZE is selected in such a way that a buffer of + * this size can accommodate the file system path, the value fragment, the + * variable name or the package dependency specification. The latest implies it + * also fits for the package key or the package version. This optimization + * decreased the stack usage for the described use case to 140K. + * + * Note that we moved PKGCONF_BUFSIZE definition from libpkgconf.h. + */ +#define PKGCONF_BUFSIZE (65535) + +#ifdef _WIN32 +# define PKGCONF_SBUFSIZE (_MAX_PATH + 1024) +#else +# include +# ifdef PATH_MAX +# define PKGCONF_SBUFSIZE (PATH_MAX + 1024) +# else +# define PKGCONF_SBUFSIZE (4096 + 1024) +# endif +#endif + #endif diff --git a/libpkgconf/tuple.c b/libpkgconf/tuple.c index b0cda45..ae5fe55 100644 --- a/libpkgconf/tuple.c +++ b/libpkgconf/tuple.c @@ -231,7 +231,7 @@ pkgconf_tuple_parse(const pkgconf_client_t *client, pkgconf_list_t *vars, const *bptr++ = *ptr; else if (*(ptr + 1) == '{') { - char varname[PKGCONF_BUFSIZE]; + char varname[PKGCONF_SBUFSIZE]; char *vptr = varname; const char *pptr; char *kv, *parsekv; @@ -295,7 +295,7 @@ pkgconf_tuple_parse(const pkgconf_client_t *client, pkgconf_list_t *vars, const strlen(buf) > strlen(client->sysroot_dir) && strstr(buf + strlen(client->sysroot_dir), client->sysroot_dir) != NULL) { - char cleanpath[PKGCONF_BUFSIZE]; + char cleanpath[PKGCONF_SBUFSIZE]; pkgconf_strlcpy(cleanpath, buf + strlen(client->sysroot_dir), sizeof cleanpath); pkgconf_path_relocate(cleanpath, sizeof cleanpath); diff --git a/libpkgconf/tuple.c.orig b/libpkgconf/tuple.c.orig new file mode 100644 index 0000000..b0cda45 --- /dev/null +++ b/libpkgconf/tuple.c.orig @@ -0,0 +1,348 @@ +/* + * tuple.c + * management of key->value tuples + * + * Copyright (c) 2011, 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 +#include + +/* + * !doc + * + * libpkgconf `tuple` module + * ========================= + * + * The `tuple` module provides key-value mappings backed by a linked list. The key-value + * mapping is mainly used for variable substitution when parsing .pc files. + * + * There are two sets of mappings: a ``pkgconf_pkg_t`` specific mapping, and a `global` mapping. + * The `tuple` module provides convenience wrappers for managing the `global` mapping, which is + * attached to a given client object. + */ + +/* + * !doc + * + * .. c:function:: void pkgconf_tuple_add_global(pkgconf_client_t *client, const char *key, const char *value) + * + * Defines a global variable, replacing the previous declaration if one was set. + * + * :param pkgconf_client_t* client: The pkgconf client object to modify. + * :param char* key: The key for the mapping (variable name). + * :param char* value: The value for the mapped entry. + * :return: nothing + */ +void +pkgconf_tuple_add_global(pkgconf_client_t *client, const char *key, const char *value) +{ + pkgconf_tuple_add(client, &client->global_vars, key, value, false); +} + +/* + * !doc + * + * .. c:function:: void pkgconf_tuple_find_global(const pkgconf_client_t *client, const char *key) + * + * Looks up a global variable. + * + * :param pkgconf_client_t* client: The pkgconf client object to access. + * :param char* key: The key or variable name to look up. + * :return: the contents of the variable or ``NULL`` + * :rtype: char * + */ +char * +pkgconf_tuple_find_global(const pkgconf_client_t *client, const char *key) +{ + pkgconf_node_t *node; + + PKGCONF_FOREACH_LIST_ENTRY(client->global_vars.head, node) + { + pkgconf_tuple_t *tuple = node->data; + + if (!strcmp(tuple->key, key)) + return tuple->value; + } + + return NULL; +} + +/* + * !doc + * + * .. c:function:: void pkgconf_tuple_free_global(pkgconf_client_t *client) + * + * Delete all global variables associated with a pkgconf client object. + * + * :param pkgconf_client_t* client: The pkgconf client object to modify. + * :return: nothing + */ +void +pkgconf_tuple_free_global(pkgconf_client_t *client) +{ + pkgconf_tuple_free(&client->global_vars); +} + +/* + * !doc + * + * .. c:function:: void pkgconf_tuple_define_global(pkgconf_client_t *client, const char *kv) + * + * Parse and define a global variable. + * + * :param pkgconf_client_t* client: The pkgconf client object to modify. + * :param char* kv: The variable in the form of ``key=value``. + * :return: nothing + */ +void +pkgconf_tuple_define_global(pkgconf_client_t *client, const char *kv) +{ + char *workbuf = strdup(kv); + char *value; + + value = strchr(workbuf, '='); + if (value == NULL) + goto out; + + *value++ = '\0'; + pkgconf_tuple_add_global(client, workbuf, value); +out: + free(workbuf); +} + +static void +pkgconf_tuple_find_delete(pkgconf_list_t *list, const char *key) +{ + pkgconf_node_t *node, *next; + + PKGCONF_FOREACH_LIST_ENTRY_SAFE(list->head, next, node) + { + pkgconf_tuple_t *tuple = node->data; + + if (!strcmp(tuple->key, key)) + { + pkgconf_tuple_free_entry(tuple, list); + return; + } + } +} + +/* + * !doc + * + * .. c:function:: pkgconf_tuple_t *pkgconf_tuple_add(const pkgconf_client_t *client, pkgconf_list_t *list, const char *key, const char *value, bool parse) + * + * Optionally parse and then define a variable. + * + * :param pkgconf_client_t* client: The pkgconf client object to access. + * :param pkgconf_list_t* list: The variable list to add the new variable to. + * :param char* key: The name of the variable being added. + * :param char* value: The value of the variable being added. + * :param bool parse: Whether or not to parse the value for variable substitution. + * :return: a variable object + * :rtype: pkgconf_tuple_t * + */ +pkgconf_tuple_t * +pkgconf_tuple_add(const pkgconf_client_t *client, pkgconf_list_t *list, const char *key, const char *value, bool parse) +{ + pkgconf_tuple_t *tuple = calloc(sizeof(pkgconf_tuple_t), 1); + + pkgconf_tuple_find_delete(list, key); + + tuple->key = strdup(key); + if (parse) + tuple->value = pkgconf_tuple_parse(client, list, value); + else + tuple->value = strdup(value); + + pkgconf_node_insert(&tuple->iter, tuple, list); + + return tuple; +} + +/* + * !doc + * + * .. c:function:: char *pkgconf_tuple_find(const pkgconf_client_t *client, pkgconf_list_t *list, const char *key) + * + * Look up a variable in a variable list. + * + * :param pkgconf_client_t* client: The pkgconf client object to access. + * :param pkgconf_list_t* list: The variable list to search. + * :param char* key: The variable name to search for. + * :return: the value of the variable or ``NULL`` + * :rtype: char * + */ +char * +pkgconf_tuple_find(const pkgconf_client_t *client, pkgconf_list_t *list, const char *key) +{ + pkgconf_node_t *node; + char *res; + + if ((res = pkgconf_tuple_find_global(client, key)) != NULL) + return res; + + PKGCONF_FOREACH_LIST_ENTRY(list->head, node) + { + pkgconf_tuple_t *tuple = node->data; + + if (!strcmp(tuple->key, key)) + return tuple->value; + } + + return NULL; +} + +/* + * !doc + * + * .. c:function:: char *pkgconf_tuple_parse(const pkgconf_client_t *client, pkgconf_list_t *vars, const char *value) + * + * Parse an expression for variable substitution. + * + * :param pkgconf_client_t* client: The pkgconf client object to access. + * :param pkgconf_list_t* list: The variable list to search for variables (along side the global variable list). + * :param char* value: The ``key=value`` string to parse. + * :return: the variable data with any variables substituted + * :rtype: char * + */ +char * +pkgconf_tuple_parse(const pkgconf_client_t *client, pkgconf_list_t *vars, const char *value) +{ + char buf[PKGCONF_BUFSIZE]; + const char *ptr; + char *bptr = buf; + + if (*value == '/' && client->sysroot_dir != NULL && strncmp(value, client->sysroot_dir, strlen(client->sysroot_dir))) + bptr += pkgconf_strlcpy(buf, client->sysroot_dir, sizeof buf); + + for (ptr = value; *ptr != '\0' && bptr - buf < PKGCONF_BUFSIZE; ptr++) + { + if (*ptr != '$' || (*ptr == '$' && *(ptr + 1) != '{')) + *bptr++ = *ptr; + else if (*(ptr + 1) == '{') + { + char varname[PKGCONF_BUFSIZE]; + char *vptr = varname; + const char *pptr; + char *kv, *parsekv; + + *vptr = '\0'; + + for (pptr = ptr + 2; *pptr != '\0'; pptr++) + { + if (*pptr != '}') + *vptr++ = *pptr; + else + { + *vptr = '\0'; + break; + } + } + + ptr += (pptr - ptr); + kv = pkgconf_tuple_find_global(client, varname); + if (kv != NULL) + { + strncpy(bptr, kv, PKGCONF_BUFSIZE - (bptr - buf)); + bptr += strlen(kv); + } + else + { + kv = pkgconf_tuple_find(client, vars, varname); + + if (kv != NULL) + { + parsekv = pkgconf_tuple_parse(client, vars, kv); + + strncpy(bptr, parsekv, PKGCONF_BUFSIZE - (bptr - buf)); + bptr += strlen(parsekv); + + free(parsekv); + } + } + } + } + + *bptr = '\0'; + + /* + * Sigh. Somebody actually attempted to use freedesktop.org pkg-config's broken sysroot support, + * which was written by somebody who did not understand how sysroots are supposed to work. This + * results in an incorrect path being built as the sysroot will be prepended twice, once explicitly, + * and once by variable expansion (the pkgconf approach). We could simply make ${pc_sysrootdir} blank, + * but sometimes it is necessary to know the explicit sysroot path for other reasons, so we can't really + * do that. + * + * As a result, we check to see if ${pc_sysrootdir} is prepended as a duplicate, and if so, remove the + * prepend. This allows us to handle both our approach and the broken freedesktop.org implementation's + * approach. Because a path can be shorter than ${pc_sysrootdir}, we do some checks first to ensure it's + * safe to skip ahead in the string to scan for our sysroot dir. + * + * Finally, we call pkgconf_path_relocate() to clean the path of spurious elements. + */ + if (*buf == '/' && + client->sysroot_dir != NULL && + strlen(buf) > strlen(client->sysroot_dir) && + strstr(buf + strlen(client->sysroot_dir), client->sysroot_dir) != NULL) + { + char cleanpath[PKGCONF_BUFSIZE]; + + pkgconf_strlcpy(cleanpath, buf + strlen(client->sysroot_dir), sizeof cleanpath); + pkgconf_path_relocate(cleanpath, sizeof cleanpath); + + return strdup(cleanpath); + } + + return strdup(buf); +} + +/* + * !doc + * + * .. c:function:: void pkgconf_tuple_free_entry(pkgconf_tuple_t *tuple, pkgconf_list_t *list) + * + * Deletes a variable object, removing it from any variable lists and releasing any memory associated + * with it. + * + * :param pkgconf_tuple_t* tuple: The variable object to release. + * :param pkgconf_list_t* list: The variable list the variable object is attached to. + * :return: nothing + */ +void +pkgconf_tuple_free_entry(pkgconf_tuple_t *tuple, pkgconf_list_t *list) +{ + pkgconf_node_delete(&tuple->iter, list); + + free(tuple->key); + free(tuple->value); + free(tuple); +} + +/* + * !doc + * + * .. c:function:: void pkgconf_tuple_free(pkgconf_list_t *list) + * + * Deletes a variable list and any variables attached to it. + * + * :param pkgconf_list_t* list: The variable list to delete. + * :return: nothing + */ +void +pkgconf_tuple_free(pkgconf_list_t *list) +{ + pkgconf_node_t *node, *next; + + PKGCONF_FOREACH_LIST_ENTRY_SAFE(list->head, next, node) + pkgconf_tuple_free_entry(node->data, list); +} -- cgit v1.1