version.c
1 /*- 2 * Copyright (c) 2011-2023 Baptiste Daroussin <bapt@FreeBSD.org> 3 * Copyright (c) 2011-2012 Julien Laffaye <jlaffaye@FreeBSD.org> 4 * Copyright (c) 2011 Philippe Pepiot <phil@philpep.org> 5 * Copyright (c) 2011-2012 Marin Atanasov Nikolov <dnaeon@gmail.com> 6 * Copyright (c) 2012 Bryan Drewery <bryan@shatow.net> 7 * Copyright (c) 2013-2014 Matthew Seaman <matthew@FreeBSD.org> 8 * All rights reserved. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer 15 * in this position and unchanged. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 23 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <sys/param.h> 33 #include <sys/utsname.h> 34 #include <sys/wait.h> 35 36 #include <err.h> 37 #include <errno.h> 38 #include <getopt.h> 39 #include <fcntl.h> 40 #include <pkg.h> 41 #include <stdbool.h> 42 #include <stdio.h> 43 #include <stdlib.h> 44 #include <string.h> 45 #include <unistd.h> 46 #include <fnmatch.h> 47 #include <spawn.h> 48 #include <sys/types.h> 49 #include <sys/stat.h> 50 #include <pkghash.h> 51 #include <xmalloc.h> 52 53 #include "pkgcli.h" 54 55 extern char **environ; 56 57 struct index_entry { 58 char *name; 59 char *version; 60 }; 61 62 struct category { 63 char *name; 64 pkghash *ports; 65 }; 66 67 pkghash *categories = NULL; 68 69 void 70 usage_version(void) 71 { 72 fprintf(stderr, "Usage: pkg version [-IPR] [-hoqvU] [-l limchar] [-L limchar] [-Cegix pattern]\n"); 73 fprintf(stderr, " [-r reponame] [-O origin|-n pkgname] [index]\n"); 74 fprintf(stderr, " pkg version -t <version1> <version2>\n"); 75 fprintf(stderr, " pkg version -T <pkgname> <pattern>\n\n"); 76 fprintf(stderr, "For more information see 'pkg help version'.\n"); 77 } 78 79 static void 80 print_version(struct pkg *pkg, const char *source, const char *ver, 81 char limchar, unsigned int opt) 82 { 83 char key; 84 const char *version = NULL; 85 int cout; 86 87 pkg_get(pkg, PKG_ATTR_VERSION, &version); 88 if (ver == NULL) { 89 if (source == NULL) 90 key = '!'; 91 else 92 key = '?'; 93 } else { 94 switch (pkg_version_cmp(version, ver)) { 95 case -1: 96 key = '<'; 97 break; 98 case 0: 99 key = '='; 100 break; 101 case 1: 102 key = '>'; 103 break; 104 default: 105 key = '!'; 106 break; 107 } 108 } 109 110 if ((opt & VERSION_STATUS) && limchar != key) 111 return; 112 113 if ((opt & VERSION_NOSTATUS) && limchar == key) 114 return; 115 116 if (opt & VERSION_ORIGIN) { 117 pkg_printf("%-34o", pkg); 118 printf("%c", key); 119 } 120 else { 121 cout = pkg_printf("%n-%v", pkg, pkg); 122 cout = 35 - cout; 123 if (cout < 1) 124 cout = 1; 125 printf("%*s%c", cout, " ", key); 126 } 127 128 if (opt & VERSION_VERBOSE) { 129 switch (key) { 130 case '<': 131 printf(" needs updating (%s has %s)", source, ver); 132 break; 133 case '=': 134 printf(" up-to-date with %s", source); 135 break; 136 case '>': 137 printf(" succeeds %s (%s has %s)", source, source, ver); 138 break; 139 case '?': 140 pkg_printf(" orphaned: %o", pkg); 141 break; 142 case '!': 143 default: 144 printf(" Comparison failed"); 145 break; 146 } 147 } 148 149 putchar('\n'); 150 } 151 152 static int 153 do_testversion(unsigned int opt, int argc, char ** restrict argv) 154 { 155 /* -t must be unique and takes two arguments */ 156 if ( opt != VERSION_TESTVERSION || argc < 2 ) { 157 usage_version(); 158 return (EXIT_FAILURE); 159 } 160 161 switch (pkg_version_cmp(argv[0], argv[1])) { 162 case -1: 163 printf("<\n"); 164 break; 165 case 0: 166 printf("=\n"); 167 break; 168 case 1: 169 printf(">\n"); 170 break; 171 } 172 173 return (EXIT_SUCCESS); 174 } 175 176 static int 177 do_testpattern(unsigned int opt, int argc, char ** restrict argv) 178 { 179 bool pattern_from_stdin = false; 180 bool pkgname_from_stdin = false; 181 char *line = NULL; 182 size_t linecap = 0; 183 ssize_t linelen; 184 int retval = FNM_NOMATCH; 185 186 /* -T must be unique and takes two arguments */ 187 if ( opt != VERSION_TESTPATTERN || argc < 2 ) { 188 usage_version(); 189 return (EXIT_FAILURE); 190 } 191 192 if (argv[0][0] == '-') 193 pattern_from_stdin = true; 194 195 if (argv[1][0] == '-') 196 pkgname_from_stdin = true; 197 198 if (pattern_from_stdin && pkgname_from_stdin) { 199 usage_version(); 200 return (EXIT_FAILURE); 201 } 202 203 if (!pattern_from_stdin && !pkgname_from_stdin) 204 return (fnmatch(argv[1], argv[0], 0)); 205 206 while ((linelen = getline(&line, &linecap, stdin)) > 0) { 207 line[linelen - 1] = '\0'; /* Strip trailing newline */ 208 209 if ((pattern_from_stdin && (fnmatch(argv[1], line, 0) == 0)) || 210 (pkgname_from_stdin && (fnmatch(line, argv[0], 0) == 0))) { 211 retval = EPKG_OK; 212 printf("%.*s\n", (int)linelen, line); 213 } 214 } 215 216 free(line); 217 218 return (retval); 219 } 220 221 static bool 222 have_ports(const char **portsdir, bool show_error) 223 { 224 char portsdirmakefile[MAXPATHLEN]; 225 struct stat sb; 226 bool have_ports; 227 228 /* Look for Makefile within $PORTSDIR as indicative of 229 * installed ports tree. */ 230 231 *portsdir = pkg_object_string(pkg_config_get("PORTSDIR")); 232 if (*portsdir == NULL) 233 err(1, "Cannot get portsdir config entry!"); 234 235 snprintf(portsdirmakefile, sizeof(portsdirmakefile), 236 "%s/Makefile", *portsdir); 237 238 have_ports = (stat(portsdirmakefile, &sb) == 0 && S_ISREG(sb.st_mode)); 239 240 if (show_error && !have_ports) 241 warnx("Cannot find ports tree: unable to open %s", 242 portsdirmakefile); 243 244 return (have_ports); 245 } 246 247 static const char* 248 indexfilename(char *filebuf, size_t filebuflen) 249 { 250 const char *indexdir; 251 const char *indexfile; 252 253 /* Construct the canonical name of the indexfile from the 254 * ports directory and the major version number of the OS. 255 * Overridden by INDEXDIR and INDEXFILE if defined. (Mimics 256 * the behaviour of ${PORTSDIR}/Makefile) */ 257 258 indexdir = pkg_object_string(pkg_config_get("INDEXDIR")); 259 if (indexdir == NULL) { 260 indexdir = pkg_object_string(pkg_config_get("PORTSDIR")); 261 262 if (indexdir == NULL) 263 err(EXIT_FAILURE, "Cannot get either INDEXDIR or " 264 "PORTSDIR config entry!"); 265 } 266 267 indexfile = pkg_object_string(pkg_config_get("INDEXFILE")); 268 if (indexfile == NULL) 269 err(EXIT_FAILURE, "Cannot get INDEXFILE config entry!"); 270 271 strlcpy(filebuf, indexdir, filebuflen); 272 273 if (filebuf[0] != '\0' && filebuf[strlen(filebuf) - 1] != '/') 274 strlcat(filebuf, "/", filebuflen); 275 276 strlcat(filebuf, indexfile, filebuflen); 277 278 return (filebuf); 279 } 280 281 static pkghash * 282 hash_indexfile(const char *indexfilename) 283 { 284 FILE *indexfile; 285 pkghash *index = NULL; 286 struct index_entry *entry; 287 char *version, *name; 288 char *line = NULL, *l; 289 size_t linecap = 0; 290 291 292 /* Create a hash table of all the package names and port 293 * directories from the index file. */ 294 295 indexfile = fopen(indexfilename, "re"); 296 if (!indexfile) 297 err(EXIT_FAILURE, "Unable to open %s", indexfilename); 298 299 while (getline(&line, &linecap, indexfile) > 0) { 300 /* line is pkgname|portdir|... */ 301 302 l = line; 303 304 version = strsep(&l, "|"); 305 name = version; 306 version = strrchr(version, '-'); 307 if (version == NULL) 308 errx(EXIT_FAILURE, "Invalid INDEX file format: %s", 309 indexfilename); 310 version[0] = '\0'; 311 version++; 312 313 entry = xmalloc(sizeof(struct index_entry)); 314 entry->name = xstrdup(name); 315 entry->version = xstrdup(version); 316 317 if (index == NULL) 318 index = pkghash_new(); 319 320 if (!pkghash_add(index, entry->name, entry, NULL)) { 321 free(entry->version); 322 free(entry->name); 323 free(entry); 324 } 325 } 326 327 free(line); 328 fclose(indexfile); 329 330 if (index == NULL) 331 errx(EXIT_FAILURE, "No valid entries found in '%s'", 332 indexfilename); 333 334 return (index); 335 } 336 337 static void 338 free_categories(void) 339 { 340 struct category *cat; 341 pkghash_it it; 342 343 it = pkghash_iterator(categories); 344 while (pkghash_next(&it)) { 345 cat = (struct category *) it.value; 346 free(cat->name); 347 pkghash_destroy(cat->ports); 348 free(cat); 349 } 350 pkghash_destroy(categories); 351 } 352 353 static void 354 free_index(pkghash *index) 355 { 356 pkghash_it it; 357 struct index_entry *entry; 358 359 it = pkghash_iterator(index); 360 while (pkghash_next(&it)) { 361 entry = (struct index_entry *)it.value; 362 free(entry->version); 363 free(entry->name); 364 free(entry); 365 } 366 pkghash_destroy(index); 367 } 368 369 static bool 370 have_indexfile(const char **indexfile, char *filebuf, size_t filebuflen, 371 int argc, char ** restrict argv, bool show_error) 372 { 373 bool have_indexfile = true; 374 struct stat sb; 375 376 /* If there is a remaining command line argument, take 377 that as the name of the INDEX file to use. Otherwise, 378 search for INDEX-N within the ports tree */ 379 380 if (argc == 0) 381 *indexfile = indexfilename(filebuf, filebuflen); 382 else 383 *indexfile = argv[0]; 384 385 if (stat(*indexfile, &sb) == -1) { 386 if (errno == ENOENT) 387 have_indexfile = false; 388 else 389 warn("Failed to get stat for the INDEX file!"); 390 } 391 392 if (show_error && !have_indexfile) 393 warn("Can't access %s", *indexfile); 394 395 return (have_indexfile); 396 } 397 398 static int 399 do_source_index(unsigned int opt, char limchar, char *pattern, match_t match, 400 const char *matchorigin, const char *matchname, const char *indexfile) 401 { 402 pkghash *index; 403 struct index_entry *ie; 404 struct pkgdb *db = NULL; 405 struct pkgdb_it *it = NULL; 406 struct pkg *pkg = NULL; 407 const char *name = NULL; 408 const char *origin = NULL; 409 bool gotnone = true; 410 411 if ( (opt & VERSION_SOURCES) != VERSION_SOURCE_INDEX) { 412 usage_version(); 413 return (EXIT_FAILURE); 414 } 415 416 if (pkgdb_open(&db, PKGDB_DEFAULT_READONLY) != EPKG_OK) 417 return (EXIT_FAILURE); 418 419 index = hash_indexfile(indexfile); 420 421 if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY) != EPKG_OK) { 422 pkgdb_close(db); 423 free_index(index); 424 warnx("Cannot get a read lock on the database. " 425 "It is locked by another process"); 426 return (EXIT_FAILURE); 427 } 428 429 it = pkgdb_query(db, pattern, match); 430 if (it == NULL) 431 goto cleanup; 432 433 while (pkgdb_it_next(it, &pkg, PKG_LOAD_BASIC) == EPKG_OK) { 434 pkg_get(pkg, PKG_ATTR_NAME, &name); 435 pkg_get(pkg, PKG_ATTR_ORIGIN, &origin); 436 437 /* If -O was specified, check if this origin matches */ 438 if ((opt & VERSION_WITHORIGIN) && 439 !STREQ(origin, matchorigin)) 440 continue; 441 442 /* If -n was specified, check if this name matches */ 443 if ((opt & VERSION_WITHNAME) && 444 !STREQ(name, matchname)) 445 continue; 446 447 ie = pkghash_get_value(index, name); 448 print_version(pkg, "index", ie != NULL ? ie->version : NULL, 449 limchar, opt); 450 451 /* If we reach here, it means at least one package 452 has matched with our query. */ 453 gotnone = false; 454 } 455 456 cleanup: 457 pkgdb_release_lock(db, PKGDB_LOCK_READONLY); 458 free_index(index); 459 pkg_free(pkg); 460 pkgdb_it_free(it); 461 pkgdb_close(db); 462 463 return (gotnone); 464 } 465 466 static int 467 do_source_remote(unsigned int opt, char limchar, char *pattern, match_t match, 468 bool auto_update, c_charv_t *reponames, const char *matchorigin, 469 const char *matchname) 470 { 471 struct pkgdb *db = NULL; 472 struct pkgdb_it *it = NULL; 473 struct pkgdb_it *it_remote = NULL; 474 struct pkg *pkg = NULL; 475 struct pkg *pkg_remote = NULL; 476 const char *name = NULL; 477 const char *origin = NULL; 478 const char *version_remote = NULL; 479 bool is_origin = false; 480 int retcode = EXIT_FAILURE; 481 482 if ( (opt & VERSION_SOURCES) != VERSION_SOURCE_REMOTE ) { 483 usage_version(); 484 return (EXIT_FAILURE); 485 } 486 487 /* Only force remote mode if looking up remote, otherwise 488 user is forced to have a repo.sqlite */ 489 490 if (auto_update) { 491 retcode = pkgcli_update(false, false, reponames); 492 if (retcode != EPKG_OK) 493 return (retcode); 494 else 495 retcode = EXIT_FAILURE; 496 } 497 498 if (pkgdb_open_all2(&db, PKGDB_REMOTE, reponames) != EPKG_OK) 499 return (EXIT_FAILURE); 500 501 if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY) != EPKG_OK) { 502 pkgdb_close(db); 503 warnx("Cannot get a read lock on a database. " 504 "It is locked by another process"); 505 return (EXIT_FAILURE); 506 } 507 508 it = pkgdb_query(db, pattern, match); 509 if (it == NULL) 510 goto cleanup; 511 512 while (pkgdb_it_next(it, &pkg, PKG_LOAD_BASIC) == EPKG_OK) { 513 pkg_get(pkg, PKG_ATTR_NAME, &name); 514 pkg_get(pkg, PKG_ATTR_ORIGIN, &origin); 515 516 /* If -O was specified, check if this origin matches */ 517 if ((opt & VERSION_WITHORIGIN) && 518 !STREQ(origin, matchorigin)) { 519 is_origin = true; 520 continue; 521 } 522 523 /* If -n was specified, check if this name matches */ 524 if ((opt & VERSION_WITHNAME) && 525 !STREQ(name, matchname)) { 526 is_origin = false; 527 continue; 528 } 529 530 it_remote = pkgdb_repo_query2(db, is_origin ? origin : name, MATCH_EXACT, reponames); 531 if (it_remote == NULL) { 532 retcode = EXIT_FAILURE; 533 goto cleanup; 534 } 535 536 if (pkgdb_it_next(it_remote, &pkg_remote, PKG_LOAD_BASIC) 537 == EPKG_OK) { 538 pkg_get(pkg_remote, PKG_ATTR_VERSION, &version_remote); 539 print_version(pkg, "remote", version_remote, limchar, 540 opt); 541 } else { 542 print_version(pkg, "remote", NULL, limchar, opt); 543 } 544 pkgdb_it_free(it_remote); 545 546 /* If we reach here, it means at least one package 547 has matched with our query. */ 548 retcode = EXIT_SUCCESS; 549 } 550 551 cleanup: 552 pkgdb_release_lock(db, PKGDB_LOCK_READONLY); 553 554 pkg_free(pkg); 555 pkg_free(pkg_remote); 556 pkgdb_it_free(it); 557 pkgdb_close(db); 558 559 return (retcode); 560 } 561 562 static int 563 exec_buf(xstring *res, char **argv) { 564 char buf[BUFSIZ]; 565 int spawn_err; 566 pid_t pid; 567 int pfd[2]; 568 int r, pstat; 569 posix_spawn_file_actions_t actions; 570 571 if (pipe(pfd) < 0) { 572 warn("pipe()"); 573 return (0); 574 } 575 576 if ((spawn_err = posix_spawn_file_actions_init(&actions)) != 0) { 577 warnx("%s:%s", argv[0], strerror(spawn_err)); 578 return (0); 579 } 580 581 if ((spawn_err = posix_spawn_file_actions_addopen(&actions, 582 STDERR_FILENO, "/dev/null", O_RDWR, 0)) != 0 || 583 (spawn_err = posix_spawn_file_actions_addopen(&actions, 584 STDIN_FILENO, "/dev/null", O_RDONLY, 0)) != 0 || 585 (spawn_err = posix_spawn_file_actions_adddup2(&actions, 586 pfd[1], STDOUT_FILENO)!= 0) || 587 (spawn_err = posix_spawnp(&pid, argv[0], &actions, NULL, 588 argv, environ)) != 0) { 589 posix_spawn_file_actions_destroy(&actions); 590 warnx("%s:%s", argv[0], strerror(spawn_err)); 591 return (0); 592 } 593 posix_spawn_file_actions_destroy(&actions); 594 595 close(pfd[1]); 596 597 xstring_reset(res); 598 while ((r = read(pfd[0], buf, BUFSIZ)) > 0) 599 fwrite(buf, sizeof(char), r, res->fp); 600 601 close(pfd[0]); 602 while (waitpid(pid, &pstat, 0) == -1) { 603 if (errno != EINTR) 604 return (-1); 605 } 606 if (WEXITSTATUS(pstat) != 0) 607 return (-1); 608 609 fflush(res->fp); 610 return (strlen(res->buf)); 611 } 612 613 static struct category * 614 category_new(int portsfd, const char *category) 615 { 616 struct category *cat = NULL; 617 xstring *makecmd; 618 char *results, *d; 619 char *argv[5]; 620 621 makecmd = xstring_new(); 622 fchdir(portsfd); 623 624 argv[0] = "make"; 625 argv[1] = "-C"; 626 argv[2] = (char *)category; 627 argv[3] = "-VSUBDIR"; 628 argv[4] = NULL; 629 630 if (exec_buf(makecmd, argv) <= 0) 631 goto cleanup; 632 633 fflush(makecmd->fp); 634 results = makecmd->buf; 635 636 if (categories == NULL) 637 categories = pkghash_new(); 638 639 cat = xcalloc(1, sizeof(*cat)); 640 cat->name = xstrdup(category); 641 642 pkghash_add(categories, cat->name, cat, NULL); 643 while ((d = strsep(&results, " \n")) != NULL) 644 pkghash_safe_add(cat->ports, d, NULL, NULL); 645 646 cleanup: 647 xstring_free(makecmd); 648 649 return (cat); 650 } 651 652 static bool 653 validate_origin(int portsfd, const char *origin) 654 { 655 struct category *cat; 656 char *category, *buf; 657 658 /* If the origin does not contain a / ignore it like for 659 * "base" 660 */ 661 if (strchr(origin, '/') == NULL) 662 return (false); 663 664 category = xstrdup(origin); 665 buf = strrchr(category, '/'); 666 buf[0] = '\0'; 667 668 cat = pkghash_get_value(categories, category); 669 if (cat == NULL) 670 cat = category_new(portsfd, category); 671 if (cat == NULL) 672 return (false); 673 674 buf = strrchr(origin, '/'); 675 buf++; 676 677 if (STREQ(origin, "base")) 678 return (false); 679 680 return (pkghash_get(cat->ports, buf) != NULL); 681 } 682 683 static const char * 684 port_version(xstring *cmd, int portsfd, const char *origin, const char *pkgname) 685 { 686 char *output, *walk, *name; 687 char *version = NULL; 688 char *argv[5]; 689 690 /* Validate the port origin -- check the SUBDIR settings 691 in the ports and category Makefiles, then extract the 692 version from the port itself. */ 693 694 if (validate_origin(portsfd, origin)) { 695 argv[0] = "make"; 696 argv[1] = "-C"; 697 argv[2] = (char *)origin; 698 argv[3] = "flavors-package-names"; 699 argv[4] = NULL; 700 701 if (exec_buf(cmd, argv) > 0) { 702 fflush(cmd->fp); 703 output = cmd->buf; 704 while ((walk = strsep(&output, "\n")) != NULL) { 705 name = walk; 706 walk = strrchr(walk, '-'); 707 if (walk == NULL) 708 continue; 709 walk[0] = '\0'; 710 walk++; 711 if (STREQ(name, pkgname)) { 712 version = walk; 713 break; 714 } 715 } 716 } 717 } 718 719 return (version); 720 } 721 722 static int 723 do_source_ports(unsigned int opt, char limchar, char *pattern, match_t match, 724 const char *matchorigin, const char *matchname, const char *portsdir) 725 { 726 struct pkgdb *db = NULL; 727 struct pkgdb_it *it = NULL; 728 struct pkg *pkg = NULL; 729 xstring *cmd; 730 const char *name = NULL; 731 const char *origin = NULL; 732 const char *version = NULL; 733 int portsfd; 734 bool gotnone = true; 735 736 if ( (opt & VERSION_SOURCES) != VERSION_SOURCE_PORTS ) { 737 usage_version(); 738 return (EXIT_FAILURE); 739 } 740 741 portsfd = open(portsdir, O_DIRECTORY); 742 if (portsfd == -1) 743 err(EXIT_FAILURE, "Cannot open '%s'", portsdir); 744 745 if (pkgdb_open(&db, PKGDB_DEFAULT_READONLY) != EPKG_OK) 746 return (EXIT_FAILURE); 747 748 if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY) != EPKG_OK) { 749 pkgdb_close(db); 750 warnx("Cannot get a read lock on a database. " 751 "It is locked by another process"); 752 return (EXIT_FAILURE); 753 } 754 755 if ((it = pkgdb_query(db, pattern, match)) == NULL) 756 goto cleanup; 757 758 cmd = xstring_new(); 759 760 while (pkgdb_it_next(it, &pkg, PKG_LOAD_BASIC) == EPKG_OK) { 761 pkg_get(pkg, PKG_ATTR_NAME, &name); 762 pkg_get(pkg, PKG_ATTR_ORIGIN, &origin); 763 764 /* If -O was specified, check if this origin matches */ 765 if ((opt & VERSION_WITHORIGIN) && 766 !STREQ(origin, matchorigin)) 767 continue; 768 769 /* If -n was specified, check if this name matches */ 770 if ((opt & VERSION_WITHNAME) && 771 !STREQ(name, matchname)) 772 continue; 773 774 version = port_version(cmd, portsfd, origin, name); 775 print_version(pkg, "port", version, limchar, opt); 776 xstring_reset(cmd); 777 778 /* If we reach here, it means at least one package 779 has matched with our query. */ 780 gotnone = false; 781 } 782 783 xstring_free(cmd); 784 785 cleanup: 786 pkgdb_release_lock(db, PKGDB_LOCK_READONLY); 787 788 free_categories(); 789 pkg_free(pkg); 790 pkgdb_it_free(it); 791 pkgdb_close(db); 792 793 return (gotnone); 794 } 795 796 int 797 exec_version(int argc, char **argv) 798 { 799 unsigned int opt = 0; 800 char limchar = '-'; 801 const char *matchorigin = NULL; 802 const char *matchname = NULL; 803 const char *portsdir; 804 const char *indexfile; 805 const char *versionsource; 806 char filebuf[MAXPATHLEN]; 807 match_t match = MATCH_ALL; 808 char *pattern = NULL; 809 int ch; 810 c_charv_t reponames = vec_init(); 811 812 struct option longopts[] = { 813 { "case-sensitive", no_argument, NULL, 'C' }, 814 { "exact", required_argument, NULL, 'e' }, 815 { "glob", required_argument, NULL, 'g' }, 816 { "help", no_argument, NULL, 'h' }, 817 { "index", no_argument, NULL, 'I' }, 818 { "case-insensitive", no_argument, NULL, 'i' }, 819 { "not-like", required_argument, NULL, 'L' }, 820 { "like", required_argument, NULL, 'l' }, 821 { "match-name", required_argument, NULL, 'n' }, 822 { "match-origin", required_argument, NULL, 'O' }, 823 { "origin", no_argument, NULL, 'o' }, 824 { "ports", no_argument, NULL, 'P' }, 825 { "quiet", no_argument, NULL, 'q' }, 826 { "remote", no_argument, NULL, 'R' }, 827 { "repository", required_argument, NULL, 'r' }, 828 { "test-pattern", no_argument, NULL, 'T' }, 829 { "test-version", no_argument, NULL, 't' }, 830 { "no-repo-update", no_argument, NULL, 'U' }, 831 { "verbose", no_argument, NULL, 'v' }, 832 { "regex", required_argument, NULL, 'x' }, 833 { NULL, 0, NULL, 0 }, 834 }; 835 836 while ((ch = getopt_long(argc, argv, "+Ce:g:hIiL:l:n:O:oPqRr:TtUvx:", 837 longopts, NULL)) != -1) { 838 switch (ch) { 839 case 'C': 840 pkgdb_set_case_sensitivity(true); 841 break; 842 case 'e': 843 match = MATCH_EXACT; 844 pattern = optarg; 845 break; 846 case 'g': 847 match = MATCH_GLOB; 848 pattern = optarg; 849 break; 850 case 'h': 851 usage_version(); 852 return (EXIT_SUCCESS); 853 case 'I': 854 opt |= VERSION_SOURCE_INDEX; 855 break; 856 case 'i': 857 pkgdb_set_case_sensitivity(false); 858 break; 859 case 'L': 860 opt |= VERSION_NOSTATUS; 861 limchar = *optarg; 862 break; 863 case 'l': 864 opt |= VERSION_STATUS; 865 limchar = *optarg; 866 break; 867 case 'n': 868 opt |= VERSION_WITHNAME; 869 matchname = optarg; 870 break; 871 case 'O': 872 opt |= VERSION_WITHORIGIN; 873 matchorigin = optarg; 874 break; 875 case 'o': 876 opt |= VERSION_ORIGIN; 877 break; 878 case 'P': 879 opt |= VERSION_SOURCE_PORTS; 880 break; 881 case 'q': 882 opt |= VERSION_QUIET; 883 break; 884 case 'R': 885 opt |= VERSION_SOURCE_REMOTE; 886 break; 887 case 'r': 888 opt |= VERSION_SOURCE_REMOTE; 889 vec_push(&reponames, optarg); 890 break; 891 case 'T': 892 opt |= VERSION_TESTPATTERN; 893 break; 894 case 't': 895 opt |= VERSION_TESTVERSION; 896 break; 897 case 'U': 898 auto_update = false; 899 break; 900 case 'v': 901 opt |= VERSION_VERBOSE; 902 break; 903 case 'x': 904 match = MATCH_REGEX; 905 pattern = optarg; 906 break; 907 default: 908 usage_version(); 909 return (EXIT_FAILURE); 910 } 911 } 912 argc -= optind; 913 argv += optind; 914 915 /* 916 * Allowed option combinations: 917 * -t ver1 ver2 -- only 918 * -T pkgname pattern -- only 919 * Only one of -I -P -R can be given 920 */ 921 922 if (matchorigin != NULL && matchname != NULL) { 923 usage_version(); 924 return (EXIT_FAILURE); 925 } 926 927 if ( (opt & VERSION_TESTVERSION) == VERSION_TESTVERSION ) 928 return (do_testversion(opt, argc, argv)); 929 930 if ( (opt & VERSION_TESTPATTERN) == VERSION_TESTPATTERN ) 931 return (do_testpattern(opt, argc, argv)); 932 933 if (opt & (VERSION_STATUS|VERSION_NOSTATUS)) { 934 if (limchar != '<' && 935 limchar != '>' && 936 limchar != '=' && 937 limchar != '?' && 938 limchar != '!') { 939 usage_version(); 940 return (EXIT_FAILURE); 941 } 942 } 943 944 if (opt & VERSION_QUIET) 945 quiet = true; 946 947 if (argc > 1) { 948 usage_version(); 949 return (EXIT_FAILURE); 950 } 951 952 if ( !(opt & VERSION_SOURCES ) ) { 953 versionsource = pkg_object_string( 954 pkg_config_get("VERSION_SOURCE")); 955 if (versionsource != NULL) { 956 switch (versionsource[0]) { 957 case 'I': 958 opt |= VERSION_SOURCE_INDEX; 959 break; 960 case 'P': 961 opt |= VERSION_SOURCE_PORTS; 962 break; 963 case 'R': 964 opt |= VERSION_SOURCE_REMOTE; 965 break; 966 default: 967 warnx("Invalid VERSION_SOURCE" 968 " in configuration."); 969 } 970 } 971 } 972 973 if ( (opt & VERSION_SOURCE_INDEX) == VERSION_SOURCE_INDEX ) { 974 if (!have_indexfile(&indexfile, filebuf, sizeof(filebuf), 975 argc, argv, true)) 976 return (EXIT_FAILURE); 977 else 978 return (do_source_index(opt, limchar, pattern, match, 979 matchorigin, matchname, indexfile)); 980 } 981 982 if ( (opt & VERSION_SOURCE_REMOTE) == VERSION_SOURCE_REMOTE ) 983 return (do_source_remote(opt, limchar, pattern, match, 984 auto_update, &reponames, matchorigin, matchname)); 985 986 if ( (opt & VERSION_SOURCE_PORTS) == VERSION_SOURCE_PORTS ) { 987 if (!have_ports(&portsdir, true)) 988 return (EXIT_FAILURE); 989 else 990 return (do_source_ports(opt, limchar, pattern, 991 match, matchorigin, matchname, portsdir)); 992 } 993 994 /* If none of -IPR were specified, and INDEX exists use that. 995 Failing that, if portsdir exists and is valid, use that 996 (slow) otherwise fallback to remote. */ 997 998 if (have_indexfile(&indexfile, filebuf, sizeof(filebuf), argc, argv, 999 false)) { 1000 opt |= VERSION_SOURCE_INDEX; 1001 return (do_source_index(opt, limchar, pattern, match, 1002 matchorigin, matchname, indexfile)); 1003 } else if (have_ports(&portsdir, false)) { 1004 if (argc == 1) { 1005 warnx("No such INDEX file: '%s'", argv[0]); 1006 return (EXIT_FAILURE); 1007 } 1008 opt |= VERSION_SOURCE_PORTS; 1009 return (do_source_ports(opt, limchar, pattern, match, 1010 matchorigin, matchname, portsdir)); 1011 } else { 1012 opt |= VERSION_SOURCE_REMOTE; 1013 return (do_source_remote(opt, limchar, pattern, match, 1014 auto_update, &reponames, matchorigin, matchname)); 1015 } 1016 1017 /* NOTREACHED */ 1018 return (EXIT_FAILURE); 1019 } 1020 /* 1021 * That's All Folks! 1022 */