From 95fee14dfa5bd3896c510077af36ea371a9a2975 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Fri, 26 Oct 2018 13:47:53 +0300 Subject: Merge with 1.5.4 upstream package version --- libpkgconf/buildfile | 27 +++- libpkgconf/client.c | 52 ++++++-- libpkgconf/config.h | 2 +- libpkgconf/dependency.c | 94 ++++++++++++-- libpkgconf/libpkgconf.h | 52 ++++++-- libpkgconf/parser.c | 95 ++++++++++++++ libpkgconf/path.c | 32 +++++ libpkgconf/personality.c | 251 +++++++++++++++++++++++++++++++++++ libpkgconf/pkg.c | 330 +++++++++++++++++++++++------------------------ libpkgconf/queue.c | 2 +- libpkgconf/stdinc.h | 3 + libpkgconf/tuple.c | 39 +++++- 12 files changed, 767 insertions(+), 212 deletions(-) create mode 100644 libpkgconf/parser.c create mode 100644 libpkgconf/personality.c (limited to 'libpkgconf') 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") # # /../lib/pkgconfig;/../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 #include #include +#include #include #include -#include #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 +#include +#include + +/* + * !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 +#include +#include + +#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 +# endif # include # include # include 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; } -- cgit v1.1