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