- 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:
parent
5a182f762c
commit
8a3534831e
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user