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