rquery.c
1 /*- 2 * Copyright (c) 2011-2012 Baptiste Daroussin <bapt@FreeBSD.org> 3 * Copyright (c) 2011-2012 Marin Atanasov Nikolov <dnaeon@gmail.com> 4 * Copyright (c) 2012-2013 Bryan Drewery <bdrewery@FreeBSD.org> 5 * Copyright (c) 2013-2014 Matthew Seaman <matthew@FreeBSD.org> 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer 13 * in this position and unchanged. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30 #include <sys/types.h> 31 32 #include <ctype.h> 33 #include <err.h> 34 #include <getopt.h> 35 #include <inttypes.h> 36 #include <pkg.h> 37 #include <stdio.h> 38 #include <stdlib.h> 39 #include <string.h> 40 #include <unistd.h> 41 42 #include "pkgcli.h" 43 44 static struct query_flags accepted_rquery_flags[] = { 45 { 'd', "nov", 1, PKG_LOAD_DEPS }, 46 { 'r', "nov", 1, PKG_LOAD_RDEPS }, 47 { 'C', "", 1, PKG_LOAD_CATEGORIES }, 48 { 'O', "kvdD", 1, PKG_LOAD_OPTIONS }, 49 { 'L', "", 1, PKG_LOAD_LICENSES }, 50 { 'B', "", 1, PKG_LOAD_SHLIBS_REQUIRED }, 51 { 'b', "", 1, PKG_LOAD_SHLIBS_PROVIDED }, 52 { 'A', "tv", 1, PKG_LOAD_ANNOTATIONS }, 53 { '?', "drCOLBbA", 1, PKG_LOAD_BASIC }, /* dbflags handled in analyse_query_string() */ 54 { '#', "drCOLBbA", 1, PKG_LOAD_BASIC }, /* dbflags handled in analyse_query_string() */ 55 { 's', "hb", 0, PKG_LOAD_BASIC }, 56 { 'n', "", 0, PKG_LOAD_BASIC }, 57 { 'e', "", 0, PKG_LOAD_BASIC }, 58 { 'v', "", 0, PKG_LOAD_BASIC }, 59 { 'o', "", 0, PKG_LOAD_BASIC }, 60 { 'R', "", 0, PKG_LOAD_BASIC }, 61 { 'p', "", 0, PKG_LOAD_BASIC }, 62 { 'm', "", 0, PKG_LOAD_BASIC }, 63 { 'c', "", 0, PKG_LOAD_BASIC }, 64 { 'w', "", 0, PKG_LOAD_BASIC }, 65 { 'l', "", 0, PKG_LOAD_BASIC }, 66 { 'q', "", 0, PKG_LOAD_BASIC }, 67 { 'M', "", 0, PKG_LOAD_BASIC } 68 }; 69 70 void 71 usage_rquery(void) 72 { 73 fprintf(stderr, "Usage: pkg rquery [-r reponame] [-I|<query-format>] <pkg-name>\n"); 74 fprintf(stderr, " pkg rquery [-a] [-r reponame] [-I|<query-format>]\n"); 75 fprintf(stderr, " pkg rquery -e <evaluation> [-r reponame] <query-format>\n"); 76 fprintf(stderr, " pkg rquery [-Cgix] [-r reponame] [-I|<query-format>] <pattern> <...>\n\n"); 77 fprintf(stderr, "For more information see 'pkg help rquery.'\n"); 78 } 79 80 static void 81 print_index(struct pkg *pkg, const char *portsdir) 82 { 83 84 pkg_printf( 85 "%n-%v|" /* PKGNAME */ 86 "%S/%o|" /* PORTDIR */ 87 "%p|" /* PREFIX */ 88 "%c|" /* COMMENT */ 89 "%S/%o/pkg-descr|" /* _DESCR */ 90 "%m|" /* MAINTAINER */ 91 "%C%{%Cn%| %}|" /* CATEGORIES */ 92 "|" /* BUILD_DEPENDS */ 93 "%d%{%dn-%dv%| %}|" /* RUN_DEPENDS */ 94 "%w|" /* WWW */ 95 "|" /* EXTRACT_DEPENDS */ 96 "|" /* PATCH_DEPENDS */ 97 "\n", /* FETCH_DEPENDS */ 98 pkg, pkg, portsdir, pkg, pkg, pkg, portsdir, pkg, pkg, pkg, pkg, 99 pkg); 100 } 101 102 int 103 exec_rquery(int argc, char **argv) 104 { 105 struct pkgdb *db = NULL; 106 struct pkgdb_it *it = NULL; 107 struct pkg *pkg = NULL; 108 char *pkgname = NULL; 109 int query_flags = PKG_LOAD_BASIC; 110 match_t match = MATCH_EXACT; 111 int ch; 112 int ret = EPKG_OK; 113 int retcode = EXIT_SUCCESS; 114 int i; 115 char multiline = 0; 116 char *condition = NULL; 117 const char *condition_sql = NULL; 118 const char *portsdir; 119 xstring *sqlcond = NULL; 120 const unsigned int q_flags_len = NELEM(accepted_rquery_flags); 121 bool onematched = false; 122 bool old_quiet; 123 bool index_output = false; 124 c_charv_t reponames = vec_init(); 125 126 struct option longopts[] = { 127 { "all", no_argument, NULL, 'a' }, 128 { "case-sensitive", no_argument, NULL, 'C' }, 129 { "evaluate", required_argument, NULL, 'e' }, 130 { "glob", no_argument, NULL, 'g' }, 131 { "case-insensitive", no_argument, NULL, 'i' }, 132 { "index-line", no_argument, NULL, 'I' }, 133 { "repository", required_argument, NULL, 'r' }, 134 { "no-repo-update", no_argument, NULL, 'U' }, 135 { "regex", no_argument, NULL, 'x' }, 136 { NULL, 0, NULL, 0 }, 137 }; 138 139 portsdir = pkg_object_string(pkg_config_get("PORTSDIR")); 140 141 while ((ch = getopt_long(argc, argv, "+aCgiIxe:r:U", longopts, NULL)) != -1) { 142 switch (ch) { 143 case 'a': 144 match = MATCH_ALL; 145 break; 146 case 'C': 147 pkgdb_set_case_sensitivity(true); 148 break; 149 case 'e': 150 condition = optarg; 151 break; 152 case 'g': 153 match = MATCH_GLOB; 154 break; 155 case 'i': 156 pkgdb_set_case_sensitivity(false); 157 break; 158 case 'I': 159 index_output = true; 160 break; 161 case 'r': 162 vec_push(&reponames, optarg); 163 break; 164 case 'U': 165 auto_update = false; 166 break; 167 case 'x': 168 match = MATCH_REGEX; 169 break; 170 default: 171 usage_rquery(); 172 vec_free(&reponames); 173 return (EXIT_FAILURE); 174 } 175 } 176 177 argc -= optind; 178 argv += optind; 179 180 if (argc == 0 && !index_output) { 181 usage_rquery(); 182 vec_free(&reponames); 183 return (EXIT_FAILURE); 184 } 185 186 /* Default to all packages if no pkg provided */ 187 if (!index_output) { 188 if (argc == 1 && match == MATCH_EXACT) { 189 match = MATCH_ALL; 190 } else if (((argc == 1) ^ (match == MATCH_ALL )) && condition == NULL) { 191 usage_rquery(); 192 vec_free(&reponames); 193 return (EXIT_FAILURE); 194 } 195 } else { 196 if (argc == 0) 197 match = MATCH_ALL; 198 } 199 200 if (!index_output && analyse_query_string(argv[0], accepted_rquery_flags, q_flags_len, &query_flags, &multiline) != EPKG_OK) { 201 vec_free(&reponames); 202 return (EXIT_FAILURE); 203 } 204 205 if (condition != NULL) { 206 sqlcond = xstring_new(); 207 if (format_sql_condition(condition, sqlcond, true) != EPKG_OK) { 208 xstring_free(sqlcond); 209 vec_free(&reponames); 210 return (EXIT_FAILURE); 211 } 212 } 213 214 ret = pkgdb_access2(PKGDB_MODE_READ, PKGDB_DB_REPO, &reponames); 215 if (ret == EPKG_ENOACCESS) { 216 warnx("Insufficient privileges to query the package database"); 217 xstring_free(sqlcond); 218 vec_free(&reponames); 219 return (EXIT_FAILURE); 220 } else if (ret != EPKG_OK) { 221 xstring_free(sqlcond); 222 vec_free(&reponames); 223 return (EXIT_FAILURE); 224 } 225 226 /* first update the remote repositories if needed */ 227 old_quiet = quiet; 228 quiet = true; 229 if (auto_update && (ret = pkgcli_update(false, false, &reponames)) != EPKG_OK) { 230 xstring_free(sqlcond); 231 vec_free(&reponames); 232 return (ret); 233 } 234 quiet = old_quiet; 235 236 ret = pkgdb_open_all2(&db, PKGDB_REMOTE, &reponames); 237 if (ret != EPKG_OK) { 238 xstring_free(sqlcond); 239 vec_free(&reponames); 240 return (EXIT_FAILURE); 241 } 242 pkg_drop_privileges(); 243 244 if (index_output) 245 query_flags = PKG_LOAD_BASIC|PKG_LOAD_CATEGORIES|PKG_LOAD_DEPS; 246 247 if (sqlcond) { 248 fflush(sqlcond->fp); 249 condition_sql = sqlcond->buf; 250 } 251 if (match == MATCH_ALL) { 252 if ((it = pkgdb_repo_query_cond2(db, condition_sql, NULL, match, &reponames)) == NULL) { 253 xstring_free(sqlcond); 254 vec_free(&reponames); 255 return (EXIT_FAILURE); 256 } 257 258 while ((ret = pkgdb_it_next(it, &pkg, query_flags)) == EPKG_OK) { 259 if (index_output) 260 print_index(pkg, portsdir); 261 else 262 print_query(pkg, argv[0], multiline); 263 } 264 265 if (ret != EPKG_END) 266 retcode = EXIT_FAILURE; 267 268 pkgdb_it_free(it); 269 } else { 270 for (i = (index_output ? 0 : 1); i < argc; i++) { 271 pkgname = argv[i]; 272 273 if ((it = pkgdb_repo_query_cond2(db, condition_sql, pkgname, match, &reponames)) == NULL) { 274 xstring_free(sqlcond); 275 vec_free(&reponames); 276 return (EXIT_FAILURE); 277 } 278 279 while ((ret = pkgdb_it_next(it, &pkg, query_flags)) == EPKG_OK) { 280 onematched = true; 281 if (index_output) 282 print_index(pkg, portsdir); 283 else 284 print_query(pkg, argv[0], multiline); 285 } 286 287 if (ret != EPKG_END) { 288 retcode = EXIT_FAILURE; 289 break; 290 } 291 292 pkgdb_it_free(it); 293 } 294 if (!onematched && retcode == EXIT_SUCCESS) 295 retcode = EXIT_FAILURE; 296 } 297 298 xstring_free(sqlcond); 299 pkg_free(pkg); 300 pkgdb_close(db); 301 vec_free(&reponames); 302 303 return (retcode); 304 }