LCOV - code coverage report
Current view: top level - src - version.c (source / functions) Hit Total Coverage
Test: cov.info Lines: 26 461 5.6 %
Date: 2015-08-15 Functions: 2 33 6.1 %

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

Generated by: LCOV version 1.10