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