- By default treat supplied arguments as a shell globs to be matched against

names of installed packages;
- add new `-G' option to disable glob matching and revert to previous
  behaviour (I have no idea why this could be necessary, though);
- add a new `-x' option, which instructs pkg_info(1) to treat supplied
  arguments as a regular expressions.

For example:
$ pkg_info foo\* - displays information about all packages whose names start
		   from foo
$ pkg_info -G foo\*-1.1 - displays information about package named "foo*-1.1"
$ pkg_info -x ^foo.\* - displays information about all packages whose names
			start from foo

Original idea submitted by:	Edwin Groothuis <mavetju@chello.nl> (bin/24695)
Reviewed by:			jkh, roam
Approved by:			jkh
This commit is contained in:
sobomax 2001-02-08 17:44:00 +00:00
parent 5a182f762c
commit 8a3534831e
4 changed files with 192 additions and 42 deletions

View File

@ -47,12 +47,18 @@
#define SHOW_ORIGIN 0x2000
#define SHOW_CKSUM 0x4000
enum _match_t {
MATCH_ALL, MATCH_EXACT, MATCH_GLOB, MATCH_REGEX
};
typedef enum _match_t match_t;
extern int Flags;
extern Boolean AllInstalled;
extern Boolean Quiet;
extern char *InfoPrefix;
extern char PlayPen[];
extern char *CheckPkg;
extern match_t MatchType;
extern void show_file(char *, char *);
extern void show_plist(char *, Package *, plist_t);

View File

@ -28,10 +28,10 @@ static const char rcsid[] =
"$FreeBSD$";
#endif
static char Options[] = "acdDe:fghiIkl:LmopqrRst:v";
static char Options[] = "acdDe:fgGhiIkl:LmopqrRst:vx";
int Flags = 0;
Boolean AllInstalled = FALSE;
match_t MatchType = MATCH_GLOB;
Boolean Quiet = FALSE;
char *InfoPrefix = "";
char PlayPen[FILENAME_MAX];
@ -48,13 +48,13 @@ main(int argc, char **argv)
pkgs = start = argv;
if (argc == 1) {
AllInstalled = TRUE;
MatchType = MATCH_ALL;
Flags = SHOW_INDEX;
}
else while ((ch = getopt(argc, argv, Options)) != -1) {
switch(ch) {
case 'a':
AllInstalled = TRUE;
MatchType = MATCH_ALL;
break;
case 'v':
@ -92,6 +92,10 @@ main(int argc, char **argv)
Flags |= SHOW_CKSUM;
break;
case 'G':
MatchType = MATCH_EXACT;
break;
case 'i':
Flags |= SHOW_INSTALL;
break;
@ -116,9 +120,9 @@ main(int argc, char **argv)
Flags |= SHOW_MTREE;
break;
case 's':
Flags |= SHOW_SIZE;
break;
case 's':
Flags |= SHOW_SIZE;
break;
case 'o':
Flags |= SHOW_ORIGIN;
@ -136,6 +140,10 @@ main(int argc, char **argv)
strcpy(PlayPen, optarg);
break;
case 'x':
MatchType = MATCH_REGEX;
break;
case 'e':
CheckPkg = optarg;
break;
@ -157,23 +165,27 @@ main(int argc, char **argv)
/* Get all the remaining package names, if any */
while (*argv) {
while ((pkgs_split = strrchr(*argv, (int)'/')) != NULL) {
*pkgs_split++ = '\0';
/*
* If character after the '/' is alphanumeric, then we've found the
* package name. Otherwise we've come across a trailing '/' and
* need to continue our quest.
*/
if (isalpha(*pkgs_split)) {
*argv = pkgs_split;
break;
/* Don't try to apply heuristics if arguments are regexs */
if (MatchType != MATCH_REGEX)
while ((pkgs_split = strrchr(*argv, (int)'/')) != NULL) {
*pkgs_split++ = '\0';
/*
* If character after the '/' is alphanumeric or shell
* metachar, then we've found the package name. Otherwise
* we've come across a trailing '/' and need to continue our
* quest.
*/
if (isalpha(*pkgs_split) || ((MatchType == MATCH_GLOB) && \
strpbrk(pkgs_split, "*?[]") != NULL)) {
*argv = pkgs_split;
break;
}
}
}
*pkgs++ = *argv++;
}
/* If no packages, yelp */
if (pkgs == start && !AllInstalled && !CheckPkg)
if (pkgs == start && MatchType != MATCH_ALL && !CheckPkg)
warnx("missing package name(s)"), usage();
*pkgs = NULL;
return pkg_perform(start);

View File

@ -26,12 +26,16 @@ static const char rcsid[] =
#include "lib.h"
#include "info.h"
#include <fts.h>
#include <signal.h>
#include <sys/types.h>
#include <err.h>
#include <glob.h>
#include <fts.h>
#include <regex.h>
#include <signal.h>
static int fname_cmp(const FTSENT **, const FTSENT **);
static int pkg_do(char *);
static int rexs_match(char **, char *);
int
pkg_perform(char **pkgs)
@ -50,31 +54,93 @@ pkg_perform(char **pkgs)
snprintf(buf, FILENAME_MAX, "%s/%s", tmp, CheckPkg);
return abs(access(buf, R_OK));
/* Not reached */
}
else if (AllInstalled) {
FTS *ftsp;
FTSENT *f;
char *paths[2];
if (!isdir(tmp))
return 1;
paths[0] = tmp;
paths[1] = NULL;
ftsp = fts_open(paths, FTS_LOGICAL | FTS_NOCHDIR | FTS_NOSTAT,
fname_cmp);
if (ftsp != NULL) {
while ((f = fts_read(ftsp)) != NULL) {
if (f->fts_info == FTS_D && f->fts_level == 1) {
err_cnt += pkg_do(f->fts_name);
fts_set(ftsp, f, FTS_SKIP);
switch (MatchType) {
case MATCH_ALL:
case MATCH_REGEX:
{
FTS *ftsp;
FTSENT *f;
char *paths[2];
int errcode;
if (!isdir(tmp))
return 1;
paths[0] = tmp;
paths[1] = NULL;
ftsp = fts_open(paths, FTS_LOGICAL | FTS_NOCHDIR | FTS_NOSTAT,
fname_cmp);
if (ftsp != NULL) {
while ((f = fts_read(ftsp)) != NULL) {
if (f->fts_info == FTS_D && f->fts_level == 1) {
fts_set(ftsp, f, FTS_SKIP);
if (MatchType == MATCH_REGEX) {
errcode = rexs_match(pkgs, f->fts_name);
if (errcode == -1) {
err_cnt += 1;
break;
}
else if (errcode == 0)
continue;
}
err_cnt += pkg_do(f->fts_name);
}
}
fts_close(ftsp);
}
fts_close(ftsp);
}
}
else
break;
case MATCH_GLOB:
{
glob_t g;
char *gexpr;
char *cp;
int gflags;
int prev_matchc;
gflags = GLOB_ERR;
prev_matchc = 0;
for (i = 0; pkgs[i]; i++) {
asprintf(&gexpr, "%s/%s", tmp, pkgs[i]);
if (glob(gexpr, gflags, NULL, &g) != 0) {
warn("%s: error encountered when matching glob", pkgs[i]);
return 1;
}
/*
* If glob doesn't match try to use pkgs[i] directly - it
* could be name of the tarball.
*/
if (g.gl_matchc == prev_matchc)
err_cnt += pkg_do(pkgs[i]);
prev_matchc = g.gl_matchc;
gflags |= GLOB_APPEND;
free(gexpr);
}
for (i = 0; i < g.gl_matchc; i++) {
cp = strrchr(g.gl_pathv[i], '/');
if (cp == NULL)
cp = g.gl_pathv[i];
else
cp++;
err_cnt += pkg_do(cp);
}
globfree(&g);
}
break;
default:
for (i = 0; pkgs[i]; i++)
err_cnt += pkg_do(pkgs[i]);
break;
}
return err_cnt;
}
@ -227,7 +293,7 @@ cleanup(int sig)
if (!in_cleanup) {
in_cleanup = 1;
leave_playpen();
leave_playpen();
}
if (sig)
exit(1);
@ -238,3 +304,52 @@ fname_cmp(const FTSENT **a, const FTSENT **b)
{
return strcmp((*a)->fts_name, (*b)->fts_name);
}
/*
* Returns 1 if specified pkgname matches at least one
* of the RE from patterns. Otherwise return 0 if no
* matches were found or -1 if RE engine reported an
* error (usually invalid syntax).
*/
static int
rexs_match(char **patterns, char *pkgname)
{
Boolean matched;
char errbuf[128];
int i;
int errcode;
int retval;
regex_t rex;
errcode = 0;
retval = 0;
matched = FALSE;
for (i = 0; patterns[i]; i++) {
errcode = regcomp(&rex, patterns[i], REG_BASIC | REG_NOSUB);
if (errcode != 0)
break;
errcode = regexec(&rex, pkgname, 0, NULL, 0);
if (errcode == 0) {
matched = TRUE;
retval = 1;
break;
}
else if (errcode != REG_NOMATCH)
break;
regfree(&rex);
errcode = 0;
}
if (errcode != 0) {
regerror(errcode, &rex, errbuf, sizeof(errbuf));
warnx("%s: %s", patterns[i], errbuf);
retval = -1;
}
if ((errcode != 0) || (matched == TRUE))
regfree(&rex);
return retval;
}

View File

@ -25,7 +25,7 @@
.Nd a utility for displaying information on software packages
.Sh SYNOPSIS
.Nm
.Op Fl cdDfgiIkLmopqrRsv
.Op Fl cdDfgGiIkLmopqrRsvx
.Op Fl e Ar package
.Op Fl l Ar prefix
.Op Fl t Ar template
@ -98,6 +98,23 @@ intended to give an idea as to where the underlying port, from which
package was generated, is located in the
.Fx
.Em "Ports Collection" .
.It Fl G
Do not try to expand shell glob patterns in the
.Ar pkg-name
when selecting packages to be displayed (by default
.Nm
automatically expands shell glob patterns in the
.Ar pkg-name
).
.It Fl x
Treat the
.Ar pkg-name
as a regular expression and display information only for packages
whose names match that regular expression. Multiple regular
expressions could be provided, in that case
.Nm
displays information about all packages that match at least one
regular expression from the list.
.It Fl e Ar pkg-name
If the package identified by
.Ar pkg-name