LCOV - code coverage report
Current view: top level - src - search.c (source / functions) Hit Total Coverage
Test: cov.info Lines: 60 219 27.4 %
Date: 2015-08-15 Functions: 4 5 80.0 %

          Line data    Source code
       1             : /*-
       2             :  * Copyright (c) 2011-2012 Baptiste Daroussin <bapt@FreeBSD.org>
       3             :  * Copyright (c) 2011-2012 Julien Laffaye <jlaffaye@FreeBSD.org>
       4             :  * Copyright (c) 2011-2012 Marin Atanasov Nikolov <dnaeon@gmail.com>
       5             :  * Copyright (c) 2012-2013 Bryan Drewery <bdrewery@FreeBSD.org>
       6             :  * Copyright (c) 2014 Matthew Seaman <matthew@FreeBSD.org>
       7             :  * All rights reserved.
       8             :  * 
       9             :  * Redistribution and use in source and binary forms, with or without
      10             :  * modification, are permitted provided that the following conditions
      11             :  * are met:
      12             :  * 1. Redistributions of source code must retain the above copyright
      13             :  *    notice, this list of conditions and the following disclaimer
      14             :  *    in this position and unchanged.
      15             :  * 2. Redistributions in binary form must reproduce the above copyright
      16             :  *    notice, this list of conditions and the following disclaimer in the
      17             :  *    documentation and/or other materials provided with the distribution.
      18             :  * 
      19             :  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
      20             :  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
      21             :  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
      22             :  * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
      23             :  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
      24             :  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
      25             :  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      26             :  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
      27             :  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
      28             :  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
      29             :  */
      30             : 
      31             : #include <err.h>
      32             : #include <getopt.h>
      33             : #include <stdio.h>
      34             : #include <string.h>
      35             : #include <unistd.h>
      36             : #include <sysexits.h>
      37             : 
      38             : 
      39             : #include <pkg.h>
      40             : 
      41             : #include "pkgcli.h"
      42             : 
      43             : typedef struct _cliopt {
      44             :         const char *option;
      45             :         char key;
      46             : } cliopt;
      47             : 
      48             : /* an option string should not be a prefix of any other option string */ 
      49             : static const cliopt search_label[] = {
      50             :         { "comment",     'c'  },
      51             :         { "description", 'd'  },
      52             :         { "name",        'n'  },
      53             :         { "origin",      'o'  },
      54             :         { "pkg-name",    'p'  },
      55             :         { NULL,          '\0' },
      56             : };
      57             : 
      58             : static const cliopt modifiers[] = {
      59             :         { "annotations",          'A'  },
      60             :         { "arch",                 'a'  },
      61             :         { "categories",           'C'  },
      62             :         { "comment",              'c'  },
      63             :         { "depends-on",           'd'  },
      64             :         { "description",          'D'  },
      65             :         { "full",                 'f'  },
      66             :         { "licenses",             'l'  },
      67             :         { "maintainer",           'm'  },
      68             :         { "name",                 'n'  },
      69             :         { "options",              'o'  },
      70             :         { "pkg-size",           'P'  },
      71             :         { "prefix",               'p'  },
      72             :         { "repository",           'R'  },
      73             :         { "required-by",          'r'  },
      74             :         { "shared-libs-required", 'B'  },
      75             :         { "shared-libs-provided", 'b'  },
      76             :         { "size",                 's'  },
      77             :         { "url",                  'u'  },
      78             :         { "version",              'v'  },
      79             :         { "www",                  'w'  },
      80             :         { NULL,                   '\0' },
      81             : };      
      82             : 
      83             : static char
      84           2 : match_optarg(const cliopt *optlist, const char *opt)
      85             : {
      86           2 :         int i, matched = -1;
      87           2 :         char key = '\0';
      88             :         size_t optlen;
      89             : 
      90           2 :         optlen = strlen(opt);
      91             : 
      92             :         /* Match any unique prefix from  optlist */
      93          28 :         for (i = 0; optlist[i].option != NULL; i++) {
      94          26 :                 if (strncmp(opt, optlist[i].option, optlen) != 0)
      95          24 :                         continue;
      96           2 :                 if (matched > 0) {
      97           0 :                         warnx("\"%s\" is ambiguous. Was "
      98             :                               "\"%s\" or \"%s\" meant?", opt,
      99           0 :                               optlist[matched].option, optlist[i].option);
     100           0 :                         key = '\0';
     101           0 :                         break;
     102             :                 }
     103           2 :                 matched = i;
     104           2 :                 key = optlist[i].key;
     105             :         }
     106           2 :         return (key);
     107             : }
     108             : 
     109             : static pkgdb_field
     110           1 : search_label_opt(const char *optionarg)
     111             : {
     112             :         pkgdb_field field;
     113             : 
     114             :         /* label options */
     115           1 :         switch(match_optarg(search_label, optionarg)) {
     116             :         case 'o':
     117           0 :                 field = FIELD_ORIGIN;
     118           0 :                 break;
     119             :         case 'n':
     120           1 :                 field = FIELD_NAME;
     121           1 :                 break;
     122             :         case 'p':
     123           0 :                 field = FIELD_NAMEVER;
     124           0 :                 break;
     125             :         case 'c':
     126           0 :                 field = FIELD_COMMENT;
     127           0 :                 break;
     128             :         case 'd':
     129           0 :                 field = FIELD_DESC;
     130           0 :                 break;
     131             :         default:
     132           0 :                 usage_search();
     133           0 :                 errx(EX_USAGE, "Unknown search/label option: %s", optionarg);
     134             :                 /* NOTREACHED */
     135             :         }
     136           1 :         return field;
     137             : }
     138             : 
     139             : static unsigned int
     140           1 : modifier_opt(const char *optionarg)
     141             : {
     142             :         unsigned int opt;
     143             : 
     144             :         /* output modifiers */
     145           1 :         switch(match_optarg(modifiers, optionarg)) {
     146             :         case 'A':
     147           0 :                 opt = INFO_ANNOTATIONS;
     148           0 :                 break;
     149             :         case 'a':
     150           0 :                 opt = INFO_ARCH;
     151           0 :                 break;
     152             :         case 'C':
     153           0 :                 opt = INFO_CATEGORIES;
     154           0 :                 break;
     155             :         case 'c':
     156           1 :                 opt = INFO_COMMENT;
     157           1 :                 break;
     158             :         case 'd':
     159           0 :                 opt = INFO_DEPS;
     160           0 :                 break;
     161             :         case 'D':
     162           0 :                 opt = INFO_DESCR;
     163           0 :                 break;
     164             :         case 'f':
     165           0 :                 opt = INFO_FULL;
     166           0 :                 break;
     167             :         case 'l':
     168           0 :                 opt = INFO_LICENSES;
     169           0 :                 break;
     170             :         case 'm':
     171           0 :                 opt = INFO_MAINTAINER;
     172           0 :                 break;
     173             :         case 'n':
     174           0 :                 opt = INFO_NAME;
     175           0 :                 break;
     176             :         case 'o':
     177           0 :                 opt = INFO_OPTIONS;
     178           0 :                 break;
     179             :         case 'P':
     180           0 :                 opt = INFO_PKGSIZE;
     181           0 :                 break;
     182             :         case 'p':
     183           0 :                 opt = INFO_PREFIX;
     184           0 :                 break;
     185             :         case 'R':
     186           0 :                 opt = INFO_REPOSITORY;
     187           0 :                 break;
     188             :         case 'r':
     189           0 :                 opt = INFO_RDEPS;
     190           0 :                 break;
     191             :         case 'B':
     192           0 :                 opt = INFO_SHLIBS_REQUIRED;
     193           0 :                 break;
     194             :         case 'b':
     195           0 :                 opt = INFO_SHLIBS_PROVIDED;
     196           0 :                 break;
     197             :         case 's':
     198           0 :                 opt = INFO_FLATSIZE;
     199           0 :                 break;
     200             :         case 'u':
     201           0 :                 opt = INFO_REPOURL;
     202           0 :                 break;
     203             :         case 'v':
     204           0 :                 opt = INFO_VERSION;
     205           0 :                 break;
     206             :         case 'w':
     207           0 :                 opt = INFO_WWW;
     208           0 :                 break;
     209             :         default:
     210           0 :                 usage_search();
     211           0 :                 errx(EX_USAGE, "Unkown modifier option %s", optionarg);
     212             :                 /* NOTREACHED */
     213             :         }
     214           1 :         return opt;
     215             : }
     216             : 
     217             : void
     218           0 : usage_search(void)
     219             : {
     220             :         int i, n;
     221             : 
     222           0 :         fprintf(stderr, "Usage: pkg search [-eU] [-r repo] [-S search] "
     223             :             "[-L label] [-Q mod]... [-Cgix] <pkg-name>\n");
     224           0 :         fprintf(stderr, "       pkg search [-cDdefopqRU] [-r repo] "
     225             :             "[-Cgix] <pattern>\n\n");
     226           0 :         n = fprintf(stderr, "       Search and Label options:");
     227           0 :         for (i = 0; search_label[i].option != NULL; i++) {
     228           0 :                 if (n > 72)
     229           0 :                         n = fprintf(stderr, "\n            ");
     230           0 :                 n += fprintf(stderr, " %s", search_label[i].option);
     231             :         }
     232           0 :         fprintf(stderr, "\n");
     233           0 :         n = fprintf(stderr, "       Output Modifiers:");
     234           0 :         for (i = 0; modifiers[i].option != NULL; i++) {
     235           0 :                 if (n > 68)
     236           0 :                         n = fprintf(stderr, "\n            ");
     237           0 :                 n += fprintf(stderr, " %s", modifiers[i].option);
     238             :         }
     239           0 :         fprintf(stderr, "\n");
     240           0 :         fprintf(stderr, "For more information see 'pkg help search'.\n");
     241           0 : }
     242             : 
     243             : int
     244           1 : exec_search(int argc, char **argv)
     245             : {
     246           1 :         const char      *pattern = NULL;
     247           1 :         const char      *reponame = NULL;
     248           1 :         int              ret = EPKG_OK, ch;
     249             :         int              flags;
     250           1 :         uint64_t         opt = 0;
     251           1 :         match_t          match = MATCH_REGEX;
     252           1 :         pkgdb_field      search = FIELD_NONE;
     253           1 :         pkgdb_field      label = FIELD_NONE;
     254           1 :         struct pkgdb    *db = NULL;
     255           1 :         struct pkgdb_it *it = NULL;
     256           1 :         struct pkg      *pkg = NULL;
     257           1 :         bool             atleastone = false;
     258             :         bool             old_quiet;
     259             : 
     260           1 :         struct option longopts[] = {
     261             :                 { "case-sensitive",   no_argument,            NULL,   'C' },
     262             :                 { "comment",          no_argument,            NULL,   'c' },
     263             :                 { "description",      no_argument,            NULL,   'D' },
     264             :                 { "depends-on",               no_argument,            NULL,   'd' },
     265             :                 { "exact",            no_argument,            NULL,   'e' },
     266             :                 { "full",             no_argument,            NULL,   'f' },
     267             :                 { "glob",             no_argument,            NULL,   'g' },
     268             :                 { "case-insensitive", no_argument,            NULL,   'i' },
     269             :                 { "label",            required_argument,      NULL,   'L' },
     270             :                 { "origins",          no_argument,            NULL,   'o' },
     271             :                 { "prefix",           no_argument,            NULL,   'p' },
     272             :                 { "quiet",            no_argument,            NULL,   'q' },
     273             :                 { "query-modifier",   required_argument,      NULL,   'Q' },
     274             :                 { "repository",               required_argument,      NULL,   'r' },
     275             :                 { "raw",              no_argument,            NULL,   'R' },
     276             :                 { "search",           required_argument,      NULL,   'S' },
     277             :                 { "size",             no_argument,            NULL,   's' },
     278             :                 { "no-repo-update",   no_argument,            NULL,   'U' },
     279             :                 { "regex",            no_argument,            NULL,   'x' },
     280             :                 { "raw-format",               required_argument,      NULL,   1   },
     281             :                 { NULL,                 0,                      NULL,   0   },
     282             :         };
     283             : 
     284           5 :         while ((ch = getopt_long(argc, argv, "+CcDdefgiL:opqQ:r:RS:sUx", longopts, NULL)) != -1) {
     285           3 :                 switch (ch) {
     286             :                 case 'C':
     287           0 :                         pkgdb_set_case_sensitivity(true);
     288           0 :                         break;
     289             :                 case 'c':       /* Same as -S comment */
     290           0 :                         search = search_label_opt("comment");
     291           0 :                         break;
     292             :                 case 'D':       /* Same as -S description */
     293           0 :                         search = search_label_opt("description");
     294           0 :                         break;
     295             :                 case 'd':       /* Same as -Q depends-on  */
     296           0 :                         opt |= modifier_opt("depends-on");
     297           0 :                         break;
     298             :                 case 'e':
     299           1 :                         match = MATCH_EXACT;
     300           1 :                         break;
     301             :                 case 'f':       /* Same as -Q full */
     302           0 :                         opt |= modifier_opt("full");
     303           0 :                         break;
     304             :                 case 'g':
     305           0 :                         match = MATCH_GLOB;
     306           0 :                         break;
     307             :                 case 'i':
     308           0 :                         pkgdb_set_case_sensitivity(false);
     309           0 :                         break;
     310             :                 case 'L':
     311           0 :                         label = search_label_opt(optarg);
     312           0 :                         break;
     313             :                 case 'o':       /* Same as -L origin */
     314           0 :                         label = search_label_opt("origin");
     315           0 :                         break;
     316             :                 case 'p':       /* Same as -Q prefix */
     317           0 :                         opt |= modifier_opt("prefix");
     318           0 :                         break;
     319             :                 case 'q':
     320           0 :                         quiet = true;
     321           0 :                         break;
     322             :                 case 'Q':
     323           1 :                         opt |= modifier_opt(optarg);
     324           1 :                         break;
     325             :                 case 'r':
     326           0 :                         reponame = optarg;
     327           0 :                         break;
     328             :                 case 'R':
     329           0 :                         opt = INFO_RAW;
     330           0 :                         break;
     331             :                 case 'S':
     332           1 :                         search = search_label_opt(optarg);
     333           1 :                         break;
     334             :                 case 's':       /* Same as -Q size */
     335           0 :                         opt |= modifier_opt("size");
     336           0 :                         break;
     337             :                 case 'U':
     338           0 :                         auto_update = false;
     339           0 :                         break;
     340             :                 case 'x':
     341           0 :                         match = MATCH_REGEX;
     342           0 :                         break;
     343             :                 case 1:
     344           0 :                         if (strcasecmp(optarg, "json") == 0)
     345           0 :                                opt |= INFO_RAW_JSON;
     346           0 :                         else if (strcasecmp(optarg, "json-compact") == 0)
     347           0 :                                 opt |= INFO_RAW_JSON_COMPACT;
     348           0 :                         else if (strcasecmp(optarg, "yaml") == 0)
     349           0 :                                 opt |= INFO_RAW_YAML;
     350           0 :                         else if (strcasecmp(optarg, "ucl") == 0)
     351           0 :                                 opt |= INFO_RAW_UCL;
     352             :                         else
     353           0 :                                 errx(EX_USAGE, "Invalid format '%s' for the "
     354             :                                     "raw output, expecting json, json-compact "
     355             :                                     "or yaml", optarg);
     356           0 :                         break;
     357             :                 default:
     358           0 :                         usage_search();
     359           0 :                         return (EX_USAGE);
     360             :                 }
     361             :         }
     362             : 
     363           1 :         argc -= optind;
     364           1 :         argv += optind;
     365             : 
     366           1 :         if (argc != 1) {
     367           0 :                 usage_search();
     368           0 :                 return (EX_USAGE);
     369             :         }
     370             : 
     371           1 :         pattern = argv[0];
     372           1 :         if (pattern[0] == '\0') {
     373           0 :                 fprintf(stderr, "Pattern must not be empty.\n");
     374           0 :                 return (EX_USAGE);
     375             :         }
     376           1 :         if (search == FIELD_NONE) {
     377           0 :                 if (strchr(pattern, '/') != NULL)
     378           0 :                         search = FIELD_ORIGIN;
     379             :                 else
     380           0 :                         search = FIELD_NAMEVER; /* Default search */
     381             :         }
     382           1 :         if (label == FIELD_NONE)
     383           1 :                 label = search; /* By default, show what was searched  */
     384             : 
     385           1 :         switch(label) {
     386             :         case FIELD_NONE:
     387           0 :                 break;          /* should never happen */
     388             :         case FIELD_ORIGIN:
     389           0 :                 opt |= INFO_TAG_ORIGIN;
     390           0 :                 break;
     391             :         case FIELD_NAME:
     392           1 :                 opt |= INFO_TAG_NAME;
     393           1 :                 break;
     394             :         case FIELD_NAMEVER:
     395           0 :                 opt |= INFO_TAG_NAMEVER;
     396           0 :                 break;
     397             :         case FIELD_COMMENT:
     398           0 :                 opt |= INFO_TAG_NAMEVER|INFO_COMMENT;
     399           0 :                 break;
     400             :         case FIELD_DESC:
     401           0 :                 opt |= INFO_TAG_NAMEVER|INFO_DESCR;
     402           0 :                 break;
     403             :         }
     404             : 
     405           1 :         ret = pkgdb_access(PKGDB_MODE_READ, PKGDB_DB_REPO);
     406           1 :         switch(ret) {
     407             :         case EPKG_ENOACCESS:
     408           0 :                 warnx("Insufficient privileges to query the package database");
     409           0 :                 return (EX_NOPERM);
     410             :         case EPKG_ENODB:
     411           0 :                 if (!auto_update) {
     412           0 :                         warnx("Unable to open remote repository catalogues. Try running '%s update' first.", getprogname());
     413           0 :                         return (EX_IOERR);
     414             :                 }
     415           0 :                 break;
     416             :         case EPKG_OK:
     417           1 :                 break;
     418             :         default:
     419           0 :                 return (EX_IOERR);
     420             :         }
     421             : 
     422             :         /* first update the remote repositories if needed */
     423           1 :         old_quiet = quiet;
     424           1 :         quiet = true;
     425           1 :         if (auto_update && (ret = pkgcli_update(false, false, reponame)) != EPKG_OK)
     426           1 :                 return (ret);
     427           0 :         quiet = old_quiet;
     428             : 
     429           0 :         if (pkgdb_open_all(&db, PKGDB_REMOTE, reponame) != EPKG_OK)
     430           0 :                 return (EX_IOERR);
     431             : 
     432           0 :         if ((it = pkgdb_repo_search(db, pattern, match, search, search,
     433             :             reponame)) == NULL) {
     434           0 :                 pkgdb_close(db);
     435           0 :                 return (EX_IOERR);
     436             :         }
     437             : 
     438           0 :         if (opt & INFO_RAW) {
     439           0 :                 if ((opt & (INFO_RAW_JSON|INFO_RAW_JSON_COMPACT)) == 0)
     440           0 :                         opt |= INFO_RAW_YAML;
     441             :         }
     442             : 
     443           0 :         flags = info_flags(opt, true);
     444           0 :         while ((ret = pkgdb_it_next(it, &pkg, flags)) == EPKG_OK) {
     445           0 :                 print_info(pkg, opt);
     446           0 :                 atleastone = true;
     447             :         }
     448             : 
     449           0 :         pkg_free(pkg);
     450           0 :         pkgdb_it_free(it);
     451           0 :         pkgdb_close(db);
     452             : 
     453           0 :         if (!atleastone)
     454           0 :                 ret = EPKG_FATAL;
     455             : 
     456           0 :         return ((ret == EPKG_OK || ret == EPKG_END) ? EX_OK : EX_SOFTWARE);
     457             : }

Generated by: LCOV version 1.10