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