xref: /freebsd/contrib/pkgconf/libpkgconf/pkg.c (revision a3cefe7f2b4df0f70ff92d4570ce18e517af43ec)
1*a3cefe7fSPierre Pronchery /*
2*a3cefe7fSPierre Pronchery  * pkg.c
3*a3cefe7fSPierre Pronchery  * higher-level dependency graph compilation, management and manipulation
4*a3cefe7fSPierre Pronchery  *
5*a3cefe7fSPierre Pronchery  * Copyright (c) 2011, 2012, 2013 pkgconf authors (see AUTHORS).
6*a3cefe7fSPierre Pronchery  *
7*a3cefe7fSPierre Pronchery  * Permission to use, copy, modify, and/or distribute this software for any
8*a3cefe7fSPierre Pronchery  * purpose with or without fee is hereby granted, provided that the above
9*a3cefe7fSPierre Pronchery  * copyright notice and this permission notice appear in all copies.
10*a3cefe7fSPierre Pronchery  *
11*a3cefe7fSPierre Pronchery  * This software is provided 'as is' and without any warranty, express or
12*a3cefe7fSPierre Pronchery  * implied.  In no event shall the authors be liable for any damages arising
13*a3cefe7fSPierre Pronchery  * from the use of this software.
14*a3cefe7fSPierre Pronchery  */
15*a3cefe7fSPierre Pronchery 
16*a3cefe7fSPierre Pronchery #include <libpkgconf/config.h>
17*a3cefe7fSPierre Pronchery #include <libpkgconf/stdinc.h>
18*a3cefe7fSPierre Pronchery #include <libpkgconf/libpkgconf.h>
19*a3cefe7fSPierre Pronchery 
20*a3cefe7fSPierre Pronchery #ifndef _WIN32
21*a3cefe7fSPierre Pronchery #include <fcntl.h>    // open
22*a3cefe7fSPierre Pronchery #include <libgen.h>   // basename/dirname
23*a3cefe7fSPierre Pronchery #include <sys/stat.h> // lstat, S_ISLNK
24*a3cefe7fSPierre Pronchery #include <unistd.h>   // close, readlinkat
25*a3cefe7fSPierre Pronchery 
26*a3cefe7fSPierre Pronchery #include <string.h>
27*a3cefe7fSPierre Pronchery #endif
28*a3cefe7fSPierre Pronchery 
29*a3cefe7fSPierre Pronchery /*
30*a3cefe7fSPierre Pronchery  * !doc
31*a3cefe7fSPierre Pronchery  *
32*a3cefe7fSPierre Pronchery  * libpkgconf `pkg` module
33*a3cefe7fSPierre Pronchery  * =======================
34*a3cefe7fSPierre Pronchery  *
35*a3cefe7fSPierre Pronchery  * The `pkg` module provides dependency resolution services and the overall `.pc` file parsing
36*a3cefe7fSPierre Pronchery  * routines.
37*a3cefe7fSPierre Pronchery  */
38*a3cefe7fSPierre Pronchery 
39*a3cefe7fSPierre Pronchery #ifdef _WIN32
40*a3cefe7fSPierre Pronchery #	undef PKG_DEFAULT_PATH
41*a3cefe7fSPierre Pronchery #	define PKG_DEFAULT_PATH "../lib/pkgconfig;../share/pkgconfig"
42*a3cefe7fSPierre Pronchery #	define strncasecmp _strnicmp
43*a3cefe7fSPierre Pronchery #	define strcasecmp _stricmp
44*a3cefe7fSPierre Pronchery #endif
45*a3cefe7fSPierre Pronchery 
46*a3cefe7fSPierre Pronchery #define PKG_CONFIG_EXT ".pc"
47*a3cefe7fSPierre Pronchery 
48*a3cefe7fSPierre Pronchery static unsigned int
49*a3cefe7fSPierre Pronchery pkgconf_pkg_traverse_main(pkgconf_client_t *client,
50*a3cefe7fSPierre Pronchery 	pkgconf_pkg_t *root,
51*a3cefe7fSPierre Pronchery 	pkgconf_pkg_traverse_func_t func,
52*a3cefe7fSPierre Pronchery 	void *data,
53*a3cefe7fSPierre Pronchery 	int maxdepth,
54*a3cefe7fSPierre Pronchery 	unsigned int skip_flags);
55*a3cefe7fSPierre Pronchery 
56*a3cefe7fSPierre Pronchery static inline bool
str_has_suffix(const char * str,const char * suffix)57*a3cefe7fSPierre Pronchery str_has_suffix(const char *str, const char *suffix)
58*a3cefe7fSPierre Pronchery {
59*a3cefe7fSPierre Pronchery 	size_t str_len = strlen(str);
60*a3cefe7fSPierre Pronchery 	size_t suf_len = strlen(suffix);
61*a3cefe7fSPierre Pronchery 
62*a3cefe7fSPierre Pronchery 	if (str_len < suf_len)
63*a3cefe7fSPierre Pronchery 		return false;
64*a3cefe7fSPierre Pronchery 
65*a3cefe7fSPierre Pronchery 	return !strncasecmp(str + str_len - suf_len, suffix, suf_len);
66*a3cefe7fSPierre Pronchery }
67*a3cefe7fSPierre Pronchery 
68*a3cefe7fSPierre Pronchery static char *
pkg_get_parent_dir(pkgconf_pkg_t * pkg)69*a3cefe7fSPierre Pronchery pkg_get_parent_dir(pkgconf_pkg_t *pkg)
70*a3cefe7fSPierre Pronchery {
71*a3cefe7fSPierre Pronchery 	char buf[PKGCONF_ITEM_SIZE], *pathbuf;
72*a3cefe7fSPierre Pronchery 
73*a3cefe7fSPierre Pronchery 	pkgconf_strlcpy(buf, pkg->filename, sizeof buf);
74*a3cefe7fSPierre Pronchery #ifndef _WIN32
75*a3cefe7fSPierre Pronchery 	/*
76*a3cefe7fSPierre Pronchery 	 * We want to resolve symlinks, since ${pcfiledir} should point to the
77*a3cefe7fSPierre Pronchery 	 * parent of the file symlinked to.
78*a3cefe7fSPierre Pronchery 	 */
79*a3cefe7fSPierre Pronchery 	struct stat path_stat;
80*a3cefe7fSPierre Pronchery 	while (!lstat(buf, &path_stat) && S_ISLNK(path_stat.st_mode))
81*a3cefe7fSPierre Pronchery 	{
82*a3cefe7fSPierre Pronchery 		/*
83*a3cefe7fSPierre Pronchery 		 * Have to split the path into the dir + file components,
84*a3cefe7fSPierre Pronchery 		 * in order to extract the directory file descriptor.
85*a3cefe7fSPierre Pronchery 		 *
86*a3cefe7fSPierre Pronchery 		 * The nomenclature here uses the
87*a3cefe7fSPierre Pronchery 		 *
88*a3cefe7fSPierre Pronchery 		 *   ln <source> <target>
89*a3cefe7fSPierre Pronchery 		 *
90*a3cefe7fSPierre Pronchery 		 * model.
91*a3cefe7fSPierre Pronchery 		 */
92*a3cefe7fSPierre Pronchery 		char basenamebuf[PKGCONF_ITEM_SIZE];
93*a3cefe7fSPierre Pronchery 		pkgconf_strlcpy(basenamebuf, buf, sizeof(basenamebuf));
94*a3cefe7fSPierre Pronchery 		const char* targetfilename = basename(basenamebuf);
95*a3cefe7fSPierre Pronchery 
96*a3cefe7fSPierre Pronchery 		char dirnamebuf[PKGCONF_ITEM_SIZE];
97*a3cefe7fSPierre Pronchery 		pkgconf_strlcpy(dirnamebuf, buf, sizeof(dirnamebuf));
98*a3cefe7fSPierre Pronchery 		const char* targetdir = dirname(dirnamebuf);
99*a3cefe7fSPierre Pronchery 
100*a3cefe7fSPierre Pronchery 		const int dirfd = open(targetdir, O_DIRECTORY);
101*a3cefe7fSPierre Pronchery 		if (dirfd == -1)
102*a3cefe7fSPierre Pronchery 			break;
103*a3cefe7fSPierre Pronchery 
104*a3cefe7fSPierre Pronchery 		char sourcebuf[PKGCONF_ITEM_SIZE];
105*a3cefe7fSPierre Pronchery 		ssize_t len = readlinkat(dirfd, targetfilename, sourcebuf, sizeof(sourcebuf) - 1);
106*a3cefe7fSPierre Pronchery 		close(dirfd);
107*a3cefe7fSPierre Pronchery 
108*a3cefe7fSPierre Pronchery 		if (len == -1)
109*a3cefe7fSPierre Pronchery 			break;
110*a3cefe7fSPierre Pronchery 		sourcebuf[len] = '\0';
111*a3cefe7fSPierre Pronchery 
112*a3cefe7fSPierre Pronchery 		memset(buf, '\0', sizeof buf);
113*a3cefe7fSPierre Pronchery 		/*
114*a3cefe7fSPierre Pronchery 		 * The logic here can be a bit tricky, so here's a table:
115*a3cefe7fSPierre Pronchery 		 *
116*a3cefe7fSPierre Pronchery 		 *        <source>      |        <target>        |         result
117*a3cefe7fSPierre Pronchery 		 * -----------------------------------------------------------------------
118*a3cefe7fSPierre Pronchery 		 *     /bar (absolute)  |   foo/link (relative)  |         /bar (absolute)
119*a3cefe7fSPierre Pronchery 		 *   ../bar (relative)  |   foo/link (relative)  |   foo/../bar (relative)
120*a3cefe7fSPierre Pronchery 		 *     /bar (absolute)  |  /foo/link (absolute)  |         /bar (absolute)
121*a3cefe7fSPierre Pronchery 		 *   ../bar (relative)  |  /foo/link (absolute)  |  /foo/../bar (relative)
122*a3cefe7fSPierre Pronchery 		 */
123*a3cefe7fSPierre Pronchery 		if ((sourcebuf[0] != '/')        /* absolute path in <source> wins */
124*a3cefe7fSPierre Pronchery 		    && (strcmp(targetdir, "."))) /* do not prepend "." */
125*a3cefe7fSPierre Pronchery 		{
126*a3cefe7fSPierre Pronchery 			pkgconf_strlcat(buf, targetdir, sizeof buf);
127*a3cefe7fSPierre Pronchery 			pkgconf_strlcat(buf, "/", sizeof buf);
128*a3cefe7fSPierre Pronchery 		}
129*a3cefe7fSPierre Pronchery 		pkgconf_strlcat(buf, sourcebuf, sizeof buf);
130*a3cefe7fSPierre Pronchery 	}
131*a3cefe7fSPierre Pronchery #endif
132*a3cefe7fSPierre Pronchery 
133*a3cefe7fSPierre Pronchery 	pathbuf = strrchr(buf, PKG_DIR_SEP_S);
134*a3cefe7fSPierre Pronchery 	if (pathbuf == NULL)
135*a3cefe7fSPierre Pronchery 		pathbuf = strrchr(buf, '/');
136*a3cefe7fSPierre Pronchery 	if (pathbuf != NULL)
137*a3cefe7fSPierre Pronchery 		pathbuf[0] = '\0';
138*a3cefe7fSPierre Pronchery 
139*a3cefe7fSPierre Pronchery 	return strdup(buf);
140*a3cefe7fSPierre Pronchery }
141*a3cefe7fSPierre Pronchery 
142*a3cefe7fSPierre Pronchery typedef void (*pkgconf_pkg_parser_keyword_func_t)(pkgconf_client_t *client, pkgconf_pkg_t *pkg, const char *keyword, const size_t lineno, const ptrdiff_t offset, const char *value);
143*a3cefe7fSPierre Pronchery typedef struct {
144*a3cefe7fSPierre Pronchery 	const char *keyword;
145*a3cefe7fSPierre Pronchery 	const pkgconf_pkg_parser_keyword_func_t func;
146*a3cefe7fSPierre Pronchery 	const ptrdiff_t offset;
147*a3cefe7fSPierre Pronchery } pkgconf_pkg_parser_keyword_pair_t;
148*a3cefe7fSPierre Pronchery 
pkgconf_pkg_parser_keyword_pair_cmp(const void * key,const void * ptr)149*a3cefe7fSPierre Pronchery static int pkgconf_pkg_parser_keyword_pair_cmp(const void *key, const void *ptr)
150*a3cefe7fSPierre Pronchery {
151*a3cefe7fSPierre Pronchery 	const pkgconf_pkg_parser_keyword_pair_t *pair = ptr;
152*a3cefe7fSPierre Pronchery 	return strcasecmp(key, pair->keyword);
153*a3cefe7fSPierre Pronchery }
154*a3cefe7fSPierre Pronchery 
155*a3cefe7fSPierre Pronchery static void
pkgconf_pkg_parser_tuple_func(pkgconf_client_t * client,pkgconf_pkg_t * pkg,const char * keyword,const size_t lineno,const ptrdiff_t offset,const char * value)156*a3cefe7fSPierre Pronchery pkgconf_pkg_parser_tuple_func(pkgconf_client_t *client, pkgconf_pkg_t *pkg, const char *keyword, const size_t lineno, const ptrdiff_t offset, const char *value)
157*a3cefe7fSPierre Pronchery {
158*a3cefe7fSPierre Pronchery 	(void) keyword;
159*a3cefe7fSPierre Pronchery 	(void) lineno;
160*a3cefe7fSPierre Pronchery 
161*a3cefe7fSPierre Pronchery 	char **dest = (char **)((char *) pkg + offset);
162*a3cefe7fSPierre Pronchery 	*dest = pkgconf_tuple_parse(client, &pkg->vars, value, pkg->flags);
163*a3cefe7fSPierre Pronchery }
164*a3cefe7fSPierre Pronchery 
165*a3cefe7fSPierre Pronchery static void
pkgconf_pkg_parser_version_func(pkgconf_client_t * client,pkgconf_pkg_t * pkg,const char * keyword,const size_t lineno,const ptrdiff_t offset,const char * value)166*a3cefe7fSPierre Pronchery pkgconf_pkg_parser_version_func(pkgconf_client_t *client, pkgconf_pkg_t *pkg, const char *keyword, const size_t lineno, const ptrdiff_t offset, const char *value)
167*a3cefe7fSPierre Pronchery {
168*a3cefe7fSPierre Pronchery 	(void) keyword;
169*a3cefe7fSPierre Pronchery 	(void) lineno;
170*a3cefe7fSPierre Pronchery 	char *p, *i;
171*a3cefe7fSPierre Pronchery 	size_t len;
172*a3cefe7fSPierre Pronchery 	char **dest = (char **)((char *) pkg + offset);
173*a3cefe7fSPierre Pronchery 
174*a3cefe7fSPierre Pronchery 	/* cut at any detected whitespace */
175*a3cefe7fSPierre Pronchery 	p = pkgconf_tuple_parse(client, &pkg->vars, value, pkg->flags);
176*a3cefe7fSPierre Pronchery 
177*a3cefe7fSPierre Pronchery 	len = strcspn(p, " \t");
178*a3cefe7fSPierre Pronchery 	if (len != strlen(p))
179*a3cefe7fSPierre Pronchery 	{
180*a3cefe7fSPierre Pronchery 		i = p + (ptrdiff_t) len;
181*a3cefe7fSPierre Pronchery 		*i = '\0';
182*a3cefe7fSPierre Pronchery 
183*a3cefe7fSPierre Pronchery 		pkgconf_warn(client, "%s:" SIZE_FMT_SPECIFIER ": warning: malformed version field with whitespace, trimming to [%s]\n", pkg->filename,
184*a3cefe7fSPierre Pronchery 			     lineno, p);
185*a3cefe7fSPierre Pronchery 	}
186*a3cefe7fSPierre Pronchery 
187*a3cefe7fSPierre Pronchery 	*dest = p;
188*a3cefe7fSPierre Pronchery }
189*a3cefe7fSPierre Pronchery 
190*a3cefe7fSPierre Pronchery static void
pkgconf_pkg_parser_fragment_func(pkgconf_client_t * client,pkgconf_pkg_t * pkg,const char * keyword,const size_t lineno,const ptrdiff_t offset,const char * value)191*a3cefe7fSPierre Pronchery pkgconf_pkg_parser_fragment_func(pkgconf_client_t *client, pkgconf_pkg_t *pkg, const char *keyword, const size_t lineno, const ptrdiff_t offset, const char *value)
192*a3cefe7fSPierre Pronchery {
193*a3cefe7fSPierre Pronchery 	pkgconf_list_t *dest = (pkgconf_list_t *)((char *) pkg + offset);
194*a3cefe7fSPierre Pronchery 
195*a3cefe7fSPierre Pronchery 	/* we patch client-wide sysroot dir and then patch it back when it is overridden */
196*a3cefe7fSPierre Pronchery 	char *sysroot_dir = client->sysroot_dir;
197*a3cefe7fSPierre Pronchery 	char *pkg_sysroot_dir = pkgconf_tuple_find(client, &pkg->vars, "pc_sysrootdir");
198*a3cefe7fSPierre Pronchery 	if (pkg_sysroot_dir != NULL)
199*a3cefe7fSPierre Pronchery 		client->sysroot_dir = pkg_sysroot_dir;
200*a3cefe7fSPierre Pronchery 
201*a3cefe7fSPierre Pronchery 	bool ret = pkgconf_fragment_parse(client, dest, &pkg->vars, value, pkg->flags);
202*a3cefe7fSPierre Pronchery 	client->sysroot_dir = sysroot_dir;
203*a3cefe7fSPierre Pronchery 
204*a3cefe7fSPierre Pronchery 	if (!ret)
205*a3cefe7fSPierre Pronchery 	{
206*a3cefe7fSPierre Pronchery 		pkgconf_warn(client, "%s:" SIZE_FMT_SPECIFIER ": warning: unable to parse field '%s' into an argument vector, value [%s]\n", pkg->filename,
207*a3cefe7fSPierre Pronchery 			     lineno, keyword, value);
208*a3cefe7fSPierre Pronchery 	}
209*a3cefe7fSPierre Pronchery }
210*a3cefe7fSPierre Pronchery 
211*a3cefe7fSPierre Pronchery static void
pkgconf_pkg_parser_dependency_func(pkgconf_client_t * client,pkgconf_pkg_t * pkg,const char * keyword,const size_t lineno,const ptrdiff_t offset,const char * value)212*a3cefe7fSPierre Pronchery pkgconf_pkg_parser_dependency_func(pkgconf_client_t *client, pkgconf_pkg_t *pkg, const char *keyword, const size_t lineno, const ptrdiff_t offset, const char *value)
213*a3cefe7fSPierre Pronchery {
214*a3cefe7fSPierre Pronchery 	(void) keyword;
215*a3cefe7fSPierre Pronchery 	(void) lineno;
216*a3cefe7fSPierre Pronchery 
217*a3cefe7fSPierre Pronchery 	pkgconf_list_t *dest = (pkgconf_list_t *)((char *) pkg + offset);
218*a3cefe7fSPierre Pronchery 	pkgconf_dependency_parse(client, pkg, dest, value, 0);
219*a3cefe7fSPierre Pronchery }
220*a3cefe7fSPierre Pronchery 
221*a3cefe7fSPierre Pronchery /* a variant of pkgconf_pkg_parser_dependency_func which colors the dependency node as an "internal" dependency. */
222*a3cefe7fSPierre Pronchery static void
pkgconf_pkg_parser_internal_dependency_func(pkgconf_client_t * client,pkgconf_pkg_t * pkg,const char * keyword,const size_t lineno,const ptrdiff_t offset,const char * value)223*a3cefe7fSPierre Pronchery pkgconf_pkg_parser_internal_dependency_func(pkgconf_client_t *client, pkgconf_pkg_t *pkg, const char *keyword, const size_t lineno, const ptrdiff_t offset, const char *value)
224*a3cefe7fSPierre Pronchery {
225*a3cefe7fSPierre Pronchery 	(void) keyword;
226*a3cefe7fSPierre Pronchery 	(void) lineno;
227*a3cefe7fSPierre Pronchery 
228*a3cefe7fSPierre Pronchery 	pkgconf_list_t *dest = (pkgconf_list_t *)((char *) pkg + offset);
229*a3cefe7fSPierre Pronchery 	pkgconf_dependency_parse(client, pkg, dest, value, PKGCONF_PKG_DEPF_INTERNAL);
230*a3cefe7fSPierre Pronchery }
231*a3cefe7fSPierre Pronchery 
232*a3cefe7fSPierre Pronchery /* a variant of pkgconf_pkg_parser_dependency_func which colors the dependency node as a "private" dependency. */
233*a3cefe7fSPierre Pronchery static void
pkgconf_pkg_parser_private_dependency_func(pkgconf_client_t * client,pkgconf_pkg_t * pkg,const char * keyword,const size_t lineno,const ptrdiff_t offset,const char * value)234*a3cefe7fSPierre Pronchery pkgconf_pkg_parser_private_dependency_func(pkgconf_client_t *client, pkgconf_pkg_t *pkg, const char *keyword, const size_t lineno, const ptrdiff_t offset, const char *value)
235*a3cefe7fSPierre Pronchery {
236*a3cefe7fSPierre Pronchery 	(void) keyword;
237*a3cefe7fSPierre Pronchery 	(void) lineno;
238*a3cefe7fSPierre Pronchery 
239*a3cefe7fSPierre Pronchery 	pkgconf_list_t *dest = (pkgconf_list_t *)((char *) pkg + offset);
240*a3cefe7fSPierre Pronchery 	pkgconf_dependency_parse(client, pkg, dest, value, PKGCONF_PKG_DEPF_PRIVATE);
241*a3cefe7fSPierre Pronchery }
242*a3cefe7fSPierre Pronchery 
243*a3cefe7fSPierre Pronchery /* keep this in alphabetical order */
244*a3cefe7fSPierre Pronchery static const pkgconf_pkg_parser_keyword_pair_t pkgconf_pkg_parser_keyword_funcs[] = {
245*a3cefe7fSPierre Pronchery 	{"CFLAGS", pkgconf_pkg_parser_fragment_func, offsetof(pkgconf_pkg_t, cflags)},
246*a3cefe7fSPierre Pronchery 	{"CFLAGS.private", pkgconf_pkg_parser_fragment_func, offsetof(pkgconf_pkg_t, cflags_private)},
247*a3cefe7fSPierre Pronchery 	{"Conflicts", pkgconf_pkg_parser_dependency_func, offsetof(pkgconf_pkg_t, conflicts)},
248*a3cefe7fSPierre Pronchery 	{"Copyright", pkgconf_pkg_parser_tuple_func, offsetof(pkgconf_pkg_t, copyright)},
249*a3cefe7fSPierre Pronchery 	{"Description", pkgconf_pkg_parser_tuple_func, offsetof(pkgconf_pkg_t, description)},
250*a3cefe7fSPierre Pronchery 	{"LIBS", pkgconf_pkg_parser_fragment_func, offsetof(pkgconf_pkg_t, libs)},
251*a3cefe7fSPierre Pronchery 	{"LIBS.private", pkgconf_pkg_parser_fragment_func, offsetof(pkgconf_pkg_t, libs_private)},
252*a3cefe7fSPierre Pronchery 	{"License", pkgconf_pkg_parser_tuple_func, offsetof(pkgconf_pkg_t, license)},
253*a3cefe7fSPierre Pronchery 	{"Maintainer", pkgconf_pkg_parser_tuple_func, offsetof(pkgconf_pkg_t, maintainer)},
254*a3cefe7fSPierre Pronchery 	{"Name", pkgconf_pkg_parser_tuple_func, offsetof(pkgconf_pkg_t, realname)},
255*a3cefe7fSPierre Pronchery 	{"Provides", pkgconf_pkg_parser_dependency_func, offsetof(pkgconf_pkg_t, provides)},
256*a3cefe7fSPierre Pronchery 	{"Requires", pkgconf_pkg_parser_dependency_func, offsetof(pkgconf_pkg_t, required)},
257*a3cefe7fSPierre Pronchery 	{"Requires.internal", pkgconf_pkg_parser_internal_dependency_func, offsetof(pkgconf_pkg_t, requires_private)},
258*a3cefe7fSPierre Pronchery 	{"Requires.private", pkgconf_pkg_parser_private_dependency_func, offsetof(pkgconf_pkg_t, requires_private)},
259*a3cefe7fSPierre Pronchery 	{"URL", pkgconf_pkg_parser_tuple_func, offsetof(pkgconf_pkg_t, url)},
260*a3cefe7fSPierre Pronchery 	{"Version", pkgconf_pkg_parser_version_func, offsetof(pkgconf_pkg_t, version)},
261*a3cefe7fSPierre Pronchery };
262*a3cefe7fSPierre Pronchery 
263*a3cefe7fSPierre Pronchery static void
pkgconf_pkg_parser_keyword_set(void * opaque,const size_t lineno,const char * keyword,const char * value)264*a3cefe7fSPierre Pronchery pkgconf_pkg_parser_keyword_set(void *opaque, const size_t lineno, const char *keyword, const char *value)
265*a3cefe7fSPierre Pronchery {
266*a3cefe7fSPierre Pronchery 	pkgconf_pkg_t *pkg = opaque;
267*a3cefe7fSPierre Pronchery 
268*a3cefe7fSPierre Pronchery 	const pkgconf_pkg_parser_keyword_pair_t *pair = bsearch(keyword,
269*a3cefe7fSPierre Pronchery 		pkgconf_pkg_parser_keyword_funcs, PKGCONF_ARRAY_SIZE(pkgconf_pkg_parser_keyword_funcs),
270*a3cefe7fSPierre Pronchery 		sizeof(pkgconf_pkg_parser_keyword_pair_t), pkgconf_pkg_parser_keyword_pair_cmp);
271*a3cefe7fSPierre Pronchery 
272*a3cefe7fSPierre Pronchery 	if (pair == NULL || pair->func == NULL)
273*a3cefe7fSPierre Pronchery 		return;
274*a3cefe7fSPierre Pronchery 
275*a3cefe7fSPierre Pronchery 	pair->func(pkg->owner, pkg, keyword, lineno, pair->offset, value);
276*a3cefe7fSPierre Pronchery }
277*a3cefe7fSPierre Pronchery 
278*a3cefe7fSPierre Pronchery static const char *
determine_prefix(const pkgconf_pkg_t * pkg,char * buf,size_t buflen)279*a3cefe7fSPierre Pronchery determine_prefix(const pkgconf_pkg_t *pkg, char *buf, size_t buflen)
280*a3cefe7fSPierre Pronchery {
281*a3cefe7fSPierre Pronchery 	char *pathiter;
282*a3cefe7fSPierre Pronchery 
283*a3cefe7fSPierre Pronchery 	pkgconf_strlcpy(buf, pkg->filename, buflen);
284*a3cefe7fSPierre Pronchery 	pkgconf_path_relocate(buf, buflen);
285*a3cefe7fSPierre Pronchery 
286*a3cefe7fSPierre Pronchery 	pathiter = strrchr(buf, PKG_DIR_SEP_S);
287*a3cefe7fSPierre Pronchery 	if (pathiter == NULL)
288*a3cefe7fSPierre Pronchery 		pathiter = strrchr(buf, '/');
289*a3cefe7fSPierre Pronchery 	if (pathiter != NULL)
290*a3cefe7fSPierre Pronchery 		pathiter[0] = '\0';
291*a3cefe7fSPierre Pronchery 
292*a3cefe7fSPierre Pronchery 	pathiter = strrchr(buf, PKG_DIR_SEP_S);
293*a3cefe7fSPierre Pronchery 	if (pathiter == NULL)
294*a3cefe7fSPierre Pronchery 		pathiter = strrchr(buf, '/');
295*a3cefe7fSPierre Pronchery 	if (pathiter == NULL)
296*a3cefe7fSPierre Pronchery 		return NULL;
297*a3cefe7fSPierre Pronchery 
298*a3cefe7fSPierre Pronchery 	/* parent dir is not pkgconfig, can't relocate then */
299*a3cefe7fSPierre Pronchery 	if (strcmp(pathiter + 1, "pkgconfig"))
300*a3cefe7fSPierre Pronchery 		return NULL;
301*a3cefe7fSPierre Pronchery 
302*a3cefe7fSPierre Pronchery 	/* okay, work backwards and do it again. */
303*a3cefe7fSPierre Pronchery 	pathiter[0] = '\0';
304*a3cefe7fSPierre Pronchery 	pathiter = strrchr(buf, PKG_DIR_SEP_S);
305*a3cefe7fSPierre Pronchery 	if (pathiter == NULL)
306*a3cefe7fSPierre Pronchery 		pathiter = strrchr(buf, '/');
307*a3cefe7fSPierre Pronchery 	if (pathiter == NULL)
308*a3cefe7fSPierre Pronchery 		return NULL;
309*a3cefe7fSPierre Pronchery 
310*a3cefe7fSPierre Pronchery 	pathiter[0] = '\0';
311*a3cefe7fSPierre Pronchery 
312*a3cefe7fSPierre Pronchery 	return buf;
313*a3cefe7fSPierre Pronchery }
314*a3cefe7fSPierre Pronchery 
315*a3cefe7fSPierre Pronchery /*
316*a3cefe7fSPierre Pronchery  * Takes a real path and converts it to a pkgconf value. This means normalizing
317*a3cefe7fSPierre Pronchery  * directory separators and escaping things (only spaces covered atm).
318*a3cefe7fSPierre Pronchery  *
319*a3cefe7fSPierre Pronchery  * This is useful for things like prefix/pcfiledir which might get injected
320*a3cefe7fSPierre Pronchery  * at runtime and are not sourced from the .pc file.
321*a3cefe7fSPierre Pronchery  *
322*a3cefe7fSPierre Pronchery  * "C:\foo bar\baz" -> "C:/foo\ bar/baz"
323*a3cefe7fSPierre Pronchery  * "/foo bar/baz" -> "/foo\ bar/baz"
324*a3cefe7fSPierre Pronchery  */
325*a3cefe7fSPierre Pronchery static char *
convert_path_to_value(const char * path)326*a3cefe7fSPierre Pronchery convert_path_to_value(const char *path)
327*a3cefe7fSPierre Pronchery {
328*a3cefe7fSPierre Pronchery 	char *buf = calloc(1, (strlen(path) + 1) * 2);
329*a3cefe7fSPierre Pronchery 	if (buf == NULL)
330*a3cefe7fSPierre Pronchery 		return NULL;
331*a3cefe7fSPierre Pronchery 
332*a3cefe7fSPierre Pronchery 	char *bptr = buf;
333*a3cefe7fSPierre Pronchery 	const char *i;
334*a3cefe7fSPierre Pronchery 
335*a3cefe7fSPierre Pronchery 	for (i = path; *i != '\0'; i++)
336*a3cefe7fSPierre Pronchery 	{
337*a3cefe7fSPierre Pronchery 		if (*i == PKG_DIR_SEP_S)
338*a3cefe7fSPierre Pronchery 			*bptr++ = '/';
339*a3cefe7fSPierre Pronchery 		else if (*i == ' ') {
340*a3cefe7fSPierre Pronchery 			*bptr++ = '\\';
341*a3cefe7fSPierre Pronchery 			*bptr++ = *i;
342*a3cefe7fSPierre Pronchery 		} else
343*a3cefe7fSPierre Pronchery 			*bptr++ = *i;
344*a3cefe7fSPierre Pronchery 	}
345*a3cefe7fSPierre Pronchery 
346*a3cefe7fSPierre Pronchery 	return buf;
347*a3cefe7fSPierre Pronchery }
348*a3cefe7fSPierre Pronchery 
349*a3cefe7fSPierre Pronchery static void
remove_additional_separators(char * buf)350*a3cefe7fSPierre Pronchery remove_additional_separators(char *buf)
351*a3cefe7fSPierre Pronchery {
352*a3cefe7fSPierre Pronchery 	char *p = buf;
353*a3cefe7fSPierre Pronchery 
354*a3cefe7fSPierre Pronchery 	while (*p) {
355*a3cefe7fSPierre Pronchery 		if (*p == '/') {
356*a3cefe7fSPierre Pronchery 			char *q;
357*a3cefe7fSPierre Pronchery 
358*a3cefe7fSPierre Pronchery 			q = ++p;
359*a3cefe7fSPierre Pronchery 			while (*q && *q == '/')
360*a3cefe7fSPierre Pronchery 				q++;
361*a3cefe7fSPierre Pronchery 
362*a3cefe7fSPierre Pronchery 			if (p != q)
363*a3cefe7fSPierre Pronchery 				memmove (p, q, strlen (q) + 1);
364*a3cefe7fSPierre Pronchery 		} else {
365*a3cefe7fSPierre Pronchery 			p++;
366*a3cefe7fSPierre Pronchery 		}
367*a3cefe7fSPierre Pronchery 	}
368*a3cefe7fSPierre Pronchery }
369*a3cefe7fSPierre Pronchery 
370*a3cefe7fSPierre Pronchery static void
canonicalize_path(char * buf)371*a3cefe7fSPierre Pronchery canonicalize_path(char *buf)
372*a3cefe7fSPierre Pronchery {
373*a3cefe7fSPierre Pronchery 	remove_additional_separators(buf);
374*a3cefe7fSPierre Pronchery }
375*a3cefe7fSPierre Pronchery 
376*a3cefe7fSPierre Pronchery static bool
is_path_prefix_equal(const char * path1,const char * path2,size_t path2_len)377*a3cefe7fSPierre Pronchery is_path_prefix_equal(const char *path1, const char *path2, size_t path2_len)
378*a3cefe7fSPierre Pronchery {
379*a3cefe7fSPierre Pronchery #ifdef _WIN32
380*a3cefe7fSPierre Pronchery 	return !_strnicmp(path1, path2, path2_len);
381*a3cefe7fSPierre Pronchery #else
382*a3cefe7fSPierre Pronchery 	return !strncmp(path1, path2, path2_len);
383*a3cefe7fSPierre Pronchery #endif
384*a3cefe7fSPierre Pronchery }
385*a3cefe7fSPierre Pronchery 
386*a3cefe7fSPierre Pronchery static void
pkgconf_pkg_parser_value_set(void * opaque,const size_t lineno,const char * keyword,const char * value)387*a3cefe7fSPierre Pronchery pkgconf_pkg_parser_value_set(void *opaque, const size_t lineno, const char *keyword, const char *value)
388*a3cefe7fSPierre Pronchery {
389*a3cefe7fSPierre Pronchery 	char canonicalized_value[PKGCONF_ITEM_SIZE];
390*a3cefe7fSPierre Pronchery 	pkgconf_pkg_t *pkg = opaque;
391*a3cefe7fSPierre Pronchery 
392*a3cefe7fSPierre Pronchery 	(void) lineno;
393*a3cefe7fSPierre Pronchery 
394*a3cefe7fSPierre Pronchery 	pkgconf_strlcpy(canonicalized_value, value, sizeof canonicalized_value);
395*a3cefe7fSPierre Pronchery 	canonicalize_path(canonicalized_value);
396*a3cefe7fSPierre Pronchery 
397*a3cefe7fSPierre Pronchery 	/* Some pc files will use absolute paths for all of their directories
398*a3cefe7fSPierre Pronchery 	 * which is broken when redefining the prefix. We try to outsmart the
399*a3cefe7fSPierre Pronchery 	 * file and rewrite any directory that starts with the same prefix.
400*a3cefe7fSPierre Pronchery 	 */
401*a3cefe7fSPierre Pronchery 	if (pkg->owner->flags & PKGCONF_PKG_PKGF_REDEFINE_PREFIX && pkg->orig_prefix
402*a3cefe7fSPierre Pronchery 	    && is_path_prefix_equal(canonicalized_value, pkg->orig_prefix->value, strlen(pkg->orig_prefix->value)))
403*a3cefe7fSPierre Pronchery 	{
404*a3cefe7fSPierre Pronchery 		char newvalue[PKGCONF_ITEM_SIZE];
405*a3cefe7fSPierre Pronchery 
406*a3cefe7fSPierre Pronchery 		pkgconf_strlcpy(newvalue, pkg->prefix->value, sizeof newvalue);
407*a3cefe7fSPierre Pronchery 		pkgconf_strlcat(newvalue, canonicalized_value + strlen(pkg->orig_prefix->value), sizeof newvalue);
408*a3cefe7fSPierre Pronchery 		pkgconf_tuple_add(pkg->owner, &pkg->vars, keyword, newvalue, false, pkg->flags);
409*a3cefe7fSPierre Pronchery 	}
410*a3cefe7fSPierre Pronchery 	else if (strcmp(keyword, pkg->owner->prefix_varname) || !(pkg->owner->flags & PKGCONF_PKG_PKGF_REDEFINE_PREFIX))
411*a3cefe7fSPierre Pronchery 		pkgconf_tuple_add(pkg->owner, &pkg->vars, keyword, value, true, pkg->flags);
412*a3cefe7fSPierre Pronchery 	else
413*a3cefe7fSPierre Pronchery 	{
414*a3cefe7fSPierre Pronchery 		char pathbuf[PKGCONF_ITEM_SIZE];
415*a3cefe7fSPierre Pronchery 		const char *relvalue = determine_prefix(pkg, pathbuf, sizeof pathbuf);
416*a3cefe7fSPierre Pronchery 
417*a3cefe7fSPierre Pronchery 		if (relvalue != NULL)
418*a3cefe7fSPierre Pronchery 		{
419*a3cefe7fSPierre Pronchery 			char *prefix_value = convert_path_to_value(relvalue);
420*a3cefe7fSPierre Pronchery 			pkg->orig_prefix = pkgconf_tuple_add(pkg->owner, &pkg->vars, "orig_prefix", canonicalized_value, true, pkg->flags);
421*a3cefe7fSPierre Pronchery 			pkg->prefix = pkgconf_tuple_add(pkg->owner, &pkg->vars, keyword, prefix_value, false, pkg->flags);
422*a3cefe7fSPierre Pronchery 			free(prefix_value);
423*a3cefe7fSPierre Pronchery 		}
424*a3cefe7fSPierre Pronchery 		else
425*a3cefe7fSPierre Pronchery 			pkgconf_tuple_add(pkg->owner, &pkg->vars, keyword, value, true, pkg->flags);
426*a3cefe7fSPierre Pronchery 	}
427*a3cefe7fSPierre Pronchery }
428*a3cefe7fSPierre Pronchery 
429*a3cefe7fSPierre Pronchery typedef struct {
430*a3cefe7fSPierre Pronchery 	const char *field;
431*a3cefe7fSPierre Pronchery 	const ptrdiff_t offset;
432*a3cefe7fSPierre Pronchery } pkgconf_pkg_validity_check_t;
433*a3cefe7fSPierre Pronchery 
434*a3cefe7fSPierre Pronchery static const pkgconf_pkg_validity_check_t pkgconf_pkg_validations[] = {
435*a3cefe7fSPierre Pronchery 	{"Name", offsetof(pkgconf_pkg_t, realname)},
436*a3cefe7fSPierre Pronchery 	{"Description", offsetof(pkgconf_pkg_t, description)},
437*a3cefe7fSPierre Pronchery 	{"Version", offsetof(pkgconf_pkg_t, version)},
438*a3cefe7fSPierre Pronchery };
439*a3cefe7fSPierre Pronchery 
440*a3cefe7fSPierre Pronchery static const pkgconf_parser_operand_func_t pkg_parser_funcs[256] = {
441*a3cefe7fSPierre Pronchery 	[':'] = pkgconf_pkg_parser_keyword_set,
442*a3cefe7fSPierre Pronchery 	['='] = pkgconf_pkg_parser_value_set
443*a3cefe7fSPierre Pronchery };
444*a3cefe7fSPierre Pronchery 
445*a3cefe7fSPierre Pronchery static void pkg_warn_func(pkgconf_pkg_t *pkg, const char *fmt, ...) PRINTFLIKE(2, 3);
446*a3cefe7fSPierre Pronchery 
447*a3cefe7fSPierre Pronchery static void
pkg_warn_func(pkgconf_pkg_t * pkg,const char * fmt,...)448*a3cefe7fSPierre Pronchery pkg_warn_func(pkgconf_pkg_t *pkg, const char *fmt, ...)
449*a3cefe7fSPierre Pronchery {
450*a3cefe7fSPierre Pronchery 	char buf[PKGCONF_ITEM_SIZE];
451*a3cefe7fSPierre Pronchery 	va_list va;
452*a3cefe7fSPierre Pronchery 
453*a3cefe7fSPierre Pronchery 	va_start(va, fmt);
454*a3cefe7fSPierre Pronchery 	vsnprintf(buf, sizeof buf, fmt, va);
455*a3cefe7fSPierre Pronchery 	va_end(va);
456*a3cefe7fSPierre Pronchery 
457*a3cefe7fSPierre Pronchery 	pkgconf_warn(pkg->owner, "%s", buf);
458*a3cefe7fSPierre Pronchery }
459*a3cefe7fSPierre Pronchery 
460*a3cefe7fSPierre Pronchery static bool
pkgconf_pkg_validate(const pkgconf_client_t * client,const pkgconf_pkg_t * pkg)461*a3cefe7fSPierre Pronchery pkgconf_pkg_validate(const pkgconf_client_t *client, const pkgconf_pkg_t *pkg)
462*a3cefe7fSPierre Pronchery {
463*a3cefe7fSPierre Pronchery 	size_t i;
464*a3cefe7fSPierre Pronchery 	bool valid = true;
465*a3cefe7fSPierre Pronchery 
466*a3cefe7fSPierre Pronchery 	for (i = 0; i < PKGCONF_ARRAY_SIZE(pkgconf_pkg_validations); i++)
467*a3cefe7fSPierre Pronchery 	{
468*a3cefe7fSPierre Pronchery 		char **p = (char **)((char *) pkg + pkgconf_pkg_validations[i].offset);
469*a3cefe7fSPierre Pronchery 
470*a3cefe7fSPierre Pronchery 		if (*p != NULL)
471*a3cefe7fSPierre Pronchery 			continue;
472*a3cefe7fSPierre Pronchery 
473*a3cefe7fSPierre Pronchery 		pkgconf_warn(client, "%s: warning: file does not declare a `%s' field\n", pkg->filename, pkgconf_pkg_validations[i].field);
474*a3cefe7fSPierre Pronchery 		valid = false;
475*a3cefe7fSPierre Pronchery 	}
476*a3cefe7fSPierre Pronchery 
477*a3cefe7fSPierre Pronchery 	return valid;
478*a3cefe7fSPierre Pronchery }
479*a3cefe7fSPierre Pronchery 
480*a3cefe7fSPierre Pronchery static void
pkg_free_object(pkgconf_pkg_t * pkg)481*a3cefe7fSPierre Pronchery pkg_free_object(pkgconf_pkg_t *pkg)
482*a3cefe7fSPierre Pronchery {
483*a3cefe7fSPierre Pronchery 	if (pkg->flags & PKGCONF_PKG_PROPF_PRELOADED)
484*a3cefe7fSPierre Pronchery 		pkgconf_node_delete(&pkg->preload_node, &pkg->owner->preloaded_pkgs);
485*a3cefe7fSPierre Pronchery 
486*a3cefe7fSPierre Pronchery 	if (pkg->id != NULL)
487*a3cefe7fSPierre Pronchery 		free(pkg->id);
488*a3cefe7fSPierre Pronchery 
489*a3cefe7fSPierre Pronchery 	if (pkg->filename != NULL)
490*a3cefe7fSPierre Pronchery 		free(pkg->filename);
491*a3cefe7fSPierre Pronchery 
492*a3cefe7fSPierre Pronchery 	if (pkg->realname != NULL)
493*a3cefe7fSPierre Pronchery 		free(pkg->realname);
494*a3cefe7fSPierre Pronchery 
495*a3cefe7fSPierre Pronchery 	if (pkg->version != NULL)
496*a3cefe7fSPierre Pronchery 		free(pkg->version);
497*a3cefe7fSPierre Pronchery 
498*a3cefe7fSPierre Pronchery 	if (pkg->description != NULL)
499*a3cefe7fSPierre Pronchery 		free(pkg->description);
500*a3cefe7fSPierre Pronchery 
501*a3cefe7fSPierre Pronchery 	if (pkg->url != NULL)
502*a3cefe7fSPierre Pronchery 		free(pkg->url);
503*a3cefe7fSPierre Pronchery 
504*a3cefe7fSPierre Pronchery 	if (pkg->pc_filedir != NULL)
505*a3cefe7fSPierre Pronchery 		free(pkg->pc_filedir);
506*a3cefe7fSPierre Pronchery 
507*a3cefe7fSPierre Pronchery 	if (pkg->license != NULL)
508*a3cefe7fSPierre Pronchery 		free(pkg->license);
509*a3cefe7fSPierre Pronchery 
510*a3cefe7fSPierre Pronchery 	if (pkg->maintainer != NULL)
511*a3cefe7fSPierre Pronchery 		free(pkg->maintainer);
512*a3cefe7fSPierre Pronchery 
513*a3cefe7fSPierre Pronchery 	if (pkg->copyright != NULL)
514*a3cefe7fSPierre Pronchery 		free(pkg->copyright);
515*a3cefe7fSPierre Pronchery 
516*a3cefe7fSPierre Pronchery 	if (pkg->why != NULL)
517*a3cefe7fSPierre Pronchery 		free(pkg->why);
518*a3cefe7fSPierre Pronchery 
519*a3cefe7fSPierre Pronchery 	free(pkg);
520*a3cefe7fSPierre Pronchery }
521*a3cefe7fSPierre Pronchery 
522*a3cefe7fSPierre Pronchery static void
pkg_free_lists(pkgconf_pkg_t * pkg)523*a3cefe7fSPierre Pronchery pkg_free_lists(pkgconf_pkg_t *pkg)
524*a3cefe7fSPierre Pronchery {
525*a3cefe7fSPierre Pronchery 	pkgconf_dependency_free(&pkg->required);
526*a3cefe7fSPierre Pronchery 	pkgconf_dependency_free(&pkg->requires_private);
527*a3cefe7fSPierre Pronchery 	pkgconf_dependency_free(&pkg->conflicts);
528*a3cefe7fSPierre Pronchery 	pkgconf_dependency_free(&pkg->provides);
529*a3cefe7fSPierre Pronchery 
530*a3cefe7fSPierre Pronchery 	pkgconf_fragment_free(&pkg->cflags);
531*a3cefe7fSPierre Pronchery 	pkgconf_fragment_free(&pkg->cflags_private);
532*a3cefe7fSPierre Pronchery 	pkgconf_fragment_free(&pkg->libs);
533*a3cefe7fSPierre Pronchery 	pkgconf_fragment_free(&pkg->libs_private);
534*a3cefe7fSPierre Pronchery 
535*a3cefe7fSPierre Pronchery 	pkgconf_tuple_free(&pkg->vars);
536*a3cefe7fSPierre Pronchery }
537*a3cefe7fSPierre Pronchery 
538*a3cefe7fSPierre Pronchery /*
539*a3cefe7fSPierre Pronchery  * !doc
540*a3cefe7fSPierre Pronchery  *
541*a3cefe7fSPierre Pronchery  * .. c:function:: pkgconf_pkg_t *pkgconf_pkg_new_from_path(const pkgconf_client_t *client, const char *filename, unsigned int flags)
542*a3cefe7fSPierre Pronchery  *
543*a3cefe7fSPierre Pronchery  *    Parse a .pc file into a pkgconf_pkg_t object structure.
544*a3cefe7fSPierre Pronchery  *
545*a3cefe7fSPierre Pronchery  *    :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution.
546*a3cefe7fSPierre Pronchery  *    :param char* filename: The filename of the package file (including full path).
547*a3cefe7fSPierre Pronchery  *    :param FILE* f: The file object to read from.
548*a3cefe7fSPierre Pronchery  *    :param uint flags: The flags to use when parsing.
549*a3cefe7fSPierre Pronchery  *    :returns: A ``pkgconf_pkg_t`` object which contains the package data.
550*a3cefe7fSPierre Pronchery  *    :rtype: pkgconf_pkg_t *
551*a3cefe7fSPierre Pronchery  */
552*a3cefe7fSPierre Pronchery pkgconf_pkg_t *
pkgconf_pkg_new_from_path(pkgconf_client_t * client,const char * filename,unsigned int flags)553*a3cefe7fSPierre Pronchery pkgconf_pkg_new_from_path(pkgconf_client_t *client, const char *filename, unsigned int flags)
554*a3cefe7fSPierre Pronchery {
555*a3cefe7fSPierre Pronchery 	pkgconf_pkg_t *pkg;
556*a3cefe7fSPierre Pronchery 	char *idptr;
557*a3cefe7fSPierre Pronchery 	FILE *f;
558*a3cefe7fSPierre Pronchery 
559*a3cefe7fSPierre Pronchery 	/* make sure we only load .pc files */
560*a3cefe7fSPierre Pronchery 	if (!str_has_suffix(filename, PKG_CONFIG_EXT))
561*a3cefe7fSPierre Pronchery 		return NULL;
562*a3cefe7fSPierre Pronchery 
563*a3cefe7fSPierre Pronchery 	f = fopen(filename, "r");
564*a3cefe7fSPierre Pronchery 	if (f == NULL)
565*a3cefe7fSPierre Pronchery 		return NULL;
566*a3cefe7fSPierre Pronchery 
567*a3cefe7fSPierre Pronchery 	pkg = calloc(1, sizeof(pkgconf_pkg_t));
568*a3cefe7fSPierre Pronchery 	if (pkg == NULL)
569*a3cefe7fSPierre Pronchery 	{
570*a3cefe7fSPierre Pronchery 		fclose(f);
571*a3cefe7fSPierre Pronchery 		return NULL;
572*a3cefe7fSPierre Pronchery 	}
573*a3cefe7fSPierre Pronchery 
574*a3cefe7fSPierre Pronchery 	pkg->owner = client;
575*a3cefe7fSPierre Pronchery 	pkg->flags = flags;
576*a3cefe7fSPierre Pronchery 
577*a3cefe7fSPierre Pronchery 	pkg->filename = strdup(filename);
578*a3cefe7fSPierre Pronchery 	if (pkg->filename == NULL)
579*a3cefe7fSPierre Pronchery 	{
580*a3cefe7fSPierre Pronchery 		fclose(f);
581*a3cefe7fSPierre Pronchery 		pkg_free_object(pkg);
582*a3cefe7fSPierre Pronchery 		return NULL;
583*a3cefe7fSPierre Pronchery 	}
584*a3cefe7fSPierre Pronchery 
585*a3cefe7fSPierre Pronchery 	pkg->pc_filedir = pkg_get_parent_dir(pkg);
586*a3cefe7fSPierre Pronchery 	if (pkg->pc_filedir == NULL)
587*a3cefe7fSPierre Pronchery 	{
588*a3cefe7fSPierre Pronchery 		fclose(f);
589*a3cefe7fSPierre Pronchery 		pkg_free_object(pkg);
590*a3cefe7fSPierre Pronchery 		return NULL;
591*a3cefe7fSPierre Pronchery 	}
592*a3cefe7fSPierre Pronchery 
593*a3cefe7fSPierre Pronchery 	char *pc_filedir_value = convert_path_to_value(pkg->pc_filedir);
594*a3cefe7fSPierre Pronchery 	pkgconf_tuple_add(client, &pkg->vars, "pcfiledir", pc_filedir_value, true, pkg->flags);
595*a3cefe7fSPierre Pronchery 	free(pc_filedir_value);
596*a3cefe7fSPierre Pronchery 
597*a3cefe7fSPierre Pronchery 	/* If pc_filedir is outside of sysroot_dir, override sysroot_dir for this
598*a3cefe7fSPierre Pronchery 	 * package.
599*a3cefe7fSPierre Pronchery 	 * See https://github.com/pkgconf/pkgconf/issues/213
600*a3cefe7fSPierre Pronchery 	 */
601*a3cefe7fSPierre Pronchery 	if (client->sysroot_dir && strncmp(pkg->pc_filedir, client->sysroot_dir, strlen(client->sysroot_dir)))
602*a3cefe7fSPierre Pronchery 		pkgconf_tuple_add(client, &pkg->vars, "pc_sysrootdir", "", false, pkg->flags);
603*a3cefe7fSPierre Pronchery 
604*a3cefe7fSPierre Pronchery 	/* make module id */
605*a3cefe7fSPierre Pronchery 	if ((idptr = strrchr(pkg->filename, PKG_DIR_SEP_S)) != NULL)
606*a3cefe7fSPierre Pronchery 		idptr++;
607*a3cefe7fSPierre Pronchery 	else
608*a3cefe7fSPierre Pronchery 		idptr = pkg->filename;
609*a3cefe7fSPierre Pronchery 
610*a3cefe7fSPierre Pronchery #ifdef _WIN32
611*a3cefe7fSPierre Pronchery 	/* On Windows, both \ and / are allowed in paths, so we have to chop both.
612*a3cefe7fSPierre Pronchery 	 * strrchr() took us to the last \ in that case, so we just have to see if
613*a3cefe7fSPierre Pronchery 	 * it is followed by a /.  If so, lop it off.
614*a3cefe7fSPierre Pronchery 	 */
615*a3cefe7fSPierre Pronchery 	char *mungeptr;
616*a3cefe7fSPierre Pronchery 	if ((mungeptr = strrchr(idptr, '/')) != NULL)
617*a3cefe7fSPierre Pronchery 		idptr = ++mungeptr;
618*a3cefe7fSPierre Pronchery #endif
619*a3cefe7fSPierre Pronchery 
620*a3cefe7fSPierre Pronchery 	pkg->id = strdup(idptr);
621*a3cefe7fSPierre Pronchery 	if (pkg->id == NULL)
622*a3cefe7fSPierre Pronchery 	{
623*a3cefe7fSPierre Pronchery 		fclose(f);
624*a3cefe7fSPierre Pronchery 		pkg_free_lists(pkg);
625*a3cefe7fSPierre Pronchery 		pkg_free_object(pkg);
626*a3cefe7fSPierre Pronchery 		return NULL;
627*a3cefe7fSPierre Pronchery 	}
628*a3cefe7fSPierre Pronchery 
629*a3cefe7fSPierre Pronchery 	idptr = strrchr(pkg->id, '.');
630*a3cefe7fSPierre Pronchery 	if (idptr)
631*a3cefe7fSPierre Pronchery 		*idptr = '\0';
632*a3cefe7fSPierre Pronchery 
633*a3cefe7fSPierre Pronchery 	if (pkg->flags & PKGCONF_PKG_PROPF_UNINSTALLED)
634*a3cefe7fSPierre Pronchery 	{
635*a3cefe7fSPierre Pronchery 		idptr = strrchr(pkg->id, '-');
636*a3cefe7fSPierre Pronchery 		if (idptr)
637*a3cefe7fSPierre Pronchery 			*idptr = '\0';
638*a3cefe7fSPierre Pronchery 	}
639*a3cefe7fSPierre Pronchery 
640*a3cefe7fSPierre Pronchery 	pkgconf_parser_parse(f, pkg, pkg_parser_funcs, (pkgconf_parser_warn_func_t) pkg_warn_func, pkg->filename);
641*a3cefe7fSPierre Pronchery 	fclose(f);
642*a3cefe7fSPierre Pronchery 
643*a3cefe7fSPierre Pronchery 	if (!pkgconf_pkg_validate(client, pkg))
644*a3cefe7fSPierre Pronchery 	{
645*a3cefe7fSPierre Pronchery 		pkgconf_warn(client, "%s: warning: skipping invalid file\n", pkg->filename);
646*a3cefe7fSPierre Pronchery 		pkgconf_pkg_free(client, pkg);
647*a3cefe7fSPierre Pronchery 		return NULL;
648*a3cefe7fSPierre Pronchery 	}
649*a3cefe7fSPierre Pronchery 
650*a3cefe7fSPierre Pronchery 	pkgconf_dependency_t *dep = pkgconf_dependency_add(client, &pkg->provides, pkg->id, pkg->version, PKGCONF_CMP_EQUAL, 0);
651*a3cefe7fSPierre Pronchery 	pkgconf_dependency_unref(dep->owner, dep);
652*a3cefe7fSPierre Pronchery 
653*a3cefe7fSPierre Pronchery 	return pkgconf_pkg_ref(client, pkg);
654*a3cefe7fSPierre Pronchery }
655*a3cefe7fSPierre Pronchery 
656*a3cefe7fSPierre Pronchery /*
657*a3cefe7fSPierre Pronchery  * !doc
658*a3cefe7fSPierre Pronchery  *
659*a3cefe7fSPierre Pronchery  * .. c:function:: void pkgconf_pkg_free(pkgconf_client_t *client, pkgconf_pkg_t *pkg)
660*a3cefe7fSPierre Pronchery  *
661*a3cefe7fSPierre Pronchery  *    Releases all releases for a given ``pkgconf_pkg_t`` object.
662*a3cefe7fSPierre Pronchery  *
663*a3cefe7fSPierre Pronchery  *    :param pkgconf_client_t* client: The client which owns the ``pkgconf_pkg_t`` object, `pkg`.
664*a3cefe7fSPierre Pronchery  *    :param pkgconf_pkg_t* pkg: The package to free.
665*a3cefe7fSPierre Pronchery  *    :return: nothing
666*a3cefe7fSPierre Pronchery  */
667*a3cefe7fSPierre Pronchery void
pkgconf_pkg_free(pkgconf_client_t * client,pkgconf_pkg_t * pkg)668*a3cefe7fSPierre Pronchery pkgconf_pkg_free(pkgconf_client_t *client, pkgconf_pkg_t *pkg)
669*a3cefe7fSPierre Pronchery {
670*a3cefe7fSPierre Pronchery 	if (pkg == NULL)
671*a3cefe7fSPierre Pronchery 		return;
672*a3cefe7fSPierre Pronchery 
673*a3cefe7fSPierre Pronchery 	if (pkg->flags & PKGCONF_PKG_PROPF_STATIC && !(pkg->flags & PKGCONF_PKG_PROPF_VIRTUAL))
674*a3cefe7fSPierre Pronchery 		return;
675*a3cefe7fSPierre Pronchery 
676*a3cefe7fSPierre Pronchery 	pkgconf_cache_remove(client, pkg);
677*a3cefe7fSPierre Pronchery 
678*a3cefe7fSPierre Pronchery 	pkg_free_lists(pkg);
679*a3cefe7fSPierre Pronchery 
680*a3cefe7fSPierre Pronchery 	if (pkg->flags & PKGCONF_PKG_PROPF_VIRTUAL)
681*a3cefe7fSPierre Pronchery 		return;
682*a3cefe7fSPierre Pronchery 
683*a3cefe7fSPierre Pronchery 	pkg_free_object(pkg);
684*a3cefe7fSPierre Pronchery }
685*a3cefe7fSPierre Pronchery 
686*a3cefe7fSPierre Pronchery /*
687*a3cefe7fSPierre Pronchery  * !doc
688*a3cefe7fSPierre Pronchery  *
689*a3cefe7fSPierre Pronchery  * .. c:function:: pkgconf_pkg_t *pkgconf_pkg_ref(const pkgconf_client_t *client, pkgconf_pkg_t *pkg)
690*a3cefe7fSPierre Pronchery  *
691*a3cefe7fSPierre Pronchery  *    Adds an additional reference to the package object.
692*a3cefe7fSPierre Pronchery  *
693*a3cefe7fSPierre Pronchery  *    :param pkgconf_client_t* client: The pkgconf client object which owns the package being referenced.
694*a3cefe7fSPierre Pronchery  *    :param pkgconf_pkg_t* pkg: The package object being referenced.
695*a3cefe7fSPierre Pronchery  *    :return: The package itself with an incremented reference count.
696*a3cefe7fSPierre Pronchery  *    :rtype: pkgconf_pkg_t *
697*a3cefe7fSPierre Pronchery  */
698*a3cefe7fSPierre Pronchery pkgconf_pkg_t *
pkgconf_pkg_ref(pkgconf_client_t * client,pkgconf_pkg_t * pkg)699*a3cefe7fSPierre Pronchery pkgconf_pkg_ref(pkgconf_client_t *client, pkgconf_pkg_t *pkg)
700*a3cefe7fSPierre Pronchery {
701*a3cefe7fSPierre Pronchery 	if (pkg->owner != NULL && pkg->owner != client)
702*a3cefe7fSPierre Pronchery 		PKGCONF_TRACE(client, "WTF: client %p refers to package %p owned by other client %p", client, pkg, pkg->owner);
703*a3cefe7fSPierre Pronchery 
704*a3cefe7fSPierre Pronchery 	pkg->refcount++;
705*a3cefe7fSPierre Pronchery 	PKGCONF_TRACE(client, "%s refcount@%p: %d", pkg->id, pkg, pkg->refcount);
706*a3cefe7fSPierre Pronchery 
707*a3cefe7fSPierre Pronchery 	return pkg;
708*a3cefe7fSPierre Pronchery }
709*a3cefe7fSPierre Pronchery 
710*a3cefe7fSPierre Pronchery /*
711*a3cefe7fSPierre Pronchery  * !doc
712*a3cefe7fSPierre Pronchery  *
713*a3cefe7fSPierre Pronchery  * .. c:function:: void pkgconf_pkg_unref(pkgconf_client_t *client, pkgconf_pkg_t *pkg)
714*a3cefe7fSPierre Pronchery  *
715*a3cefe7fSPierre Pronchery  *    Releases a reference on the package object.  If the reference count is 0, then also free the package.
716*a3cefe7fSPierre Pronchery  *
717*a3cefe7fSPierre Pronchery  *    :param pkgconf_client_t* client: The pkgconf client object which owns the package being dereferenced.
718*a3cefe7fSPierre Pronchery  *    :param pkgconf_pkg_t* pkg: The package object being dereferenced.
719*a3cefe7fSPierre Pronchery  *    :return: nothing
720*a3cefe7fSPierre Pronchery  */
721*a3cefe7fSPierre Pronchery void
pkgconf_pkg_unref(pkgconf_client_t * client,pkgconf_pkg_t * pkg)722*a3cefe7fSPierre Pronchery pkgconf_pkg_unref(pkgconf_client_t *client, pkgconf_pkg_t *pkg)
723*a3cefe7fSPierre Pronchery {
724*a3cefe7fSPierre Pronchery 	if (pkg == NULL) {
725*a3cefe7fSPierre Pronchery 		PKGCONF_TRACE(client, "WTF: client %p unrefs a NULL package", client);
726*a3cefe7fSPierre Pronchery 		return;
727*a3cefe7fSPierre Pronchery 	}
728*a3cefe7fSPierre Pronchery 
729*a3cefe7fSPierre Pronchery 	if (pkg->owner != NULL && pkg->owner != client)
730*a3cefe7fSPierre Pronchery 		PKGCONF_TRACE(client, "WTF: client %p unrefs package %p owned by other client %p", client, pkg, pkg->owner);
731*a3cefe7fSPierre Pronchery 
732*a3cefe7fSPierre Pronchery 	pkg->refcount--;
733*a3cefe7fSPierre Pronchery 	PKGCONF_TRACE(pkg->owner, "%s refcount@%p: %d", pkg->id, pkg, pkg->refcount);
734*a3cefe7fSPierre Pronchery 
735*a3cefe7fSPierre Pronchery 	if (pkg->refcount <= 0)
736*a3cefe7fSPierre Pronchery 		pkgconf_pkg_free(pkg->owner, pkg);
737*a3cefe7fSPierre Pronchery }
738*a3cefe7fSPierre Pronchery 
739*a3cefe7fSPierre Pronchery static inline pkgconf_pkg_t *
pkgconf_pkg_try_specific_path(pkgconf_client_t * client,const char * path,const char * name)740*a3cefe7fSPierre Pronchery pkgconf_pkg_try_specific_path(pkgconf_client_t *client, const char *path, const char *name)
741*a3cefe7fSPierre Pronchery {
742*a3cefe7fSPierre Pronchery 	pkgconf_pkg_t *pkg = NULL;
743*a3cefe7fSPierre Pronchery 	char locbuf[PKGCONF_ITEM_SIZE];
744*a3cefe7fSPierre Pronchery 	char uninst_locbuf[PKGCONF_ITEM_SIZE];
745*a3cefe7fSPierre Pronchery 
746*a3cefe7fSPierre Pronchery 	PKGCONF_TRACE(client, "trying path: %s for %s", path, name);
747*a3cefe7fSPierre Pronchery 
748*a3cefe7fSPierre Pronchery 	snprintf(locbuf, sizeof locbuf, "%s%c%s" PKG_CONFIG_EXT, path, PKG_DIR_SEP_S, name);
749*a3cefe7fSPierre Pronchery 	snprintf(uninst_locbuf, sizeof uninst_locbuf, "%s%c%s-uninstalled" PKG_CONFIG_EXT, path, PKG_DIR_SEP_S, name);
750*a3cefe7fSPierre Pronchery 
751*a3cefe7fSPierre Pronchery 	if (!(client->flags & PKGCONF_PKG_PKGF_NO_UNINSTALLED))
752*a3cefe7fSPierre Pronchery 		pkg = pkgconf_pkg_new_from_path(client, uninst_locbuf, PKGCONF_PKG_PROPF_UNINSTALLED);
753*a3cefe7fSPierre Pronchery 
754*a3cefe7fSPierre Pronchery 	if (pkg == NULL)
755*a3cefe7fSPierre Pronchery 		pkg = pkgconf_pkg_new_from_path(client, locbuf, 0);
756*a3cefe7fSPierre Pronchery 
757*a3cefe7fSPierre Pronchery 	if (pkg != NULL)
758*a3cefe7fSPierre Pronchery 		PKGCONF_TRACE(client, "found%s: %s", pkg->flags & PKGCONF_PKG_PROPF_UNINSTALLED ? " (uninstalled)" : "", uninst_locbuf);
759*a3cefe7fSPierre Pronchery 
760*a3cefe7fSPierre Pronchery 	return pkg;
761*a3cefe7fSPierre Pronchery }
762*a3cefe7fSPierre Pronchery 
763*a3cefe7fSPierre Pronchery static pkgconf_pkg_t *
pkgconf_pkg_scan_dir(pkgconf_client_t * client,const char * path,void * data,pkgconf_pkg_iteration_func_t func)764*a3cefe7fSPierre Pronchery pkgconf_pkg_scan_dir(pkgconf_client_t *client, const char *path, void *data, pkgconf_pkg_iteration_func_t func)
765*a3cefe7fSPierre Pronchery {
766*a3cefe7fSPierre Pronchery 	DIR *dir;
767*a3cefe7fSPierre Pronchery 	struct dirent *dirent;
768*a3cefe7fSPierre Pronchery 	pkgconf_pkg_t *outpkg = NULL;
769*a3cefe7fSPierre Pronchery 
770*a3cefe7fSPierre Pronchery 	dir = opendir(path);
771*a3cefe7fSPierre Pronchery 	if (dir == NULL)
772*a3cefe7fSPierre Pronchery 		return NULL;
773*a3cefe7fSPierre Pronchery 
774*a3cefe7fSPierre Pronchery 	PKGCONF_TRACE(client, "scanning dir [%s]", path);
775*a3cefe7fSPierre Pronchery 
776*a3cefe7fSPierre Pronchery 	for (dirent = readdir(dir); dirent != NULL; dirent = readdir(dir))
777*a3cefe7fSPierre Pronchery 	{
778*a3cefe7fSPierre Pronchery 		char filebuf[PKGCONF_ITEM_SIZE];
779*a3cefe7fSPierre Pronchery 		pkgconf_pkg_t *pkg;
780*a3cefe7fSPierre Pronchery 
781*a3cefe7fSPierre Pronchery 		pkgconf_strlcpy(filebuf, path, sizeof filebuf);
782*a3cefe7fSPierre Pronchery 		pkgconf_strlcat(filebuf, "/", sizeof filebuf);
783*a3cefe7fSPierre Pronchery 		pkgconf_strlcat(filebuf, dirent->d_name, sizeof filebuf);
784*a3cefe7fSPierre Pronchery 
785*a3cefe7fSPierre Pronchery 		if (!str_has_suffix(filebuf, PKG_CONFIG_EXT))
786*a3cefe7fSPierre Pronchery 			continue;
787*a3cefe7fSPierre Pronchery 
788*a3cefe7fSPierre Pronchery 		PKGCONF_TRACE(client, "trying file [%s]", filebuf);
789*a3cefe7fSPierre Pronchery 
790*a3cefe7fSPierre Pronchery 		pkg = pkgconf_pkg_new_from_path(client, filebuf, 0);
791*a3cefe7fSPierre Pronchery 		if (pkg != NULL)
792*a3cefe7fSPierre Pronchery 		{
793*a3cefe7fSPierre Pronchery 			if (func(pkg, data))
794*a3cefe7fSPierre Pronchery 			{
795*a3cefe7fSPierre Pronchery 				outpkg = pkg;
796*a3cefe7fSPierre Pronchery 				goto out;
797*a3cefe7fSPierre Pronchery 			}
798*a3cefe7fSPierre Pronchery 
799*a3cefe7fSPierre Pronchery 			pkgconf_pkg_unref(client, pkg);
800*a3cefe7fSPierre Pronchery 		}
801*a3cefe7fSPierre Pronchery 	}
802*a3cefe7fSPierre Pronchery 
803*a3cefe7fSPierre Pronchery out:
804*a3cefe7fSPierre Pronchery 	closedir(dir);
805*a3cefe7fSPierre Pronchery 	return outpkg;
806*a3cefe7fSPierre Pronchery }
807*a3cefe7fSPierre Pronchery 
808*a3cefe7fSPierre Pronchery /*
809*a3cefe7fSPierre Pronchery  * !doc
810*a3cefe7fSPierre Pronchery  *
811*a3cefe7fSPierre Pronchery  * .. c:function:: pkgconf_pkg_t *pkgconf_scan_all(pkgconf_client_t *client, void *data, pkgconf_pkg_iteration_func_t func)
812*a3cefe7fSPierre Pronchery  *
813*a3cefe7fSPierre Pronchery  *    Iterates over all packages found in the `package directory list`, running ``func`` on them.  If ``func`` returns true,
814*a3cefe7fSPierre Pronchery  *    then stop iteration and return the last iterated package.
815*a3cefe7fSPierre Pronchery  *
816*a3cefe7fSPierre Pronchery  *    :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution.
817*a3cefe7fSPierre Pronchery  *    :param void* data: An opaque pointer to data to provide the iteration function with.
818*a3cefe7fSPierre Pronchery  *    :param pkgconf_pkg_iteration_func_t func: A function which is called for each package to determine if the package matches,
819*a3cefe7fSPierre Pronchery  *        always return ``false`` to iterate over all packages.
820*a3cefe7fSPierre Pronchery  *    :return: A package object reference if one is found by the scan function, else ``NULL``.
821*a3cefe7fSPierre Pronchery  *    :rtype: pkgconf_pkg_t *
822*a3cefe7fSPierre Pronchery  */
823*a3cefe7fSPierre Pronchery pkgconf_pkg_t *
pkgconf_scan_all(pkgconf_client_t * client,void * data,pkgconf_pkg_iteration_func_t func)824*a3cefe7fSPierre Pronchery pkgconf_scan_all(pkgconf_client_t *client, void *data, pkgconf_pkg_iteration_func_t func)
825*a3cefe7fSPierre Pronchery {
826*a3cefe7fSPierre Pronchery 	pkgconf_node_t *n;
827*a3cefe7fSPierre Pronchery 	pkgconf_pkg_t *pkg;
828*a3cefe7fSPierre Pronchery 
829*a3cefe7fSPierre Pronchery 	PKGCONF_TRACE(client, "scanning preloaded list");
830*a3cefe7fSPierre Pronchery 	PKGCONF_FOREACH_LIST_ENTRY(client->preloaded_pkgs.head, n)
831*a3cefe7fSPierre Pronchery 	{
832*a3cefe7fSPierre Pronchery 		pkg = n->data;
833*a3cefe7fSPierre Pronchery 
834*a3cefe7fSPierre Pronchery 		/* add an additional reference to ensure preloaded packages have the same
835*a3cefe7fSPierre Pronchery 		 * object ownership semantics as non-preloaded packages
836*a3cefe7fSPierre Pronchery 		 */
837*a3cefe7fSPierre Pronchery 		pkgconf_pkg_ref(client, pkg);
838*a3cefe7fSPierre Pronchery 
839*a3cefe7fSPierre Pronchery 		if (func(pkg, data))
840*a3cefe7fSPierre Pronchery 			return pkg;
841*a3cefe7fSPierre Pronchery 
842*a3cefe7fSPierre Pronchery 		pkgconf_pkg_unref(client, pkg);
843*a3cefe7fSPierre Pronchery 	}
844*a3cefe7fSPierre Pronchery 
845*a3cefe7fSPierre Pronchery 	PKGCONF_FOREACH_LIST_ENTRY(client->dir_list.head, n)
846*a3cefe7fSPierre Pronchery 	{
847*a3cefe7fSPierre Pronchery 		pkgconf_path_t *pnode = n->data;
848*a3cefe7fSPierre Pronchery 
849*a3cefe7fSPierre Pronchery 		PKGCONF_TRACE(client, "scanning directory: %s", pnode->path);
850*a3cefe7fSPierre Pronchery 
851*a3cefe7fSPierre Pronchery 		if ((pkg = pkgconf_pkg_scan_dir(client, pnode->path, data, func)) != NULL)
852*a3cefe7fSPierre Pronchery 			return pkg;
853*a3cefe7fSPierre Pronchery 	}
854*a3cefe7fSPierre Pronchery 
855*a3cefe7fSPierre Pronchery 	return NULL;
856*a3cefe7fSPierre Pronchery }
857*a3cefe7fSPierre Pronchery 
858*a3cefe7fSPierre Pronchery static pkgconf_pkg_t *
search_preload_list(pkgconf_client_t * client,const char * name)859*a3cefe7fSPierre Pronchery search_preload_list(pkgconf_client_t *client, const char *name)
860*a3cefe7fSPierre Pronchery {
861*a3cefe7fSPierre Pronchery 	pkgconf_node_t *n;
862*a3cefe7fSPierre Pronchery 
863*a3cefe7fSPierre Pronchery 	PKGCONF_FOREACH_LIST_ENTRY(client->preloaded_pkgs.head, n)
864*a3cefe7fSPierre Pronchery 	{
865*a3cefe7fSPierre Pronchery 		pkgconf_pkg_t *pkg = n->data;
866*a3cefe7fSPierre Pronchery 
867*a3cefe7fSPierre Pronchery 		if (!strcmp(pkg->id, name))
868*a3cefe7fSPierre Pronchery 		{
869*a3cefe7fSPierre Pronchery 			pkgconf_pkg_ref(client, pkg);
870*a3cefe7fSPierre Pronchery 			return pkg;
871*a3cefe7fSPierre Pronchery 		}
872*a3cefe7fSPierre Pronchery 	}
873*a3cefe7fSPierre Pronchery 
874*a3cefe7fSPierre Pronchery 	return NULL;
875*a3cefe7fSPierre Pronchery }
876*a3cefe7fSPierre Pronchery 
877*a3cefe7fSPierre Pronchery /*
878*a3cefe7fSPierre Pronchery  * !doc
879*a3cefe7fSPierre Pronchery  *
880*a3cefe7fSPierre Pronchery  * .. c:function:: pkgconf_pkg_t *pkgconf_pkg_find(pkgconf_client_t *client, const char *name)
881*a3cefe7fSPierre Pronchery  *
882*a3cefe7fSPierre Pronchery  *    Search for a package.
883*a3cefe7fSPierre Pronchery  *
884*a3cefe7fSPierre Pronchery  *    :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution.
885*a3cefe7fSPierre Pronchery  *    :param char* name: The name of the package `atom` to use for searching.
886*a3cefe7fSPierre Pronchery  *    :return: A package object reference if the package was found, else ``NULL``.
887*a3cefe7fSPierre Pronchery  *    :rtype: pkgconf_pkg_t *
888*a3cefe7fSPierre Pronchery  */
889*a3cefe7fSPierre Pronchery pkgconf_pkg_t *
pkgconf_pkg_find(pkgconf_client_t * client,const char * name)890*a3cefe7fSPierre Pronchery pkgconf_pkg_find(pkgconf_client_t *client, const char *name)
891*a3cefe7fSPierre Pronchery {
892*a3cefe7fSPierre Pronchery 	pkgconf_pkg_t *pkg = NULL;
893*a3cefe7fSPierre Pronchery 	pkgconf_node_t *n;
894*a3cefe7fSPierre Pronchery 
895*a3cefe7fSPierre Pronchery 	PKGCONF_TRACE(client, "looking for: %s", name);
896*a3cefe7fSPierre Pronchery 
897*a3cefe7fSPierre Pronchery 	/* name might actually be a filename. */
898*a3cefe7fSPierre Pronchery 	if (str_has_suffix(name, PKG_CONFIG_EXT))
899*a3cefe7fSPierre Pronchery 	{
900*a3cefe7fSPierre Pronchery 		if (client->unveil_handler != NULL)
901*a3cefe7fSPierre Pronchery 			client->unveil_handler(client, name, "r");
902*a3cefe7fSPierre Pronchery 
903*a3cefe7fSPierre Pronchery 		pkg = pkgconf_pkg_new_from_path(client, name, 0);
904*a3cefe7fSPierre Pronchery 		if (pkg != NULL)
905*a3cefe7fSPierre Pronchery 		{
906*a3cefe7fSPierre Pronchery 			PKGCONF_TRACE(client, "%s is a file", name);
907*a3cefe7fSPierre Pronchery 
908*a3cefe7fSPierre Pronchery 			if (client->unveil_handler != NULL)
909*a3cefe7fSPierre Pronchery 				client->unveil_handler(client, pkg->pc_filedir, "r");
910*a3cefe7fSPierre Pronchery 
911*a3cefe7fSPierre Pronchery 			pkgconf_path_add(pkg->pc_filedir, &client->dir_list, true);
912*a3cefe7fSPierre Pronchery 			goto out;
913*a3cefe7fSPierre Pronchery 		}
914*a3cefe7fSPierre Pronchery 	}
915*a3cefe7fSPierre Pronchery 
916*a3cefe7fSPierre Pronchery 	/* check builtins */
917*a3cefe7fSPierre Pronchery 	if ((pkg = pkgconf_builtin_pkg_get(name)) != NULL)
918*a3cefe7fSPierre Pronchery 	{
919*a3cefe7fSPierre Pronchery 		PKGCONF_TRACE(client, "%s is a builtin", name);
920*a3cefe7fSPierre Pronchery 		return pkg;
921*a3cefe7fSPierre Pronchery 	}
922*a3cefe7fSPierre Pronchery 
923*a3cefe7fSPierre Pronchery 	/* check cache */
924*a3cefe7fSPierre Pronchery 	if (!(client->flags & PKGCONF_PKG_PKGF_NO_CACHE))
925*a3cefe7fSPierre Pronchery 	{
926*a3cefe7fSPierre Pronchery 		if ((pkg = pkgconf_cache_lookup(client, name)) != NULL)
927*a3cefe7fSPierre Pronchery 		{
928*a3cefe7fSPierre Pronchery 			PKGCONF_TRACE(client, "%s is cached", name);
929*a3cefe7fSPierre Pronchery 			return pkg;
930*a3cefe7fSPierre Pronchery 		}
931*a3cefe7fSPierre Pronchery 	}
932*a3cefe7fSPierre Pronchery 
933*a3cefe7fSPierre Pronchery 	/* check preload list */
934*a3cefe7fSPierre Pronchery 	if ((pkg = search_preload_list(client, name)) != NULL)
935*a3cefe7fSPierre Pronchery 	{
936*a3cefe7fSPierre Pronchery 		PKGCONF_TRACE(client, "%s is preloaded", name);
937*a3cefe7fSPierre Pronchery 		return pkg;
938*a3cefe7fSPierre Pronchery 	}
939*a3cefe7fSPierre Pronchery 
940*a3cefe7fSPierre Pronchery 	PKGCONF_FOREACH_LIST_ENTRY(client->dir_list.head, n)
941*a3cefe7fSPierre Pronchery 	{
942*a3cefe7fSPierre Pronchery 		pkgconf_path_t *pnode = n->data;
943*a3cefe7fSPierre Pronchery 
944*a3cefe7fSPierre Pronchery 		pkg = pkgconf_pkg_try_specific_path(client, pnode->path, name);
945*a3cefe7fSPierre Pronchery 		if (pkg != NULL)
946*a3cefe7fSPierre Pronchery 			goto out;
947*a3cefe7fSPierre Pronchery 	}
948*a3cefe7fSPierre Pronchery 
949*a3cefe7fSPierre Pronchery out:
950*a3cefe7fSPierre Pronchery 	pkgconf_cache_add(client, pkg);
951*a3cefe7fSPierre Pronchery 
952*a3cefe7fSPierre Pronchery 	return pkg;
953*a3cefe7fSPierre Pronchery }
954*a3cefe7fSPierre Pronchery 
955*a3cefe7fSPierre Pronchery /*
956*a3cefe7fSPierre Pronchery  * !doc
957*a3cefe7fSPierre Pronchery  *
958*a3cefe7fSPierre Pronchery  * .. c:function:: int pkgconf_compare_version(const char *a, const char *b)
959*a3cefe7fSPierre Pronchery  *
960*a3cefe7fSPierre Pronchery  *    Compare versions using RPM version comparison rules as described in the LSB.
961*a3cefe7fSPierre Pronchery  *
962*a3cefe7fSPierre Pronchery  *    :param char* a: The first version to compare in the pair.
963*a3cefe7fSPierre Pronchery  *    :param char* b: The second version to compare in the pair.
964*a3cefe7fSPierre Pronchery  *    :return: -1 if the first version is less than, 0 if both versions are equal, 1 if the second version is less than.
965*a3cefe7fSPierre Pronchery  *    :rtype: int
966*a3cefe7fSPierre Pronchery  */
967*a3cefe7fSPierre Pronchery int
pkgconf_compare_version(const char * a,const char * b)968*a3cefe7fSPierre Pronchery pkgconf_compare_version(const char *a, const char *b)
969*a3cefe7fSPierre Pronchery {
970*a3cefe7fSPierre Pronchery 	char oldch1, oldch2;
971*a3cefe7fSPierre Pronchery 	char buf1[PKGCONF_ITEM_SIZE], buf2[PKGCONF_ITEM_SIZE];
972*a3cefe7fSPierre Pronchery 	char *str1, *str2;
973*a3cefe7fSPierre Pronchery 	char *one, *two;
974*a3cefe7fSPierre Pronchery 	int ret;
975*a3cefe7fSPierre Pronchery 	bool isnum;
976*a3cefe7fSPierre Pronchery 
977*a3cefe7fSPierre Pronchery 	/* optimization: if version matches then it's the same version. */
978*a3cefe7fSPierre Pronchery 	if (a == NULL)
979*a3cefe7fSPierre Pronchery 		return -1;
980*a3cefe7fSPierre Pronchery 
981*a3cefe7fSPierre Pronchery 	if (b == NULL)
982*a3cefe7fSPierre Pronchery 		return 1;
983*a3cefe7fSPierre Pronchery 
984*a3cefe7fSPierre Pronchery 	if (!strcasecmp(a, b))
985*a3cefe7fSPierre Pronchery 		return 0;
986*a3cefe7fSPierre Pronchery 
987*a3cefe7fSPierre Pronchery 	pkgconf_strlcpy(buf1, a, sizeof buf1);
988*a3cefe7fSPierre Pronchery 	pkgconf_strlcpy(buf2, b, sizeof buf2);
989*a3cefe7fSPierre Pronchery 
990*a3cefe7fSPierre Pronchery 	one = buf1;
991*a3cefe7fSPierre Pronchery 	two = buf2;
992*a3cefe7fSPierre Pronchery 
993*a3cefe7fSPierre Pronchery 	while (*one || *two)
994*a3cefe7fSPierre Pronchery 	{
995*a3cefe7fSPierre Pronchery 		while (*one && !isalnum((unsigned char)*one) && *one != '~')
996*a3cefe7fSPierre Pronchery 			one++;
997*a3cefe7fSPierre Pronchery 		while (*two && !isalnum((unsigned char)*two) && *two != '~')
998*a3cefe7fSPierre Pronchery 			two++;
999*a3cefe7fSPierre Pronchery 
1000*a3cefe7fSPierre Pronchery 		if (*one == '~' || *two == '~')
1001*a3cefe7fSPierre Pronchery 		{
1002*a3cefe7fSPierre Pronchery 			if (*one != '~')
1003*a3cefe7fSPierre Pronchery 				return 1;
1004*a3cefe7fSPierre Pronchery 			if (*two != '~')
1005*a3cefe7fSPierre Pronchery 				return -1;
1006*a3cefe7fSPierre Pronchery 
1007*a3cefe7fSPierre Pronchery 			one++;
1008*a3cefe7fSPierre Pronchery 			two++;
1009*a3cefe7fSPierre Pronchery 			continue;
1010*a3cefe7fSPierre Pronchery 		}
1011*a3cefe7fSPierre Pronchery 
1012*a3cefe7fSPierre Pronchery 		if (!(*one && *two))
1013*a3cefe7fSPierre Pronchery 			break;
1014*a3cefe7fSPierre Pronchery 
1015*a3cefe7fSPierre Pronchery 		str1 = one;
1016*a3cefe7fSPierre Pronchery 		str2 = two;
1017*a3cefe7fSPierre Pronchery 
1018*a3cefe7fSPierre Pronchery 		if (isdigit((unsigned char)*str1))
1019*a3cefe7fSPierre Pronchery 		{
1020*a3cefe7fSPierre Pronchery 			while (*str1 && isdigit((unsigned char)*str1))
1021*a3cefe7fSPierre Pronchery 				str1++;
1022*a3cefe7fSPierre Pronchery 
1023*a3cefe7fSPierre Pronchery 			while (*str2 && isdigit((unsigned char)*str2))
1024*a3cefe7fSPierre Pronchery 				str2++;
1025*a3cefe7fSPierre Pronchery 
1026*a3cefe7fSPierre Pronchery 			isnum = true;
1027*a3cefe7fSPierre Pronchery 		}
1028*a3cefe7fSPierre Pronchery 		else
1029*a3cefe7fSPierre Pronchery 		{
1030*a3cefe7fSPierre Pronchery 			while (*str1 && isalpha((unsigned char)*str1))
1031*a3cefe7fSPierre Pronchery 				str1++;
1032*a3cefe7fSPierre Pronchery 
1033*a3cefe7fSPierre Pronchery 			while (*str2 && isalpha((unsigned char)*str2))
1034*a3cefe7fSPierre Pronchery 				str2++;
1035*a3cefe7fSPierre Pronchery 
1036*a3cefe7fSPierre Pronchery 			isnum = false;
1037*a3cefe7fSPierre Pronchery 		}
1038*a3cefe7fSPierre Pronchery 
1039*a3cefe7fSPierre Pronchery 		oldch1 = *str1;
1040*a3cefe7fSPierre Pronchery 		oldch2 = *str2;
1041*a3cefe7fSPierre Pronchery 
1042*a3cefe7fSPierre Pronchery 		*str1 = '\0';
1043*a3cefe7fSPierre Pronchery 		*str2 = '\0';
1044*a3cefe7fSPierre Pronchery 
1045*a3cefe7fSPierre Pronchery 		if (one == str1)
1046*a3cefe7fSPierre Pronchery 			return -1;
1047*a3cefe7fSPierre Pronchery 
1048*a3cefe7fSPierre Pronchery 		if (two == str2)
1049*a3cefe7fSPierre Pronchery 			return (isnum ? 1 : -1);
1050*a3cefe7fSPierre Pronchery 
1051*a3cefe7fSPierre Pronchery 		if (isnum)
1052*a3cefe7fSPierre Pronchery 		{
1053*a3cefe7fSPierre Pronchery 			int onelen, twolen;
1054*a3cefe7fSPierre Pronchery 
1055*a3cefe7fSPierre Pronchery 			while (*one == '0')
1056*a3cefe7fSPierre Pronchery 				one++;
1057*a3cefe7fSPierre Pronchery 
1058*a3cefe7fSPierre Pronchery 			while (*two == '0')
1059*a3cefe7fSPierre Pronchery 				two++;
1060*a3cefe7fSPierre Pronchery 
1061*a3cefe7fSPierre Pronchery 			onelen = strlen(one);
1062*a3cefe7fSPierre Pronchery 			twolen = strlen(two);
1063*a3cefe7fSPierre Pronchery 
1064*a3cefe7fSPierre Pronchery 			if (onelen > twolen)
1065*a3cefe7fSPierre Pronchery 				return 1;
1066*a3cefe7fSPierre Pronchery 			else if (twolen > onelen)
1067*a3cefe7fSPierre Pronchery 				return -1;
1068*a3cefe7fSPierre Pronchery 		}
1069*a3cefe7fSPierre Pronchery 
1070*a3cefe7fSPierre Pronchery 		ret = strcmp(one, two);
1071*a3cefe7fSPierre Pronchery 		if (ret != 0)
1072*a3cefe7fSPierre Pronchery 			return ret < 0 ? -1 : 1;
1073*a3cefe7fSPierre Pronchery 
1074*a3cefe7fSPierre Pronchery 		*str1 = oldch1;
1075*a3cefe7fSPierre Pronchery 		*str2 = oldch2;
1076*a3cefe7fSPierre Pronchery 
1077*a3cefe7fSPierre Pronchery 		one = str1;
1078*a3cefe7fSPierre Pronchery 		two = str2;
1079*a3cefe7fSPierre Pronchery 	}
1080*a3cefe7fSPierre Pronchery 
1081*a3cefe7fSPierre Pronchery 	if ((!*one) && (!*two))
1082*a3cefe7fSPierre Pronchery 		return 0;
1083*a3cefe7fSPierre Pronchery 
1084*a3cefe7fSPierre Pronchery 	if (!*one)
1085*a3cefe7fSPierre Pronchery 		return -1;
1086*a3cefe7fSPierre Pronchery 
1087*a3cefe7fSPierre Pronchery 	return 1;
1088*a3cefe7fSPierre Pronchery }
1089*a3cefe7fSPierre Pronchery 
1090*a3cefe7fSPierre Pronchery static pkgconf_pkg_t pkg_config_virtual = {
1091*a3cefe7fSPierre Pronchery 	.id = "pkg-config",
1092*a3cefe7fSPierre Pronchery 	.realname = "pkg-config",
1093*a3cefe7fSPierre Pronchery 	.description = "virtual package defining pkg-config API version supported",
1094*a3cefe7fSPierre Pronchery 	.url = PACKAGE_BUGREPORT,
1095*a3cefe7fSPierre Pronchery 	.version = PACKAGE_VERSION,
1096*a3cefe7fSPierre Pronchery 	.flags = PKGCONF_PKG_PROPF_STATIC,
1097*a3cefe7fSPierre Pronchery 	.vars = {
1098*a3cefe7fSPierre Pronchery 		.head = &(pkgconf_node_t){
1099*a3cefe7fSPierre Pronchery 			.next = &(pkgconf_node_t){
1100*a3cefe7fSPierre Pronchery 				.next = &(pkgconf_node_t){
1101*a3cefe7fSPierre Pronchery 					.data = &(pkgconf_tuple_t){
1102*a3cefe7fSPierre Pronchery 						.key = "pc_system_libdirs",
1103*a3cefe7fSPierre Pronchery 						.value = SYSTEM_LIBDIR,
1104*a3cefe7fSPierre Pronchery 					}
1105*a3cefe7fSPierre Pronchery 				},
1106*a3cefe7fSPierre Pronchery 				.data = &(pkgconf_tuple_t){
1107*a3cefe7fSPierre Pronchery 					.key = "pc_system_includedirs",
1108*a3cefe7fSPierre Pronchery 					.value = SYSTEM_INCLUDEDIR,
1109*a3cefe7fSPierre Pronchery 				}
1110*a3cefe7fSPierre Pronchery 			},
1111*a3cefe7fSPierre Pronchery 			.data = &(pkgconf_tuple_t){
1112*a3cefe7fSPierre Pronchery 				.key = "pc_path",
1113*a3cefe7fSPierre Pronchery 				.value = PKG_DEFAULT_PATH,
1114*a3cefe7fSPierre Pronchery 			},
1115*a3cefe7fSPierre Pronchery 		},
1116*a3cefe7fSPierre Pronchery 		.tail = NULL,
1117*a3cefe7fSPierre Pronchery 	}
1118*a3cefe7fSPierre Pronchery };
1119*a3cefe7fSPierre Pronchery 
1120*a3cefe7fSPierre Pronchery static pkgconf_pkg_t pkgconf_virtual = {
1121*a3cefe7fSPierre Pronchery 	.id = "pkgconf",
1122*a3cefe7fSPierre Pronchery 	.realname = "pkgconf",
1123*a3cefe7fSPierre Pronchery 	.description = "virtual package defining pkgconf API version supported",
1124*a3cefe7fSPierre Pronchery 	.url = PACKAGE_BUGREPORT,
1125*a3cefe7fSPierre Pronchery 	.version = PACKAGE_VERSION,
1126*a3cefe7fSPierre Pronchery 	.license = "ISC",
1127*a3cefe7fSPierre Pronchery 	.flags = PKGCONF_PKG_PROPF_STATIC,
1128*a3cefe7fSPierre Pronchery 	.vars = {
1129*a3cefe7fSPierre Pronchery 		.head = &(pkgconf_node_t){
1130*a3cefe7fSPierre Pronchery 			.next = &(pkgconf_node_t){
1131*a3cefe7fSPierre Pronchery 				.next = &(pkgconf_node_t){
1132*a3cefe7fSPierre Pronchery 					.data = &(pkgconf_tuple_t){
1133*a3cefe7fSPierre Pronchery 						.key = "pc_system_libdirs",
1134*a3cefe7fSPierre Pronchery 						.value = SYSTEM_LIBDIR,
1135*a3cefe7fSPierre Pronchery 					}
1136*a3cefe7fSPierre Pronchery 				},
1137*a3cefe7fSPierre Pronchery 				.data = &(pkgconf_tuple_t){
1138*a3cefe7fSPierre Pronchery 					.key = "pc_system_includedirs",
1139*a3cefe7fSPierre Pronchery 					.value = SYSTEM_INCLUDEDIR,
1140*a3cefe7fSPierre Pronchery 				}
1141*a3cefe7fSPierre Pronchery 			},
1142*a3cefe7fSPierre Pronchery 			.data = &(pkgconf_tuple_t){
1143*a3cefe7fSPierre Pronchery 				.key = "pc_path",
1144*a3cefe7fSPierre Pronchery 				.value = PKG_DEFAULT_PATH,
1145*a3cefe7fSPierre Pronchery 			},
1146*a3cefe7fSPierre Pronchery 		},
1147*a3cefe7fSPierre Pronchery 		.tail = NULL,
1148*a3cefe7fSPierre Pronchery 	},
1149*a3cefe7fSPierre Pronchery };
1150*a3cefe7fSPierre Pronchery 
1151*a3cefe7fSPierre Pronchery typedef struct {
1152*a3cefe7fSPierre Pronchery 	const char *name;
1153*a3cefe7fSPierre Pronchery 	pkgconf_pkg_t *pkg;
1154*a3cefe7fSPierre Pronchery } pkgconf_builtin_pkg_pair_t;
1155*a3cefe7fSPierre Pronchery 
1156*a3cefe7fSPierre Pronchery /* keep these in alphabetical order */
1157*a3cefe7fSPierre Pronchery static const pkgconf_builtin_pkg_pair_t pkgconf_builtin_pkg_pair_set[] = {
1158*a3cefe7fSPierre Pronchery 	{"pkg-config", &pkg_config_virtual},
1159*a3cefe7fSPierre Pronchery 	{"pkgconf", &pkgconf_virtual},
1160*a3cefe7fSPierre Pronchery };
1161*a3cefe7fSPierre Pronchery 
pkgconf_builtin_pkg_pair_cmp(const void * key,const void * ptr)1162*a3cefe7fSPierre Pronchery static int pkgconf_builtin_pkg_pair_cmp(const void *key, const void *ptr)
1163*a3cefe7fSPierre Pronchery {
1164*a3cefe7fSPierre Pronchery 	const pkgconf_builtin_pkg_pair_t *pair = ptr;
1165*a3cefe7fSPierre Pronchery 	return strcasecmp(key, pair->name);
1166*a3cefe7fSPierre Pronchery }
1167*a3cefe7fSPierre Pronchery 
1168*a3cefe7fSPierre Pronchery /*
1169*a3cefe7fSPierre Pronchery  * !doc
1170*a3cefe7fSPierre Pronchery  *
1171*a3cefe7fSPierre Pronchery  * .. c:function:: pkgconf_pkg_t *pkgconf_builtin_pkg_get(const char *name)
1172*a3cefe7fSPierre Pronchery  *
1173*a3cefe7fSPierre Pronchery  *    Looks up a built-in package.  The package should not be freed or dereferenced.
1174*a3cefe7fSPierre Pronchery  *
1175*a3cefe7fSPierre Pronchery  *    :param char* name: An atom corresponding to a built-in package to search for.
1176*a3cefe7fSPierre Pronchery  *    :return: the built-in package if present, else ``NULL``.
1177*a3cefe7fSPierre Pronchery  *    :rtype: pkgconf_pkg_t *
1178*a3cefe7fSPierre Pronchery  */
1179*a3cefe7fSPierre Pronchery pkgconf_pkg_t *
pkgconf_builtin_pkg_get(const char * name)1180*a3cefe7fSPierre Pronchery pkgconf_builtin_pkg_get(const char *name)
1181*a3cefe7fSPierre Pronchery {
1182*a3cefe7fSPierre Pronchery 	const pkgconf_builtin_pkg_pair_t *pair = bsearch(name, pkgconf_builtin_pkg_pair_set,
1183*a3cefe7fSPierre Pronchery 		PKGCONF_ARRAY_SIZE(pkgconf_builtin_pkg_pair_set), sizeof(pkgconf_builtin_pkg_pair_t),
1184*a3cefe7fSPierre Pronchery 		pkgconf_builtin_pkg_pair_cmp);
1185*a3cefe7fSPierre Pronchery 
1186*a3cefe7fSPierre Pronchery 	return (pair != NULL) ? pair->pkg : NULL;
1187*a3cefe7fSPierre Pronchery }
1188*a3cefe7fSPierre Pronchery 
1189*a3cefe7fSPierre Pronchery typedef bool (*pkgconf_vercmp_res_func_t)(const char *a, const char *b);
1190*a3cefe7fSPierre Pronchery 
1191*a3cefe7fSPierre Pronchery typedef struct {
1192*a3cefe7fSPierre Pronchery 	const char *name;
1193*a3cefe7fSPierre Pronchery 	pkgconf_pkg_comparator_t compare;
1194*a3cefe7fSPierre Pronchery } pkgconf_pkg_comparator_pair_t;
1195*a3cefe7fSPierre Pronchery 
1196*a3cefe7fSPierre Pronchery static const pkgconf_pkg_comparator_pair_t pkgconf_pkg_comparator_names[] = {
1197*a3cefe7fSPierre Pronchery 	{"!=",		PKGCONF_CMP_NOT_EQUAL},
1198*a3cefe7fSPierre Pronchery 	{"(any)",	PKGCONF_CMP_ANY},
1199*a3cefe7fSPierre Pronchery 	{"<",		PKGCONF_CMP_LESS_THAN},
1200*a3cefe7fSPierre Pronchery 	{"<=",		PKGCONF_CMP_LESS_THAN_EQUAL},
1201*a3cefe7fSPierre Pronchery 	{"=",		PKGCONF_CMP_EQUAL},
1202*a3cefe7fSPierre Pronchery 	{">",		PKGCONF_CMP_GREATER_THAN},
1203*a3cefe7fSPierre Pronchery 	{">=",		PKGCONF_CMP_GREATER_THAN_EQUAL},
1204*a3cefe7fSPierre Pronchery };
1205*a3cefe7fSPierre Pronchery 
pkgconf_pkg_comparator_pair_namecmp(const void * key,const void * ptr)1206*a3cefe7fSPierre Pronchery static int pkgconf_pkg_comparator_pair_namecmp(const void *key, const void *ptr)
1207*a3cefe7fSPierre Pronchery {
1208*a3cefe7fSPierre Pronchery 	const pkgconf_pkg_comparator_pair_t *pair = ptr;
1209*a3cefe7fSPierre Pronchery 	return strcmp(key, pair->name);
1210*a3cefe7fSPierre Pronchery }
1211*a3cefe7fSPierre Pronchery 
pkgconf_pkg_comparator_lt(const char * a,const char * b)1212*a3cefe7fSPierre Pronchery static bool pkgconf_pkg_comparator_lt(const char *a, const char *b)
1213*a3cefe7fSPierre Pronchery {
1214*a3cefe7fSPierre Pronchery 	return (pkgconf_compare_version(a, b) < 0);
1215*a3cefe7fSPierre Pronchery }
1216*a3cefe7fSPierre Pronchery 
pkgconf_pkg_comparator_gt(const char * a,const char * b)1217*a3cefe7fSPierre Pronchery static bool pkgconf_pkg_comparator_gt(const char *a, const char *b)
1218*a3cefe7fSPierre Pronchery {
1219*a3cefe7fSPierre Pronchery 	return (pkgconf_compare_version(a, b) > 0);
1220*a3cefe7fSPierre Pronchery }
1221*a3cefe7fSPierre Pronchery 
pkgconf_pkg_comparator_lte(const char * a,const char * b)1222*a3cefe7fSPierre Pronchery static bool pkgconf_pkg_comparator_lte(const char *a, const char *b)
1223*a3cefe7fSPierre Pronchery {
1224*a3cefe7fSPierre Pronchery 	return (pkgconf_compare_version(a, b) <= 0);
1225*a3cefe7fSPierre Pronchery }
1226*a3cefe7fSPierre Pronchery 
pkgconf_pkg_comparator_gte(const char * a,const char * b)1227*a3cefe7fSPierre Pronchery static bool pkgconf_pkg_comparator_gte(const char *a, const char *b)
1228*a3cefe7fSPierre Pronchery {
1229*a3cefe7fSPierre Pronchery 	return (pkgconf_compare_version(a, b) >= 0);
1230*a3cefe7fSPierre Pronchery }
1231*a3cefe7fSPierre Pronchery 
pkgconf_pkg_comparator_eq(const char * a,const char * b)1232*a3cefe7fSPierre Pronchery static bool pkgconf_pkg_comparator_eq(const char *a, const char *b)
1233*a3cefe7fSPierre Pronchery {
1234*a3cefe7fSPierre Pronchery 	return (pkgconf_compare_version(a, b) == 0);
1235*a3cefe7fSPierre Pronchery }
1236*a3cefe7fSPierre Pronchery 
pkgconf_pkg_comparator_ne(const char * a,const char * b)1237*a3cefe7fSPierre Pronchery static bool pkgconf_pkg_comparator_ne(const char *a, const char *b)
1238*a3cefe7fSPierre Pronchery {
1239*a3cefe7fSPierre Pronchery 	return (pkgconf_compare_version(a, b) != 0);
1240*a3cefe7fSPierre Pronchery }
1241*a3cefe7fSPierre Pronchery 
pkgconf_pkg_comparator_any(const char * a,const char * b)1242*a3cefe7fSPierre Pronchery static bool pkgconf_pkg_comparator_any(const char *a, const char *b)
1243*a3cefe7fSPierre Pronchery {
1244*a3cefe7fSPierre Pronchery 	(void) a;
1245*a3cefe7fSPierre Pronchery 	(void) b;
1246*a3cefe7fSPierre Pronchery 
1247*a3cefe7fSPierre Pronchery 	return true;
1248*a3cefe7fSPierre Pronchery }
1249*a3cefe7fSPierre Pronchery 
pkgconf_pkg_comparator_none(const char * a,const char * b)1250*a3cefe7fSPierre Pronchery static bool pkgconf_pkg_comparator_none(const char *a, const char *b)
1251*a3cefe7fSPierre Pronchery {
1252*a3cefe7fSPierre Pronchery 	(void) a;
1253*a3cefe7fSPierre Pronchery 	(void) b;
1254*a3cefe7fSPierre Pronchery 
1255*a3cefe7fSPierre Pronchery 	return false;
1256*a3cefe7fSPierre Pronchery }
1257*a3cefe7fSPierre Pronchery 
1258*a3cefe7fSPierre Pronchery static const pkgconf_vercmp_res_func_t pkgconf_pkg_comparator_impls[] = {
1259*a3cefe7fSPierre Pronchery 	[PKGCONF_CMP_ANY]			= pkgconf_pkg_comparator_any,
1260*a3cefe7fSPierre Pronchery 	[PKGCONF_CMP_LESS_THAN]			= pkgconf_pkg_comparator_lt,
1261*a3cefe7fSPierre Pronchery 	[PKGCONF_CMP_GREATER_THAN]		= pkgconf_pkg_comparator_gt,
1262*a3cefe7fSPierre Pronchery 	[PKGCONF_CMP_LESS_THAN_EQUAL]		= pkgconf_pkg_comparator_lte,
1263*a3cefe7fSPierre Pronchery 	[PKGCONF_CMP_GREATER_THAN_EQUAL]	= pkgconf_pkg_comparator_gte,
1264*a3cefe7fSPierre Pronchery 	[PKGCONF_CMP_EQUAL]			= pkgconf_pkg_comparator_eq,
1265*a3cefe7fSPierre Pronchery 	[PKGCONF_CMP_NOT_EQUAL]			= pkgconf_pkg_comparator_ne,
1266*a3cefe7fSPierre Pronchery };
1267*a3cefe7fSPierre Pronchery 
1268*a3cefe7fSPierre Pronchery /*
1269*a3cefe7fSPierre Pronchery  * !doc
1270*a3cefe7fSPierre Pronchery  *
1271*a3cefe7fSPierre Pronchery  * .. c:function:: const char *pkgconf_pkg_get_comparator(const pkgconf_dependency_t *pkgdep)
1272*a3cefe7fSPierre Pronchery  *
1273*a3cefe7fSPierre Pronchery  *    Returns the comparator used in a depgraph dependency node as a string.
1274*a3cefe7fSPierre Pronchery  *
1275*a3cefe7fSPierre Pronchery  *    :param pkgconf_dependency_t* pkgdep: The depgraph dependency node to return the comparator for.
1276*a3cefe7fSPierre Pronchery  *    :return: A string matching the comparator or ``"???"``.
1277*a3cefe7fSPierre Pronchery  *    :rtype: char *
1278*a3cefe7fSPierre Pronchery  */
1279*a3cefe7fSPierre Pronchery const char *
pkgconf_pkg_get_comparator(const pkgconf_dependency_t * pkgdep)1280*a3cefe7fSPierre Pronchery pkgconf_pkg_get_comparator(const pkgconf_dependency_t *pkgdep)
1281*a3cefe7fSPierre Pronchery {
1282*a3cefe7fSPierre Pronchery 	if (pkgdep->compare >= PKGCONF_ARRAY_SIZE(pkgconf_pkg_comparator_names))
1283*a3cefe7fSPierre Pronchery 		return "???";
1284*a3cefe7fSPierre Pronchery 
1285*a3cefe7fSPierre Pronchery 	return pkgconf_pkg_comparator_names[pkgdep->compare].name;
1286*a3cefe7fSPierre Pronchery }
1287*a3cefe7fSPierre Pronchery 
1288*a3cefe7fSPierre Pronchery /*
1289*a3cefe7fSPierre Pronchery  * !doc
1290*a3cefe7fSPierre Pronchery  *
1291*a3cefe7fSPierre Pronchery  * .. c:function:: pkgconf_pkg_comparator_t pkgconf_pkg_comparator_lookup_by_name(const char *name)
1292*a3cefe7fSPierre Pronchery  *
1293*a3cefe7fSPierre Pronchery  *    Look up the appropriate comparator bytecode in the comparator set (defined
1294*a3cefe7fSPierre Pronchery  *    in ``pkg.c``, see ``pkgconf_pkg_comparator_names`` and ``pkgconf_pkg_comparator_impls``).
1295*a3cefe7fSPierre Pronchery  *
1296*a3cefe7fSPierre Pronchery  *    :param char* name: The comparator to look up by `name`.
1297*a3cefe7fSPierre Pronchery  *    :return: The comparator bytecode if found, else ``PKGCONF_CMP_ANY``.
1298*a3cefe7fSPierre Pronchery  *    :rtype: pkgconf_pkg_comparator_t
1299*a3cefe7fSPierre Pronchery  */
1300*a3cefe7fSPierre Pronchery pkgconf_pkg_comparator_t
pkgconf_pkg_comparator_lookup_by_name(const char * name)1301*a3cefe7fSPierre Pronchery pkgconf_pkg_comparator_lookup_by_name(const char *name)
1302*a3cefe7fSPierre Pronchery {
1303*a3cefe7fSPierre Pronchery 	const pkgconf_pkg_comparator_pair_t *p = bsearch(name, pkgconf_pkg_comparator_names,
1304*a3cefe7fSPierre Pronchery 		PKGCONF_ARRAY_SIZE(pkgconf_pkg_comparator_names), sizeof(pkgconf_pkg_comparator_pair_t),
1305*a3cefe7fSPierre Pronchery 		pkgconf_pkg_comparator_pair_namecmp);
1306*a3cefe7fSPierre Pronchery 
1307*a3cefe7fSPierre Pronchery 	return (p != NULL) ? p->compare : PKGCONF_CMP_ANY;
1308*a3cefe7fSPierre Pronchery }
1309*a3cefe7fSPierre Pronchery 
1310*a3cefe7fSPierre Pronchery typedef struct {
1311*a3cefe7fSPierre Pronchery 	pkgconf_dependency_t *pkgdep;
1312*a3cefe7fSPierre Pronchery } pkgconf_pkg_scan_providers_ctx_t;
1313*a3cefe7fSPierre Pronchery 
1314*a3cefe7fSPierre Pronchery typedef struct {
1315*a3cefe7fSPierre Pronchery 	const pkgconf_vercmp_res_func_t rulecmp[PKGCONF_CMP_COUNT];
1316*a3cefe7fSPierre Pronchery 	const pkgconf_vercmp_res_func_t depcmp[PKGCONF_CMP_COUNT];
1317*a3cefe7fSPierre Pronchery } pkgconf_pkg_provides_vermatch_rule_t;
1318*a3cefe7fSPierre Pronchery 
1319*a3cefe7fSPierre Pronchery static const pkgconf_pkg_provides_vermatch_rule_t pkgconf_pkg_provides_vermatch_rules[] = {
1320*a3cefe7fSPierre Pronchery 	[PKGCONF_CMP_ANY] = {
1321*a3cefe7fSPierre Pronchery 		.rulecmp = {
1322*a3cefe7fSPierre Pronchery 			[PKGCONF_CMP_ANY]			= pkgconf_pkg_comparator_none,
1323*a3cefe7fSPierre Pronchery                 },
1324*a3cefe7fSPierre Pronchery 		.depcmp = {
1325*a3cefe7fSPierre Pronchery 			[PKGCONF_CMP_ANY]			= pkgconf_pkg_comparator_none,
1326*a3cefe7fSPierre Pronchery                 },
1327*a3cefe7fSPierre Pronchery 	},
1328*a3cefe7fSPierre Pronchery 	[PKGCONF_CMP_LESS_THAN] = {
1329*a3cefe7fSPierre Pronchery 		.rulecmp = {
1330*a3cefe7fSPierre Pronchery 			[PKGCONF_CMP_ANY]			= pkgconf_pkg_comparator_none,
1331*a3cefe7fSPierre Pronchery 			[PKGCONF_CMP_LESS_THAN]			= pkgconf_pkg_comparator_lt,
1332*a3cefe7fSPierre Pronchery 			[PKGCONF_CMP_GREATER_THAN]		= pkgconf_pkg_comparator_gt,
1333*a3cefe7fSPierre Pronchery 			[PKGCONF_CMP_LESS_THAN_EQUAL]		= pkgconf_pkg_comparator_lte,
1334*a3cefe7fSPierre Pronchery 			[PKGCONF_CMP_GREATER_THAN_EQUAL]	= pkgconf_pkg_comparator_gte,
1335*a3cefe7fSPierre Pronchery 		},
1336*a3cefe7fSPierre Pronchery 		.depcmp = {
1337*a3cefe7fSPierre Pronchery 			[PKGCONF_CMP_GREATER_THAN]		= pkgconf_pkg_comparator_lt,
1338*a3cefe7fSPierre Pronchery 			[PKGCONF_CMP_GREATER_THAN_EQUAL]	= pkgconf_pkg_comparator_lt,
1339*a3cefe7fSPierre Pronchery 			[PKGCONF_CMP_EQUAL]			= pkgconf_pkg_comparator_lt,
1340*a3cefe7fSPierre Pronchery 			[PKGCONF_CMP_NOT_EQUAL]			= pkgconf_pkg_comparator_gte,
1341*a3cefe7fSPierre Pronchery 		},
1342*a3cefe7fSPierre Pronchery 	},
1343*a3cefe7fSPierre Pronchery 	[PKGCONF_CMP_GREATER_THAN] = {
1344*a3cefe7fSPierre Pronchery 		.rulecmp = {
1345*a3cefe7fSPierre Pronchery 			[PKGCONF_CMP_ANY]			= pkgconf_pkg_comparator_none,
1346*a3cefe7fSPierre Pronchery 			[PKGCONF_CMP_LESS_THAN]			= pkgconf_pkg_comparator_lt,
1347*a3cefe7fSPierre Pronchery 			[PKGCONF_CMP_GREATER_THAN]		= pkgconf_pkg_comparator_gt,
1348*a3cefe7fSPierre Pronchery 			[PKGCONF_CMP_LESS_THAN_EQUAL]		= pkgconf_pkg_comparator_lte,
1349*a3cefe7fSPierre Pronchery 			[PKGCONF_CMP_GREATER_THAN_EQUAL]	= pkgconf_pkg_comparator_gte,
1350*a3cefe7fSPierre Pronchery 		},
1351*a3cefe7fSPierre Pronchery 		.depcmp = {
1352*a3cefe7fSPierre Pronchery 			[PKGCONF_CMP_LESS_THAN]			= pkgconf_pkg_comparator_gt,
1353*a3cefe7fSPierre Pronchery 			[PKGCONF_CMP_LESS_THAN_EQUAL]		= pkgconf_pkg_comparator_gt,
1354*a3cefe7fSPierre Pronchery 			[PKGCONF_CMP_EQUAL]			= pkgconf_pkg_comparator_gt,
1355*a3cefe7fSPierre Pronchery 			[PKGCONF_CMP_NOT_EQUAL]			= pkgconf_pkg_comparator_lte,
1356*a3cefe7fSPierre Pronchery 		},
1357*a3cefe7fSPierre Pronchery 	},
1358*a3cefe7fSPierre Pronchery 	[PKGCONF_CMP_LESS_THAN_EQUAL] = {
1359*a3cefe7fSPierre Pronchery 		.rulecmp = {
1360*a3cefe7fSPierre Pronchery 			[PKGCONF_CMP_ANY]			= pkgconf_pkg_comparator_none,
1361*a3cefe7fSPierre Pronchery 			[PKGCONF_CMP_LESS_THAN]			= pkgconf_pkg_comparator_lt,
1362*a3cefe7fSPierre Pronchery 			[PKGCONF_CMP_GREATER_THAN]		= pkgconf_pkg_comparator_gt,
1363*a3cefe7fSPierre Pronchery 			[PKGCONF_CMP_LESS_THAN_EQUAL]		= pkgconf_pkg_comparator_lte,
1364*a3cefe7fSPierre Pronchery 			[PKGCONF_CMP_GREATER_THAN_EQUAL]	= pkgconf_pkg_comparator_gte,
1365*a3cefe7fSPierre Pronchery 		},
1366*a3cefe7fSPierre Pronchery 		.depcmp = {
1367*a3cefe7fSPierre Pronchery 			[PKGCONF_CMP_GREATER_THAN]		= pkgconf_pkg_comparator_lte,
1368*a3cefe7fSPierre Pronchery 			[PKGCONF_CMP_GREATER_THAN_EQUAL]	= pkgconf_pkg_comparator_lte,
1369*a3cefe7fSPierre Pronchery 			[PKGCONF_CMP_EQUAL]			= pkgconf_pkg_comparator_lte,
1370*a3cefe7fSPierre Pronchery 			[PKGCONF_CMP_NOT_EQUAL]			= pkgconf_pkg_comparator_gt,
1371*a3cefe7fSPierre Pronchery 		},
1372*a3cefe7fSPierre Pronchery 	},
1373*a3cefe7fSPierre Pronchery 	[PKGCONF_CMP_GREATER_THAN_EQUAL] = {
1374*a3cefe7fSPierre Pronchery 		.rulecmp = {
1375*a3cefe7fSPierre Pronchery 			[PKGCONF_CMP_ANY]			= pkgconf_pkg_comparator_none,
1376*a3cefe7fSPierre Pronchery 			[PKGCONF_CMP_LESS_THAN]			= pkgconf_pkg_comparator_lt,
1377*a3cefe7fSPierre Pronchery 			[PKGCONF_CMP_GREATER_THAN]		= pkgconf_pkg_comparator_gt,
1378*a3cefe7fSPierre Pronchery 			[PKGCONF_CMP_LESS_THAN_EQUAL]		= pkgconf_pkg_comparator_lte,
1379*a3cefe7fSPierre Pronchery 			[PKGCONF_CMP_GREATER_THAN_EQUAL]	= pkgconf_pkg_comparator_gte,
1380*a3cefe7fSPierre Pronchery 		},
1381*a3cefe7fSPierre Pronchery 		.depcmp = {
1382*a3cefe7fSPierre Pronchery 			[PKGCONF_CMP_LESS_THAN]			= pkgconf_pkg_comparator_gte,
1383*a3cefe7fSPierre Pronchery 			[PKGCONF_CMP_LESS_THAN_EQUAL]		= pkgconf_pkg_comparator_gte,
1384*a3cefe7fSPierre Pronchery 			[PKGCONF_CMP_EQUAL]			= pkgconf_pkg_comparator_gte,
1385*a3cefe7fSPierre Pronchery 			[PKGCONF_CMP_NOT_EQUAL]			= pkgconf_pkg_comparator_lt,
1386*a3cefe7fSPierre Pronchery 		},
1387*a3cefe7fSPierre Pronchery 	},
1388*a3cefe7fSPierre Pronchery 	[PKGCONF_CMP_EQUAL] = {
1389*a3cefe7fSPierre Pronchery 		.rulecmp = {
1390*a3cefe7fSPierre Pronchery 			[PKGCONF_CMP_ANY]			= pkgconf_pkg_comparator_none,
1391*a3cefe7fSPierre Pronchery 			[PKGCONF_CMP_LESS_THAN]			= pkgconf_pkg_comparator_lt,
1392*a3cefe7fSPierre Pronchery 			[PKGCONF_CMP_GREATER_THAN]		= pkgconf_pkg_comparator_gt,
1393*a3cefe7fSPierre Pronchery 			[PKGCONF_CMP_LESS_THAN_EQUAL]		= pkgconf_pkg_comparator_lte,
1394*a3cefe7fSPierre Pronchery 			[PKGCONF_CMP_GREATER_THAN_EQUAL]	= pkgconf_pkg_comparator_gte,
1395*a3cefe7fSPierre Pronchery 			[PKGCONF_CMP_EQUAL]			= pkgconf_pkg_comparator_eq,
1396*a3cefe7fSPierre Pronchery 			[PKGCONF_CMP_NOT_EQUAL]			= pkgconf_pkg_comparator_ne
1397*a3cefe7fSPierre Pronchery 		},
1398*a3cefe7fSPierre Pronchery 		.depcmp = {
1399*a3cefe7fSPierre Pronchery 			[PKGCONF_CMP_ANY]			= pkgconf_pkg_comparator_none,
1400*a3cefe7fSPierre Pronchery                 },
1401*a3cefe7fSPierre Pronchery 	},
1402*a3cefe7fSPierre Pronchery 	[PKGCONF_CMP_NOT_EQUAL] = {
1403*a3cefe7fSPierre Pronchery 		.rulecmp = {
1404*a3cefe7fSPierre Pronchery 			[PKGCONF_CMP_ANY]			= pkgconf_pkg_comparator_none,
1405*a3cefe7fSPierre Pronchery 			[PKGCONF_CMP_LESS_THAN]			= pkgconf_pkg_comparator_gte,
1406*a3cefe7fSPierre Pronchery 			[PKGCONF_CMP_GREATER_THAN]		= pkgconf_pkg_comparator_lte,
1407*a3cefe7fSPierre Pronchery 			[PKGCONF_CMP_LESS_THAN_EQUAL]		= pkgconf_pkg_comparator_gt,
1408*a3cefe7fSPierre Pronchery 			[PKGCONF_CMP_GREATER_THAN_EQUAL]	= pkgconf_pkg_comparator_lt,
1409*a3cefe7fSPierre Pronchery 			[PKGCONF_CMP_EQUAL]			= pkgconf_pkg_comparator_ne,
1410*a3cefe7fSPierre Pronchery 			[PKGCONF_CMP_NOT_EQUAL]			= pkgconf_pkg_comparator_eq
1411*a3cefe7fSPierre Pronchery 		},
1412*a3cefe7fSPierre Pronchery 		.depcmp = {
1413*a3cefe7fSPierre Pronchery 			[PKGCONF_CMP_ANY]			= pkgconf_pkg_comparator_none,
1414*a3cefe7fSPierre Pronchery                 },
1415*a3cefe7fSPierre Pronchery 	},
1416*a3cefe7fSPierre Pronchery };
1417*a3cefe7fSPierre Pronchery 
1418*a3cefe7fSPierre Pronchery /*
1419*a3cefe7fSPierre Pronchery  * pkgconf_pkg_scan_provides_vercmp(pkgdep, provider)
1420*a3cefe7fSPierre Pronchery  *
1421*a3cefe7fSPierre Pronchery  * compare a provides node against the requested dependency node.
1422*a3cefe7fSPierre Pronchery  *
1423*a3cefe7fSPierre Pronchery  * XXX: maybe handle PKGCONF_CMP_ANY in a versioned comparison
1424*a3cefe7fSPierre Pronchery  */
1425*a3cefe7fSPierre Pronchery static bool
pkgconf_pkg_scan_provides_vercmp(const pkgconf_dependency_t * pkgdep,const pkgconf_dependency_t * provider)1426*a3cefe7fSPierre Pronchery pkgconf_pkg_scan_provides_vercmp(const pkgconf_dependency_t *pkgdep, const pkgconf_dependency_t *provider)
1427*a3cefe7fSPierre Pronchery {
1428*a3cefe7fSPierre Pronchery 	const pkgconf_pkg_provides_vermatch_rule_t *rule = &pkgconf_pkg_provides_vermatch_rules[pkgdep->compare];
1429*a3cefe7fSPierre Pronchery 
1430*a3cefe7fSPierre Pronchery 	if (rule->depcmp[provider->compare] != NULL &&
1431*a3cefe7fSPierre Pronchery 	    !rule->depcmp[provider->compare](provider->version, pkgdep->version))
1432*a3cefe7fSPierre Pronchery 		return false;
1433*a3cefe7fSPierre Pronchery 
1434*a3cefe7fSPierre Pronchery 	if (rule->rulecmp[provider->compare] != NULL &&
1435*a3cefe7fSPierre Pronchery 	    !rule->rulecmp[provider->compare](pkgdep->version, provider->version))
1436*a3cefe7fSPierre Pronchery 		return false;
1437*a3cefe7fSPierre Pronchery 
1438*a3cefe7fSPierre Pronchery 	return true;
1439*a3cefe7fSPierre Pronchery }
1440*a3cefe7fSPierre Pronchery 
1441*a3cefe7fSPierre Pronchery /*
1442*a3cefe7fSPierre Pronchery  * pkgconf_pkg_scan_provides_entry(pkg, ctx)
1443*a3cefe7fSPierre Pronchery  *
1444*a3cefe7fSPierre Pronchery  * attempt to match a single package's Provides rules against the requested dependency node.
1445*a3cefe7fSPierre Pronchery  */
1446*a3cefe7fSPierre Pronchery static bool
pkgconf_pkg_scan_provides_entry(const pkgconf_pkg_t * pkg,const pkgconf_pkg_scan_providers_ctx_t * ctx)1447*a3cefe7fSPierre Pronchery pkgconf_pkg_scan_provides_entry(const pkgconf_pkg_t *pkg, const pkgconf_pkg_scan_providers_ctx_t *ctx)
1448*a3cefe7fSPierre Pronchery {
1449*a3cefe7fSPierre Pronchery 	const pkgconf_dependency_t *pkgdep = ctx->pkgdep;
1450*a3cefe7fSPierre Pronchery 	pkgconf_node_t *node;
1451*a3cefe7fSPierre Pronchery 
1452*a3cefe7fSPierre Pronchery 	PKGCONF_FOREACH_LIST_ENTRY(pkg->provides.head, node)
1453*a3cefe7fSPierre Pronchery 	{
1454*a3cefe7fSPierre Pronchery 		const pkgconf_dependency_t *provider = node->data;
1455*a3cefe7fSPierre Pronchery 		if (!strcmp(provider->package, pkgdep->package))
1456*a3cefe7fSPierre Pronchery 			return pkgconf_pkg_scan_provides_vercmp(pkgdep, provider);
1457*a3cefe7fSPierre Pronchery 	}
1458*a3cefe7fSPierre Pronchery 
1459*a3cefe7fSPierre Pronchery 	return false;
1460*a3cefe7fSPierre Pronchery }
1461*a3cefe7fSPierre Pronchery 
1462*a3cefe7fSPierre Pronchery /*
1463*a3cefe7fSPierre Pronchery  * pkgconf_pkg_scan_providers(client, pkgdep, eflags)
1464*a3cefe7fSPierre Pronchery  *
1465*a3cefe7fSPierre Pronchery  * scan all available packages to see if a Provides rule matches the pkgdep.
1466*a3cefe7fSPierre Pronchery  */
1467*a3cefe7fSPierre Pronchery static pkgconf_pkg_t *
pkgconf_pkg_scan_providers(pkgconf_client_t * client,pkgconf_dependency_t * pkgdep,unsigned int * eflags)1468*a3cefe7fSPierre Pronchery pkgconf_pkg_scan_providers(pkgconf_client_t *client, pkgconf_dependency_t *pkgdep, unsigned int *eflags)
1469*a3cefe7fSPierre Pronchery {
1470*a3cefe7fSPierre Pronchery 	pkgconf_pkg_t *pkg;
1471*a3cefe7fSPierre Pronchery 	pkgconf_pkg_scan_providers_ctx_t ctx = {
1472*a3cefe7fSPierre Pronchery 		.pkgdep = pkgdep,
1473*a3cefe7fSPierre Pronchery 	};
1474*a3cefe7fSPierre Pronchery 
1475*a3cefe7fSPierre Pronchery 	pkg = pkgconf_scan_all(client, &ctx, (pkgconf_pkg_iteration_func_t) pkgconf_pkg_scan_provides_entry);
1476*a3cefe7fSPierre Pronchery 	if (pkg != NULL)
1477*a3cefe7fSPierre Pronchery 	{
1478*a3cefe7fSPierre Pronchery 		pkgdep->match = pkgconf_pkg_ref(client, pkg);
1479*a3cefe7fSPierre Pronchery 		return pkg;
1480*a3cefe7fSPierre Pronchery 	}
1481*a3cefe7fSPierre Pronchery 
1482*a3cefe7fSPierre Pronchery 	if (eflags != NULL)
1483*a3cefe7fSPierre Pronchery 		*eflags |= PKGCONF_PKG_ERRF_PACKAGE_NOT_FOUND;
1484*a3cefe7fSPierre Pronchery 
1485*a3cefe7fSPierre Pronchery 	return NULL;
1486*a3cefe7fSPierre Pronchery }
1487*a3cefe7fSPierre Pronchery 
1488*a3cefe7fSPierre Pronchery /*
1489*a3cefe7fSPierre Pronchery  * !doc
1490*a3cefe7fSPierre Pronchery  *
1491*a3cefe7fSPierre Pronchery  * .. c:function:: pkgconf_pkg_t *pkgconf_pkg_verify_dependency(pkgconf_client_t *client, pkgconf_dependency_t *pkgdep, unsigned int *eflags)
1492*a3cefe7fSPierre Pronchery  *
1493*a3cefe7fSPierre Pronchery  *    Verify a pkgconf_dependency_t node in the depgraph.  If the dependency is solvable,
1494*a3cefe7fSPierre Pronchery  *    return the appropriate ``pkgconf_pkg_t`` object, else ``NULL``.
1495*a3cefe7fSPierre Pronchery  *
1496*a3cefe7fSPierre Pronchery  *    :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution.
1497*a3cefe7fSPierre Pronchery  *    :param pkgconf_dependency_t* pkgdep: The dependency graph node to solve.
1498*a3cefe7fSPierre Pronchery  *    :param uint* eflags: An optional pointer that, if set, will be populated with an error code from the resolver.
1499*a3cefe7fSPierre Pronchery  *    :return: On success, the appropriate ``pkgconf_pkg_t`` object to solve the dependency, else ``NULL``.
1500*a3cefe7fSPierre Pronchery  *    :rtype: pkgconf_pkg_t *
1501*a3cefe7fSPierre Pronchery  */
1502*a3cefe7fSPierre Pronchery pkgconf_pkg_t *
pkgconf_pkg_verify_dependency(pkgconf_client_t * client,pkgconf_dependency_t * pkgdep,unsigned int * eflags)1503*a3cefe7fSPierre Pronchery pkgconf_pkg_verify_dependency(pkgconf_client_t *client, pkgconf_dependency_t *pkgdep, unsigned int *eflags)
1504*a3cefe7fSPierre Pronchery {
1505*a3cefe7fSPierre Pronchery 	pkgconf_pkg_t *pkg = NULL;
1506*a3cefe7fSPierre Pronchery 
1507*a3cefe7fSPierre Pronchery 	if (eflags != NULL)
1508*a3cefe7fSPierre Pronchery 		*eflags = PKGCONF_PKG_ERRF_OK;
1509*a3cefe7fSPierre Pronchery 
1510*a3cefe7fSPierre Pronchery 	PKGCONF_TRACE(client, "trying to verify dependency: %s", pkgdep->package);
1511*a3cefe7fSPierre Pronchery 
1512*a3cefe7fSPierre Pronchery 	if (pkgdep->match != NULL)
1513*a3cefe7fSPierre Pronchery 	{
1514*a3cefe7fSPierre Pronchery 		PKGCONF_TRACE(client, "cached dependency: %s -> %s@%p", pkgdep->package, pkgdep->match->id, pkgdep->match);
1515*a3cefe7fSPierre Pronchery 		return pkgconf_pkg_ref(client, pkgdep->match);
1516*a3cefe7fSPierre Pronchery 	}
1517*a3cefe7fSPierre Pronchery 
1518*a3cefe7fSPierre Pronchery 	pkg = pkgconf_pkg_find(client, pkgdep->package);
1519*a3cefe7fSPierre Pronchery 	if (pkg == NULL)
1520*a3cefe7fSPierre Pronchery 	{
1521*a3cefe7fSPierre Pronchery 		if (client->flags & PKGCONF_PKG_PKGF_SKIP_PROVIDES)
1522*a3cefe7fSPierre Pronchery 		{
1523*a3cefe7fSPierre Pronchery 			if (eflags != NULL)
1524*a3cefe7fSPierre Pronchery 				*eflags |= PKGCONF_PKG_ERRF_PACKAGE_NOT_FOUND;
1525*a3cefe7fSPierre Pronchery 
1526*a3cefe7fSPierre Pronchery 			return NULL;
1527*a3cefe7fSPierre Pronchery 		}
1528*a3cefe7fSPierre Pronchery 
1529*a3cefe7fSPierre Pronchery 		pkg = pkgconf_pkg_scan_providers(client, pkgdep, eflags);
1530*a3cefe7fSPierre Pronchery 	}
1531*a3cefe7fSPierre Pronchery 	else
1532*a3cefe7fSPierre Pronchery 	{
1533*a3cefe7fSPierre Pronchery 		if (pkg->id == NULL)
1534*a3cefe7fSPierre Pronchery 			pkg->id = strdup(pkgdep->package);
1535*a3cefe7fSPierre Pronchery 
1536*a3cefe7fSPierre Pronchery 		if (pkgconf_pkg_comparator_impls[pkgdep->compare](pkg->version, pkgdep->version) != true)
1537*a3cefe7fSPierre Pronchery 		{
1538*a3cefe7fSPierre Pronchery 			if (eflags != NULL)
1539*a3cefe7fSPierre Pronchery 				*eflags |= PKGCONF_PKG_ERRF_PACKAGE_VER_MISMATCH;
1540*a3cefe7fSPierre Pronchery 		}
1541*a3cefe7fSPierre Pronchery 		else
1542*a3cefe7fSPierre Pronchery 			pkgdep->match = pkgconf_pkg_ref(client, pkg);
1543*a3cefe7fSPierre Pronchery 	}
1544*a3cefe7fSPierre Pronchery 
1545*a3cefe7fSPierre Pronchery 	if (pkg != NULL && pkg->why == NULL)
1546*a3cefe7fSPierre Pronchery 		pkg->why = strdup(pkgdep->package);
1547*a3cefe7fSPierre Pronchery 
1548*a3cefe7fSPierre Pronchery 	return pkg;
1549*a3cefe7fSPierre Pronchery }
1550*a3cefe7fSPierre Pronchery 
1551*a3cefe7fSPierre Pronchery /*
1552*a3cefe7fSPierre Pronchery  * !doc
1553*a3cefe7fSPierre Pronchery  *
1554*a3cefe7fSPierre Pronchery  * .. c:function:: unsigned int pkgconf_pkg_verify_graph(pkgconf_client_t *client, pkgconf_pkg_t *root, int depth)
1555*a3cefe7fSPierre Pronchery  *
1556*a3cefe7fSPierre Pronchery  *    Verify the graph dependency nodes are satisfiable by walking the tree using
1557*a3cefe7fSPierre Pronchery  *    ``pkgconf_pkg_traverse()``.
1558*a3cefe7fSPierre Pronchery  *
1559*a3cefe7fSPierre Pronchery  *    :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution.
1560*a3cefe7fSPierre Pronchery  *    :param pkgconf_pkg_t* root: The root entry in the package dependency graph which should contain the top-level dependencies to resolve.
1561*a3cefe7fSPierre Pronchery  *    :param int depth: The maximum allowed depth for dependency resolution.
1562*a3cefe7fSPierre Pronchery  *    :return: On success, ``PKGCONF_PKG_ERRF_OK`` (0), else an error code.
1563*a3cefe7fSPierre Pronchery  *    :rtype: unsigned int
1564*a3cefe7fSPierre Pronchery  */
1565*a3cefe7fSPierre Pronchery unsigned int
pkgconf_pkg_verify_graph(pkgconf_client_t * client,pkgconf_pkg_t * root,int depth)1566*a3cefe7fSPierre Pronchery pkgconf_pkg_verify_graph(pkgconf_client_t *client, pkgconf_pkg_t *root, int depth)
1567*a3cefe7fSPierre Pronchery {
1568*a3cefe7fSPierre Pronchery 	return pkgconf_pkg_traverse(client, root, NULL, NULL, depth, 0);
1569*a3cefe7fSPierre Pronchery }
1570*a3cefe7fSPierre Pronchery 
1571*a3cefe7fSPierre Pronchery static unsigned int
pkgconf_pkg_report_graph_error(pkgconf_client_t * client,pkgconf_pkg_t * parent,pkgconf_pkg_t * pkg,pkgconf_dependency_t * node,unsigned int eflags)1572*a3cefe7fSPierre Pronchery pkgconf_pkg_report_graph_error(pkgconf_client_t *client, pkgconf_pkg_t *parent, pkgconf_pkg_t *pkg, pkgconf_dependency_t *node, unsigned int eflags)
1573*a3cefe7fSPierre Pronchery {
1574*a3cefe7fSPierre Pronchery 	if (eflags & PKGCONF_PKG_ERRF_PACKAGE_NOT_FOUND)
1575*a3cefe7fSPierre Pronchery 	{
1576*a3cefe7fSPierre Pronchery 		if (!(client->flags & PKGCONF_PKG_PKGF_SIMPLIFY_ERRORS) & !client->already_sent_notice)
1577*a3cefe7fSPierre Pronchery 		{
1578*a3cefe7fSPierre Pronchery 			pkgconf_error(client, "Package %s was not found in the pkg-config search path.\n", node->package);
1579*a3cefe7fSPierre Pronchery 			pkgconf_error(client, "Perhaps you should add the directory containing `%s.pc'\n", node->package);
1580*a3cefe7fSPierre Pronchery 			pkgconf_error(client, "to the PKG_CONFIG_PATH environment variable\n");
1581*a3cefe7fSPierre Pronchery 			client->already_sent_notice = true;
1582*a3cefe7fSPierre Pronchery 		}
1583*a3cefe7fSPierre Pronchery 
1584*a3cefe7fSPierre Pronchery 		if (parent->flags & PKGCONF_PKG_PROPF_VIRTUAL)
1585*a3cefe7fSPierre Pronchery 			pkgconf_error(client, "Package '%s' not found\n", node->package);
1586*a3cefe7fSPierre Pronchery 		else
1587*a3cefe7fSPierre Pronchery 			pkgconf_error(client, "Package '%s', required by '%s', not found\n", node->package, parent->id);
1588*a3cefe7fSPierre Pronchery 
1589*a3cefe7fSPierre Pronchery 		pkgconf_audit_log(client, "%s NOT-FOUND\n", node->package);
1590*a3cefe7fSPierre Pronchery 	}
1591*a3cefe7fSPierre Pronchery 	else if (eflags & PKGCONF_PKG_ERRF_PACKAGE_VER_MISMATCH)
1592*a3cefe7fSPierre Pronchery 	{
1593*a3cefe7fSPierre Pronchery 		pkgconf_error(client, "Package dependency requirement '%s %s %s' could not be satisfied.\n",
1594*a3cefe7fSPierre Pronchery 			node->package, pkgconf_pkg_get_comparator(node), node->version);
1595*a3cefe7fSPierre Pronchery 
1596*a3cefe7fSPierre Pronchery 		if (pkg != NULL)
1597*a3cefe7fSPierre Pronchery 			pkgconf_error(client, "Package '%s' has version '%s', required version is '%s %s'\n",
1598*a3cefe7fSPierre Pronchery 				node->package, pkg->version, pkgconf_pkg_get_comparator(node), node->version);
1599*a3cefe7fSPierre Pronchery 	}
1600*a3cefe7fSPierre Pronchery 
1601*a3cefe7fSPierre Pronchery 	if (pkg != NULL)
1602*a3cefe7fSPierre Pronchery 		pkgconf_pkg_unref(client, pkg);
1603*a3cefe7fSPierre Pronchery 
1604*a3cefe7fSPierre Pronchery 	return eflags;
1605*a3cefe7fSPierre Pronchery }
1606*a3cefe7fSPierre Pronchery 
1607*a3cefe7fSPierre Pronchery static inline unsigned int
pkgconf_pkg_walk_list(pkgconf_client_t * client,pkgconf_pkg_t * parent,pkgconf_list_t * deplist,pkgconf_pkg_traverse_func_t func,void * data,int depth,unsigned int skip_flags)1608*a3cefe7fSPierre Pronchery pkgconf_pkg_walk_list(pkgconf_client_t *client,
1609*a3cefe7fSPierre Pronchery 	pkgconf_pkg_t *parent,
1610*a3cefe7fSPierre Pronchery 	pkgconf_list_t *deplist,
1611*a3cefe7fSPierre Pronchery 	pkgconf_pkg_traverse_func_t func,
1612*a3cefe7fSPierre Pronchery 	void *data,
1613*a3cefe7fSPierre Pronchery 	int depth,
1614*a3cefe7fSPierre Pronchery 	unsigned int skip_flags)
1615*a3cefe7fSPierre Pronchery {
1616*a3cefe7fSPierre Pronchery 	unsigned int eflags = PKGCONF_PKG_ERRF_OK;
1617*a3cefe7fSPierre Pronchery 	pkgconf_node_t *node, *next;
1618*a3cefe7fSPierre Pronchery 
1619*a3cefe7fSPierre Pronchery 	parent->flags |= PKGCONF_PKG_PROPF_ANCESTOR;
1620*a3cefe7fSPierre Pronchery 
1621*a3cefe7fSPierre Pronchery 	PKGCONF_FOREACH_LIST_ENTRY_SAFE(deplist->head, next, node)
1622*a3cefe7fSPierre Pronchery 	{
1623*a3cefe7fSPierre Pronchery 		unsigned int eflags_local = PKGCONF_PKG_ERRF_OK;
1624*a3cefe7fSPierre Pronchery 		pkgconf_dependency_t *depnode = node->data;
1625*a3cefe7fSPierre Pronchery 		pkgconf_pkg_t *pkgdep;
1626*a3cefe7fSPierre Pronchery 
1627*a3cefe7fSPierre Pronchery 		if (*depnode->package == '\0')
1628*a3cefe7fSPierre Pronchery 			continue;
1629*a3cefe7fSPierre Pronchery 
1630*a3cefe7fSPierre Pronchery 		pkgdep = pkgconf_pkg_verify_dependency(client, depnode, &eflags_local);
1631*a3cefe7fSPierre Pronchery 
1632*a3cefe7fSPierre Pronchery 		eflags |= eflags_local;
1633*a3cefe7fSPierre Pronchery 		if (eflags_local != PKGCONF_PKG_ERRF_OK && !(client->flags & PKGCONF_PKG_PKGF_SKIP_ERRORS))
1634*a3cefe7fSPierre Pronchery 		{
1635*a3cefe7fSPierre Pronchery 			pkgconf_pkg_report_graph_error(client, parent, pkgdep, depnode, eflags_local);
1636*a3cefe7fSPierre Pronchery 			continue;
1637*a3cefe7fSPierre Pronchery 		}
1638*a3cefe7fSPierre Pronchery 		if (pkgdep == NULL)
1639*a3cefe7fSPierre Pronchery 			continue;
1640*a3cefe7fSPierre Pronchery 
1641*a3cefe7fSPierre Pronchery 		if((pkgdep->flags & PKGCONF_PKG_PROPF_ANCESTOR) != 0)
1642*a3cefe7fSPierre Pronchery 		{
1643*a3cefe7fSPierre Pronchery 			/* In this case we have a circular reference.
1644*a3cefe7fSPierre Pronchery 			 * We break that by deleteing the circular node from the
1645*a3cefe7fSPierre Pronchery 			 * the list, so that we dont create a situation where
1646*a3cefe7fSPierre Pronchery 			 * memory is leaked due to circular ownership.
1647*a3cefe7fSPierre Pronchery 			 * i.e: A owns B owns A
1648*a3cefe7fSPierre Pronchery 			 *
1649*a3cefe7fSPierre Pronchery 			 * TODO(ariadne): Breaking circular references between Requires and Requires.private
1650*a3cefe7fSPierre Pronchery 			 * lists causes problems.  Find a way to refactor the Requires.private list out.
1651*a3cefe7fSPierre Pronchery 			 */
1652*a3cefe7fSPierre Pronchery 			if (!(depnode->flags & PKGCONF_PKG_DEPF_PRIVATE) &&
1653*a3cefe7fSPierre Pronchery 			    !(parent->flags & PKGCONF_PKG_PROPF_VIRTUAL))
1654*a3cefe7fSPierre Pronchery 			{
1655*a3cefe7fSPierre Pronchery 				pkgconf_warn(client, "%s: breaking circular reference (%s -> %s -> %s)\n",
1656*a3cefe7fSPierre Pronchery 					     parent->id, parent->id, pkgdep->id, parent->id);
1657*a3cefe7fSPierre Pronchery 
1658*a3cefe7fSPierre Pronchery 				pkgconf_node_delete(node, deplist);
1659*a3cefe7fSPierre Pronchery 				pkgconf_dependency_unref(client, depnode);
1660*a3cefe7fSPierre Pronchery 			}
1661*a3cefe7fSPierre Pronchery 
1662*a3cefe7fSPierre Pronchery 			goto next;
1663*a3cefe7fSPierre Pronchery 		}
1664*a3cefe7fSPierre Pronchery 
1665*a3cefe7fSPierre Pronchery 		if (skip_flags && (depnode->flags & skip_flags) == skip_flags)
1666*a3cefe7fSPierre Pronchery 			goto next;
1667*a3cefe7fSPierre Pronchery 
1668*a3cefe7fSPierre Pronchery 		pkgconf_audit_log_dependency(client, pkgdep, depnode);
1669*a3cefe7fSPierre Pronchery 
1670*a3cefe7fSPierre Pronchery 		eflags |= pkgconf_pkg_traverse_main(client, pkgdep, func, data, depth - 1, skip_flags);
1671*a3cefe7fSPierre Pronchery next:
1672*a3cefe7fSPierre Pronchery 		pkgconf_pkg_unref(client, pkgdep);
1673*a3cefe7fSPierre Pronchery 	}
1674*a3cefe7fSPierre Pronchery 
1675*a3cefe7fSPierre Pronchery 	parent->flags &= ~PKGCONF_PKG_PROPF_ANCESTOR;
1676*a3cefe7fSPierre Pronchery 
1677*a3cefe7fSPierre Pronchery 	return eflags;
1678*a3cefe7fSPierre Pronchery }
1679*a3cefe7fSPierre Pronchery 
1680*a3cefe7fSPierre Pronchery static inline unsigned int
pkgconf_pkg_walk_conflicts_list(pkgconf_client_t * client,pkgconf_pkg_t * root,pkgconf_list_t * deplist)1681*a3cefe7fSPierre Pronchery pkgconf_pkg_walk_conflicts_list(pkgconf_client_t *client,
1682*a3cefe7fSPierre Pronchery 	pkgconf_pkg_t *root, pkgconf_list_t *deplist)
1683*a3cefe7fSPierre Pronchery {
1684*a3cefe7fSPierre Pronchery 	unsigned int eflags;
1685*a3cefe7fSPierre Pronchery 	pkgconf_node_t *node, *childnode;
1686*a3cefe7fSPierre Pronchery 
1687*a3cefe7fSPierre Pronchery 	PKGCONF_FOREACH_LIST_ENTRY(deplist->head, node)
1688*a3cefe7fSPierre Pronchery 	{
1689*a3cefe7fSPierre Pronchery 		pkgconf_dependency_t *parentnode = node->data;
1690*a3cefe7fSPierre Pronchery 
1691*a3cefe7fSPierre Pronchery 		if (*parentnode->package == '\0')
1692*a3cefe7fSPierre Pronchery 			continue;
1693*a3cefe7fSPierre Pronchery 
1694*a3cefe7fSPierre Pronchery 		PKGCONF_FOREACH_LIST_ENTRY(root->required.head, childnode)
1695*a3cefe7fSPierre Pronchery 		{
1696*a3cefe7fSPierre Pronchery 			pkgconf_pkg_t *pkgdep;
1697*a3cefe7fSPierre Pronchery 			pkgconf_dependency_t *depnode = childnode->data;
1698*a3cefe7fSPierre Pronchery 
1699*a3cefe7fSPierre Pronchery 			if (*depnode->package == '\0' || strcmp(depnode->package, parentnode->package))
1700*a3cefe7fSPierre Pronchery 				continue;
1701*a3cefe7fSPierre Pronchery 
1702*a3cefe7fSPierre Pronchery 			pkgdep = pkgconf_pkg_verify_dependency(client, parentnode, &eflags);
1703*a3cefe7fSPierre Pronchery 			if (eflags == PKGCONF_PKG_ERRF_OK)
1704*a3cefe7fSPierre Pronchery 			{
1705*a3cefe7fSPierre Pronchery 				pkgconf_error(client, "Version '%s' of '%s' conflicts with '%s' due to satisfying conflict rule '%s %s%s%s'.\n",
1706*a3cefe7fSPierre Pronchery 					pkgdep->version, pkgdep->realname, root->realname, parentnode->package, pkgconf_pkg_get_comparator(parentnode),
1707*a3cefe7fSPierre Pronchery 					parentnode->version != NULL ? " " : "", parentnode->version != NULL ? parentnode->version : "");
1708*a3cefe7fSPierre Pronchery 
1709*a3cefe7fSPierre Pronchery 				if (!(client->flags & PKGCONF_PKG_PKGF_SIMPLIFY_ERRORS))
1710*a3cefe7fSPierre Pronchery 				{
1711*a3cefe7fSPierre Pronchery 					pkgconf_error(client, "It may be possible to ignore this conflict and continue, try the\n");
1712*a3cefe7fSPierre Pronchery 					pkgconf_error(client, "PKG_CONFIG_IGNORE_CONFLICTS environment variable.\n");
1713*a3cefe7fSPierre Pronchery 				}
1714*a3cefe7fSPierre Pronchery 
1715*a3cefe7fSPierre Pronchery 				pkgconf_pkg_unref(client, pkgdep);
1716*a3cefe7fSPierre Pronchery 
1717*a3cefe7fSPierre Pronchery 				return PKGCONF_PKG_ERRF_PACKAGE_CONFLICT;
1718*a3cefe7fSPierre Pronchery 			}
1719*a3cefe7fSPierre Pronchery 
1720*a3cefe7fSPierre Pronchery 			pkgconf_pkg_unref(client, pkgdep);
1721*a3cefe7fSPierre Pronchery 		}
1722*a3cefe7fSPierre Pronchery 	}
1723*a3cefe7fSPierre Pronchery 
1724*a3cefe7fSPierre Pronchery 	return PKGCONF_PKG_ERRF_OK;
1725*a3cefe7fSPierre Pronchery }
1726*a3cefe7fSPierre Pronchery 
1727*a3cefe7fSPierre Pronchery /*
1728*a3cefe7fSPierre Pronchery  * !doc
1729*a3cefe7fSPierre Pronchery  *
1730*a3cefe7fSPierre Pronchery  * .. 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)
1731*a3cefe7fSPierre Pronchery  *
1732*a3cefe7fSPierre Pronchery  *    Walk and resolve the dependency graph up to `maxdepth` levels.
1733*a3cefe7fSPierre Pronchery  *
1734*a3cefe7fSPierre Pronchery  *    :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution.
1735*a3cefe7fSPierre Pronchery  *    :param pkgconf_pkg_t* root: The root of the dependency graph.
1736*a3cefe7fSPierre Pronchery  *    :param pkgconf_pkg_traverse_func_t func: A traversal function to call for each resolved node in the dependency graph.
1737*a3cefe7fSPierre Pronchery  *    :param void* data: An opaque pointer to data to be passed to the traversal function.
1738*a3cefe7fSPierre Pronchery  *    :param int maxdepth: The maximum depth to walk the dependency graph for.  -1 means infinite recursion.
1739*a3cefe7fSPierre Pronchery  *    :param uint skip_flags: Skip over dependency nodes containing the specified flags.  A setting of 0 skips no dependency nodes.
1740*a3cefe7fSPierre Pronchery  *    :return: ``PKGCONF_PKG_ERRF_OK`` on success, else an error code.
1741*a3cefe7fSPierre Pronchery  *    :rtype: unsigned int
1742*a3cefe7fSPierre Pronchery  */
1743*a3cefe7fSPierre Pronchery static unsigned int
pkgconf_pkg_traverse_main(pkgconf_client_t * client,pkgconf_pkg_t * root,pkgconf_pkg_traverse_func_t func,void * data,int maxdepth,unsigned int skip_flags)1744*a3cefe7fSPierre Pronchery pkgconf_pkg_traverse_main(pkgconf_client_t *client,
1745*a3cefe7fSPierre Pronchery 	pkgconf_pkg_t *root,
1746*a3cefe7fSPierre Pronchery 	pkgconf_pkg_traverse_func_t func,
1747*a3cefe7fSPierre Pronchery 	void *data,
1748*a3cefe7fSPierre Pronchery 	int maxdepth,
1749*a3cefe7fSPierre Pronchery 	unsigned int skip_flags)
1750*a3cefe7fSPierre Pronchery {
1751*a3cefe7fSPierre Pronchery 	unsigned int eflags = PKGCONF_PKG_ERRF_OK;
1752*a3cefe7fSPierre Pronchery 
1753*a3cefe7fSPierre Pronchery 	if (maxdepth == 0)
1754*a3cefe7fSPierre Pronchery 		return eflags;
1755*a3cefe7fSPierre Pronchery 
1756*a3cefe7fSPierre Pronchery 	/* Short-circuit if we have already visited this node.
1757*a3cefe7fSPierre Pronchery 	 */
1758*a3cefe7fSPierre Pronchery 	if (root->serial == client->serial)
1759*a3cefe7fSPierre Pronchery 		return eflags;
1760*a3cefe7fSPierre Pronchery 
1761*a3cefe7fSPierre Pronchery 	root->serial = client->serial;
1762*a3cefe7fSPierre Pronchery 
1763*a3cefe7fSPierre Pronchery 	if (root->identifier == 0)
1764*a3cefe7fSPierre Pronchery 		root->identifier = ++client->identifier;
1765*a3cefe7fSPierre Pronchery 
1766*a3cefe7fSPierre Pronchery 	PKGCONF_TRACE(client, "%s: level %d, serial %"PRIu64, root->id, maxdepth, client->serial);
1767*a3cefe7fSPierre Pronchery 
1768*a3cefe7fSPierre Pronchery 	if ((root->flags & PKGCONF_PKG_PROPF_VIRTUAL) != PKGCONF_PKG_PROPF_VIRTUAL || (client->flags & PKGCONF_PKG_PKGF_SKIP_ROOT_VIRTUAL) != PKGCONF_PKG_PKGF_SKIP_ROOT_VIRTUAL)
1769*a3cefe7fSPierre Pronchery 	{
1770*a3cefe7fSPierre Pronchery 		if (func != NULL)
1771*a3cefe7fSPierre Pronchery 			func(client, root, data);
1772*a3cefe7fSPierre Pronchery 	}
1773*a3cefe7fSPierre Pronchery 
1774*a3cefe7fSPierre Pronchery 	if (!(client->flags & PKGCONF_PKG_PKGF_SKIP_CONFLICTS) && root->conflicts.head != NULL)
1775*a3cefe7fSPierre Pronchery 	{
1776*a3cefe7fSPierre Pronchery 		PKGCONF_TRACE(client, "%s: walking 'Conflicts' list", root->id);
1777*a3cefe7fSPierre Pronchery 
1778*a3cefe7fSPierre Pronchery 		eflags = pkgconf_pkg_walk_conflicts_list(client, root, &root->conflicts);
1779*a3cefe7fSPierre Pronchery 		if (eflags != PKGCONF_PKG_ERRF_OK)
1780*a3cefe7fSPierre Pronchery 			return eflags;
1781*a3cefe7fSPierre Pronchery 	}
1782*a3cefe7fSPierre Pronchery 
1783*a3cefe7fSPierre Pronchery 	PKGCONF_TRACE(client, "%s: walking 'Requires' list", root->id);
1784*a3cefe7fSPierre Pronchery 	eflags = pkgconf_pkg_walk_list(client, root, &root->required, func, data, maxdepth, skip_flags);
1785*a3cefe7fSPierre Pronchery 	if (eflags != PKGCONF_PKG_ERRF_OK)
1786*a3cefe7fSPierre Pronchery 		return eflags;
1787*a3cefe7fSPierre Pronchery 
1788*a3cefe7fSPierre Pronchery 	PKGCONF_TRACE(client, "%s: walking 'Requires.private' list", root->id);
1789*a3cefe7fSPierre Pronchery 
1790*a3cefe7fSPierre Pronchery 	/* XXX: ugly */
1791*a3cefe7fSPierre Pronchery 	client->flags |= PKGCONF_PKG_PKGF_ITER_PKG_IS_PRIVATE;
1792*a3cefe7fSPierre Pronchery 	eflags = pkgconf_pkg_walk_list(client, root, &root->requires_private, func, data, maxdepth, skip_flags);
1793*a3cefe7fSPierre Pronchery 	client->flags &= ~PKGCONF_PKG_PKGF_ITER_PKG_IS_PRIVATE;
1794*a3cefe7fSPierre Pronchery 
1795*a3cefe7fSPierre Pronchery 	if (eflags != PKGCONF_PKG_ERRF_OK)
1796*a3cefe7fSPierre Pronchery 		return eflags;
1797*a3cefe7fSPierre Pronchery 
1798*a3cefe7fSPierre Pronchery 	return eflags;
1799*a3cefe7fSPierre Pronchery }
1800*a3cefe7fSPierre Pronchery 
1801*a3cefe7fSPierre Pronchery 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)1802*a3cefe7fSPierre Pronchery pkgconf_pkg_traverse(pkgconf_client_t *client,
1803*a3cefe7fSPierre Pronchery 	pkgconf_pkg_t *root,
1804*a3cefe7fSPierre Pronchery 	pkgconf_pkg_traverse_func_t func,
1805*a3cefe7fSPierre Pronchery 	void *data,
1806*a3cefe7fSPierre Pronchery 	int maxdepth,
1807*a3cefe7fSPierre Pronchery 	unsigned int skip_flags)
1808*a3cefe7fSPierre Pronchery {
1809*a3cefe7fSPierre Pronchery 	if (root->flags & PKGCONF_PKG_PROPF_VIRTUAL)
1810*a3cefe7fSPierre Pronchery 		client->serial++;
1811*a3cefe7fSPierre Pronchery 
1812*a3cefe7fSPierre Pronchery 	if ((client->flags & PKGCONF_PKG_PKGF_SEARCH_PRIVATE) == 0)
1813*a3cefe7fSPierre Pronchery 		skip_flags |= PKGCONF_PKG_DEPF_PRIVATE;
1814*a3cefe7fSPierre Pronchery 
1815*a3cefe7fSPierre Pronchery 	return pkgconf_pkg_traverse_main(client, root, func, data, maxdepth, skip_flags);
1816*a3cefe7fSPierre Pronchery }
1817*a3cefe7fSPierre Pronchery 
1818*a3cefe7fSPierre Pronchery static void
pkgconf_pkg_cflags_collect(pkgconf_client_t * client,pkgconf_pkg_t * pkg,void * data)1819*a3cefe7fSPierre Pronchery pkgconf_pkg_cflags_collect(pkgconf_client_t *client, pkgconf_pkg_t *pkg, void *data)
1820*a3cefe7fSPierre Pronchery {
1821*a3cefe7fSPierre Pronchery 	pkgconf_list_t *list = data;
1822*a3cefe7fSPierre Pronchery 	pkgconf_node_t *node;
1823*a3cefe7fSPierre Pronchery 
1824*a3cefe7fSPierre Pronchery 	PKGCONF_FOREACH_LIST_ENTRY(pkg->cflags.head, node)
1825*a3cefe7fSPierre Pronchery 	{
1826*a3cefe7fSPierre Pronchery 		pkgconf_fragment_t *frag = node->data;
1827*a3cefe7fSPierre Pronchery 		pkgconf_fragment_copy(client, list, frag, false);
1828*a3cefe7fSPierre Pronchery 	}
1829*a3cefe7fSPierre Pronchery }
1830*a3cefe7fSPierre Pronchery 
1831*a3cefe7fSPierre Pronchery static void
pkgconf_pkg_cflags_private_collect(pkgconf_client_t * client,pkgconf_pkg_t * pkg,void * data)1832*a3cefe7fSPierre Pronchery pkgconf_pkg_cflags_private_collect(pkgconf_client_t *client, pkgconf_pkg_t *pkg, void *data)
1833*a3cefe7fSPierre Pronchery {
1834*a3cefe7fSPierre Pronchery 	pkgconf_list_t *list = data;
1835*a3cefe7fSPierre Pronchery 	pkgconf_node_t *node;
1836*a3cefe7fSPierre Pronchery 
1837*a3cefe7fSPierre Pronchery 	PKGCONF_FOREACH_LIST_ENTRY(pkg->cflags_private.head, node)
1838*a3cefe7fSPierre Pronchery 	{
1839*a3cefe7fSPierre Pronchery 		pkgconf_fragment_t *frag = node->data;
1840*a3cefe7fSPierre Pronchery 		pkgconf_fragment_copy(client, list, frag, true);
1841*a3cefe7fSPierre Pronchery 	}
1842*a3cefe7fSPierre Pronchery }
1843*a3cefe7fSPierre Pronchery 
1844*a3cefe7fSPierre Pronchery /*
1845*a3cefe7fSPierre Pronchery  * !doc
1846*a3cefe7fSPierre Pronchery  *
1847*a3cefe7fSPierre Pronchery  * .. c:function:: int pkgconf_pkg_cflags(pkgconf_client_t *client, pkgconf_pkg_t *root, pkgconf_list_t *list, int maxdepth)
1848*a3cefe7fSPierre Pronchery  *
1849*a3cefe7fSPierre Pronchery  *    Walks a dependency graph and extracts relevant ``CFLAGS`` fragments.
1850*a3cefe7fSPierre Pronchery  *
1851*a3cefe7fSPierre Pronchery  *    :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution.
1852*a3cefe7fSPierre Pronchery  *    :param pkgconf_pkg_t* root: The root of the dependency graph.
1853*a3cefe7fSPierre Pronchery  *    :param pkgconf_list_t* list: The fragment list to add the extracted ``CFLAGS`` fragments to.
1854*a3cefe7fSPierre Pronchery  *    :param int maxdepth: The maximum allowed depth for dependency resolution.  -1 means infinite recursion.
1855*a3cefe7fSPierre Pronchery  *    :return: ``PKGCONF_PKG_ERRF_OK`` if successful, otherwise an error code.
1856*a3cefe7fSPierre Pronchery  *    :rtype: unsigned int
1857*a3cefe7fSPierre Pronchery  */
1858*a3cefe7fSPierre Pronchery unsigned int
pkgconf_pkg_cflags(pkgconf_client_t * client,pkgconf_pkg_t * root,pkgconf_list_t * list,int maxdepth)1859*a3cefe7fSPierre Pronchery pkgconf_pkg_cflags(pkgconf_client_t *client, pkgconf_pkg_t *root, pkgconf_list_t *list, int maxdepth)
1860*a3cefe7fSPierre Pronchery {
1861*a3cefe7fSPierre Pronchery 	unsigned int eflag;
1862*a3cefe7fSPierre Pronchery 	unsigned int skip_flags = (client->flags & PKGCONF_PKG_PKGF_DONT_FILTER_INTERNAL_CFLAGS) == 0 ? PKGCONF_PKG_DEPF_INTERNAL : 0;
1863*a3cefe7fSPierre Pronchery 	pkgconf_list_t frags = PKGCONF_LIST_INITIALIZER;
1864*a3cefe7fSPierre Pronchery 
1865*a3cefe7fSPierre Pronchery 	eflag = pkgconf_pkg_traverse(client, root, pkgconf_pkg_cflags_collect, &frags, maxdepth, skip_flags);
1866*a3cefe7fSPierre Pronchery 
1867*a3cefe7fSPierre Pronchery 	if (eflag == PKGCONF_PKG_ERRF_OK && client->flags & PKGCONF_PKG_PKGF_MERGE_PRIVATE_FRAGMENTS)
1868*a3cefe7fSPierre Pronchery 		eflag = pkgconf_pkg_traverse(client, root, pkgconf_pkg_cflags_private_collect, &frags, maxdepth, skip_flags);
1869*a3cefe7fSPierre Pronchery 
1870*a3cefe7fSPierre Pronchery 	if (eflag != PKGCONF_PKG_ERRF_OK)
1871*a3cefe7fSPierre Pronchery 	{
1872*a3cefe7fSPierre Pronchery 		pkgconf_fragment_free(&frags);
1873*a3cefe7fSPierre Pronchery 		return eflag;
1874*a3cefe7fSPierre Pronchery 	}
1875*a3cefe7fSPierre Pronchery 
1876*a3cefe7fSPierre Pronchery 	pkgconf_fragment_copy_list(client, list, &frags);
1877*a3cefe7fSPierre Pronchery 	pkgconf_fragment_free(&frags);
1878*a3cefe7fSPierre Pronchery 
1879*a3cefe7fSPierre Pronchery 	return eflag;
1880*a3cefe7fSPierre Pronchery }
1881*a3cefe7fSPierre Pronchery 
1882*a3cefe7fSPierre Pronchery static void
pkgconf_pkg_libs_collect(pkgconf_client_t * client,pkgconf_pkg_t * pkg,void * data)1883*a3cefe7fSPierre Pronchery pkgconf_pkg_libs_collect(pkgconf_client_t *client, pkgconf_pkg_t *pkg, void *data)
1884*a3cefe7fSPierre Pronchery {
1885*a3cefe7fSPierre Pronchery 	pkgconf_list_t *list = data;
1886*a3cefe7fSPierre Pronchery 	pkgconf_node_t *node;
1887*a3cefe7fSPierre Pronchery 
1888*a3cefe7fSPierre Pronchery 	if (!(client->flags & PKGCONF_PKG_PKGF_SEARCH_PRIVATE) && pkg->flags & PKGCONF_PKG_PROPF_VISITED_PRIVATE)
1889*a3cefe7fSPierre Pronchery 		return;
1890*a3cefe7fSPierre Pronchery 
1891*a3cefe7fSPierre Pronchery 	PKGCONF_FOREACH_LIST_ENTRY(pkg->libs.head, node)
1892*a3cefe7fSPierre Pronchery 	{
1893*a3cefe7fSPierre Pronchery 		pkgconf_fragment_t *frag = node->data;
1894*a3cefe7fSPierre Pronchery 		pkgconf_fragment_copy(client, list, frag, (client->flags & PKGCONF_PKG_PKGF_ITER_PKG_IS_PRIVATE) != 0);
1895*a3cefe7fSPierre Pronchery 	}
1896*a3cefe7fSPierre Pronchery 
1897*a3cefe7fSPierre Pronchery 	if (client->flags & PKGCONF_PKG_PKGF_MERGE_PRIVATE_FRAGMENTS)
1898*a3cefe7fSPierre Pronchery 	{
1899*a3cefe7fSPierre Pronchery 		PKGCONF_FOREACH_LIST_ENTRY(pkg->libs_private.head, node)
1900*a3cefe7fSPierre Pronchery 		{
1901*a3cefe7fSPierre Pronchery 			pkgconf_fragment_t *frag = node->data;
1902*a3cefe7fSPierre Pronchery 			pkgconf_fragment_copy(client, list, frag, true);
1903*a3cefe7fSPierre Pronchery 		}
1904*a3cefe7fSPierre Pronchery 	}
1905*a3cefe7fSPierre Pronchery }
1906*a3cefe7fSPierre Pronchery 
1907*a3cefe7fSPierre Pronchery /*
1908*a3cefe7fSPierre Pronchery  * !doc
1909*a3cefe7fSPierre Pronchery  *
1910*a3cefe7fSPierre Pronchery  * .. c:function:: int pkgconf_pkg_libs(pkgconf_client_t *client, pkgconf_pkg_t *root, pkgconf_list_t *list, int maxdepth)
1911*a3cefe7fSPierre Pronchery  *
1912*a3cefe7fSPierre Pronchery  *    Walks a dependency graph and extracts relevant ``LIBS`` fragments.
1913*a3cefe7fSPierre Pronchery  *
1914*a3cefe7fSPierre Pronchery  *    :param pkgconf_client_t* client: The pkgconf client object to use for dependency resolution.
1915*a3cefe7fSPierre Pronchery  *    :param pkgconf_pkg_t* root: The root of the dependency graph.
1916*a3cefe7fSPierre Pronchery  *    :param pkgconf_list_t* list: The fragment list to add the extracted ``LIBS`` fragments to.
1917*a3cefe7fSPierre Pronchery  *    :param int maxdepth: The maximum allowed depth for dependency resolution.  -1 means infinite recursion.
1918*a3cefe7fSPierre Pronchery  *    :return: ``PKGCONF_PKG_ERRF_OK`` if successful, otherwise an error code.
1919*a3cefe7fSPierre Pronchery  *    :rtype: unsigned int
1920*a3cefe7fSPierre Pronchery  */
1921*a3cefe7fSPierre Pronchery unsigned int
pkgconf_pkg_libs(pkgconf_client_t * client,pkgconf_pkg_t * root,pkgconf_list_t * list,int maxdepth)1922*a3cefe7fSPierre Pronchery pkgconf_pkg_libs(pkgconf_client_t *client, pkgconf_pkg_t *root, pkgconf_list_t *list, int maxdepth)
1923*a3cefe7fSPierre Pronchery {
1924*a3cefe7fSPierre Pronchery 	unsigned int eflag;
1925*a3cefe7fSPierre Pronchery 
1926*a3cefe7fSPierre Pronchery 	eflag = pkgconf_pkg_traverse(client, root, pkgconf_pkg_libs_collect, list, maxdepth, 0);
1927*a3cefe7fSPierre Pronchery 
1928*a3cefe7fSPierre Pronchery 	if (eflag != PKGCONF_PKG_ERRF_OK)
1929*a3cefe7fSPierre Pronchery 	{
1930*a3cefe7fSPierre Pronchery 		pkgconf_fragment_free(list);
1931*a3cefe7fSPierre Pronchery 		return eflag;
1932*a3cefe7fSPierre Pronchery 	}
1933*a3cefe7fSPierre Pronchery 
1934*a3cefe7fSPierre Pronchery 	return eflag;
1935*a3cefe7fSPierre Pronchery }
1936