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