- Merge recently added into pkg_info(1) regex/glob matching functionality into

pkg_delete(1) as well;
- add a new `-a' option for pkg_delete(1) to delete all installed packages;
- add a new `-i' option for pkg_delete(1) to request simple rm(1)-like
  interactive confirmation before attempting to delete each package.

Silently approved by:	jkh, -ports
This commit is contained in:
Maxim Sobolev 2001-02-27 09:00:18 +00:00
parent b1fe97412b
commit 29114b9283
9 changed files with 337 additions and 167 deletions

View file

@ -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 */

View file

@ -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);
}

View file

@ -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"

View file

@ -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

View file

@ -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;

View file

@ -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 (MatchType != MATCH_EXACT) {
matched = matchinstalled(MatchType, pkgs, &errcode);
if (errcode != 0)
return 1;
/* Not reached */
if (!isdir(tmp))
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);
}
/* Not reached */
default:
break;
}
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;
}
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;
}

View file

@ -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

View file

@ -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;

View file

@ -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);
}