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