/ src / rquery.c
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  }