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