aboutsummaryrefslogtreecommitdiff
path: root/libpkgconf
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2018-10-26 13:47:53 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2018-10-26 18:26:28 +0300
commit95fee14dfa5bd3896c510077af36ea371a9a2975 (patch)
treeb1a85ba6ab458731e3aeba30efc645c7b2a50225 /libpkgconf
parent9be7ac171fba73340e2974ff7ba55739a7ca81fb (diff)
Merge with 1.5.4 upstream package version
Diffstat (limited to 'libpkgconf')
-rw-r--r--libpkgconf/buildfile27
-rw-r--r--libpkgconf/client.c52
-rw-r--r--libpkgconf/config.h2
-rw-r--r--libpkgconf/dependency.c94
-rw-r--r--libpkgconf/libpkgconf.h52
-rw-r--r--libpkgconf/parser.c95
-rw-r--r--libpkgconf/path.c32
-rw-r--r--libpkgconf/personality.c251
-rw-r--r--libpkgconf/pkg.c330
-rw-r--r--libpkgconf/queue.c2
-rw-r--r--libpkgconf/stdinc.h3
-rw-r--r--libpkgconf/tuple.c39
12 files changed, 767 insertions, 212 deletions
diff --git a/libpkgconf/buildfile b/libpkgconf/buildfile
index 9dd9fea..5231c42 100644
--- a/libpkgconf/buildfile
+++ b/libpkgconf/buildfile
@@ -22,6 +22,11 @@ tclass = $c.target.class
# The whole idea feels utterly broken (hello cross-compilation) so we will
# just do bare minimum and wait and see.
#
+# There is also PERSONALITY_PATH macro added in 1.5.1. It looks like the
+# personality files are invented to fix cross-compilation but are unlikely to
+# have any noticeable distribution yet. Normally these files are located in
+# the personality.d/ subdirectory of the .pc files directory.
+#
# @@ We should probably allow to configure these macros via configuration
# variables config.pkgconfig.pkg_default_path and alike.
#
@@ -37,23 +42,32 @@ if ($tclass == "windows")
#
# <dir>/../lib/pkgconfig;<dir>/../share/pkgconfig
#
- # So we keep it empty.
+ # So we keep the macros empty.
#
def_paths = ""
+ personality_dirs = ""
}
else
{
inc_dirs = "/usr/include"
lib_dirs = "/usr/lib"
- def_dirs = ($install.root != [null] \
- ? "$install.resolve($install.pkgconfig)" \
- : "")
+ if ($install.root != [null])
+ {
+ def_dirs = "$install.resolve($install.pkgconfig)"
+ personality_dirs = "$def_dirs/personality.d"
+ }
+ else
+ {
+ def_dirs = ""
+ personality_dirs = ""
+ }
}
c.poptions += -DPKG_DEFAULT_PATH=\"$def_dirs\" \
-DSYSTEM_INCLUDEDIR=\"$inc_dirs\" \
- -DSYSTEM_LIBDIR=\"$lib_dirs\"
+ -DSYSTEM_LIBDIR=\"$lib_dirs\" \
+ -DPERSONALITY_PATH=\"$personality_dirs\"
# Disable warnings.
#
@@ -101,7 +115,8 @@ if ($tclass != "windows")
if ($tclass != "bsd" && $tclass != "macos")
c.poptions += -D_POSIX_C_SOURCE=200112L
- obj{client fragment path pkg queue tuple}: c.poptions += -D_GNU_SOURCE
+ obj{client fragment path personality pkg queue tuple}: \
+ c.poptions += -D_GNU_SOURCE
}
# Install into the pkgconf/libpkgconf/ subdirectory of, say, /usr/include/.
diff --git a/libpkgconf/client.c b/libpkgconf/client.c
index 811e043..f4155b3 100644
--- a/libpkgconf/client.c
+++ b/libpkgconf/client.c
@@ -48,17 +48,39 @@ trace_path_list(const pkgconf_client_t *client, const char *desc, pkgconf_list_t
/*
* !doc
*
- * .. c:function:: void pkgconf_client_init(pkgconf_client_t *client, pkgconf_error_handler_func_t error_handler)
+ * .. c:function:: void pkgconf_client_dir_list_build(pkgconf_client_t *client)
+ *
+ * Bootstraps the package search paths. If the ``PKGCONF_PKG_PKGF_ENV_ONLY`` `flag` is set on the client,
+ * then only the ``PKG_CONFIG_PATH`` environment variable will be used, otherwise both the
+ * ``PKG_CONFIG_PATH`` and ``PKG_CONFIG_LIBDIR`` environment variables will be used.
+ *
+ * :param pkgconf_client_t* client: The pkgconf client object to bootstrap.
+ * :return: nothing
+ */
+void
+pkgconf_client_dir_list_build(pkgconf_client_t *client, const pkgconf_cross_personality_t *personality)
+{
+ pkgconf_path_build_from_environ("PKG_CONFIG_PATH", NULL, &client->dir_list, true);
+
+ if (!(client->flags & PKGCONF_PKG_PKGF_ENV_ONLY) && (pkgconf_path_build_from_environ("PKG_CONFIG_LIBDIR", NULL, &client->dir_list, true)) < 1)
+ pkgconf_path_copy_list(&client->dir_list, &personality->dir_list);
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: void pkgconf_client_init(pkgconf_client_t *client, pkgconf_error_handler_func_t error_handler, void *error_handler_data, const pkgconf_cross_personality_t *personality)
*
* Initialise a pkgconf client object.
*
* :param pkgconf_client_t* client: The client to initialise.
* :param pkgconf_error_handler_func_t error_handler: An optional error handler to use for logging errors.
* :param void* error_handler_data: user data passed to optional error handler
+ * :param pkgconf_cross_personality_t* personality: the cross-compile personality to use for defaults
* :return: nothing
*/
void
-pkgconf_client_init(pkgconf_client_t *client, pkgconf_error_handler_func_t error_handler, void *error_handler_data)
+pkgconf_client_init(pkgconf_client_t *client, pkgconf_error_handler_func_t error_handler, void *error_handler_data, const pkgconf_cross_personality_t *personality)
{
client->error_handler_data = error_handler_data;
client->error_handler = error_handler;
@@ -70,15 +92,26 @@ pkgconf_client_init(pkgconf_client_t *client, pkgconf_error_handler_func_t error
pkgconf_client_set_error_handler(client, error_handler, error_handler_data);
pkgconf_client_set_warn_handler(client, NULL, NULL);
- pkgconf_client_set_sysroot_dir(client, NULL);
+ pkgconf_client_set_sysroot_dir(client, personality->sysroot_dir);
pkgconf_client_set_buildroot_dir(client, NULL);
pkgconf_client_set_prefix_varname(client, NULL);
- pkgconf_path_build_from_environ("PKG_CONFIG_SYSTEM_LIBRARY_PATH", SYSTEM_LIBDIR, &client->filter_libdirs, false);
- pkgconf_path_build_from_environ("PKG_CONFIG_SYSTEM_INCLUDE_PATH", SYSTEM_INCLUDEDIR, &client->filter_includedirs, false);
+ if(getenv("PKG_CONFIG_SYSTEM_LIBRARY_PATH") == NULL)
+ pkgconf_path_copy_list(&client->filter_libdirs, &personality->filter_libdirs);
+ else
+ pkgconf_path_build_from_environ("PKG_CONFIG_SYSTEM_LIBRARY_PATH", NULL, &client->filter_libdirs, false);
+
+ if(getenv("PKG_CONFIG_SYSTEM_INCLUDE_PATH") == NULL)
+ pkgconf_path_copy_list(&client->filter_includedirs, &personality->filter_includedirs);
+ else
+ pkgconf_path_build_from_environ("PKG_CONFIG_SYSTEM_INCLUDE_PATH", NULL, &client->filter_includedirs, false);
/* GCC uses these environment variables to define system include paths, so we should check them. */
+#ifdef __HAIKU__
+ pkgconf_path_build_from_environ("BELIBRARIES", NULL, &client->filter_libdirs, false);
+#else
pkgconf_path_build_from_environ("LIBRARY_PATH", NULL, &client->filter_libdirs, false);
+#endif
pkgconf_path_build_from_environ("CPATH", NULL, &client->filter_includedirs, false);
pkgconf_path_build_from_environ("C_INCLUDE_PATH", NULL, &client->filter_includedirs, false);
pkgconf_path_build_from_environ("CPLUS_INCLUDE_PATH", NULL, &client->filter_includedirs, false);
@@ -98,20 +131,21 @@ pkgconf_client_init(pkgconf_client_t *client, pkgconf_error_handler_func_t error
/*
* !doc
*
- * .. c:function:: pkgconf_client_t* pkgconf_client_new(pkgconf_error_handler_func_t error_handler)
+ * .. c:function:: pkgconf_client_t* pkgconf_client_new(pkgconf_error_handler_func_t error_handler, void *error_handler_data, const pkgconf_cross_personality_t *personality)
*
* Allocate and initialise a pkgconf client object.
*
* :param pkgconf_error_handler_func_t error_handler: An optional error handler to use for logging errors.
* :param void* error_handler_data: user data passed to optional error handler
+ * :param pkgconf_cross_personality_t* personality: cross-compile personality to use
* :return: A pkgconf client object.
* :rtype: pkgconf_client_t*
*/
pkgconf_client_t *
-pkgconf_client_new(pkgconf_error_handler_func_t error_handler, void *error_handler_data)
+pkgconf_client_new(pkgconf_error_handler_func_t error_handler, void *error_handler_data, const pkgconf_cross_personality_t *personality)
{
pkgconf_client_t *out = calloc(sizeof(pkgconf_client_t), 1);
- pkgconf_client_init(out, error_handler, error_handler_data);
+ pkgconf_client_init(out, error_handler, error_handler_data, personality);
return out;
}
@@ -324,7 +358,7 @@ pkgconf_trace(const pkgconf_client_t *client, const char *filename, size_t linen
size_t len;
va_list va;
- if (client == NULL)
+ if (client == NULL || client->trace_handler == NULL)
return false;
len = snprintf(errbuf, sizeof errbuf, "%s:" SIZE_FMT_SPECIFIER " [%s]: ", filename, lineno, funcname);
diff --git a/libpkgconf/config.h b/libpkgconf/config.h
index 9b83361..c490624 100644
--- a/libpkgconf/config.h
+++ b/libpkgconf/config.h
@@ -33,4 +33,4 @@
*/
#define HAVE_SYS_STAT_H 1
-#define PACKAGE_BUGREPORT "http://github.com/pkgconf/pkgconf/issues"
+#define PACKAGE_BUGREPORT "https://git.dereferenced.org/pkgconf/pkgconf/issues"
diff --git a/libpkgconf/dependency.c b/libpkgconf/dependency.c
index cd01a06..b3722b1 100644
--- a/libpkgconf/dependency.c
+++ b/libpkgconf/dependency.c
@@ -52,11 +52,76 @@ dependency_to_str(const pkgconf_dependency_t *dep, char *buf, size_t buflen)
return buf;
}
+/* find a colliding dependency that is coloured differently */
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)
+find_colliding_dependency(const pkgconf_dependency_t *dep, const pkgconf_list_t *list)
+{
+ const pkgconf_node_t *n;
+
+ PKGCONF_FOREACH_LIST_ENTRY(list->head, n)
+ {
+ pkgconf_dependency_t *dep2 = n->data;
+
+ if (strcmp(dep->package, dep2->package))
+ continue;
+
+ if (dep->flags != dep2->flags)
+ return dep2;
+ }
+
+ return NULL;
+}
+
+static inline pkgconf_dependency_t *
+add_or_replace_dependency_node(const pkgconf_client_t *client, pkgconf_dependency_t *dep, pkgconf_list_t *list)
{
- pkgconf_dependency_t *dep;
char depbuf[PKGCONF_ITEM_SIZE];
+ pkgconf_dependency_t *dep2 = find_colliding_dependency(dep, list);
+
+ /* there is already a node in the graph which describes this dependency */
+ if (dep2 != NULL)
+ {
+ char depbuf2[PKGCONF_ITEM_SIZE];
+
+ PKGCONF_TRACE(client, "dependency collision: [%s/%x] -- [%s/%x]",
+ dependency_to_str(dep, depbuf, sizeof depbuf), dep->flags,
+ dependency_to_str(dep2, depbuf2, sizeof depbuf2), dep2->flags);
+
+ /* prefer the uncoloured node, either dep or dep2 */
+ if (dep->flags && dep2->flags == 0)
+ {
+ PKGCONF_TRACE(client, "dropping dependency [%s]@%p because of collision", depbuf, dep);
+
+ free(dep);
+ return NULL;
+ }
+ else if (dep2->flags && dep->flags == 0)
+ {
+ PKGCONF_TRACE(client, "dropping dependency [%s]@%p because of collision", depbuf2, dep2);
+
+ pkgconf_node_delete(&dep2->iter, list);
+ free(dep2);
+ }
+ else
+ /* If both dependencies have equal strength, we keep both, because of situations like:
+ * Requires: foo > 1, foo < 3
+ *
+ * If the situation is that both dependencies are literally equal, it is still harmless because
+ * fragment deduplication will handle the excessive fragments.
+ */
+ PKGCONF_TRACE(client, "keeping both dependencies (harmless)");
+ }
+
+ PKGCONF_TRACE(client, "added dependency [%s] to list @%p; flags=%x", dependency_to_str(dep, depbuf, sizeof depbuf), list, dep->flags);
+ pkgconf_node_insert_tail(&dep->iter, dep, list);
+
+ return dep;
+}
+
+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, unsigned int flags)
+{
+ pkgconf_dependency_t *dep;
dep = calloc(sizeof(pkgconf_dependency_t), 1);
dep->package = pkgconf_strndup(package, package_sz);
@@ -65,11 +130,9 @@ pkgconf_dependency_addraw(const pkgconf_client_t *client, pkgconf_list_t *list,
dep->version = pkgconf_strndup(version, version_sz);
dep->compare = compare;
+ dep->flags = flags;
- 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;
+ return add_or_replace_dependency_node(client, dep, list);
}
/*
@@ -84,16 +147,17 @@ pkgconf_dependency_addraw(const pkgconf_client_t *client, pkgconf_list_t *list,
* :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.
+ * :param uint flags: Any flags to attach to 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)
+pkgconf_dependency_add(const pkgconf_client_t *client, pkgconf_list_t *list, const char *package, const char *version, pkgconf_pkg_comparator_t compare, unsigned int flags)
{
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), version, strlen(version), compare, flags);
- return pkgconf_dependency_addraw(client, list, package, strlen(package), NULL, 0, compare);
+ return pkgconf_dependency_addraw(client, list, package, strlen(package), NULL, 0, compare, flags);
}
/*
@@ -157,10 +221,11 @@ pkgconf_dependency_free(pkgconf_list_t *list)
* :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.
+ * :param uint flags: Any flags to attach to the dependency nodes.
* :return: nothing
*/
void
-pkgconf_dependency_parse_str(const pkgconf_client_t *client, pkgconf_list_t *deplist_head, const char *depends)
+pkgconf_dependency_parse_str(const pkgconf_client_t *client, pkgconf_list_t *deplist_head, const char *depends, unsigned int flags)
{
parse_state_t state = OUTSIDE_MODULE;
pkgconf_pkg_comparator_t compare = PKGCONF_CMP_ANY;
@@ -227,7 +292,7 @@ pkgconf_dependency_parse_str(const pkgconf_client_t *client, pkgconf_list_t *dep
if (state == OUTSIDE_MODULE)
{
- pkgconf_dependency_addraw(client, deplist_head, package, package_sz, NULL, 0, compare);
+ pkgconf_dependency_addraw(client, deplist_head, package, package_sz, NULL, 0, compare, flags);
compare = PKGCONF_CMP_ANY;
package_sz = 0;
@@ -270,7 +335,7 @@ pkgconf_dependency_parse_str(const pkgconf_client_t *client, pkgconf_list_t *dep
version_sz = ptr - vstart;
state = OUTSIDE_MODULE;
- pkgconf_dependency_addraw(client, deplist_head, package, package_sz, version, version_sz, compare);
+ pkgconf_dependency_addraw(client, deplist_head, package, package_sz, version, version_sz, compare, flags);
compare = PKGCONF_CMP_ANY;
cnameptr = cmpname;
@@ -300,13 +365,14 @@ pkgconf_dependency_parse_str(const pkgconf_client_t *client, pkgconf_list_t *dep
* :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.
+ * :param uint flags: Any flags to attach to the dependency nodes.
* :return: nothing
*/
void
-pkgconf_dependency_parse(const pkgconf_client_t *client, pkgconf_pkg_t *pkg, pkgconf_list_t *deplist, const char *depends)
+pkgconf_dependency_parse(const pkgconf_client_t *client, pkgconf_pkg_t *pkg, pkgconf_list_t *deplist, const char *depends, unsigned int flags)
{
char *kvdepends = pkgconf_tuple_parse(client, &pkg->vars, depends);
- pkgconf_dependency_parse_str(client, deplist, kvdepends);
+ pkgconf_dependency_parse_str(client, deplist, kvdepends, flags);
free(kvdepends);
}
diff --git a/libpkgconf/libpkgconf.h b/libpkgconf/libpkgconf.h
index 0e8d83e..3e8db08 100644
--- a/libpkgconf/libpkgconf.h
+++ b/libpkgconf/libpkgconf.h
@@ -20,9 +20,9 @@
#include <stdarg.h>
#include <stddef.h>
#include <stdbool.h>
+#include <libpkgconf/libpkgconf-api.h>
#include <libpkgconf/iter.h>
#include <libpkgconf/bsdstubs.h>
-#include <libpkgconf/libpkgconf-api.h>
#ifdef __cplusplus
extern "C" {
@@ -61,6 +61,7 @@ typedef struct pkgconf_tuple_ pkgconf_tuple_t;
typedef struct pkgconf_fragment_ pkgconf_fragment_t;
typedef struct pkgconf_path_ pkgconf_path_t;
typedef struct pkgconf_client_ pkgconf_client_t;
+typedef struct pkgconf_cross_personality_ pkgconf_cross_personality_t;
#define PKGCONF_ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x)))
@@ -90,6 +91,8 @@ struct pkgconf_dependency_ {
char *version;
pkgconf_pkg_t *parent;
pkgconf_pkg_t *match;
+
+ unsigned int flags;
};
struct pkgconf_tuple_ {
@@ -141,6 +144,12 @@ struct pkgconf_pkg_ {
unsigned int flags;
pkgconf_client_t *owner;
+
+ /* these resources are owned by the package and do not need special management,
+ * under no circumstance attempt to allocate or free objects belonging to these pointers
+ */
+ pkgconf_tuple_t *orig_prefix;
+ pkgconf_tuple_t *prefix;
};
typedef bool (*pkgconf_pkg_iteration_func_t)(const pkgconf_pkg_t *pkg, void *data);
@@ -177,9 +186,20 @@ struct pkgconf_client_ {
bool already_sent_notice;
};
+struct pkgconf_cross_personality_ {
+ const char *name;
+
+ pkgconf_list_t dir_list;
+
+ pkgconf_list_t filter_libdirs;
+ pkgconf_list_t filter_includedirs;
+
+ char *sysroot_dir;
+};
+
/* client.c */
-PKGCONF_API void pkgconf_client_init(pkgconf_client_t *client, pkgconf_error_handler_func_t error_handler, void *error_handler_data);
-PKGCONF_API pkgconf_client_t * pkgconf_client_new(pkgconf_error_handler_func_t error_handler, void *error_handler_data);
+PKGCONF_API void pkgconf_client_init(pkgconf_client_t *client, pkgconf_error_handler_func_t error_handler, void *error_handler_data, const pkgconf_cross_personality_t *personality);
+PKGCONF_API pkgconf_client_t * pkgconf_client_new(pkgconf_error_handler_func_t error_handler, void *error_handler_data, const pkgconf_cross_personality_t *personality);
PKGCONF_API void pkgconf_client_deinit(pkgconf_client_t *client);
PKGCONF_API void pkgconf_client_free(pkgconf_client_t *client);
PKGCONF_API const char *pkgconf_client_get_sysroot_dir(const pkgconf_client_t *client);
@@ -196,6 +216,11 @@ PKGCONF_API pkgconf_error_handler_func_t pkgconf_client_get_error_handler(const
PKGCONF_API void pkgconf_client_set_error_handler(pkgconf_client_t *client, pkgconf_error_handler_func_t error_handler, void *error_handler_data);
PKGCONF_API pkgconf_error_handler_func_t pkgconf_client_get_trace_handler(const pkgconf_client_t *client);
PKGCONF_API void pkgconf_client_set_trace_handler(pkgconf_client_t *client, pkgconf_error_handler_func_t trace_handler, void *trace_handler_data);
+PKGCONF_API void pkgconf_client_dir_list_build(pkgconf_client_t *client, const pkgconf_cross_personality_t *personality);
+
+/* personality.c */
+PKGCONF_API pkgconf_cross_personality_t *pkgconf_cross_personality_default(void);
+PKGCONF_API pkgconf_cross_personality_t *pkgconf_cross_personality_find(const char *triplet);
#define PKGCONF_IS_MODULE_SEPARATOR(c) ((c) == ',' || isspace ((unsigned int)(c)))
#define PKGCONF_IS_OPERATOR_CHAR(c) ((c) == '<' || (c) == '>' || (c) == '!' || (c) == '=')
@@ -214,6 +239,9 @@ PKGCONF_API void pkgconf_client_set_trace_handler(pkgconf_client_t *client, pkgc
#define PKGCONF_PKG_PKGF_REDEFINE_PREFIX 0x0400
#define PKGCONF_PKG_PKGF_DONT_RELOCATE_PATHS 0x0800
#define PKGCONF_PKG_PKGF_SIMPLIFY_ERRORS 0x1000
+#define PKGCONF_PKG_PKGF_DONT_FILTER_INTERNAL_CFLAGS 0x2000
+
+#define PKGCONF_PKG_DEPF_INTERNAL 0x1
#define PKGCONF_PKG_ERRF_OK 0x0
#define PKGCONF_PKG_ERRF_PACKAGE_NOT_FOUND 0x1
@@ -221,7 +249,6 @@ PKGCONF_API void pkgconf_client_set_trace_handler(pkgconf_client_t *client, pkgc
#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)))
@@ -232,6 +259,13 @@ PKGCONF_API void pkgconf_client_set_trace_handler(pkgconf_client_t *client, pkgc
#define DEPRECATED
#endif /* defined(__INTEL_COMPILER) || defined(__GNUC__) */
+/* parser.c */
+typedef void (*pkgconf_parser_operand_func_t)(void *data, const size_t lineno, const char *key, const char *value);
+typedef void (*pkgconf_parser_warn_func_t)(void *data, const char *fmt, ...);
+
+PKGCONF_API void pkgconf_parser_parse(FILE *f, void *data, const pkgconf_parser_operand_func_t *ops, const pkgconf_parser_warn_func_t warnfunc, const char *filename);
+
+/* pkg.c */
PKGCONF_API bool pkgconf_error(const pkgconf_client_t *client, const char *format, ...) PRINTFLIKE(2, 3);
PKGCONF_API bool pkgconf_warn(const pkgconf_client_t *client, const char *format, ...) PRINTFLIKE(2, 3);
PKGCONF_API bool pkgconf_trace(const pkgconf_client_t *client, const char *filename, size_t lineno, const char *funcname, const char *format, ...) PRINTFLIKE(5, 6);
@@ -251,7 +285,7 @@ PKGCONF_API pkgconf_pkg_t *pkgconf_pkg_ref(pkgconf_client_t *client, pkgconf_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_traverse(pkgconf_client_t *client, pkgconf_pkg_t *root, pkgconf_pkg_traverse_func_t func, void *data, int maxdepth, unsigned int skip_flags);
PKGCONF_API unsigned int pkgconf_pkg_verify_graph(pkgconf_client_t *client, pkgconf_pkg_t *root, int depth);
PKGCONF_API pkgconf_pkg_t *pkgconf_pkg_verify_dependency(pkgconf_client_t *client, pkgconf_dependency_t *pkgdep, unsigned int *eflags);
PKGCONF_API const char *pkgconf_pkg_get_comparator(const pkgconf_dependency_t *pkgdep);
@@ -262,15 +296,14 @@ 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_parse_str(const pkgconf_client_t *client, pkgconf_list_t *deplist_head, const char *depends, unsigned int flags);
+PKGCONF_API void pkgconf_dependency_parse(const pkgconf_client_t *client, pkgconf_pkg_t *pkg, pkgconf_list_t *deplist_head, const char *depends, unsigned int flags);
PKGCONF_API void pkgconf_dependency_append(pkgconf_list_t *list, pkgconf_dependency_t *tail);
PKGCONF_API void pkgconf_dependency_free(pkgconf_list_t *list);
-PKGCONF_API pkgconf_dependency_t *pkgconf_dependency_add(const pkgconf_client_t *client, pkgconf_list_t *list, const char *package, const char *version, pkgconf_pkg_comparator_t compare);
+PKGCONF_API pkgconf_dependency_t *pkgconf_dependency_add(const pkgconf_client_t *client, pkgconf_list_t *list, const char *package, const char *version, pkgconf_pkg_comparator_t compare, unsigned int flags);
/* argvsplit.c */
PKGCONF_API int pkgconf_argv_split(const char *src, int *argc, char ***argv);
@@ -333,6 +366,7 @@ PKGCONF_API size_t pkgconf_path_build_from_environ(const char *envvarname, const
PKGCONF_API bool pkgconf_path_match_list(const char *path, const pkgconf_list_t *dirlist);
PKGCONF_API void pkgconf_path_free(pkgconf_list_t *dirlist);
PKGCONF_API bool pkgconf_path_relocate(char *buf, size_t buflen);
+PKGCONF_API void pkgconf_path_copy_list(pkgconf_list_t *dst, const pkgconf_list_t *src);
#ifdef __cplusplus
}
diff --git a/libpkgconf/parser.c b/libpkgconf/parser.c
new file mode 100644
index 0000000..704c4de
--- /dev/null
+++ b/libpkgconf/parser.c
@@ -0,0 +1,95 @@
+/*
+ * parser.c
+ * rfc822 message parser
+ *
+ * Copyright (c) 2018 pkgconf authors (see AUTHORS).
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * This software is provided 'as is' and without any warranty, express or
+ * implied. In no event shall the authors be liable for any damages arising
+ * from the use of this software.
+ */
+
+#include <libpkgconf/stdinc.h>
+#include <libpkgconf/config.h>
+#include <libpkgconf/libpkgconf.h>
+
+/*
+ * !doc
+ *
+ * .. c:function:: pkgconf_pkg_t *pkgconf_pkg_new_from_file(const pkgconf_client_t *client, const char *filename, FILE *f)
+ *
+ * Parse a .pc file into a pkgconf_pkg_t object structure.
+ *
+ * :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution.
+ * :param char* filename: The filename of the package file (including full path).
+ * :param FILE* f: The file object to read from.
+ * :returns: A ``pkgconf_pkg_t`` object which contains the package data.
+ * :rtype: pkgconf_pkg_t *
+ */
+void
+pkgconf_parser_parse(FILE *f, void *data, const pkgconf_parser_operand_func_t *ops, const pkgconf_parser_warn_func_t warnfunc, const char *filename)
+{
+ char readbuf[PKGCONF_BUFSIZE];
+ size_t lineno = 0;
+
+ while (pkgconf_fgetline(readbuf, PKGCONF_BUFSIZE, f) != NULL)
+ {
+ char op, *p, *key, *value;
+ bool warned_key_whitespace = false, warned_value_whitespace = false;
+
+ lineno++;
+
+ p = readbuf;
+ while (*p && (isalpha((unsigned int)*p) || isdigit((unsigned int)*p) || *p == '_' || *p == '.'))
+ p++;
+
+ key = readbuf;
+ if (!isalpha((unsigned int)*key) && !isdigit((unsigned int)*p))
+ continue;
+
+ while (*p && isspace((unsigned int)*p))
+ {
+ if (!warned_key_whitespace)
+ {
+ warnfunc(data, "%s:" SIZE_FMT_SPECIFIER ": warning: whitespace encountered while parsing key section\n",
+ filename, lineno);
+ warned_key_whitespace = true;
+ }
+
+ /* set to null to avoid trailing spaces in key */
+ *p = '\0';
+ p++;
+ }
+
+ op = *p;
+ *p = '\0';
+ p++;
+
+ while (*p && isspace((unsigned int)*p))
+ p++;
+
+ value = p;
+ p = value + (strlen(value) - 1);
+ while (*p && isspace((unsigned int) *p) && p > value)
+ {
+ if (!warned_value_whitespace && op == '=')
+ {
+ warnfunc(data, "%s:" SIZE_FMT_SPECIFIER ": warning: trailing whitespace encountered while parsing value section\n",
+ filename, lineno);
+ warned_value_whitespace = true;
+ }
+
+ *p = '\0';
+ p--;
+ }
+
+ if (ops[(unsigned char) op])
+ ops[(unsigned char) op](data, lineno, key, value);
+ }
+
+ fclose(f);
+}
diff --git a/libpkgconf/path.c b/libpkgconf/path.c
index 2f4cfbb..02d1c97 100644
--- a/libpkgconf/path.c
+++ b/libpkgconf/path.c
@@ -225,6 +225,38 @@ pkgconf_path_match_list(const char *path, const pkgconf_list_t *dirlist)
/*
* !doc
*
+ * .. c:function:: void pkgconf_path_copy_list(pkgconf_list_t *dst, const pkgconf_list_t *src)
+ *
+ * Copies a path list to another path list.
+ *
+ * :param pkgconf_list_t* dst: The path list to copy to.
+ * :param pkgconf_list_t* src: The path list to copy from.
+ * :return: nothing
+ */
+void
+pkgconf_path_copy_list(pkgconf_list_t *dst, const pkgconf_list_t *src)
+{
+ pkgconf_node_t *n;
+
+ PKGCONF_FOREACH_LIST_ENTRY(src->head, n)
+ {
+ pkgconf_path_t *srcpath = n->data, *path;
+
+ path = calloc(sizeof(pkgconf_path_t), 1);
+ path->path = strdup(srcpath->path);
+
+#ifdef PKGCONF_CACHE_INODES
+ path->handle_path = srcpath->handle_path;
+ path->handle_device = srcpath->handle_device;
+#endif
+
+ pkgconf_node_insert_tail(&path->lnode, path, dst);
+ }
+}
+
+/*
+ * !doc
+ *
* .. c:function:: void pkgconf_path_free(pkgconf_list_t *dirlist)
*
* Releases any path nodes attached to the given path list.
diff --git a/libpkgconf/personality.c b/libpkgconf/personality.c
new file mode 100644
index 0000000..a15f8cf
--- /dev/null
+++ b/libpkgconf/personality.c
@@ -0,0 +1,251 @@
+/*
+ * personality.c
+ * libpkgconf cross-compile personality database
+ *
+ * Copyright (c) 2018 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>
+
+#ifdef _WIN32
+# define strcasecmp _stricmp
+#endif
+
+static bool default_personality_init = false;
+static pkgconf_cross_personality_t default_personality = {
+ .name = "default",
+};
+
+static inline void
+build_default_search_path(pkgconf_list_t* dirlist)
+{
+#ifdef _WIN32
+ char namebuf[MAX_PATH];
+ char outbuf[MAX_PATH];
+ char *p;
+
+ int sizepath = GetModuleFileName(NULL, namebuf, sizeof namebuf);
+ char * winslash;
+ namebuf[sizepath] = '\0';
+ while ((winslash = strchr (namebuf, '\\')) != NULL)
+ {
+ *winslash = '/';
+ }
+ p = strrchr(namebuf, '/');
+ if (p == NULL)
+ pkgconf_path_split(PKG_DEFAULT_PATH, dirlist, true);
+
+ *p = '\0';
+ pkgconf_strlcpy(outbuf, namebuf, sizeof outbuf);
+ pkgconf_strlcat(outbuf, "/", sizeof outbuf);
+ pkgconf_strlcat(outbuf, "../lib/pkgconfig", sizeof outbuf);
+ pkgconf_path_add(outbuf, dirlist, true);
+ pkgconf_strlcpy(outbuf, namebuf, sizeof outbuf);
+ pkgconf_strlcat(outbuf, "/", sizeof outbuf);
+ pkgconf_strlcat(outbuf, "../share/pkgconfig", sizeof outbuf);
+ pkgconf_path_add(outbuf, dirlist, true);
+#elif __HAIKU__
+ char **paths;
+ size_t count;
+ if (find_paths(B_FIND_PATH_DEVELOP_LIB_DIRECTORY, "pkgconfig", &paths, &count) == B_OK) {
+ for (size_t i = 0; i < count; i++)
+ pkgconf_path_add(paths[i], dirlist, true);
+ free(paths);
+ paths = NULL;
+ }
+ if (find_paths(B_FIND_PATH_DATA_DIRECTORY, "pkgconfig", &paths, &count) == B_OK) {
+ for (size_t i = 0; i < count; i++)
+ pkgconf_path_add(paths[i], dirlist, true);
+ free(paths);
+ paths = NULL;
+ }
+#else
+ pkgconf_path_split(PKG_DEFAULT_PATH, dirlist, true);
+#endif
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: const pkgconf_cross_personality_t *pkgconf_cross_personality_default(void)
+ *
+ * Returns the default cross-compile personality.
+ *
+ * :rtype: pkgconf_cross_personality_t*
+ * :return: the default cross-compile personality
+ */
+pkgconf_cross_personality_t *
+pkgconf_cross_personality_default(void)
+{
+ if (default_personality_init)
+ return &default_personality;
+
+ build_default_search_path(&default_personality.dir_list);
+
+ pkgconf_path_split(SYSTEM_LIBDIR, &default_personality.filter_libdirs, true);
+ pkgconf_path_split(SYSTEM_INCLUDEDIR, &default_personality.filter_includedirs, true);
+
+ default_personality_init = true;
+ return &default_personality;
+}
+
+static bool
+valid_triplet(const char *triplet)
+{
+ const char *c = triplet;
+
+ for (; *c; c++)
+ if (!isalnum(*c) && *c != '-' && *c != '_')
+ return false;
+
+ return true;
+}
+
+typedef void (*personality_keyword_func_t)(pkgconf_cross_personality_t *p, const char *keyword, const size_t lineno, const ptrdiff_t offset, char *value);
+typedef struct {
+ const char *keyword;
+ const personality_keyword_func_t func;
+ const ptrdiff_t offset;
+} personality_keyword_pair_t;
+
+static void
+personality_copy_func(pkgconf_cross_personality_t *p, const char *keyword, const size_t lineno, const ptrdiff_t offset, char *value)
+{
+ (void) keyword;
+ (void) lineno;
+
+ char **dest = (char **)((char *) p + offset);
+ *dest = strdup(value);
+}
+
+static void
+personality_fragment_func(pkgconf_cross_personality_t *p, const char *keyword, const size_t lineno, const ptrdiff_t offset, char *value)
+{
+ (void) keyword;
+ (void) lineno;
+
+ pkgconf_list_t *dest = (pkgconf_list_t *)((char *) p + offset);
+ pkgconf_path_split(value, dest, false);
+}
+
+/* keep in alphabetical order! */
+static const personality_keyword_pair_t personality_keyword_pairs[] = {
+ {"DefaultSearchPaths", personality_fragment_func, offsetof(pkgconf_cross_personality_t, dir_list)},
+ {"SysrootDir", personality_copy_func, offsetof(pkgconf_cross_personality_t, sysroot_dir)},
+ {"SystemIncludePaths", personality_fragment_func, offsetof(pkgconf_cross_personality_t, filter_includedirs)},
+ {"SystemLibraryPaths", personality_fragment_func, offsetof(pkgconf_cross_personality_t, filter_libdirs)},
+ {"Triplet", personality_copy_func, offsetof(pkgconf_cross_personality_t, name)},
+};
+
+static int
+personality_keyword_pair_cmp(const void *key, const void *ptr)
+{
+ const personality_keyword_pair_t *pair = ptr;
+ return strcasecmp(key, pair->keyword);
+}
+
+static void
+personality_keyword_set(pkgconf_cross_personality_t *p, const size_t lineno, const char *keyword, char *value)
+{
+ const personality_keyword_pair_t *pair = bsearch(keyword,
+ personality_keyword_pairs, PKGCONF_ARRAY_SIZE(personality_keyword_pairs),
+ sizeof(personality_keyword_pair_t), personality_keyword_pair_cmp);
+
+ if (pair == NULL || pair->func == NULL)
+ return;
+
+ pair->func(p, keyword, lineno, pair->offset, value);
+}
+
+static const pkgconf_parser_operand_func_t personality_parser_ops[] = {
+ [':'] = (pkgconf_parser_operand_func_t) personality_keyword_set
+};
+
+static void personality_warn_func(void *p, const char *fmt, ...) PRINTFLIKE(2, 3);
+
+static void
+personality_warn_func(void *p, const char *fmt, ...)
+{
+ va_list va;
+
+ (void) p;
+
+ va_start(va, fmt);
+ vfprintf(stderr, fmt, va);
+ va_end(va);
+}
+
+static pkgconf_cross_personality_t *
+load_personality_with_path(const char *path, const char *triplet)
+{
+ char pathbuf[PKGCONF_ITEM_SIZE];
+ FILE *f;
+ pkgconf_cross_personality_t *p;
+
+ /* if triplet is null, assume that path is a direct path to the personality file */
+ if (triplet != NULL)
+ snprintf(pathbuf, sizeof pathbuf, "%s/%s.personality", path, triplet);
+ else
+ pkgconf_strlcpy(pathbuf, path, sizeof pathbuf);
+
+ f = fopen(pathbuf, "r");
+ if (f == NULL)
+ return NULL;
+
+ p = calloc(sizeof(pkgconf_cross_personality_t), 1);
+ if (triplet != NULL)
+ p->name = strdup(triplet);
+ pkgconf_parser_parse(f, p, personality_parser_ops, personality_warn_func, pathbuf);
+
+ return p;
+}
+
+/*
+ * !doc
+ *
+ * .. c:function:: pkgconf_cross_personality_t *pkgconf_cross_personality_find(const char *triplet)
+ *
+ * Attempts to find a cross-compile personality given a triplet.
+ *
+ * :rtype: pkgconf_cross_personality_t*
+ * :return: the default cross-compile personality
+ */
+pkgconf_cross_personality_t *
+pkgconf_cross_personality_find(const char *triplet)
+{
+ pkgconf_list_t plist = PKGCONF_LIST_INITIALIZER;
+ pkgconf_node_t *n;
+ pkgconf_cross_personality_t *out = NULL;
+
+ out = load_personality_with_path(triplet, NULL);
+ if (out != NULL)
+ return out;
+
+ if (!valid_triplet(triplet))
+ return NULL;
+
+ pkgconf_path_split(PERSONALITY_PATH, &plist, true);
+
+ PKGCONF_FOREACH_LIST_ENTRY(plist.head, n)
+ {
+ pkgconf_path_t *pn = n->data;
+
+ out = load_personality_with_path(pn->path, triplet);
+ if (out != NULL)
+ goto finish;
+ }
+
+finish:
+ pkgconf_path_free(&plist);
+ return out;
+}
diff --git a/libpkgconf/pkg.c b/libpkgconf/pkg.c
index 0feb4d6..24ace6f 100644
--- a/libpkgconf/pkg.c
+++ b/libpkgconf/pkg.c
@@ -49,80 +49,19 @@ str_has_suffix(const char *str, const char *suffix)
return !strncasecmp(str + str_len - suf_len, suffix, suf_len);
}
-static inline const char *
-get_default_pkgconfig_path(char *outbuf, size_t outlen)
+static char *
+pkg_get_parent_dir(pkgconf_pkg_t *pkg)
{
-#ifdef _WIN32
- char namebuf[MAX_PATH];
- char *p;
-
- int sizepath = GetModuleFileName(NULL, namebuf, sizeof namebuf);
- char * winslash;
- namebuf[sizepath] = '\0';
- while ((winslash = strchr (namebuf, '\\')) != NULL)
- {
- *winslash = '/';
- }
- p = strrchr(namebuf, '/');
- if (p == NULL)
- return PKG_DEFAULT_PATH;
-
- *p = '\0';
- pkgconf_strlcpy(outbuf, namebuf, outlen);
- pkgconf_strlcat(outbuf, "/", outlen);
- pkgconf_strlcat(outbuf, "../lib/pkgconfig", outlen);
- pkgconf_strlcat(outbuf, ";", outlen);
- pkgconf_strlcat(outbuf, namebuf, outlen);
- pkgconf_strlcat(outbuf, "/", outlen);
- pkgconf_strlcat(outbuf, "../share/pkgconfig", outlen);
-
- return outbuf;
-#else
- (void) outbuf;
- (void) outlen;
-#endif
-
- return PKG_DEFAULT_PATH;
-}
-
-static const char *
-pkg_get_parent_dir(pkgconf_pkg_t *pkg, char *buf, size_t buflen)
-{
- char *pathbuf;
+ char buf[PKGCONF_ITEM_SIZE], *pathbuf;
- pkgconf_strlcpy(buf, pkg->filename, buflen);
+ pkgconf_strlcpy(buf, pkg->filename, sizeof buf);
pathbuf = strrchr(buf, PKG_DIR_SEP_S);
if (pathbuf == NULL)
pathbuf = strrchr(buf, '/');
if (pathbuf != NULL)
pathbuf[0] = '\0';
- return buf;
-}
-
-/*
- * !doc
- *
- * .. c:function:: void pkgconf_pkg_dir_list_build(pkgconf_client_t *client)
- *
- * Bootstraps the package search paths. If the ``PKGCONF_PKG_PKGF_ENV_ONLY`` `flag` is set on the client,
- * then only the ``PKG_CONFIG_PATH`` environment variable will be used, otherwise both the
- * ``PKG_CONFIG_PATH`` and ``PKG_CONFIG_LIBDIR`` environment variables will be used.
- *
- * :param pkgconf_client_t* client: The pkgconf client object to bootstrap.
- * :return: nothing
- */
-void
-pkgconf_pkg_dir_list_build(pkgconf_client_t *client)
-{
- pkgconf_path_build_from_environ("PKG_CONFIG_PATH", NULL, &client->dir_list, true);
-
- if (!(client->flags & PKGCONF_PKG_PKGF_ENV_ONLY))
- {
- char pathbuf[PKGCONF_BUFSIZE];
-
- pkgconf_path_build_from_environ("PKG_CONFIG_LIBDIR", get_default_pkgconfig_path(pathbuf, sizeof pathbuf), &client->dir_list, true);
- }
+ return strdup(buf);
}
typedef void (*pkgconf_pkg_parser_keyword_func_t)(const pkgconf_client_t *client, pkgconf_pkg_t *pkg, const char *keyword, const size_t lineno, const ptrdiff_t offset, char *value);
@@ -168,7 +107,18 @@ pkgconf_pkg_parser_dependency_func(const pkgconf_client_t *client, pkgconf_pkg_t
(void) lineno;
pkgconf_list_t *dest = (pkgconf_list_t *)((char *) pkg + offset);
- pkgconf_dependency_parse(client, pkg, dest, value);
+ pkgconf_dependency_parse(client, pkg, dest, value, 0);
+}
+
+/* a variant of pkgconf_pkg_parser_dependency_func which colors the dependency node as an "internal" dependency. */
+static void
+pkgconf_pkg_parser_internal_dependency_func(const pkgconf_client_t *client, pkgconf_pkg_t *pkg, const char *keyword, const size_t lineno, const ptrdiff_t offset, char *value)
+{
+ (void) keyword;
+ (void) lineno;
+
+ pkgconf_list_t *dest = (pkgconf_list_t *)((char *) pkg + offset);
+ pkgconf_dependency_parse(client, pkg, dest, value, PKGCONF_PKG_DEPF_INTERNAL);
}
/* keep this in alphabetical order */
@@ -182,22 +132,25 @@ static const pkgconf_pkg_parser_keyword_pair_t pkgconf_pkg_parser_keyword_funcs[
{"Name", pkgconf_pkg_parser_tuple_func, offsetof(pkgconf_pkg_t, realname)},
{"Provides", pkgconf_pkg_parser_dependency_func, offsetof(pkgconf_pkg_t, provides)},
{"Requires", pkgconf_pkg_parser_dependency_func, offsetof(pkgconf_pkg_t, required)},
+ {"Requires.internal", pkgconf_pkg_parser_internal_dependency_func, offsetof(pkgconf_pkg_t, requires_private)},
{"Requires.private", pkgconf_pkg_parser_dependency_func, offsetof(pkgconf_pkg_t, requires_private)},
{"Version", pkgconf_pkg_parser_tuple_func, offsetof(pkgconf_pkg_t, version)},
};
-static bool
-pkgconf_pkg_parser_keyword_set(const pkgconf_client_t *client, pkgconf_pkg_t *pkg, const size_t lineno, const char *keyword, char *value)
+/*
+ * Fix the return type (issue #13 is reported).
+ */
+static void
+pkgconf_pkg_parser_keyword_set(pkgconf_pkg_t *pkg, const size_t lineno, const char *keyword, char *value)
{
const pkgconf_pkg_parser_keyword_pair_t *pair = bsearch(keyword,
pkgconf_pkg_parser_keyword_funcs, PKGCONF_ARRAY_SIZE(pkgconf_pkg_parser_keyword_funcs),
sizeof(pkgconf_pkg_parser_keyword_pair_t), pkgconf_pkg_parser_keyword_pair_cmp);
if (pair == NULL || pair->func == NULL)
- return false;
+ return;
- pair->func(client, pkg, keyword, lineno, pair->offset, value);
- return true;
+ pair->func(pkg->owner, pkg, keyword, lineno, pair->offset, value);
}
static const char *
@@ -237,6 +190,96 @@ determine_prefix(const pkgconf_pkg_t *pkg, char *buf, size_t buflen)
return buf;
}
+static void
+remove_additional_separators(char *buf)
+{
+ char *p = buf;
+
+ while (*p) {
+ if (*p == '/') {
+ char *q;
+
+ q = ++p;
+ while (*q && *q == '/')
+ q++;
+
+ if (p != q)
+ memmove (p, q, strlen (q) + 1);
+ } else {
+ p++;
+ }
+ }
+}
+
+static void
+canonicalize_path(char *buf)
+{
+#ifdef _WIN32
+ char *p = buf;
+
+ while (*p) {
+ if (*p == '\\')
+ *p = '/';
+ p++;
+ }
+#endif
+
+ remove_additional_separators(buf);
+}
+
+static bool
+is_path_prefix_equal(const char *path1, const char *path2, size_t path2_len)
+{
+#ifdef _WIN32
+ return !_strnicmp(path1, path2, path2_len);
+#else
+ return !strncmp(path1, path2, path2_len);
+#endif
+}
+
+/*
+ * Fix the return type (issue #13 is reported).
+ */
+static void
+pkgconf_pkg_parser_value_set(pkgconf_pkg_t *pkg, const size_t lineno, const char *keyword, char *value)
+{
+ char canonicalized_value[PKGCONF_ITEM_SIZE];
+
+ (void) lineno;
+
+ pkgconf_strlcpy(canonicalized_value, value, sizeof canonicalized_value);
+ canonicalize_path(canonicalized_value);
+
+ /* Some pc files will use absolute paths for all of their directories
+ * which is broken when redefining the prefix. We try to outsmart the
+ * file and rewrite any directory that starts with the same prefix.
+ */
+ if (pkg->owner->flags & PKGCONF_PKG_PKGF_REDEFINE_PREFIX && pkg->orig_prefix
+ && is_path_prefix_equal(canonicalized_value, pkg->orig_prefix->value, strlen(pkg->orig_prefix->value)))
+ {
+ char newvalue[PKGCONF_ITEM_SIZE];
+
+ pkgconf_strlcpy(newvalue, pkg->prefix->value, sizeof newvalue);
+ pkgconf_strlcat(newvalue, canonicalized_value + strlen(pkg->orig_prefix->value), sizeof newvalue);
+ pkgconf_tuple_add(pkg->owner, &pkg->vars, keyword, newvalue, false);
+ }
+ else if (strcmp(keyword, pkg->owner->prefix_varname) || !(pkg->owner->flags & PKGCONF_PKG_PKGF_REDEFINE_PREFIX))
+ pkgconf_tuple_add(pkg->owner, &pkg->vars, keyword, value, true);
+ else
+ {
+ char pathbuf[PKGCONF_ITEM_SIZE];
+ const char *relvalue = determine_prefix(pkg, pathbuf, sizeof pathbuf);
+
+ if (relvalue != NULL)
+ {
+ pkg->orig_prefix = pkgconf_tuple_add(pkg->owner, &pkg->vars, "orig_prefix", canonicalized_value, true);
+ pkg->prefix = pkgconf_tuple_add(pkg->owner, &pkg->vars, keyword, relvalue, false);
+ }
+ else
+ pkgconf_tuple_add(pkg->owner, &pkg->vars, keyword, value, true);
+ }
+}
+
typedef struct {
const char *field;
const ptrdiff_t offset;
@@ -248,6 +291,26 @@ static const pkgconf_pkg_validity_check_t pkgconf_pkg_validations[] = {
{"Version", offsetof(pkgconf_pkg_t, version)},
};
+static const pkgconf_parser_operand_func_t pkg_parser_funcs[256] = {
+ [':'] = (pkgconf_parser_operand_func_t) pkgconf_pkg_parser_keyword_set,
+ ['='] = (pkgconf_parser_operand_func_t) pkgconf_pkg_parser_value_set
+};
+
+static void pkg_warn_func(pkgconf_pkg_t *pkg, const char *fmt, ...) PRINTFLIKE(2, 3);
+
+static void
+pkg_warn_func(pkgconf_pkg_t *pkg, const char *fmt, ...)
+{
+ char buf[PKGCONF_ITEM_SIZE];
+ va_list va;
+
+ va_start(va, fmt);
+ vsnprintf(buf, sizeof buf, fmt, va);
+ va_end(va);
+
+ pkgconf_warn(pkg->owner, "%s", buf);
+}
+
static bool
pkgconf_pkg_validate(const pkgconf_client_t *client, const pkgconf_pkg_t *pkg)
{
@@ -285,14 +348,13 @@ pkgconf_pkg_t *
pkgconf_pkg_new_from_file(pkgconf_client_t *client, const char *filename, FILE *f)
{
pkgconf_pkg_t *pkg;
- char readbuf[PKGCONF_BUFSIZE];
- char pathbuf[PKGCONF_ITEM_SIZE];
char *idptr;
- size_t lineno = 0;
pkg = calloc(sizeof(pkgconf_pkg_t), 1);
+ pkg->owner = client;
pkg->filename = strdup(filename);
- pkgconf_tuple_add(client, &pkg->vars, "pcfiledir", pkg_get_parent_dir(pkg, pathbuf, sizeof pathbuf), true);
+ pkg->pc_filedir = pkg_get_parent_dir(pkg);
+ pkgconf_tuple_add(client, &pkg->vars, "pcfiledir", pkg->pc_filedir, true);
/* make module id */
if ((idptr = strrchr(pkg->filename, PKG_DIR_SEP_S)) != NULL)
@@ -309,85 +371,7 @@ pkgconf_pkg_new_from_file(pkgconf_client_t *client, const char *filename, FILE *
if (idptr)
*idptr = '\0';
- while (pkgconf_fgetline(readbuf, PKGCONF_BUFSIZE, f) != NULL)
- {
- char op, *p, *key, *value;
- bool warned_key_whitespace = false, warned_value_whitespace = false;
-
- lineno++;
-
- PKGCONF_TRACE(client, "%s:" SIZE_FMT_SPECIFIER " > [%s]", filename, lineno, readbuf);
-
- p = readbuf;
- while (*p && (isalpha((unsigned int)*p) || isdigit((unsigned int)*p) || *p == '_' || *p == '.'))
- p++;
-
- key = readbuf;
- if (!isalpha((unsigned int)*key) && !isdigit((unsigned int)*p))
- continue;
-
- while (*p && isspace((unsigned int)*p))
- {
- if (!warned_key_whitespace)
- {
- pkgconf_warn(client, "%s:" SIZE_FMT_SPECIFIER ": warning: whitespace encountered while parsing key section\n",
- pkg->filename, lineno);
- warned_key_whitespace = true;
- }
-
- /* set to null to avoid trailing spaces in key */
- *p = '\0';
- p++;
- }
-
- op = *p;
- *p = '\0';
- p++;
-
- while (*p && isspace((unsigned int)*p))
- p++;
-
- value = p;
- p = value + (strlen(value) - 1);
- while (*p && isspace((unsigned int) *p) && p > value)
- {
- if (!warned_value_whitespace && op == '=')
- {
- pkgconf_warn(client, "%s:" SIZE_FMT_SPECIFIER ": warning: trailing whitespace encountered while parsing value section\n",
- pkg->filename, lineno);
- warned_value_whitespace = true;
- }
-
- *p = '\0';
- p--;
- }
-
- switch (op)
- {
- case ':':
- pkgconf_pkg_parser_keyword_set(client, pkg, lineno, key, value);
- break;
- case '=':
- if (strcmp(key, client->prefix_varname) || !(client->flags & PKGCONF_PKG_PKGF_REDEFINE_PREFIX))
- pkgconf_tuple_add(client, &pkg->vars, key, value, true);
- else
- {
- const char *relvalue = determine_prefix(pkg, pathbuf, sizeof pathbuf);
- if (relvalue != NULL)
- {
- pkgconf_tuple_add(client, &pkg->vars, "orig_prefix", value, true);
- pkgconf_tuple_add(client, &pkg->vars, key, relvalue, false);
- }
- else
- pkgconf_tuple_add(client, &pkg->vars, key, value, true);
- }
- break;
- default:
- break;
- }
- }
-
- fclose(f);
+ pkgconf_parser_parse(f, pkg, pkg_parser_funcs, (pkgconf_parser_warn_func_t) pkg_warn_func, pkg->filename);
if (!pkgconf_pkg_validate(client, pkg))
{
@@ -396,7 +380,7 @@ pkgconf_pkg_new_from_file(pkgconf_client_t *client, const char *filename, FILE *
return NULL;
}
- pkgconf_dependency_add(client, &pkg->provides, pkg->id, pkg->version, PKGCONF_CMP_EQUAL);
+ pkgconf_dependency_add(client, &pkg->provides, pkg->id, pkg->version, PKGCONF_CMP_EQUAL, 0);
return pkgconf_pkg_ref(client, pkg);
}
@@ -480,7 +464,6 @@ pkgconf_pkg_ref(pkgconf_client_t *client, pkgconf_pkg_t *pkg)
if (pkg->owner != NULL && pkg->owner != client)
PKGCONF_TRACE(client, "WTF: client %p refers to package %p owned by other client %p", client, pkg, pkg->owner);
- pkg->owner = client;
pkg->refcount++;
PKGCONF_TRACE(client, "refcount@%p: %d", pkg, pkg->refcount);
@@ -676,7 +659,6 @@ 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_ITEM_SIZE];
pkgconf_pkg_t *pkg = NULL;
pkgconf_node_t *n;
FILE *f;
@@ -695,7 +677,7 @@ pkgconf_pkg_find(pkgconf_client_t *client, const char *name)
pkg = pkgconf_pkg_new_from_file(client, name, f);
if (pkg != NULL)
{
- pkgconf_path_add(pkg_get_parent_dir(pkg, pathbuf, sizeof pathbuf), &client->dir_list, true);
+ pkgconf_path_add(pkg->pc_filedir, &client->dir_list, true);
return pkg;
}
}
@@ -1347,7 +1329,7 @@ pkgconf_pkg_verify_dependency(pkgconf_client_t *client, pkgconf_dependency_t *pk
unsigned int
pkgconf_pkg_verify_graph(pkgconf_client_t *client, pkgconf_pkg_t *root, int depth)
{
- return pkgconf_pkg_traverse(client, root, NULL, NULL, depth);
+ return pkgconf_pkg_traverse(client, root, NULL, NULL, depth, 0);
}
static unsigned int
@@ -1388,7 +1370,8 @@ pkgconf_pkg_walk_list(pkgconf_client_t *client,
pkgconf_list_t *deplist,
pkgconf_pkg_traverse_func_t func,
void *data,
- int depth)
+ int depth,
+ unsigned int skip_flags)
{
unsigned int eflags = PKGCONF_PKG_ERRF_OK;
pkgconf_node_t *node;
@@ -1419,10 +1402,16 @@ pkgconf_pkg_walk_list(pkgconf_client_t *client,
continue;
}
+ if (skip_flags && (depnode->flags & skip_flags) == skip_flags)
+ {
+ pkgconf_pkg_unref(client, pkgdep);
+ continue;
+ }
+
pkgconf_audit_log_dependency(client, pkgdep, depnode);
pkgdep->flags |= PKGCONF_PKG_PROPF_SEEN;
- eflags |= pkgconf_pkg_traverse(client, pkgdep, func, data, depth - 1);
+ eflags |= pkgconf_pkg_traverse(client, pkgdep, func, data, depth - 1, skip_flags);
pkgdep->flags &= ~PKGCONF_PKG_PROPF_SEEN;
pkgconf_pkg_unref(client, pkgdep);
}
@@ -1480,7 +1469,7 @@ pkgconf_pkg_walk_conflicts_list(pkgconf_client_t *client,
/*
* !doc
*
- * .. c:function:: unsigned int pkgconf_pkg_traverse(pkgconf_client_t *client, pkgconf_pkg_t *root, pkgconf_pkg_traverse_func_t func, void *data, int maxdepth)
+ * .. c:function:: unsigned int pkgconf_pkg_traverse(pkgconf_client_t *client, pkgconf_pkg_t *root, pkgconf_pkg_traverse_func_t func, void *data, int maxdepth, unsigned int skip_flags)
*
* Walk and resolve the dependency graph up to `maxdepth` levels.
*
@@ -1489,6 +1478,7 @@ pkgconf_pkg_walk_conflicts_list(pkgconf_client_t *client,
* :param pkgconf_pkg_traverse_func_t func: A traversal function to call for each resolved node in the dependency graph.
* :param void* data: An opaque pointer to data to be passed to the traversal function.
* :param int maxdepth: The maximum depth to walk the dependency graph for. -1 means infinite recursion.
+ * :param uint skip_flags: Skip over dependency nodes containing the specified flags. A setting of 0 skips no dependency nodes.
* :return: ``PKGCONF_PKG_ERRF_OK`` on success, else an error code.
* :rtype: unsigned int
*/
@@ -1497,7 +1487,8 @@ pkgconf_pkg_traverse(pkgconf_client_t *client,
pkgconf_pkg_t *root,
pkgconf_pkg_traverse_func_t func,
void *data,
- int maxdepth)
+ int maxdepth,
+ unsigned int skip_flags)
{
unsigned int eflags = PKGCONF_PKG_ERRF_OK;
@@ -1520,7 +1511,7 @@ pkgconf_pkg_traverse(pkgconf_client_t *client,
}
PKGCONF_TRACE(client, "%s: walking requires list", root->id);
- eflags = pkgconf_pkg_walk_list(client, root, &root->required, func, data, maxdepth);
+ eflags = pkgconf_pkg_walk_list(client, root, &root->required, func, data, maxdepth, skip_flags);
if (eflags != PKGCONF_PKG_ERRF_OK)
return eflags;
@@ -1530,7 +1521,7 @@ pkgconf_pkg_traverse(pkgconf_client_t *client,
/* XXX: ugly */
client->flags |= PKGCONF_PKG_PKGF_ITER_PKG_IS_PRIVATE;
- eflags = pkgconf_pkg_walk_list(client, root, &root->requires_private, func, data, maxdepth);
+ eflags = pkgconf_pkg_walk_list(client, root, &root->requires_private, func, data, maxdepth, skip_flags);
client->flags &= ~PKGCONF_PKG_PKGF_ITER_PKG_IS_PRIVATE;
if (eflags != PKGCONF_PKG_ERRF_OK)
@@ -1584,14 +1575,15 @@ unsigned int
pkgconf_pkg_cflags(pkgconf_client_t *client, pkgconf_pkg_t *root, pkgconf_list_t *list, int maxdepth)
{
unsigned int eflag;
+ unsigned int skip_flags = (client->flags & PKGCONF_PKG_PKGF_DONT_FILTER_INTERNAL_CFLAGS) == 0 ? PKGCONF_PKG_DEPF_INTERNAL : 0;
- eflag = pkgconf_pkg_traverse(client, root, pkgconf_pkg_cflags_collect, list, maxdepth);
+ eflag = pkgconf_pkg_traverse(client, root, pkgconf_pkg_cflags_collect, list, maxdepth, skip_flags);
if (eflag != PKGCONF_PKG_ERRF_OK)
pkgconf_fragment_free(list);
if (client->flags & PKGCONF_PKG_PKGF_MERGE_PRIVATE_FRAGMENTS)
{
- eflag = pkgconf_pkg_traverse(client, root, pkgconf_pkg_cflags_private_collect, list, maxdepth);
+ eflag = pkgconf_pkg_traverse(client, root, pkgconf_pkg_cflags_private_collect, list, maxdepth, skip_flags);
if (eflag != PKGCONF_PKG_ERRF_OK)
pkgconf_fragment_free(list);
}
@@ -1640,7 +1632,7 @@ pkgconf_pkg_libs(pkgconf_client_t *client, pkgconf_pkg_t *root, pkgconf_list_t *
{
unsigned int eflag;
- eflag = pkgconf_pkg_traverse(client, root, pkgconf_pkg_libs_collect, list, maxdepth);
+ eflag = pkgconf_pkg_traverse(client, root, pkgconf_pkg_libs_collect, list, maxdepth, 0);
if (eflag != PKGCONF_PKG_ERRF_OK)
{
diff --git a/libpkgconf/queue.c b/libpkgconf/queue.c
index 1bfcb30..5220a5b 100644
--- a/libpkgconf/queue.c
+++ b/libpkgconf/queue.c
@@ -77,7 +77,7 @@ pkgconf_queue_compile(pkgconf_client_t *client, pkgconf_pkg_t *world, pkgconf_li
pkgconf_queue_t *pkgq;
pkgq = iter->data;
- pkgconf_dependency_parse(client, world, &world->required, pkgq->package);
+ pkgconf_dependency_parse(client, world, &world->required, pkgq->package, 0);
}
return (world->required.head != NULL);
diff --git a/libpkgconf/stdinc.h b/libpkgconf/stdinc.h
index 3b90853..ca3a7dc 100644
--- a/libpkgconf/stdinc.h
+++ b/libpkgconf/stdinc.h
@@ -53,6 +53,9 @@
#else
# define PATH_DEV_NULL "/dev/null"
# define SIZE_FMT_SPECIFIER "%zu"
+# ifdef __HAIKU__
+# include <FindDirectory.h>
+# endif
# include <dirent.h>
# include <unistd.h>
# include <limits.h>
diff --git a/libpkgconf/tuple.c b/libpkgconf/tuple.c
index a4b4250..6a610a1 100644
--- a/libpkgconf/tuple.c
+++ b/libpkgconf/tuple.c
@@ -136,6 +136,34 @@ pkgconf_tuple_find_delete(pkgconf_list_t *list, const char *key)
}
}
+static char *
+dequote(const char *value)
+{
+ char *buf = calloc((strlen(value) + 1) * 2, 1);
+ char *bptr = buf;
+ const char *i;
+ char quote = 0;
+
+/*
+ * Fix the broken quote escaping (issue #12 is reported).
+ */
+ if (*value == '\'' || *value == '"')
+ quote = *value;
+
+ for (i = value; *i != '\0'; i++)
+ {
+ if (*i == '\\' && quote && *(i + 1) == quote)
+ {
+ i++;
+ *bptr++ = *i;
+ }
+ else if (*i != quote)
+ *bptr++ = *i;
+ }
+
+ return buf;
+}
+
/*
* !doc
*
@@ -154,20 +182,25 @@ pkgconf_tuple_find_delete(pkgconf_list_t *list, const char *key)
pkgconf_tuple_t *
pkgconf_tuple_add(const pkgconf_client_t *client, pkgconf_list_t *list, const char *key, const char *value, bool parse)
{
+ char *dequote_value;
pkgconf_tuple_t *tuple = calloc(sizeof(pkgconf_tuple_t), 1);
pkgconf_tuple_find_delete(list, key);
- PKGCONF_TRACE(client, "adding tuple to @%p: %s => %s (parsed? %d)", list, key, value, parse);
+ dequote_value = dequote(value);
+
+ PKGCONF_TRACE(client, "adding tuple to @%p: %s => %s (parsed? %d)", list, key, dequote_value, parse);
tuple->key = strdup(key);
if (parse)
- tuple->value = pkgconf_tuple_parse(client, list, value);
+ tuple->value = pkgconf_tuple_parse(client, list, dequote_value);
else
- tuple->value = strdup(value);
+ tuple->value = strdup(dequote_value);
pkgconf_node_insert(&tuple->iter, tuple, list);
+ free(dequote_value);
+
return tuple;
}