Radish alpha
H
rad:z3QDZAW2FAfuLvihrhiyDC9fAD8G9
HardenedBSD Package Manager
Radicle
Git
pkg search rework * Allow searching of pkg repos by any of: name name-version origin comment descr * Add options to display all the different items of data in the repo catalogue * refactor print_info() * consequential changes to pkg info * various style(9) cleanup
Matthew Seaman committed 13 years ago
commit 771bc8521297a28ac5ec0410a54a152531c58259
parent a11b6ed
6 files changed +685 -231
modified pkg/info.c
@@ -50,8 +50,8 @@ usage_info(void)
{
	fprintf(stderr, "usage: pkg info <pkg-name>\n");
	fprintf(stderr, "       pkg info -a\n");
-
	fprintf(stderr, "       pkg info [-eDgxXdrlBsqOf] <pkg-name>\n");
-
	fprintf(stderr, "       pkg info [-drlBsqfR] -F <pkg-file>\n\n");
+
	fprintf(stderr, "       pkg info [-BDdefgIlOqRrsXx] <pkg-name>\n");
+
	fprintf(stderr, "       pkg info [-BDdfIlqRrs] -F <pkg-file>\n\n");
	fprintf(stderr, "For more information see 'pkg help info'.\n");
}

@@ -67,7 +67,7 @@ exec_info(int argc, char **argv)
	struct pkgdb_it *it = NULL;
	int query_flags = PKG_LOAD_BASIC;
	struct pkg *pkg = NULL;
-
	unsigned int opt = 0;
+
	unsigned int opt = INFO_TAG_NAMEVER;
	match_t match = MATCH_EXACT;
	char *pkgname;
	char *pkgversion = NULL, *pkgversion2 = NULL;
@@ -79,19 +79,20 @@ exec_info(int argc, char **argv)
	int i, j;
	int sign = 0;
	int sign2 = 0;
+
	bool pkg_exists = false;
+
	bool origin_search = false;

	/* TODO: exclusive opts ? */
-
	while ((ch = getopt(argc, argv, "aDegxXEdrlBsqopOfF:R")) != -1) {
+
	while ((ch = getopt(argc, argv, "aDegxXEIdrlBsqopOfF:R")) != -1) {
		switch (ch) {
		case 'a':
			match = MATCH_ALL;
			break;
		case 'O':
-
			/* this is only for ports compat */
-
			opt |= INFO_ORIGIN_SEARCH;
+
			origin_search = true;  /* this is only for ports compat */
			break;
		case 'e':
-
			opt |= INFO_EXISTS;
+
			pkg_exists = true;;
			retcode = 1;
			break;
		case 'g':
@@ -104,31 +105,34 @@ exec_info(int argc, char **argv)
			match = MATCH_EREGEX;
			break;
		case 'D':
-
			opt |= INFO_PRINT_MESSAGE;
+
			opt |= INFO_MESSAGE;
			query_flags |= PKG_LOAD_BASIC;
			break;
		case 'd':
-
			opt |= INFO_PRINT_DEP;
+
			opt |= INFO_DEPS;
			query_flags |= PKG_LOAD_DEPS;
			break;
+
		case 'I':
+
			opt |= INFO_COMMENT;
+
			break;
		case 'r':
-
			opt |= INFO_PRINT_RDEP;
+
			opt |= INFO_RDEPS;
			query_flags |= PKG_LOAD_RDEPS;
			break;
		case 'l':
-
			opt |= INFO_LIST_FILES;
+
			opt |= INFO_FILES;
			query_flags |= PKG_LOAD_FILES;
			break;
		case 'B':
-
			opt |= INFO_LIST_SHLIBS;
+
			opt |= INFO_SHLIBS;
			query_flags |= PKG_LOAD_SHLIBS;
			break;
		case 's':
-
			opt |= INFO_SIZE;
+
			opt |= INFO_FLATSIZE;
			break;
		case 'E': /* ports compatibility */
		case 'q':
-
			opt |= INFO_QUIET;
+
			quiet = true;
			break;
		case 'o':
			opt |= INFO_ORIGIN;
@@ -138,14 +142,25 @@ exec_info(int argc, char **argv)
			break;
		case 'f':
			opt |= INFO_FULL;
-
			query_flags |= PKG_LOAD_CATEGORIES|PKG_LOAD_LICENSES|PKG_LOAD_OPTIONS;
+
			query_flags |= PKG_LOAD_CATEGORIES |
+
				PKG_LOAD_LICENSES	   |
+
				PKG_LOAD_OPTIONS;
			break;
		case 'F':
			file = optarg;
			break;
		case 'R':
			opt |= INFO_RAW;
-
			query_flags |= PKG_LOAD_FILES|PKG_LOAD_DIRS|PKG_LOAD_CATEGORIES|PKG_LOAD_LICENSES|PKG_LOAD_OPTIONS|PKG_LOAD_SCRIPTS|PKG_LOAD_USERS|PKG_LOAD_GROUPS|PKG_LOAD_DEPS|PKG_LOAD_SHLIBS;
+
			query_flags |= PKG_LOAD_FILES |
+
				PKG_LOAD_DIRS	      |
+
				PKG_LOAD_CATEGORIES   |
+
				PKG_LOAD_LICENSES     |
+
				PKG_LOAD_OPTIONS      |
+
				PKG_LOAD_SCRIPTS      |
+
				PKG_LOAD_USERS	      |
+
				PKG_LOAD_GROUPS	      |
+
				PKG_LOAD_DEPS	      |
+
				PKG_LOAD_SHLIBS;
			break;
		default:
			usage_info();
@@ -161,12 +176,27 @@ exec_info(int argc, char **argv)

	if (argc == 0 && file == NULL && match != MATCH_ALL) {
		/* which -O bsd.*.mk always execpt clean output */
-
		if (opt & INFO_ORIGIN_SEARCH)
+
		if (origin_search)
			return (EX_OK);
		usage_info();
		return (EX_USAGE);
	}

+
	/* When no other data is requested, default is to print
+
	 * 'name-ver comment' For -O, just print name-ver */
+
	if (!origin_search && (opt & INFO_ALL) == 0 && match == MATCH_ALL) 
+
		opt |= INFO_COMMENT;
+

+
	/* Special compatibility: handle -O and -q -O */
+
	if (origin_search) {
+
		if (quiet) {
+
			opt = INFO_TAG_NAMEVER;
+
			quiet = false;
+
		} else {
+
			opt = INFO_TAG_NAMEVER|INFO_COMMENT;
+
		}
+
	}
+

	if (file != NULL) {
		if (pkg_open(&pkg, file, NULL) != EPKG_OK) {
			return (1);
@@ -184,7 +214,7 @@ exec_info(int argc, char **argv)
		if (match == MATCH_ALL)
			return (EX_OK);

-
		if ((opt & INFO_QUIET) == 0)
+
		if (!quiet)
			printf("No packages installed.\n");

		return (EX_UNAVAILABLE);
@@ -294,7 +324,7 @@ exec_info(int argc, char **argv)

		/* ports infrastructure expects pkg info -q -O to always return 0 even
		 * if the ports doesn't exists */
-
		if (opt & INFO_ORIGIN_SEARCH)
+
		if (origin_search)
			gotone = true;

		/* end of compatibility hacks */
@@ -352,7 +382,7 @@ exec_info(int argc, char **argv)
					break;
				}
			}
-
			if (opt & INFO_EXISTS)
+
			if (pkg_exists)
				retcode = EX_OK;
			else
				print_info(pkg, opt);
@@ -362,7 +392,7 @@ exec_info(int argc, char **argv)
		}

		if (retcode == EX_OK && !gotone && match != MATCH_ALL) {
-
			if ((opt & INFO_QUIET) == 0)
+
			if (!quiet)
				warnx("No package(s) matching %s", argv[i]);
			retcode = EX_SOFTWARE;
		}
modified pkg/pkg-info.8
@@ -27,10 +27,10 @@
.Nm
.Fl a
.Nm
-
.Op Fl eDgxXEdrlsqopOfRF
+
.Op Fl BDdefgIlOqRrsXx
.Ar <pkg-name>
.Nm
-
.Op Fl drlsq
+
.Op Fl BDdfIlqRrs
.Fl F Ar <pkg-file>
.Sh DESCRIPTION
.Nm
modified pkg/pkg-search.8
@@ -20,43 +20,60 @@
.Os
.Sh NAME
.Nm "pkg search"
-
.Nd searches in remote package repositories
+
.Nd search package repository catalogues
.Sh SYNOPSIS
.Nm
+
.Op Fl "r repo"
+
.Op Fl egxX
+
.Op Fl "S search"
+
.Op Fl "L label"
+
.Op Fl "M mod"
.Ar pkg-name
.Nm
-
.Op Fl fDsqop
+
.Op Fl "r repo"
+
.Op Fl egxX
+
.Op Fl qcdfDsop
.Ar pkg-name
-
.Nm
-
.Op Fl gexXcdfDsqop
-
.Ar pattern
.Sh DESCRIPTION
.Nm
-
is used for searching in the remote package repositories
-
and it displays the requested information for the matching
-
packages.
+
is used for searching package repository catalogues.
+
Packages available for installation can be matched by name, by name
+
and version, by origin or by text in the package comments or package
+
descriptions.
+
The output defaults to displaying the field matched by the search
+
term, but any of the searchable fields may be displayed.
+
The output may be modified to additionally show many other data
+
associated with the matched package available from the repository
+
catalogues.
.Pp
-
.Nm
-
will search for the given pattern in the remote package
-
repositories, which are defined in the
-
.Xr pkg.conf 5
-
file.
+
It is recommended to update the local copies of the repository
+
catalogues before running
+
.Nm .
+
See
+
.Xr pkg-update 8 .
+
Package repositories are defined in the
+
.Fa pkg.conf
+
file; see
+
.Xr pkg.conf 5.
.Sh OPTIONS
The following options are supported by
.Nm :
.Bl -tag -width F1
.It Fl e
-
Treat
+
Match
.Ar pattern
-
as exact pattern
+
exactly against the search field.
.It Fl g
Treat
.Ar pattern
as a shell glob pattern.
+
The globbing expression must match the entirety of the field being seached.
.It Fl x
Treat
.Ar pattern
-
as a regular expression.
+
as a regular expression.  This is the default.
+
Matches any substring of the search field unless explicit beginning
+
(^) and ending (?) anchor terms are used.
.It Fl X
Treat
.Ar pattern
modified pkg/pkgcli.h
@@ -146,24 +146,62 @@ int exec_which(int, char **);
void usage_which(void);

/* utils */
-
#define INFO_PRINT_DEP (1<<0)
-
#define INFO_PRINT_RDEP (1<<1)
-
#define INFO_EXISTS (1<<2)
-
#define INFO_LIST_FILES (1<<3)
-
#define INFO_SIZE (1<<4)
-
#define INFO_QUIET (1<<5)
-
#define INFO_ORIGIN (1<<6)
-
#define INFO_ORIGIN_SEARCH (1<<7)
-
#define INFO_PREFIX (1<<8)
-
#define INFO_FULL (1<<9)
-
#define INFO_RAW (1<<10)
-
#define INFO_LIST_SHLIBS (1<<11)
-
#define INFO_PRINT_MESSAGE (1<<12)
+

+
/* These are the fields of the Full output, in order */
+
#define INFO_NAME	(1<<0)
+
#define INFO_VERSION	(1<<1)
+
#define INFO_ORIGIN	(1<<2)
+
#define INFO_PREFIX	(1<<3)
+
#define INFO_REPOSITORY	(1<<4)
+
#define INFO_CATEGORIES	(1<<5)
+
#define INFO_LICENSES	(1<<6)
+
#define INFO_MAINTAINER	(1<<7)
+
#define INFO_WWW	(1<<8)
+
#define INFO_COMMENT	(1<<9)
+
#define INFO_OPTIONS	(1<<10)
+
#define INFO_SHLIBS	(1<<11)
+
#define INFO_FLATSIZE	(1<<12)
+
#define INFO_PKGSIZE	(1<<13)
+
#define INFO_DESCR	(1<<14)
+

+
/* Other fields not part of the Full output */
+
#define INFO_MESSAGE	(1<<15)
+
#define INFO_DEPS	(1<<16)
+
#define INFO_RDEPS	(1<<17)
+
#define INFO_FILES	(1<<18)
+
#define INFO_DIRS	(1<<19)
+
#define INFO_USERS	(1<<20)
+
#define INFO_GROUPS	(1<<21)
+
#define INFO_ARCH	(1<<22)
+
#define INFO_REPOURL	(1<<23)
+

+
#define INFO_LASTFIELD	INFO_REPOURL
+
#define INFO_ALL	(((INFO_LASTFIELD) << 1) - 1)
+

+
/* Identifying tags */
+
#define INFO_TAG_NAME		(1<<28)
+
#define INFO_TAG_ORIGIN		(1<<29)
+
#define INFO_TAG_NAMEVER	(1<<30)
+

+
/* Output YAML format */
+
#define INFO_RAW	(1<<31)
+

+
/* Everything in the 'full' package output */
+
#define INFO_FULL	(INFO_NAME|INFO_VERSION|INFO_ORIGIN|INFO_PREFIX| \
+
			 INFO_REPOSITORY|INFO_CATEGORIES|INFO_LICENSES|  \
+
			 INFO_MAINTAINER|INFO_WWW|INFO_COMMENT|          \
+
			 INFO_OPTIONS|INFO_SHLIBS|INFO_FLATSIZE|         \
+
			 INFO_PKGSIZE|INFO_DESCR)
+

+
/* Everything that can take more than one line to print */
+
#define INFO_MULTILINE	(INFO_OPTIONS|INFO_SHLIBS|INFO_DESCR|INFO_MESSAGE| \
+
			 INFO_DEPS|INFO_RDEPS|INFO_FILES|INFO_DIRS)

bool query_yesno(const char *msg, ...);
void print_info(struct pkg * const pkg, unsigned int opt);
char *absolutepath(const char *src, char *dest, size_t dest_len);
-
void print_jobs_summary(struct pkg_jobs *j, pkg_jobs_t type, const char *msg, ...);
+
void print_jobs_summary(struct pkg_jobs *j, pkg_jobs_t type,
+
			const char *msg, ...);
struct sbuf *exec_buf(const char *cmd);

int event_callback(void *data, struct pkg_event *ev);
@@ -180,7 +218,10 @@ struct query_flags {
};

void print_query(struct pkg *pkg, char *qstr, char multiline);
-
int format_sql_condition(const char *str, struct sbuf *sqlcond, bool for_remote);
-
int analyse_query_string(char *qstr, struct query_flags *q_flags, const unsigned int q_flags_len, int *flags, char *multiline);
+
int format_sql_condition(const char *str, struct sbuf *sqlcond,
+
			 bool for_remote);
+
int analyse_query_string(char *qstr, struct query_flags *q_flags,
+
			 const unsigned int q_flags_len, int *flags,
+
			 char *multiline);

#endif
modified pkg/search.c
@@ -30,17 +30,92 @@
#include <string.h>
#include <unistd.h>
#include <sysexits.h>
+
#include <err.h>

#include <pkg.h>

#include "pkgcli.h"

+
typedef struct _cliopt {
+
	const char *option;
+
	char key;
+
} cliopt;
+

+
/* an option string should not be a prefix of any other option */ 
+
static const cliopt search_label[] = {
+
	{ "comment",     'c'  },
+
	{ "description", 'd'  },
+
	{ "name",        'n'  },
+
	{ "origin",      'o'  },
+
	{ "pkg-name",    'p'  },
+
	{ NULL,          '\0' },
+
};
+

+
static const cliopt modifiers[] = {
+
	{ "arch",         'a'  },
+
	{ "comment",      'c'  },
+
	{ "depends-on",   'd'  },
+
	{ "description",  'D'  },
+
	{ "full",         'f'  },
+
	{ "maintainer",   'm'  },
+
	{ "pkg-size",	  'P'  },
+
	{ "prefix",       'p'  },
+
	{ "repository",   'R'  },
+
	{ "required-by",  'r'  },
+
	{ "shared-libs",  'S'  },
+
	{ "size",         's'  },
+
	{ "url",          'u'  },
+
	{ "www",          'w'  },
+
	{ NULL,           '\0' },
+
};	
+

+
static char
+
match_optarg(const cliopt *optlist, const char *opt)
+
{
+
	int i, matched = -1;
+
	char key = '\0';
+
	size_t optlen;
+

+
	optlen = strlen(opt);
+

+
	/* Match any unique prefix from  optlist */
+
	for (i = 0; optlist[i].option != NULL; i++) {
+
		if (strncmp(opt, optlist[i].option, optlen) != 0)
+
			continue;
+
		if (matched > 0) {
+
			warnx("\"%s\" is ambiguous: did you mean "
+
			      "\"%s\" or \"%s\"?", opt,
+
			      optlist[matched].option, optlist[i].option);
+
			key = '\0';
+
			break;
+
		}
+
		matched = i;
+
		key = optlist[i].key;
+
	}
+
	return (key);
+
}
+

void
usage_search(void)
{
-
	fprintf(stderr, "usage: pkg search [-r reponame] <pkg-name>\n");
-
	fprintf(stderr, "       pkg search [-r reponame] [-fDsqop] <pkg-name>\n");
-
	fprintf(stderr, "       pkg search [-r reponame] [-egxXcdfDsqop] <pattern>\n\n");
+
	int i, n;
+

+
	fprintf(stderr, "usage: pkg search [-r repo] [-egXx] [-S search] [-L label] [-M mod]... <pkg-name>\n");
+
	fprintf(stderr, "       pkg search [-r repo] [-egXx] [-cDdfopqS] <pattern>\n\n");
+
	n = fprintf(stderr, "       Search and Label options:");
+
	for (i = 0; search_label[i].option != NULL; i++) {
+
		if (n > 72)
+
			n = fprintf(stderr, "\n            ");
+
		n += fprintf(stderr, " %s", search_label[i].option);
+
	}
+
	fprintf(stderr, "\n");
+
	n = fprintf(stderr, "       Output Modifiers:");
+
	for (i = 0; modifiers[i].option != NULL; i++) {
+
		if (n > 68)
+
			n = fprintf(stderr, "\n            ");
+
		n += fprintf(stderr, " %s", modifiers[i].option);
+
	}
+
	fprintf(stderr, "\n");
	fprintf(stderr, "For more information see 'pkg help search'.\n");
}

@@ -53,13 +128,14 @@ exec_search(int argc, char **argv)
	int flags = PKG_LOAD_BASIC;
	unsigned int opt = 0;
	match_t match = MATCH_REGEX;
-
	pkgdb_field field = FIELD_NAME;
+
	pkgdb_field search = FIELD_NONE;
+
	pkgdb_field label = FIELD_NONE;
	struct pkgdb *db = NULL;
	struct pkgdb_it *it = NULL;
	struct pkg *pkg = NULL;
	bool atleastone = false;

-
	while ((ch = getopt(argc, argv, "gxXcdr:fDsqop")) != -1) {
+
	while ((ch = getopt(argc, argv, "egxXr:S:L:M:cdfDsopq")) != -1) {
		switch (ch) {
		case 'e':
			match = MATCH_EXACT;
@@ -73,33 +149,135 @@ exec_search(int argc, char **argv)
		case 'X':
			match = MATCH_EREGEX;
			break;
-
		case 'c':
-
			field = FIELD_COMMENT;
-
			break;
-
		case 'd':
-
			field = FIELD_DESC;
-
			break;
		case 'r':
			reponame = optarg;
-
		case 'f':
-
			opt |= INFO_FULL;
-
			flags |= PKG_LOAD_CATEGORIES|PKG_LOAD_LICENSES|PKG_LOAD_OPTIONS|PKG_LOAD_SHLIBS;
-
			break;
-
		case 'D':
-
			opt |= INFO_PRINT_DEP;
-
			flags |= PKG_LOAD_DEPS;
			break;
-
		case 's':
-
			opt |= INFO_SIZE;
+
		case 'S':
+
			/* search options */
+
			switch(match_optarg(search_label, optarg)) {
+
			case 'o':
+
				search = FIELD_ORIGIN;
+
				break;
+
			case 'n':
+
				search = FIELD_NAME;
+
				break;
+
			case 'p':
+
				search = FIELD_NAMEVER;
+
				break;
+
			case 'c':
+
			opt_S_c:
+
				search = FIELD_COMMENT;
+
				break;
+
			case 'd':
+
			opt_S_d:
+
				search = FIELD_DESC;
+
				break;
+
			default:
+
				usage_search();
+
				return (EX_USAGE);
+
			}
			break;
-
		case 'q':
-
			opt |= INFO_QUIET;
+
		case 'L':
+
			/* label options */
+
			switch(match_optarg(search_label, optarg)) {
+
			case 'o':
+
			opt_L_o:
+
				label = FIELD_ORIGIN;
+
				break;
+
			case 'n':
+
				label = FIELD_NAME;
+
				break;
+
			case 'p':
+
				label = FIELD_NAMEVER;
+
				break;
+
			case 'c':
+
				label = FIELD_COMMENT;
+
				break;
+
			case 'd':
+
				label = FIELD_DESC;
+
				break;
+
			default:
+
				usage_search();
+
				return (EX_USAGE);
+
			}
			break;
-
		case 'o':
-
			opt |= INFO_ORIGIN;
+
		case 'M':
+
			/* output modifiers */
+
			switch(match_optarg(modifiers, optarg)) {
+
			case 'a':
+
				opt |= INFO_ARCH;
+
				break;
+
			case 'c':
+
				opt |= INFO_COMMENT;
+
				break;
+
			case 'd':
+
			opt_M_d:
+
				opt |= INFO_DEPS;
+
				flags |= PKG_LOAD_DEPS;
+
				break;
+
			case 'D':
+
				opt |= INFO_DESCR;
+
				break;
+
			case 'f':
+
			opt_M_f:
+
				opt |= INFO_FULL;
+
				flags |= PKG_LOAD_CATEGORIES |
+
					PKG_LOAD_LICENSES    |
+
					PKG_LOAD_OPTIONS     |
+
					PKG_LOAD_SHLIBS;
+
				break;
+
			case 'm':
+
				opt |= INFO_MAINTAINER;
+
				break;
+
			case 'P':
+
				opt |= INFO_PKGSIZE;
+
				break;
+
			case 'p':
+
			opt_M_p:
+
				opt |= INFO_PREFIX;
+
				break;
+
			case 'R':
+
				opt |= INFO_REPOSITORY;
+
				break;
+
			case 'r':
+
				opt |= INFO_RDEPS;
+
				flags |= PKG_LOAD_RDEPS;
+
				break;
+
			case 'S':
+
				opt |= INFO_SHLIBS;
+
				flags |= PKG_LOAD_SHLIBS;
+
				break;
+
			case 's':
+
			opt_M_s:
+
				opt |= INFO_FLATSIZE;
+
				break;
+
			case 'u':
+
				opt |= INFO_REPOURL;
+
				break;
+
			case 'w':
+
				opt |= INFO_WWW;
+
				break;
+
			default:
+
				usage_search();
+
				return (EX_USAGE);
+
			}
			break;
-
		case 'p':
-
			opt |= INFO_PREFIX;
+
		case 'c':	/* Same as -S comment */
+
			goto opt_S_c;
+
		case 'd':	/* Same as -S depends-on */
+
			goto opt_S_d;
+
		case 'f':	/* Same as -M full */
+
			goto opt_M_f;
+
		case 'D':	/* Same as -M depends-on  */
+
			goto opt_M_d;
+
		case 's':	/* Same as -M size */
+
			goto opt_M_s;
+
		case 'o':	/* Same as -L origin */
+
			goto opt_L_o;
+
		case 'p':	/* Same as -M prefix */
+
			goto opt_M_p;
+
		case 'q':
+
			quiet = true;
			break;
		default:
			usage_search();
@@ -120,13 +298,39 @@ exec_search(int argc, char **argv)
		fprintf(stderr, "Pattern must not be empty!\n");
		return (EX_USAGE);
	}
-
	if (strchr(pattern, '/') != NULL)
-
		field = FIELD_ORIGIN;
+
	if (search == FIELD_NONE) {
+
		if (strchr(pattern, '/') != NULL)
+
			search = FIELD_ORIGIN;
+
		else
+
			search = FIELD_NAMEVER; /* Default search */
+
	}
+
	if (label == FIELD_NONE)
+
		label = search; /* By default, show what was searched  */
+

+
	switch(label) {
+
	case FIELD_NONE:
+
		break;		/* should never happen */
+
	case FIELD_ORIGIN:
+
		opt |= INFO_TAG_ORIGIN;
+
		break;
+
	case FIELD_NAME:
+
		opt |= INFO_TAG_NAME;
+
		break;
+
	case FIELD_NAMEVER:
+
		opt |= INFO_TAG_NAMEVER;
+
		break;
+
	case FIELD_COMMENT:
+
		opt |= INFO_TAG_NAMEVER|INFO_COMMENT;
+
		break;
+
	case FIELD_DESC:
+
		opt |= INFO_TAG_NAMEVER|INFO_DESCR;
+
		break;
+
	}

	if (pkgdb_open(&db, PKGDB_REMOTE) != EPKG_OK)
		return (EX_IOERR);

-
	if ((it = pkgdb_search(db, pattern, match, field, reponame)) == NULL) {
+
	if ((it = pkgdb_search(db, pattern, match, search, reponame)) == NULL) {
		pkgdb_close(db);
		return (EX_IOERR);
	}
@@ -143,8 +347,5 @@ exec_search(int argc, char **argv)
	if (!atleastone)
		ret = EPKG_FATAL;

-
	if (ret == EPKG_END)
-
		ret = EPKG_OK;
-

-
	return ((ret == EPKG_OK) ? EX_OK : EX_SOFTWARE);
+
	return ((ret == EPKG_OK || ret == EPKG_END) ? EX_OK : EX_SOFTWARE);
}
modified pkg/utils.c
@@ -2,6 +2,7 @@
 * Copyright (c) 2011-2012 Baptiste Daroussin <bapt@FreeBSD.org>
 * Copyright (c) 2011-2012 Julien Laffaye <jlaffaye@FreeBSD.org>
 * Copyright (c) 2011-2012 Marin Atanasov Nikolov <dnaeon@gmail.com>
+
 * Copyright (c) 2012 Matthew Seaman <matthew@FreeBSD.org>
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
@@ -54,7 +55,7 @@ query_yesno(const char *msg, ...)
	else if (c == '\n' || c == EOF)
		return false;

-
	while((c = getchar()) != '\n' && c != EOF)
+
	while ((c = getchar()) != '\n' && c != EOF)
		continue;

	return r;
@@ -129,166 +130,330 @@ absolutepath(const char *src, char *dest, size_t dest_len) {
}

void
-
print_info(struct pkg * const pkg, unsigned int opt)
+
print_info(struct pkg * const pkg, unsigned int options)
{
-
	struct pkg_dep *dep = NULL;
-
	struct pkg_file *file = NULL;
-
	struct pkg_category *cat = NULL;
-
	struct pkg_license *lic = NULL;
-
	struct pkg_option *option = NULL;
-
	struct pkg_shlib *shlib = NULL;
+
	struct pkg_category *cat    = NULL;
+
	struct pkg_dep	    *dep    = NULL;
+
	struct pkg_dir	    *dir    = NULL;
+
	struct pkg_file	    *file   = NULL;
+
	struct pkg_group    *group  = NULL;
+
	struct pkg_license  *lic    = NULL;
+
	struct pkg_option   *option = NULL;
+
	struct pkg_shlib    *shlib  = NULL;
+
	struct pkg_user	    *user   = NULL;
	bool multirepos_enabled = false;
-
	char buf[BUFSIZ];
-
	char *m;
+
	bool print_tag = false;
	char size[7];
	const char *name, *version, *prefix, *origin, *reponame, *repourl;
-
	const char *maintainer, *www, *comment, *desc, *message;
+
	const char *maintainer, *www, *comment, *desc, *message, *arch;
+
	const char *repopath;
+
	char *m;
+
	unsigned opt;
	int64_t flatsize, newflatsize, newpkgsize;
	lic_t licenselogic;
+
	int cout = 0;		/* Number of characters output */
+
	int info_num;		/* Number of different data items to print */

	pkg_config_bool(PKG_CONFIG_MULTIREPOS, &multirepos_enabled);

-
	pkg_get(pkg, PKG_NAME, &name, PKG_VERSION, &version, PKG_PREFIX, &prefix,
-
	    PKG_ORIGIN, &origin, PKG_REPONAME, &reponame, PKG_REPOURL, &repourl,
-
	    PKG_MAINTAINER, &maintainer, PKG_WWW, &www, PKG_COMMENT, &comment,
-
	    PKG_DESC, &desc, PKG_FLATSIZE, &flatsize, PKG_NEW_FLATSIZE, &newflatsize,
-
		PKG_NEW_PKGSIZE, &newpkgsize, PKG_LICENSE_LOGIC, &licenselogic, PKG_MESSAGE, &message);
-

-
	if (opt & INFO_RAW) {
-
		pkg_emit_manifest(pkg, &m);
-
		printf("%s\n", m);
-
		free(m);
-
	} else if (opt & INFO_FULL) {
-
		printf("%-15s: %s\n", "Name", name);
-
		printf("%-15s: %s\n", "Version", version);
-
		printf("%-15s: %s\n", "Origin", origin);
-
		printf("%-15s: %s\n", "Prefix", prefix);
-

-
		if ((pkg_type(pkg) == PKG_REMOTE) && multirepos_enabled)
-
			printf("%-15s: %s [%s]\n", "Repository", reponame, repourl);
-

-
                if (!pkg_list_is_empty(pkg, PKG_CATEGORIES)) {
-
                        printf("%-15s:", "Categories");
-
                        while (pkg_categories(pkg, &cat) == EPKG_OK)
-
                                printf(" %s", pkg_category_name(cat));
-
                        printf("\n");
-
                }
-

-
		if (!pkg_list_is_empty(pkg, PKG_LICENSES)) {
-
			printf("%-15s:", "Licenses");
-
			while (pkg_licenses(pkg, &lic) == EPKG_OK) {
-
				printf(" %s", pkg_license_name(lic));
-
				if (licenselogic != 1)
-
					printf(" %c", licenselogic);
-
				else
-
					printf(" ");
-
			}
-
			printf("\b \n");
+
	pkg_get(pkg,
+
		PKG_NAME,          &name,
+
		PKG_VERSION,       &version,
+
		PKG_PREFIX,        &prefix,
+
		PKG_ORIGIN,        &origin,
+
		PKG_REPONAME,      &reponame,
+
		PKG_REPOURL,       &repourl,
+
		PKG_MAINTAINER,    &maintainer,
+
		PKG_WWW,           &www,
+
		PKG_COMMENT,       &comment,
+
		PKG_DESC,          &desc,
+
		PKG_FLATSIZE,      &flatsize,
+
		PKG_NEW_FLATSIZE,  &newflatsize,
+
		PKG_NEW_PKGSIZE,   &newpkgsize,
+
		PKG_LICENSE_LOGIC, &licenselogic,
+
		PKG_MESSAGE,       &message,
+
		PKG_ARCH,	   &arch,
+
		PKG_REPOPATH,	   &repopath);
+

+
	if (!multirepos_enabled)
+
		pkg_config_string(PKG_CONFIG_REPO, &repourl);
+

+
	if (options & INFO_RAW) { /* Not for remote packages */
+
		if (pkg_type(pkg) != PKG_REMOTE) {
+
			pkg_emit_manifest(pkg, &m);
+
			printf("%s\n", m);
+
			free(m);
		}
+
		return;
+
	}

-
		printf("%-15s: %s\n", "Maintainer", maintainer);
-
		printf("%-15s: %s\n", "WWW", www);
-
		printf("%-15s: %s\n", "Comment", comment);
+
	if (!quiet) {
+
		/* Print a tag-line identifying the package -- either
+
		   NAMEVER, ORIGIN or NAME (in that order of
+
		   preference).  This may be the only output from this
+
		   function */
+

+
		if (options & INFO_TAG_NAMEVER)
+
			cout = printf("%s-%s", name, version);
+
		else if (options & INFO_TAG_ORIGIN)
+
			cout = printf("%s", origin);
+
		else if (options & INFO_TAG_NAME)
+
			cout = printf("%s", name);
+
	}

-
                if (!pkg_list_is_empty(pkg, PKG_OPTIONS)) {
-
                        printf("%-15s: \n", "Options");
-
                        while (pkg_options(pkg, &option) == EPKG_OK)
-
                                printf("\t%s: %s\n", pkg_option_opt(option), pkg_option_value(option));
-
                }
+
	/* If we printed a tag, and there are no other items to print,
+
	   then just return now. If there's only one single-line item
+
	   to print, show it at column 32 on the same line. If there's
+
	   one multi-line item to print, start a new line. If there is
+
	   more than one item to print per pkg, use 'key : value'
+
	   style to show on a new line.  */

-
		if (!pkg_list_is_empty(pkg, PKG_SHLIBS)) {
-
			printf("%-15s:", "SharedLibraries");
-
			while (pkg_shlibs(pkg, &shlib) == EPKG_OK)
-
				printf(" %s", pkg_shlib_name(shlib));
-
			printf("\n");
-
		}
-

-
		if (pkg_type(pkg) == PKG_INSTALLED || pkg_type(pkg) == PKG_FILE) {
-
			humanize_number(size, sizeof(size), flatsize, "B", HN_AUTOSCALE, 0);
-
			printf("%-15s: %s\n", "Flat size", size);
-
		} else {
-
			humanize_number(size, sizeof(size), newflatsize, "B", HN_AUTOSCALE, 0);
-
			printf("%-15s: %s\n", "Flat size", size);
-
			humanize_number(size, sizeof(size), newpkgsize, "B", HN_AUTOSCALE, 0);
-
			printf("%-15s: %s\n", "Pkg size", size);
-
		}
+
	info_num = 0;
+
	for (opt = 0x1; opt <= INFO_LASTFIELD; opt <<= 1) 
+
		if ((opt & options) != 0)
+
			info_num++;

-
		printf("%-15s: \n%s\n", "Description", desc);
+
	if (info_num == 0 && cout > 0) {
		printf("\n");
-
	} else if (opt & INFO_PRINT_DEP) {
-
		if (!(opt & INFO_QUIET))
-
			printf("%s-%s depends on:\n", name, version);
-

-
                while (pkg_deps(pkg, &dep) == EPKG_OK) {
-
                        printf("%s-%s\n", pkg_dep_name(dep), pkg_dep_version(dep));
-
                }
-

-
                if (!(opt & INFO_QUIET))
-
                        printf("\n");
-
	} else if (opt & INFO_PRINT_MESSAGE) {
-
		if (message)
-
			printf("%s", message);
-
	} else if (opt & INFO_PRINT_RDEP) {
-
		if (!(opt & INFO_QUIET))
-
			printf("%s-%s is required by:\n", name, version);
-

-
                while (pkg_rdeps(pkg, &dep) == EPKG_OK) {
-
                        printf("%s-%s\n", pkg_dep_name(dep), pkg_dep_version(dep));
-
                }
-

-
                if (!(opt & INFO_QUIET))
-
                        printf("\n");
-
	} else if (opt & INFO_LIST_FILES) {
-
		if (!(opt & INFO_QUIET))
-
			printf("%s-%s owns the following files:\n", name, version);
-

-
                while (pkg_files(pkg, &file) == EPKG_OK) {
-
                        printf("%s\n", pkg_file_path(file));
-
                }
-

-
                if (!(opt & INFO_QUIET))
-
                        printf("\n");
-
	} else if (opt & INFO_LIST_SHLIBS) {
-
		if (!(opt & INFO_QUIET))
-
			printf("%s-%s uses the following shared libraries:\n", name, version);
-

-
                while (pkg_shlibs(pkg, &shlib) == EPKG_OK) {
-
                        printf("%s\n", pkg_shlib_name(shlib));
-
                }
-

-
                if (!(opt & INFO_QUIET))
-
                        printf("\n");
-
        } else if (opt & INFO_SIZE) {
-
		if (pkg_type(pkg) == PKG_INSTALLED) {
-
			humanize_number(size, sizeof(size), flatsize, "B", HN_AUTOSCALE, 0);
-
			printf("%s-%s size is: %s\n", name, version, size);
-
		} else {
-
			humanize_number(size, sizeof(size), newflatsize, "B", HN_AUTOSCALE, 0);
-
			printf("%s-%s flat size is: %s\n", name, version, size);
-
			humanize_number(size, sizeof(size), newpkgsize, "B", HN_AUTOSCALE, 0);
-
			printf("%s-%s package size is: %s\n", name, version, size);
+
		return;
+
	}
+

+
	if (info_num == 1) {
+
		/* Only one item to print */
+
		print_tag = false;
+
		if (!quiet) {
+
			if (options & INFO_MULTILINE)
+
				printf(":\n");
+
			else {
+
				if (cout < 31)
+
					cout = 31 - cout;
+
				else
+
					cout = 1;
+
				printf("%*s", cout, " ");
+
			}
		}
-
        } else if (opt & INFO_ORIGIN) {
-
                if (opt & INFO_QUIET)
-
                        printf("%s\n", origin);
-
                else
-
                        printf("%s-%s: %s\n", name, version, origin);
-
        } else if (opt & INFO_PREFIX) {
-
                if (opt & INFO_QUIET)
-
                        printf("%s\n", prefix);
-
                else
-
                        printf("%s-%s: %s\n", name, version, prefix);
-
        } else {
-
                if (opt & INFO_QUIET)
-
                        printf("%s-%s\n", name, version);
-
                else {
-
			snprintf(buf, BUFSIZ, "%s-%s", name, version);
-
			if ((pkg_type(pkg) == PKG_REMOTE) && multirepos_enabled)
-
				printf("%-30s [repository: %s]: %s\n", buf, reponame, comment);
+
	} else {
+
		/* Several items to print */
+
		print_tag = true;
+
		if (!quiet)
+
			printf("\n");
+
	}
+

+
	for (opt = 0x1; opt <= INFO_LASTFIELD; opt <<= 1) {
+
		if ((opt & options) == 0)
+
			continue;
+

+
		switch (opt) {
+
		case INFO_NAME:
+
			if (print_tag)
+
				printf("%-15s: ", "Name");
+
			printf("%s\n", name);
+
			break;
+
		case INFO_VERSION:
+
			if (print_tag)
+
				printf("%-15s: ", "Version");
+
			printf("%s\n", version);
+
			break;
+
		case INFO_ORIGIN:
+
			if (print_tag)
+
				printf("%-15s: ", "Origin");
+
			printf("%s\n", origin);
+
			break;
+
		case INFO_PREFIX:
+
			if (print_tag)
+
				printf("%-15s: ", "Prefix");
+
			printf("%s\n", prefix);
+
			break;
+
		case INFO_REPOSITORY:
+
			if (pkg_type(pkg) == PKG_REMOTE &&
+
			    repourl != NULL && repourl[0] != '\0') {
+
				if (print_tag)
+
					printf("%-15s: ", "Repository");
+
				printf("%s [%s]\n", reponame, repourl);
+
			} else if (!print_tag)
+
				printf("\n");
+
			break;
+
		case INFO_CATEGORIES:
+
			if (!pkg_list_is_empty(pkg, PKG_CATEGORIES)) {
+
				if (print_tag)
+
					printf("%-15s: ", "Categories");
+
				if (pkg_categories(pkg, &cat) == EPKG_OK)
+
					printf("%s", pkg_category_name(cat));
+
				while (pkg_categories(pkg, &cat) == EPKG_OK)
+
					printf(" %s", pkg_category_name(cat));
+
				printf("\n");
+
			} else if (!print_tag)
+
				printf("\n");
+
			break;
+
		case INFO_LICENSES:
+
			if (!pkg_list_is_empty(pkg, PKG_LICENSES)) {
+
				if (print_tag)
+
					printf("%-15s: ", "Licenses");
+
				if (pkg_licenses(pkg, &lic) == EPKG_OK)
+
					printf("%s", pkg_license_name(lic));
+
				while (pkg_licenses(pkg, &lic) == EPKG_OK) {
+
					if (licenselogic != 1)
+
						printf(" %c", licenselogic);
+
					printf(" %s", pkg_license_name(lic));
+
				}
+
				printf("\n");				
+
			} else if (!print_tag)
+
				printf("\n");
+
			break;
+
		case INFO_MAINTAINER:
+
			if (print_tag)
+
				printf("%-15s: ", "Maintainer");
+
			printf("%s\n", maintainer);
+
			break;
+
		case INFO_WWW:	
+
			if (print_tag)
+
				printf("%-15s: ", "WWW");
+
			printf("%s\n", www);
+
			break;
+
		case INFO_COMMENT:
+
			if (print_tag)
+
				printf("%-15s: ", "Comment");
+
			printf("%s\n", comment);
+
			break;
+
		case INFO_OPTIONS:
+
			if (!pkg_list_is_empty(pkg, PKG_OPTIONS)) {
+
				if (print_tag)
+
					printf("%-15s:\n", "Options");
+
				while (pkg_options(pkg, &option) == EPKG_OK)
+
					printf("\t%-15s: %s\n",
+
					       pkg_option_opt(option),
+
					       pkg_option_value(option));
+
			}
+
			break;
+
		case INFO_SHLIBS:
+
			if (!pkg_list_is_empty(pkg, PKG_SHLIBS)) {
+
				if (print_tag)
+
					printf("%-15s:\n", "Shared Libs");
+
				while (pkg_shlibs(pkg, &shlib) == EPKG_OK)
+
					printf("\t%s\n", pkg_shlib_name(shlib));
+
			}
+
			break;
+
		case INFO_FLATSIZE:
+
			if (pkg_type(pkg) == PKG_INSTALLED ||
+
			    pkg_type(pkg) == PKG_FILE)
+
				humanize_number(size, sizeof(size),
+
						flatsize,"B",
+
						HN_AUTOSCALE, 0);
			else
-
				printf("%-30s %s\n", buf, comment);
+
				humanize_number(size, sizeof(size),
+
						newflatsize,"B",
+
						HN_AUTOSCALE, 0);
+

+
			if (print_tag)
+
				printf("%-15s: ", "Flat size");
+
			printf("%s\n", size);
+
			break;
+
		case INFO_PKGSIZE: /* Remote pkgs only */
+
			if (pkg_type(pkg) == PKG_REMOTE) {
+
				humanize_number(size, sizeof(size),
+
						newpkgsize,"B",
+
						HN_AUTOSCALE, 0);
+
				if (print_tag)
+
					printf("%-15s: ", "Pkg size");
+
				printf("%s\n", size);
+
			} else if (!print_tag)
+
				printf("\n");
+
			break;
+
		case INFO_DESCR:
+
			if (print_tag)
+
				printf("%-15s:\n", "Description");
+
			printf("%s\n", desc);
+
			break;
+
		case INFO_MESSAGE:
+
			if (message) {
+
				if (print_tag)
+
					printf("%-15s:\n", "Message");
+
				printf("%s\n", message);
+
			}
+
			break;
+
		case INFO_DEPS:
+
			if (!pkg_list_is_empty(pkg, PKG_DEPS)) {
+
				if (print_tag)
+
					printf("%-15s:\n", "Depends on");
+
				while (pkg_deps(pkg, &dep) == EPKG_OK)
+
					printf("\t%s-%s\n",
+
					       pkg_dep_name(dep),
+
					       pkg_dep_version(dep));
+
			}
+
			break;
+
		case INFO_RDEPS:
+
			if (!pkg_list_is_empty(pkg, PKG_RDEPS)) {
+
				if (print_tag)
+
					printf("%-15s:\n", "Required by");
+
				while (pkg_rdeps(pkg, &dep) == EPKG_OK)
+
					printf("\t%s-%s\n",
+
					       pkg_dep_name(dep),
+
					       pkg_dep_version(dep));
+
			}
+
			break;
+
		case INFO_FILES: /* Installed pkgs only */
+
			if (pkg_type(pkg) != PKG_REMOTE &&
+
			    !pkg_list_is_empty(pkg, PKG_FILES)) {
+
				if (print_tag)
+
					printf("%-15s:\n", "Files");
+
				while (pkg_files(pkg, &file) == EPKG_OK)
+
					printf("\t%s\n",
+
					       pkg_file_path(file));
+
			}
+
			break;
+
		case INFO_DIRS:	/* Installed pkgs only */
+
			if (pkg_type(pkg) != PKG_REMOTE &&
+
			    !pkg_list_is_empty(pkg, PKG_DIRS)) {
+
				if (print_tag)
+
					printf("%-15s:\n", "Directories");
+
				while (pkg_dirs(pkg, &dir) == EPKG_OK)
+
					printf("\t%s\n",
+
					       pkg_dir_path(dir));
+
			}
+
			break;
+
		case INFO_USERS: /* Installed pkgs only */
+
			if (pkg_type(pkg) != PKG_REMOTE &&
+
			    !pkg_list_is_empty(pkg, PKG_USERS)) {
+
				if (print_tag)
+
					printf("%-15s: ", "Users");
+
				if (pkg_users(pkg, &user) == EPKG_OK)
+
					printf("%s", pkg_user_name(user));
+
				while (pkg_users(pkg, &user) == EPKG_OK)
+
					printf(" %s", pkg_user_name(user));
+
				printf("\n");
+
			}
+
			break;
+
		case INFO_GROUPS: /* Installed pkgs only */
+
			if (pkg_type(pkg) != PKG_REMOTE &&
+
			    !pkg_list_is_empty(pkg, PKG_GROUPS)) {
+
				if (print_tag)
+
					printf("%-15s: ", "Groups");
+
				if (pkg_groups(pkg, &group) == EPKG_OK)
+
					printf("%s", pkg_group_name(group));
+
				while (pkg_groups(pkg, &group) == EPKG_OK)
+
					printf(" %s", pkg_group_name(group));
+
				printf("\n");
+
			}
+
			break;
+
		case INFO_ARCH:
+
			if (print_tag)
+
				printf("%-15s: ", "Architecture");
+
			printf("%s\n", arch);
+
			break;
+
		case INFO_REPOURL:
+
			if (pkg_type(pkg) == PKG_REMOTE &&
+
			    repourl != NULL && repourl[0] != '\0') {
+
				if (print_tag)
+
					printf("%-15s: ", "Pkg URL");
+
				if (repourl[strlen(repourl) -1] == '/')
+
					printf("%s%s\n", repourl, repopath);
+
				else
+
					printf("%s/%s\n", repourl, repopath);
+
			} else if (!print_tag)
+
				printf("\n");
+
			break;
		}
-
        }
+
	}
}

void