xref: /csrg-svn/bin/rm/rm.c (revision 66561)
145769Sbostic /*-
261008Sbostic  * Copyright (c) 1990, 1993
361008Sbostic  *	The Regents of the University of California.  All rights reserved.
445769Sbostic  *
545769Sbostic  * %sccs.include.redist.c%
618277Sralph  */
718277Sralph 
845769Sbostic #ifndef lint
961008Sbostic static char copyright[] =
1061008Sbostic "@(#) Copyright (c) 1990, 1993\n\
1161008Sbostic 	The Regents of the University of California.  All rights reserved.\n";
1245769Sbostic #endif /* not lint */
1345769Sbostic 
1445769Sbostic #ifndef lint
15*66561Spendry static char sccsid[] = "@(#)rm.c	8.3 (Berkeley) 04/01/94";
1645769Sbostic #endif /* not lint */
1745769Sbostic 
1845769Sbostic #include <sys/types.h>
1945769Sbostic #include <sys/stat.h>
2059515Sbostic 
2159515Sbostic #include <err.h>
22*66561Spendry #include <errno.h>
2345769Sbostic #include <fts.h>
241083Sbill #include <stdio.h>
2559515Sbostic #include <stdlib.h>
2645769Sbostic #include <string.h>
2759515Sbostic #include <unistd.h>
281083Sbill 
2959515Sbostic int dflag, fflag, iflag, eval, stdin_ok;
301083Sbill 
3159515Sbostic int	check __P((char *, char *, struct stat *));
3259515Sbostic void	checkdot __P((char **));
3359515Sbostic void	rmfile __P((char **));
3459515Sbostic void	rmtree __P((char **));
3559515Sbostic void	usage __P((void));
3659515Sbostic 
3745769Sbostic /*
3845769Sbostic  * rm --
3945769Sbostic  *	This rm is different from historic rm's, but is expected to match
4045769Sbostic  *	POSIX 1003.2 behavior.  The most visible difference is that -f
4145769Sbostic  *	has two specific effects now, ignore non-existent files and force
4245769Sbostic  * 	file removal.
4345769Sbostic  */
4459515Sbostic int
451083Sbill main(argc, argv)
4634044Sbostic 	int argc;
4759515Sbostic 	char *argv[];
481083Sbill {
4945769Sbostic 	int ch, rflag;
501083Sbill 
5145769Sbostic 	rflag = 0;
5245769Sbostic 	while ((ch = getopt(argc, argv, "dfiRr")) != EOF)
5345769Sbostic 		switch(ch) {
5445769Sbostic 		case 'd':
5545769Sbostic 			dflag = 1;
5645769Sbostic 			break;
5734044Sbostic 		case 'f':
5845769Sbostic 			fflag = 1;
5945769Sbostic 			iflag = 0;
6018277Sralph 			break;
6134044Sbostic 		case 'i':
6245769Sbostic 			fflag = 0;
6345769Sbostic 			iflag = 1;
6434044Sbostic 			break;
6534044Sbostic 		case 'R':
6647195Sbostic 		case 'r':			/* compatibility */
6745769Sbostic 			rflag = 1;
6834044Sbostic 			break;
6934044Sbostic 		case '?':
7034044Sbostic 		default:
7134044Sbostic 			usage();
7234044Sbostic 		}
7345769Sbostic 	argc -= optind;
7445769Sbostic 	argv += optind;
751932Sroot 
7645769Sbostic 	if (argc < 1)
7734044Sbostic 		usage();
7845769Sbostic 
7945769Sbostic 	checkdot(argv);
8045769Sbostic 	if (!*argv)
8159515Sbostic 		exit (eval);
8245769Sbostic 
8345769Sbostic 	stdin_ok = isatty(STDIN_FILENO);
8445769Sbostic 
8545769Sbostic 	if (rflag)
8645769Sbostic 		rmtree(argv);
8745769Sbostic 	else
8845769Sbostic 		rmfile(argv);
8959515Sbostic 	exit (eval);
901083Sbill }
911083Sbill 
9259515Sbostic void
9345769Sbostic rmtree(argv)
9445769Sbostic 	char **argv;
951083Sbill {
96*66561Spendry 	FTS *fts;
97*66561Spendry 	FTSENT *p;
98*66561Spendry 	int needstat;
991083Sbill 
10045769Sbostic 	/*
10145769Sbostic 	 * Remove a file hierarchy.  If forcing removal (-f), or interactive
10245769Sbostic 	 * (-i) or can't ask anyway (stdin_ok), don't stat the file.
10345769Sbostic 	 */
10445769Sbostic 	needstat = !fflag && !iflag && stdin_ok;
10545769Sbostic 
10645769Sbostic 	/*
10745769Sbostic 	 * If the -i option is specified, the user can skip on the pre-order
10845769Sbostic 	 * visit.  The fts_number field flags skipped directories.
10945769Sbostic 	 */
11045769Sbostic #define	SKIPPED	1
11145769Sbostic 
11245769Sbostic 	if (!(fts = fts_open(argv,
11345769Sbostic 	    needstat ? FTS_PHYSICAL : FTS_PHYSICAL|FTS_NOSTAT,
11459515Sbostic 	    (int (*)())NULL)))
11559515Sbostic 		err(1, NULL);
11645769Sbostic 	while (p = fts_read(fts)) {
11759515Sbostic 		switch (p->fts_info) {
11845769Sbostic 		case FTS_DNR:
11961007Sbostic 			if (!fflag || errno != ENOENT) {
12061007Sbostic 				warn("%s", p->fts_path);
12161007Sbostic 				eval = 1;
12261007Sbostic 			}
12361007Sbostic 			continue;
12445769Sbostic 		case FTS_ERR:
12559515Sbostic 			err(1, "%s", p->fts_path);
12645769Sbostic 		case FTS_NS:
12759515Sbostic 			/*
12859515Sbostic 			 * FTS_NS: assume that if can't stat the file, it
12959515Sbostic 			 * can't be unlinked.
13059515Sbostic 			 */
13145769Sbostic 			if (!needstat)
13245769Sbostic 				break;
13359515Sbostic 			if (!fflag || errno != ENOENT) {
13459515Sbostic 				warn("%s", p->fts_path);
13559515Sbostic 				eval = 1;
13659515Sbostic 			}
13745769Sbostic 			continue;
13845769Sbostic 		case FTS_D:
13959515Sbostic 			/* Pre-order: give user chance to skip. */
14045769Sbostic 			if (iflag && !check(p->fts_path, p->fts_accpath,
14152238Sbostic 			    p->fts_statp)) {
14245769Sbostic 				(void)fts_set(fts, p, FTS_SKIP);
14345769Sbostic 				p->fts_number = SKIPPED;
14418327Sralph 			}
14545769Sbostic 			continue;
14645769Sbostic 		case FTS_DP:
14759515Sbostic 			/* Post-order: see if user skipped. */
14845769Sbostic 			if (p->fts_number == SKIPPED)
14945769Sbostic 				continue;
15045769Sbostic 			break;
15118277Sralph 		}
15245769Sbostic 		if (!fflag &&
15352238Sbostic 		    !check(p->fts_path, p->fts_accpath, p->fts_statp))
15445769Sbostic 			continue;
15545769Sbostic 
15645769Sbostic 		/*
15745769Sbostic 		 * If we can't read or search the directory, may still be
15845769Sbostic 		 * able to remove it.  Don't print out the un{read,search}able
15945769Sbostic 		 * message unless the remove fails.
16045769Sbostic 		 */
16147195Sbostic 		if (p->fts_info == FTS_DP || p->fts_info == FTS_DNR) {
16245769Sbostic 			if (!rmdir(p->fts_accpath))
16318277Sralph 				continue;
16445769Sbostic 			if (errno == ENOENT) {
16545769Sbostic 				if (fflag)
16645769Sbostic 					continue;
16745769Sbostic 			} else if (p->fts_info != FTS_DP)
16859515Sbostic 				warnx("%s: unable to read", p->fts_path);
16945769Sbostic 		} else if (!unlink(p->fts_accpath) || fflag && errno == ENOENT)
17047195Sbostic 			continue;
17159515Sbostic 		warn("%s", p->fts_path);
17259515Sbostic 		eval = 1;
1731083Sbill 	}
17445769Sbostic }
1751083Sbill 
17659515Sbostic void
17745769Sbostic rmfile(argv)
17845769Sbostic 	char **argv;
17945769Sbostic {
180*66561Spendry 	int df;
181*66561Spendry 	char *f;
18245769Sbostic 	struct stat sb;
18345769Sbostic 
18445769Sbostic 	df = dflag;
18545769Sbostic 	/*
18645769Sbostic 	 * Remove a file.  POSIX 1003.2 states that, by default, attempting
18745769Sbostic 	 * to remove a directory is an error, so must always stat the file.
18845769Sbostic 	 */
18945769Sbostic 	while (f = *argv++) {
19045769Sbostic 		/* Assume if can't stat the file, can't unlink it. */
19145769Sbostic 		if (lstat(f, &sb)) {
19259515Sbostic 			if (!fflag || errno != ENOENT) {
19359515Sbostic 				warn("%s", f);
19459515Sbostic 				eval = 1;
19559515Sbostic 			}
19645769Sbostic 			continue;
1971083Sbill 		}
19845769Sbostic 		if (S_ISDIR(sb.st_mode) && !df) {
19959515Sbostic 			warnx("%s: is a directory", f);
20059515Sbostic 			eval = 1;
20145769Sbostic 			continue;
20218327Sralph 		}
20345769Sbostic 		if (!fflag && !check(f, f, &sb))
20445769Sbostic 			continue;
20545769Sbostic 		if ((S_ISDIR(sb.st_mode) ? rmdir(f) : unlink(f)) &&
20659515Sbostic 		    (!fflag || errno != ENOENT)) {
20759515Sbostic 			warn("%s", f);
20859515Sbostic 			eval = 1;
20959515Sbostic 		}
2101083Sbill 	}
2111083Sbill }
2121083Sbill 
21359515Sbostic int
21445769Sbostic check(path, name, sp)
21545769Sbostic 	char *path, *name;
21645769Sbostic 	struct stat *sp;
2171083Sbill {
218*66561Spendry 	int ch, first;
21959515Sbostic 	char modep[15];
2201083Sbill 
22145769Sbostic 	/* Check -i first. */
22245769Sbostic 	if (iflag)
22345769Sbostic 		(void)fprintf(stderr, "remove %s? ", path);
22445769Sbostic 	else {
22545769Sbostic 		/*
22645769Sbostic 		 * If it's not a symbolic link and it's unwritable and we're
22745769Sbostic 		 * talking to a terminal, ask.  Symbolic links are excluded
22865344Sbostic 		 * because their permissions are meaningless.  Check stdin_ok
22965344Sbostic 		 * first because we may not have stat'ed the file.
23045769Sbostic 		 */
23165344Sbostic 		if (!stdin_ok || S_ISLNK(sp->st_mode) || !access(name, W_OK))
23259515Sbostic 			return (1);
23345769Sbostic 		strmode(sp->st_mode, modep);
23445769Sbostic 		(void)fprintf(stderr, "override %s%s%s/%s for %s? ",
23545769Sbostic 		    modep + 1, modep[9] == ' ' ? "" : " ",
23645769Sbostic 		    user_from_uid(sp->st_uid, 0),
23745769Sbostic 		    group_from_gid(sp->st_gid, 0), path);
23845769Sbostic 	}
23945769Sbostic 	(void)fflush(stderr);
24045769Sbostic 
24145769Sbostic 	first = ch = getchar();
24245769Sbostic 	while (ch != '\n' && ch != EOF)
24345769Sbostic 		ch = getchar();
24459515Sbostic 	return (first == 'y');
2451083Sbill }
24618277Sralph 
24745769Sbostic #define ISDOT(a)	((a)[0] == '.' && (!(a)[1] || (a)[1] == '.' && !(a)[2]))
24859515Sbostic void
24945769Sbostic checkdot(argv)
25045769Sbostic 	char **argv;
25118277Sralph {
252*66561Spendry 	char *p, **save, **t;
25345769Sbostic 	int complained;
25418277Sralph 
25545769Sbostic 	complained = 0;
25645769Sbostic 	for (t = argv; *t;) {
25759515Sbostic 		if (p = strrchr(*t, '/'))
25845769Sbostic 			++p;
25945769Sbostic 		else
26045769Sbostic 			p = *t;
26145769Sbostic 		if (ISDOT(p)) {
26245769Sbostic 			if (!complained++)
26359515Sbostic 				warnx("\".\" and \"..\" may not be removed");
26459515Sbostic 			eval = 1;
26545769Sbostic 			for (save = t; t[0] = t[1]; ++t);
26645769Sbostic 			t = save;
26745769Sbostic 		} else
26845769Sbostic 			++t;
26945769Sbostic 	}
27018277Sralph }
27134044Sbostic 
27259515Sbostic void
27334044Sbostic usage()
27434044Sbostic {
275*66561Spendry 
27645769Sbostic 	(void)fprintf(stderr, "usage: rm [-dfiRr] file ...\n");
27734044Sbostic 	exit(1);
27834044Sbostic }
279