[PATCH] /bin/rm Add "all" interactive option

Jason Smethers jason at smethers.net
Sat Dec 18 11:23:03 PST 2004


This patch adds an "all" option to the rm utility's interactive mode as 
described below.

The following is true in recursive mode:

If "all" is specified to a directory, only that directory and everything 
in it is removed. If "all" is specified to a file, all the files in the 
same directory are deleted. If "all" is specified for a file on the 
command line, all files listed on the command line are deleted. If "all" 
is specified for a directory on the command line, only that directory 
and everything in it is removed.

The following is true when NOT in recursive mode:

If "all" is specified to one of the files or directories listed on the 
command line, all the files and directories listed will be deleted 
(directories have to be empty to be deleted when in NOT in recursive mode).

The follwing is true when the -I flag is specified:

If "all" is specified, -f is assumed.



- Jason
--- rm.c.orig	Sat Dec 18 13:39:18 2004
+++ rm.c	Sat Dec 18 14:04:15 2004
@@ -50,10 +50,13 @@
 #include <sysexits.h>
 #include <unistd.h>
 
-int dflag, eval, fflag, iflag, Pflag, vflag, Wflag, stdin_ok;
-int rflag, Iflag;
-uid_t uid;
+static int dflag, fflag, Iflag, iflag, Pflag, rflag, vflag, Wflag;
+static int eval;
+static int removeAll;
+static int stdin_ok;
+static uid_t uid;
 
+static int  rm_get_answeer(void);
 static int	check(const char *, const char *, struct stat *);
 static int	check2(char **);
 static void	checkdot(char **);
@@ -164,6 +167,7 @@
 	int needstat;
 	int flags;
 	int rval;
+	int removeLevel;
 
 	/*
 	 * Remove a file hierarchy.  If forcing removal (-f), or interactive
@@ -184,6 +188,7 @@
 		flags |= FTS_WHITEOUT;
 	if (!(fts = fts_open(argv, flags, NULL)))
 		err(1, NULL);
+	removeLevel = -1;
 	while ((p = fts_read(fts)) != NULL) {
 		switch (p->fts_info) {
 		case FTS_DNR:
@@ -209,18 +214,35 @@
 			}
 			continue;
 		case FTS_D:
-			/* Pre-order: give user chance to skip. */
+			/*
+			 * Stop removing everything when we go to another
+			 * directory at the same level or higher up in the
+			 * directory hierarchy.
+			 */
+			if (removeAll && removeLevel >= p->fts_level) {
+				removeAll = 0;
+				removeLevel = -1;
+			}
+
 			if (!fflag && !check(p->fts_path, p->fts_accpath,
 			    p->fts_statp)) {
 				fts_set(fts, p, FTS_SKIP);
 				p->fts_number = SKIPPED;
+			} else {
+				/*
+				 * Check if the user has just asked for everything
+				 * in this directory to be removed.
+				 */
+				if (removeAll && removeLevel == -1)
+					removeLevel = p->fts_level;
+
+				if (!uid
+					&& (p->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE))
+					&& !(p->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE))
+					&& chflags(p->fts_accpath,
+						p->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)) < 0)
+					goto err;
 			}
-			else if (!uid &&
-				 (p->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) &&
-				 !(p->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)) &&
-				 chflags(p->fts_accpath,
-					 p->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)) < 0)
-				goto err;
 			continue;
 		case FTS_DP:
 			/* Post-order: see if user skipped. */
@@ -228,9 +250,27 @@
 				continue;
 			break;
 		default:
+			/*
+			 * Stop removing everything when we go from
+			 * directories to files at the same level, and
+			 * when we go to files higher up in the directory
+			 * hierarchy.
+			 */
+			if (removeAll && removeLevel >= p->fts_level) {
+				removeAll = 0;
+				removeLevel = -1;
+			}
+
 			if (!fflag &&
 			    !check(p->fts_path, p->fts_accpath, p->fts_statp))
 				continue;
+
+			/*
+			 * Check if the user has just asked for every file
+			 * at this directory level to be removed.
+			 */
+			if (removeAll && removeLevel == -1)
+				removeLevel = p->fts_level - 1;
 		}
 
 		rval = 0;
@@ -425,16 +465,33 @@
 	return (0);
 }
 
+static int
+rm_get_answeer(void)
+{
+	int c;
+	int answeer;
+
+	answeer = getchar();
+
+	c = answeer;
+	while (c != '\n' && c != EOF)
+		c = getchar();
+
+	return answeer;
+}
 
 static int
 check(const char *path, const char *name, struct stat *sp)
 {
-	int ch, first;
+	int answeer;
 	char modep[15], *flagsp;
 
+	if (removeAll)
+		return 1;
+
 	/* Check -i first. */
 	if (iflag)
-		fprintf(stderr, "remove %s? ", path);
+		fprintf(stderr, "remove %s? [y/n/a]: ", path);
 	else {
 		/*
 		 * If it's not a symbolic link and it's unwritable and we're
@@ -453,7 +510,7 @@
 		strmode(sp->st_mode, modep);
 		if ((flagsp = fflagstostr(sp->st_flags)) == NULL)
 			err(1, NULL);
-		fprintf(stderr, "override %s%s%s/%s %s%sfor %s? ",
+		fprintf(stderr, "override %s%s%s/%s %s%sfor %s? [y/n/a]: ",
 		    modep + 1, modep[9] == ' ' ? "" : " ",
 		    user_from_uid(sp->st_uid, 0),
 		    group_from_gid(sp->st_gid, 0),
@@ -463,18 +520,20 @@
 	}
 	fflush(stderr);
 
-	first = ch = getchar();
-	while (ch != '\n' && ch != EOF)
-		ch = getchar();
-	return (first == 'y' || first == 'Y');
+	answeer = rm_get_answeer();
+	if (answeer == 'a' || answeer == 'A') {
+		removeAll = 1;
+		return 1;
+	} else if (answeer == 'y' || answeer == 'Y')
+		return 1;
+	return 0;
 }
 
 static int
 check2(char **argv)
 {
 	struct stat st;
-	int first;
-	int ch;
+	int answeer;
 	int fcount = 0;
 	int dcount = 0;
 	int i;
@@ -490,8 +549,7 @@
 			}
 		}
 	}
-	first = 0;
-	while (first != 'n' && first != 'N' && first != 'y' && first != 'Y') {
+	for (;;) {
 		if (dcount && rflag) {
 			fprintf(stderr, "recursively remove");
 			if (dcount == 1)
@@ -507,16 +565,27 @@
 		} else {
 			return(1);
 		}
-		fprintf(stderr, "? ");
+		fprintf(stderr, "? [y/n/a]: ");
 		fflush(stderr);
 
-		first = ch = getchar();
-		while (ch != '\n' && ch != EOF)
-			ch = getchar();
-		if (ch == EOF)
+		answeer = rm_get_answeer();
+		if (answeer == EOF)
+			break;
+		if (answeer == 'n' || answeer == 'N')
 			break;
+		if (answeer == 'y' || answeer == 'Y')
+			return 1;
+		if (answeer == 'a' || answeer == 'A') {
+			/*
+			 * Since the user specified the -I flag and answeered
+			 * "all" to this question, do not prompt them for
+			 * confirmation again.
+			 */
+			fflag = 1;
+			return 1;
+		}
 	}
-	return (first == 'y' || first == 'Y');
+	return 0;
 }
 
 #define ISDOT(a)	((a)[0] == '.' && (!(a)[1] || ((a)[1] == '.' && !(a)[2])))




More information about the Submit mailing list