diff options
author | sobomax <sobomax@FreeBSD.org> | 2001-02-08 17:44:00 +0000 |
---|---|---|
committer | sobomax <sobomax@FreeBSD.org> | 2001-02-08 17:44:00 +0000 |
commit | 391b1060843d1e99546c0c4b871c89ef16217a94 (patch) | |
tree | 5c4a922cdaca2a0d079072c7efa1182372c56a31 /usr.sbin | |
parent | 4331c8a979db44fc3779e72b294c0a5d6a78d619 (diff) | |
download | FreeBSD-src-391b1060843d1e99546c0c4b871c89ef16217a94.zip FreeBSD-src-391b1060843d1e99546c0c4b871c89ef16217a94.tar.gz |
- 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
Diffstat (limited to 'usr.sbin')
-rw-r--r-- | usr.sbin/pkg_install/info/info.h | 8 | ||||
-rw-r--r-- | usr.sbin/pkg_install/info/main.c | 50 | ||||
-rw-r--r-- | usr.sbin/pkg_install/info/perform.c | 155 | ||||
-rw-r--r-- | usr.sbin/pkg_install/info/pkg_info.1 | 19 |
4 files changed, 191 insertions, 41 deletions
diff --git a/usr.sbin/pkg_install/info/info.h b/usr.sbin/pkg_install/info/info.h index cbd5cfe..ba56efb 100644 --- a/usr.sbin/pkg_install/info/info.h +++ b/usr.sbin/pkg_install/info/info.h @@ -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); diff --git a/usr.sbin/pkg_install/info/main.c b/usr.sbin/pkg_install/info/main.c index da5330a..37947cc 100644 --- a/usr.sbin/pkg_install/info/main.c +++ b/usr.sbin/pkg_install/info/main.c @@ -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); diff --git a/usr.sbin/pkg_install/info/perform.c b/usr.sbin/pkg_install/info/perform.c index f9955bc..80de910 100644 --- a/usr.sbin/pkg_install/info/perform.c +++ b/usr.sbin/pkg_install/info/perform.c @@ -26,12 +26,16 @@ static const char rcsid[] = #include "lib.h" #include "info.h" +#include <sys/types.h> +#include <err.h> +#include <glob.h> #include <fts.h> +#include <regex.h> #include <signal.h> -#include <err.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; +} diff --git a/usr.sbin/pkg_install/info/pkg_info.1 b/usr.sbin/pkg_install/info/pkg_info.1 index 8a0da3f..4977359 100644 --- a/usr.sbin/pkg_install/info/pkg_info.1 +++ b/usr.sbin/pkg_install/info/pkg_info.1 @@ -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 |