diff options
-rw-r--r-- | README | 4 | ||||
-rw-r--r-- | TODO | 3 | ||||
-rw-r--r-- | build/bootstrap.build | 9 | ||||
-rw-r--r-- | libpkgconf/buildfile | 27 | ||||
-rw-r--r-- | libpkgconf/client.c | 52 | ||||
-rw-r--r-- | libpkgconf/config.h | 2 | ||||
-rw-r--r-- | libpkgconf/dependency.c | 94 | ||||
-rw-r--r-- | libpkgconf/libpkgconf.h | 52 | ||||
-rw-r--r-- | libpkgconf/parser.c | 95 | ||||
-rw-r--r-- | libpkgconf/path.c | 32 | ||||
-rw-r--r-- | libpkgconf/personality.c | 251 | ||||
-rw-r--r-- | libpkgconf/pkg.c | 330 | ||||
-rw-r--r-- | libpkgconf/queue.c | 2 | ||||
-rw-r--r-- | libpkgconf/stdinc.h | 3 | ||||
-rw-r--r-- | libpkgconf/tuple.c | 39 | ||||
-rw-r--r-- | manifest | 6 | ||||
-rw-r--r-- | tests/api/driver.c | 4 | ||||
-rw-r--r-- | tests/api/testscript | 14 | ||||
-rw-r--r-- | tests/basic/driver.c | 6 |
19 files changed, 799 insertions, 226 deletions
@@ -2,7 +2,7 @@ libpkgconf is a C library which helps to configure compiler and linker flags for development frameworks. It provids most of the pkgconf's functionality, which itself is similar to pkg-config. For more information see: -https://github.com/pkgconf/pkgconf +https://git.dereferenced.org/pkgconf/pkgconf This package contains the original libpkgconf library source code overlaid with the build2-based build system and packaged for the build2 package manager @@ -11,7 +11,7 @@ the build2-based build system and packaged for the build2 package manager See the INSTALL file for the prerequisites and installation instructions. Post questions, bug reports, or any other feedback about the library itself at -https://github.com/pkgconf/pkgconf/issues. Send build system and +https://git.dereferenced.org/pkgconf/pkgconf/issues. Send build system and packaging-related feedback to the packaging@build2.org mailing list (see https://lists.build2.org for posting guidelines, etc). @@ -4,3 +4,6 @@ @@ Consider redefining PKGCONF_BUFSIZE from the current 65535 bytes to something smaller, given it is used to allocate buffers on the stack (sometimes several of them). + +@@ Merge in the reported bugs fixes when available (grep for 'issue #' for the + list of them). diff --git a/build/bootstrap.build b/build/bootstrap.build index e51f606..9b2bb1e 100644 --- a/build/bootstrap.build +++ b/build/bootstrap.build @@ -20,7 +20,12 @@ using install # # See also: http://kaniini.dereferenced.org/2015/07/20/pkgconf-0-9-12-and-future.html # -if ($version.major == 1 && $version.minor == 4) - release_num = 3 +# Note that the upstream project didn't increment the release number (3) for +# the 1.5 library version despite the ABI breaking changes. +# +# @@ Need to report this upstream. +# +if ($version.major == 1 && $version.minor == 5) + release_num = 4 else fail "increment the release number?" 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; } @@ -1,16 +1,16 @@ : 1 name: libpkgconf -version: 1.4.2+1 +version: 1.5.4-a.0.z project: pkgconf summary: C library for retriving pkg-config compiler and linker flags license: ISC, MIT; ISC for the most of original files. tags: pkg-config, cflags, libs description-file: README -url: https://github.com/pkgconf/pkgconf +url: https://git.dereferenced.org/pkgconf/pkgconf doc-url: http://pkgconf.readthedocs.io/en/latest/?badge=latest src-url: https://git.build2.org/cgit/packaging/pkgconf/libpkgconf/tree/ package-url: https://git.build2.org/cgit/packaging/pkgconf/ -email: packaging@build2.org; Report issues at https://github.com/pkgconf/pkgconf/issues +email: packaging@build2.org; Report issues at https://git.dereferenced.org/pkgconf/pkgconf/issues package-email: packaging@build2.org; Mailing list. build-email: builds@build2.org depends: * build2 >= 0.8.0- diff --git a/tests/api/driver.c b/tests/api/driver.c index b020bee..219549d 100644 --- a/tests/api/driver.c +++ b/tests/api/driver.c @@ -122,7 +122,9 @@ main (int argc, const char* argv[]) assert (n > 3 && strcmp (path + n - 3, ".pc") == 0); pkgconf_client_t* c = - pkgconf_client_new (error_handler, NULL /* error_handler_data */); + pkgconf_client_new (error_handler, + NULL /* error_handler_data */, + pkgconf_cross_personality_default ()); assert (c != NULL); diff --git a/tests/api/testscript b/tests/api/testscript index 7e20361..322a12d 100644 --- a/tests/api/testscript +++ b/tests/api/testscript @@ -8,6 +8,7 @@ +cat <<EOI >=libfoo.pc prefix="C:\\Program Files\\Foo" exec_prefix=${prefix} + var="A\"B" 'C\'D' Name: libfoo Description: Foo library Version: 1.0 @@ -33,8 +34,9 @@ : vars : $* --vars $f >>EOO - exec_prefix "C:\\Program Files\\Foo" - prefix "C:\\Program Files\\Foo" + var A"B 'C\'D' + exec_prefix C:\\Program Files\\Foo + prefix C:\\Program Files\\Foo EOO } @@ -43,6 +45,7 @@ +cat <<EOI >=libfoo.pc prefix='C:\Program Files\Foo' exec_prefix=${prefix} + var='A\'B' "C\"D" Name: libfoo Description: Foo library Version: 1.0 @@ -68,8 +71,9 @@ : vars : $* --vars $f >>EOO - exec_prefix 'C:\Program Files\Foo' - prefix 'C:\Program Files\Foo' + var A'B "C\"D" + exec_prefix C:\Program Files\Foo + prefix C:\Program Files\Foo EOO } @@ -79,6 +83,7 @@ +cat <<EOI >=libfoo.pc prefix=C:\\Program\ \ \ Files\\Foo exec_prefix=${prefix} + var=X A\'B' "C\"D" Name: libfoo Description: Foo library Version: 1.0 @@ -104,6 +109,7 @@ : vars : $* --vars $f >>EOO + var X A\'B' "C\"D" exec_prefix C:\\Program\ \ \ Files\\Foo prefix C:\\Program\ \ \ Files\\Foo EOO diff --git a/tests/basic/driver.c b/tests/basic/driver.c index 888a862..0e733c9 100644 --- a/tests/basic/driver.c +++ b/tests/basic/driver.c @@ -67,7 +67,9 @@ int main (int argc, const char* argv[]) { pkgconf_client_t* c = - pkgconf_client_new (error_handler, NULL /* error_handler_data */); + pkgconf_client_new (error_handler, + NULL /* error_handler_data */, + pkgconf_cross_personality_default ()); assert (c != NULL); @@ -112,7 +114,7 @@ main (int argc, const char* argv[]) * Bootstrap the package search default paths if not specified explicitly. */ if (default_dirs) - pkgconf_pkg_dir_list_build (c); + pkgconf_client_dir_list_build (c, pkgconf_cross_personality_default ()); pkgconf_pkg_t* p = pkgconf_pkg_find (c, name); |