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