xref: /csrg-svn/bin/rm/rm.c (revision 59515)
145769Sbostic /*-
245769Sbostic  * Copyright (c) 1990 The Regents of the University of California.
345769Sbostic  * All rights reserved.
445769Sbostic  *
545769Sbostic  * %sccs.include.redist.c%
618277Sralph  */
718277Sralph 
845769Sbostic #ifndef lint
945769Sbostic char copyright[] =
1045769Sbostic "@(#) Copyright (c) 1990 The Regents of the University of California.\n\
1145769Sbostic  All rights reserved.\n";
1245769Sbostic #endif /* not lint */
1345769Sbostic 
1445769Sbostic #ifndef lint
15*59515Sbostic static char sccsid[] = "@(#)rm.c	5.1 (Berkeley) 04/29/93";
1645769Sbostic #endif /* not lint */
1745769Sbostic 
1845769Sbostic #include <sys/types.h>
1945769Sbostic #include <sys/stat.h>
2045769Sbostic #include <sys/errno.h>
21*59515Sbostic 
22*59515Sbostic #include <err.h>
2345769Sbostic #include <fts.h>
241083Sbill #include <stdio.h>
25*59515Sbostic #include <stdlib.h>
2645769Sbostic #include <string.h>
27*59515Sbostic #include <unistd.h>
281083Sbill 
29*59515Sbostic int dflag, fflag, iflag, eval, stdin_ok;
301083Sbill 
31*59515Sbostic int	check __P((char *, char *, struct stat *));
32*59515Sbostic void	checkdot __P((char **));
33*59515Sbostic void	rmfile __P((char **));
34*59515Sbostic void	rmtree __P((char **));
35*59515Sbostic void	usage __P((void));
36*59515Sbostic 
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  */
44*59515Sbostic int
451083Sbill main(argc, argv)
4634044Sbostic 	int argc;
47*59515Sbostic 	char *argv[];
481083Sbill {
4934044Sbostic 	extern int optind;
5045769Sbostic 	int ch, rflag;
511083Sbill 
5245769Sbostic 	rflag = 0;
5345769Sbostic 	while ((ch = getopt(argc, argv, "dfiRr")) != EOF)
5445769Sbostic 		switch(ch) {
5545769Sbostic 		case 'd':
5645769Sbostic 			dflag = 1;
5745769Sbostic 			break;
5834044Sbostic 		case 'f':
5945769Sbostic 			fflag = 1;
6045769Sbostic 			iflag = 0;
6118277Sralph 			break;
6234044Sbostic 		case 'i':
6345769Sbostic 			fflag = 0;
6445769Sbostic 			iflag = 1;
6534044Sbostic 			break;
6634044Sbostic 		case 'R':
6747195Sbostic 		case 'r':			/* compatibility */
6845769Sbostic 			rflag = 1;
6934044Sbostic 			break;
7034044Sbostic 		case '?':
7134044Sbostic 		default:
7234044Sbostic 			usage();
7334044Sbostic 		}
7445769Sbostic 	argc -= optind;
7545769Sbostic 	argv += optind;
761932Sroot 
7745769Sbostic 	if (argc < 1)
7834044Sbostic 		usage();
7945769Sbostic 
8045769Sbostic 	checkdot(argv);
8145769Sbostic 	if (!*argv)
82*59515Sbostic 		exit (eval);
8345769Sbostic 
8445769Sbostic 	stdin_ok = isatty(STDIN_FILENO);
8545769Sbostic 
8645769Sbostic 	if (rflag)
8745769Sbostic 		rmtree(argv);
8845769Sbostic 	else
8945769Sbostic 		rmfile(argv);
90*59515Sbostic 	exit (eval);
911083Sbill }
921083Sbill 
93*59515Sbostic void
9445769Sbostic rmtree(argv)
9545769Sbostic 	char **argv;
961083Sbill {
9745769Sbostic 	register FTS *fts;
9845769Sbostic 	register FTSENT *p;
9945769Sbostic 	register int needstat;
1001083Sbill 
10145769Sbostic 	/*
10245769Sbostic 	 * Remove a file hierarchy.  If forcing removal (-f), or interactive
10345769Sbostic 	 * (-i) or can't ask anyway (stdin_ok), don't stat the file.
10445769Sbostic 	 */
10545769Sbostic 	needstat = !fflag && !iflag && stdin_ok;
10645769Sbostic 
10745769Sbostic 	/*
10845769Sbostic 	 * If the -i option is specified, the user can skip on the pre-order
10945769Sbostic 	 * visit.  The fts_number field flags skipped directories.
11045769Sbostic 	 */
11145769Sbostic #define	SKIPPED	1
11245769Sbostic 
11345769Sbostic 	if (!(fts = fts_open(argv,
11445769Sbostic 	    needstat ? FTS_PHYSICAL : FTS_PHYSICAL|FTS_NOSTAT,
115*59515Sbostic 	    (int (*)())NULL)))
116*59515Sbostic 		err(1, NULL);
11745769Sbostic 	while (p = fts_read(fts)) {
118*59515Sbostic 		switch (p->fts_info) {
11945769Sbostic 		case FTS_DNR:
12045769Sbostic 		case FTS_ERR:
121*59515Sbostic 			err(1, "%s", p->fts_path);
122*59515Sbostic 
12345769Sbostic 		case FTS_NS:
124*59515Sbostic 			/*
125*59515Sbostic 			 * FTS_NS: assume that if can't stat the file, it
126*59515Sbostic 			 * can't be unlinked.
127*59515Sbostic 			 */
12845769Sbostic 			if (!needstat)
12945769Sbostic 				break;
130*59515Sbostic 			if (!fflag || errno != ENOENT) {
131*59515Sbostic 				warn("%s", p->fts_path);
132*59515Sbostic 				eval = 1;
133*59515Sbostic 			}
13445769Sbostic 			continue;
135*59515Sbostic 
13645769Sbostic 		case FTS_D:
137*59515Sbostic 			/* Pre-order: give user chance to skip. */
13845769Sbostic 			if (iflag && !check(p->fts_path, p->fts_accpath,
13952238Sbostic 			    p->fts_statp)) {
14045769Sbostic 				(void)fts_set(fts, p, FTS_SKIP);
14145769Sbostic 				p->fts_number = SKIPPED;
14218327Sralph 			}
14345769Sbostic 			continue;
144*59515Sbostic 
14545769Sbostic 		case FTS_DP:
146*59515Sbostic 			/* Post-order: see if user skipped. */
14745769Sbostic 			if (p->fts_number == SKIPPED)
14845769Sbostic 				continue;
14945769Sbostic 			break;
15018277Sralph 		}
15145769Sbostic 
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)
168*59515Sbostic 				warnx("%s: unable to read", p->fts_path);
16945769Sbostic 		} else if (!unlink(p->fts_accpath) || fflag && errno == ENOENT)
17047195Sbostic 			continue;
171*59515Sbostic 		warn("%s", p->fts_path);
172*59515Sbostic 		eval = 1;
1731083Sbill 	}
17445769Sbostic }
1751083Sbill 
176*59515Sbostic void
17745769Sbostic rmfile(argv)
17845769Sbostic 	char **argv;
17945769Sbostic {
18045769Sbostic 	register int df;
18145769Sbostic 	register 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)) {
192*59515Sbostic 			if (!fflag || errno != ENOENT) {
193*59515Sbostic 				warn("%s", f);
194*59515Sbostic 				eval = 1;
195*59515Sbostic 			}
19645769Sbostic 			continue;
1971083Sbill 		}
19845769Sbostic 		if (S_ISDIR(sb.st_mode) && !df) {
199*59515Sbostic 			warnx("%s: is a directory", f);
200*59515Sbostic 			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)) &&
206*59515Sbostic 		    (!fflag || errno != ENOENT)) {
207*59515Sbostic 			warn("%s", f);
208*59515Sbostic 			eval = 1;
209*59515Sbostic 		}
2101083Sbill 	}
2111083Sbill }
2121083Sbill 
213*59515Sbostic int
21445769Sbostic check(path, name, sp)
21545769Sbostic 	char *path, *name;
21645769Sbostic 	struct stat *sp;
2171083Sbill {
21845769Sbostic 	register int first, ch;
219*59515Sbostic 	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
22847195Sbostic 		 * because their permissions are meaningless.
22945769Sbostic 		 */
23045769Sbostic 		if (S_ISLNK(sp->st_mode) || !stdin_ok || !access(name, W_OK))
231*59515Sbostic 			return (1);
23245769Sbostic 		strmode(sp->st_mode, modep);
23345769Sbostic 		(void)fprintf(stderr, "override %s%s%s/%s for %s? ",
23445769Sbostic 		    modep + 1, modep[9] == ' ' ? "" : " ",
23545769Sbostic 		    user_from_uid(sp->st_uid, 0),
23645769Sbostic 		    group_from_gid(sp->st_gid, 0), path);
23745769Sbostic 	}
23845769Sbostic 	(void)fflush(stderr);
23945769Sbostic 
24045769Sbostic 	first = ch = getchar();
24145769Sbostic 	while (ch != '\n' && ch != EOF)
24245769Sbostic 		ch = getchar();
243*59515Sbostic 	return (first == 'y');
2441083Sbill }
24518277Sralph 
24645769Sbostic #define ISDOT(a)	((a)[0] == '.' && (!(a)[1] || (a)[1] == '.' && !(a)[2]))
247*59515Sbostic void
24845769Sbostic checkdot(argv)
24945769Sbostic 	char **argv;
25018277Sralph {
25145769Sbostic 	register char *p, **t, **save;
25245769Sbostic 	int complained;
25318277Sralph 
25445769Sbostic 	complained = 0;
25545769Sbostic 	for (t = argv; *t;) {
256*59515Sbostic 		if (p = strrchr(*t, '/'))
25745769Sbostic 			++p;
25845769Sbostic 		else
25945769Sbostic 			p = *t;
26045769Sbostic 		if (ISDOT(p)) {
26145769Sbostic 			if (!complained++)
262*59515Sbostic 				warnx("\".\" and \"..\" may not be removed");
263*59515Sbostic 			eval = 1;
26445769Sbostic 			for (save = t; t[0] = t[1]; ++t);
26545769Sbostic 			t = save;
26645769Sbostic 		} else
26745769Sbostic 			++t;
26845769Sbostic 	}
26918277Sralph }
27034044Sbostic 
271*59515Sbostic void
27234044Sbostic usage()
27334044Sbostic {
27445769Sbostic 	(void)fprintf(stderr, "usage: rm [-dfiRr] file ...\n");
27534044Sbostic 	exit(1);
27634044Sbostic }
277