aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libpkgconf/buildfile31
-rw-r--r--libpkgconf/dependency.c4
-rw-r--r--libpkgconf/dependency.c.orig309
-rw-r--r--libpkgconf/fragment.c4
-rw-r--r--libpkgconf/fragment.c.orig627
-rw-r--r--libpkgconf/libpkgconf.h2
-rw-r--r--libpkgconf/libpkgconf.h.orig331
-rw-r--r--libpkgconf/path.c6
-rw-r--r--libpkgconf/path.c.orig327
-rw-r--r--libpkgconf/pkg.c18
-rw-r--r--libpkgconf/stdinc.h38
-rw-r--r--libpkgconf/tuple.c4
-rw-r--r--libpkgconf/tuple.c.orig348
13 files changed, 2023 insertions, 26 deletions
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 <libpkgconf/stdinc.h>
+#include <libpkgconf/libpkgconf.h>
+
+/*
+ * !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 <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)
+{
+ 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 <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 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 <libpkgconf/stdinc.h>
+#include <libpkgconf/libpkgconf.h>
+#include <libpkgconf/config.h>
+
+#if defined(HAVE_CYGWIN_CONV_PATH) && defined(__MSYS__)
+# include <sys/cygwin.h>
+#endif
+
+#if defined(HAVE_SYS_STAT_H) && ! defined(_WIN32)
+# include <sys/stat.h>
+# 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 <unistd.h>
#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 <limits.h>
+# 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 <libpkgconf/stdinc.h>
+#include <libpkgconf/libpkgconf.h>
+
+/*
+ * !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);
+}