diff options
-rw-r--r-- | usr.sbin/pkg_install/delete/delete.h | 4 | ||||
-rw-r--r-- | usr.sbin/pkg_install/delete/main.c | 53 | ||||
-rw-r--r-- | usr.sbin/pkg_install/delete/perform.c | 38 | ||||
-rw-r--r-- | usr.sbin/pkg_install/delete/pkg_delete.1 | 27 | ||||
-rw-r--r-- | usr.sbin/pkg_install/info/info.h | 6 | ||||
-rw-r--r-- | usr.sbin/pkg_install/info/perform.c | 169 | ||||
-rw-r--r-- | usr.sbin/pkg_install/lib/Makefile | 4 | ||||
-rw-r--r-- | usr.sbin/pkg_install/lib/lib.h | 8 | ||||
-rw-r--r-- | usr.sbin/pkg_install/lib/match.c | 197 |
9 files changed, 338 insertions, 168 deletions
diff --git a/usr.sbin/pkg_install/delete/delete.h b/usr.sbin/pkg_install/delete/delete.h index cc7a70e..faa2e06 100644 --- a/usr.sbin/pkg_install/delete/delete.h +++ b/usr.sbin/pkg_install/delete/delete.h @@ -24,10 +24,12 @@ #define _INST_DELETE_H_INCLUDE extern char *Prefix; -extern Boolean NoDeInstall; extern Boolean CleanDirs; +extern Boolean Interactive; +extern Boolean NoDeInstall; extern Boolean Force; extern char *Directory; extern char *PkgName; +extern match_t MatchType; #endif /* _INST_DELETE_H_INCLUDE */ diff --git a/usr.sbin/pkg_install/delete/main.c b/usr.sbin/pkg_install/delete/main.c index fc35c4e..f87af3d 100644 --- a/usr.sbin/pkg_install/delete/main.c +++ b/usr.sbin/pkg_install/delete/main.c @@ -30,11 +30,13 @@ static const char rcsid[] = #include "lib.h" #include "delete.h" -static char Options[] = "hvDdnfp:"; +static char Options[] = "adDfGhinp:vx"; char *Prefix = NULL; -Boolean NoDeInstall = FALSE; Boolean CleanDirs = FALSE; +Boolean Interactive = FALSE; +Boolean NoDeInstall = FALSE; +match_t MatchType = MATCH_GLOB; static void usage __P((void)); @@ -75,6 +77,22 @@ main(int argc, char **argv) Verbose = TRUE; break; + case 'a': + MatchType = MATCH_ALL; + break; + + case 'G': + MatchType = MATCH_EXACT; + break; + + case 'x': + MatchType = MATCH_REGEX; + break; + + case 'i': + Interactive = TRUE; + break; + case 'h': case '?': default: @@ -87,23 +105,26 @@ 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, 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) + if (pkgs == start && MatchType != MATCH_ALL) warnx("missing package name(s)"), usage(); *pkgs = NULL; tmp = getenv(PKG_DBDIR) ? getenv(PKG_DBDIR) : DEF_LOG_DIR; @@ -126,6 +147,8 @@ main(int argc, char **argv) static void usage() { - fprintf(stderr, "usage: pkg_delete [-vDdnf] [-p prefix] pkg-name ...\n"); + fprintf(stderr, "%s\n%s\n", + "usage: pkg_delete [-dDfGinvx] [-p prefix] pkg-name ...", + " pkg_delete -a [flags]"); exit(1); } diff --git a/usr.sbin/pkg_install/delete/perform.c b/usr.sbin/pkg_install/delete/perform.c index 072b6d5..3453cf8 100644 --- a/usr.sbin/pkg_install/delete/perform.c +++ b/usr.sbin/pkg_install/delete/perform.c @@ -37,10 +37,33 @@ static char LogDir[FILENAME_MAX]; int pkg_perform(char **pkgs) { + char **matched; char *tmp; int i, j; int err_cnt = 0; - int loop_cnt; + int loop_cnt, errcode; + + if (MatchType != MATCH_EXACT) { + matched = matchinstalled(MatchType, pkgs, &errcode); + if (errcode != 0) + return 1; + /* Not reached */ + + if (matched != NULL) + pkgs = matched; + else switch (MatchType) { + case MATCH_GLOB: + break; + case MATCH_ALL: + warnx("no packages installed"); + return 0; + case MATCH_REGEX: + warnx("no packages match pattern(s)"); + return 1; + default: + break; + } + } for (i = 0; pkgs[i]; i++) { /* @@ -122,6 +145,19 @@ pkg_do(char *pkg) return 1; } + if (Interactive == TRUE) { + int first, ch; + + (void)fprintf(stderr, "delete %s? ", pkg); + (void)fflush(stderr); + first = ch = getchar(); + while (ch != '\n' && ch != EOF) + ch = getchar(); + if (first != 'y' && first != 'Y') + return 0; + /* Not reached */ + } + if (!isemptyfile(REQUIRED_BY_FNAME)) { char buf[512]; warnx("package `%s' is required by these other packages\n" diff --git a/usr.sbin/pkg_install/delete/pkg_delete.1 b/usr.sbin/pkg_install/delete/pkg_delete.1 index 7ec63a6..55350bf 100644 --- a/usr.sbin/pkg_install/delete/pkg_delete.1 +++ b/usr.sbin/pkg_install/delete/pkg_delete.1 @@ -25,9 +25,12 @@ .Nd a utility for deleting previously installed software package distributions .Sh SYNOPSIS .Nm -.Op Fl vDdnf +.Op Fl dDfGinvx .Op Fl p Ar prefix .Ar pkg-name ... +.Nm +.Fl a +.Op Ar flags .Sh DESCRIPTION The .Nm @@ -68,6 +71,12 @@ The following command line options are supported: .Bl -tag -width indent .It Ar pkg-name ... The named packages are deinstalled. +.It Fl a +Unconditionally delete all currently installed packages. +.It Fl i +Request confirmation before attempting to delete each package, +regardless whether or not the standard input device is a +terminal. .It Fl v Turn on verbose output. .It Fl D @@ -94,6 +103,22 @@ the package. .It Fl f Force removal of the package, even if a dependency is recorded or the deinstall or require script fails. +.It Fl G +Do not try to expand shell glob patterns in the +.Ar pkg-name +when selecting packages to be deleted (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 delete all packages whose names match +that regular expression. Multiple regular expressions could be +provided, in that case +.Nm +deletes all packages that match at least one +regular expression from the list. .El .Sh TECHNICAL DETAILS .Nm diff --git a/usr.sbin/pkg_install/info/info.h b/usr.sbin/pkg_install/info/info.h index ba56efb..2356a7e 100644 --- a/usr.sbin/pkg_install/info/info.h +++ b/usr.sbin/pkg_install/info/info.h @@ -47,12 +47,6 @@ #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 Quiet; extern char *InfoPrefix; diff --git a/usr.sbin/pkg_install/info/perform.c b/usr.sbin/pkg_install/info/perform.c index 80de910..b350be1 100644 --- a/usr.sbin/pkg_install/info/perform.c +++ b/usr.sbin/pkg_install/info/perform.c @@ -28,26 +28,20 @@ static const char rcsid[] = #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) { - int i, err_cnt = 0; + char **matched; char *tmp; + int err_cnt = 0; + int i, errcode; signal(SIGINT, cleanup); - tmp = getenv(PKG_DBDIR); - if (!tmp) - tmp = DEF_LOG_DIR; /* Overriding action? */ if (CheckPkg) { char buf[FILENAME_MAX]; @@ -57,90 +51,33 @@ pkg_perform(char **pkgs) /* Not reached */ } - switch (MatchType) { - case MATCH_ALL: - case MATCH_REGEX: - { - FTS *ftsp; - FTSENT *f; - char *paths[2]; - int errcode; - - if (!isdir(tmp)) + if (MatchType != MATCH_EXACT) { + matched = matchinstalled(MatchType, pkgs, &errcode); + if (errcode != 0) + return 1; + /* Not reached */ + + if (matched != NULL) + pkgs = matched; + else switch (MatchType) { + case MATCH_GLOB: + break; + case MATCH_ALL: + warnx("no packages installed"); + return 0; + /* Not reached */ + case MATCH_REGEX: + warnx("no packages match pattern(s)"); 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); - } - } - 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); + /* Not reached */ + default: + break; } - break; - default: - for (i = 0; pkgs[i]; i++) - err_cnt += pkg_do(pkgs[i]); - break; } + for (i = 0; pkgs[i]; i++) + err_cnt += pkg_do(pkgs[i]); + return err_cnt; } @@ -299,57 +236,3 @@ cleanup(int sig) exit(1); } -static int -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/lib/Makefile b/usr.sbin/pkg_install/lib/Makefile index 3876b10..990bfe3 100644 --- a/usr.sbin/pkg_install/lib/Makefile +++ b/usr.sbin/pkg_install/lib/Makefile @@ -1,5 +1,7 @@ +# $FreeBSD$ + LIB= install -SRCS= file.c msg.c plist.c str.c exec.c global.c pen.c +SRCS= file.c msg.c plist.c str.c exec.c global.c pen.c match.c CFLAGS+= ${DEBUG} NOPROFILE= yes NOPIC= yes diff --git a/usr.sbin/pkg_install/lib/lib.h b/usr.sbin/pkg_install/lib/lib.h index 2584655..fae802d 100644 --- a/usr.sbin/pkg_install/lib/lib.h +++ b/usr.sbin/pkg_install/lib/lib.h @@ -88,6 +88,11 @@ enum _plist_t { }; typedef enum _plist_t plist_t; +enum _match_t { + MATCH_ALL, MATCH_EXACT, MATCH_GLOB, MATCH_REGEX +}; +typedef enum _match_t match_t; + /* Types */ typedef unsigned int Boolean; @@ -169,6 +174,9 @@ Boolean make_preserve_name(char *, int, char *, char *); /* For all */ int pkg_perform(char **); +/* Query installed packages */ +char **matchinstalled(match_t, char **, int *); + /* Externs */ extern Boolean Verbose; extern Boolean Fake; diff --git a/usr.sbin/pkg_install/lib/match.c b/usr.sbin/pkg_install/lib/match.c new file mode 100644 index 0000000..21495f6 --- /dev/null +++ b/usr.sbin/pkg_install/lib/match.c @@ -0,0 +1,197 @@ +#ifndef lint +static const char rcsid[] = + "$FreeBSD$"; +#endif + +/* + * FreeBSD install - a package for the installation and maintainance + * of non-core utilities. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Maxim Sobolev + * 24 February 2001 + * + * Routines used to query installed packages. + * + */ + +#include "lib.h" + +#include <err.h> +#include <fnmatch.h> +#include <fts.h> +#include <regex.h> + +/* + * Simple structure representing argv-like + * NULL-terminated list. + */ +struct store { + int currlen; + int used; + char **store; +}; + +static int rex_match(char *, char *); +static void storeappend(struct store *, const char *); +static int fname_cmp(const FTSENT **, const FTSENT **); + +/* + * Function to query names of installed packages. + * MatchType - one of MATCH_ALL, MATCH_REGEX, MATCH_GLOB; + * patterns - NULL-terminated list of glob or regex patterns + * (could be NULL for MATCH_ALL); + * retval - return value (could be NULL if you don't want/need + * return value). + * Returns NULL-terminated list with matching names. + * Names in list returned are dynamically allocated and should + * not be altered by the caller. + */ +char ** +matchinstalled(match_t MatchType, char **patterns, int *retval) +{ + int i, matched, errcode; + char *tmp; + char *paths[2]; + static struct store *store = NULL; + FTS *ftsp; + FTSENT *f; + + if (store == NULL) { + store = malloc(sizeof *store); + store->currlen = 0; + store->store = NULL; + } else { + if (store->store != NULL) { + /* Free previously allocated memory */ + for (i = 0; store->store[i] != NULL; i++) + free(store->store[i]); + } + } + store->used = 0; + + if (retval != NULL) + *retval = 0; + + tmp = getenv(PKG_DBDIR); + if (!tmp) + tmp = DEF_LOG_DIR; + if (!isdir(tmp)) { + if (retval != NULL) + *retval = 1; + return NULL; + /* Not reached */ + } + + 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_ALL) { + storeappend(store, f->fts_name); + continue; + } + for (i = 0; patterns[i]; i++) { + matched = 0; + switch (MatchType) { + case MATCH_REGEX: + errcode = rex_match(patterns[i], f->fts_name); + if (errcode == 1) { + storeappend(store, f->fts_name); + matched = 1; + } else if (errcode == -1) { + if (retval != NULL) + *retval = 1; + return NULL; + /* Not reached */ + } + break; + case MATCH_GLOB: + if (fnmatch(patterns[i], f->fts_name, 0) == 0) { + storeappend(store, f->fts_name); + matched = 1; + } + break; + default: + break; + } + if (matched == 1) + break; + } + } + } + fts_close(ftsp); + } + + if (store->used == 0) + return NULL; + else + return store->store; +} + +/* + * Returns 1 if specified pkgname matches RE pattern. + * Otherwise returns 0 if doesn't match or -1 if RE + * engine reported an error (usually invalid syntax). + */ +static int +rex_match(char *pattern, char *pkgname) +{ + char errbuf[128]; + int errcode; + int retval; + regex_t rex; + + retval = 0; + + errcode = regcomp(&rex, pattern, REG_BASIC | REG_NOSUB); + if (errcode == 0) + errcode = regexec(&rex, pkgname, 0, NULL, 0); + + if (errcode == 0) { + retval = 1; + } else if (errcode != REG_NOMATCH) { + regerror(errcode, &rex, errbuf, sizeof(errbuf)); + warnx("%s: %s", pattern, errbuf); + retval = -1; + } + + regfree(&rex); + + return retval; +} + +static void +storeappend(struct store *store, const char *item) +{ + char **tmp; + + if (store->used + 2 > store->currlen) { + tmp = store->store; + store->currlen += 16; + store->store = malloc(store->currlen * sizeof(*(store->store))); + memcpy(store->store, tmp, store->used * sizeof(*(store->store))); + free(tmp); + } + + asprintf(&(store->store[store->used]), "%s", item); + store->used++; + store->store[store->used] = NULL; +} + +static int +fname_cmp(const FTSENT **a, const FTSENT **b) +{ + return strcmp((*a)->fts_name, (*b)->fts_name); +} |