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