xref: /csrg-svn/bin/rm/rm.c (revision 47195)
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*47195Sbostic static char sccsid[] = "@(#)rm.c	4.26 (Berkeley) 03/10/91";
1645769Sbostic #endif /* not lint */
1745769Sbostic 
1845769Sbostic #include <sys/types.h>
1945769Sbostic #include <sys/stat.h>
2045769Sbostic #include <sys/errno.h>
2145769Sbostic #include <fts.h>
2245769Sbostic #include <unistd.h>
231083Sbill #include <stdio.h>
2445769Sbostic #include <string.h>
2545769Sbostic #include <stdlib.h>
261083Sbill 
2745769Sbostic int dflag, fflag, iflag, retval, stdin_ok;
281083Sbill 
2945769Sbostic /*
3045769Sbostic  * rm --
3145769Sbostic  *	This rm is different from historic rm's, but is expected to match
3245769Sbostic  *	POSIX 1003.2 behavior.  The most visible difference is that -f
3345769Sbostic  *	has two specific effects now, ignore non-existent files and force
3445769Sbostic  * 	file removal.
3545769Sbostic  */
3618277Sralph 
371083Sbill main(argc, argv)
3834044Sbostic 	int argc;
3934044Sbostic 	char **argv;
401083Sbill {
4145769Sbostic 	extern char *optarg;
4234044Sbostic 	extern int optind;
4345769Sbostic 	int ch, rflag;
441083Sbill 
4545769Sbostic 	rflag = 0;
4645769Sbostic 	while ((ch = getopt(argc, argv, "dfiRr")) != EOF)
4745769Sbostic 		switch(ch) {
4845769Sbostic 		case 'd':
4945769Sbostic 			dflag = 1;
5045769Sbostic 			break;
5134044Sbostic 		case 'f':
5245769Sbostic 			fflag = 1;
5345769Sbostic 			iflag = 0;
5418277Sralph 			break;
5534044Sbostic 		case 'i':
5645769Sbostic 			fflag = 0;
5745769Sbostic 			iflag = 1;
5834044Sbostic 			break;
5934044Sbostic 		case 'R':
60*47195Sbostic 		case 'r':			/* compatibility */
6145769Sbostic 			rflag = 1;
6234044Sbostic 			break;
6334044Sbostic 		case '?':
6434044Sbostic 		default:
6534044Sbostic 			usage();
6634044Sbostic 		}
6745769Sbostic 	argc -= optind;
6845769Sbostic 	argv += optind;
691932Sroot 
7045769Sbostic 	if (argc < 1)
7134044Sbostic 		usage();
7245769Sbostic 
7345769Sbostic 	checkdot(argv);
7445769Sbostic 	if (!*argv)
7545769Sbostic 		exit(retval);
7645769Sbostic 
7745769Sbostic 	stdin_ok = isatty(STDIN_FILENO);
7845769Sbostic 
7945769Sbostic 	if (rflag)
8045769Sbostic 		rmtree(argv);
8145769Sbostic 	else
8245769Sbostic 		rmfile(argv);
8345769Sbostic 	exit(retval);
841083Sbill }
851083Sbill 
8645769Sbostic rmtree(argv)
8745769Sbostic 	char **argv;
881083Sbill {
8945769Sbostic 	register FTS *fts;
9045769Sbostic 	register FTSENT *p;
9145769Sbostic 	register int needstat;
9245769Sbostic 	struct stat sb;
931083Sbill 
9445769Sbostic 	/*
9545769Sbostic 	 * Remove a file hierarchy.  If forcing removal (-f), or interactive
9645769Sbostic 	 * (-i) or can't ask anyway (stdin_ok), don't stat the file.
9745769Sbostic 	 */
9845769Sbostic 	needstat = !fflag && !iflag && stdin_ok;
9945769Sbostic 
10045769Sbostic 	/*
10145769Sbostic 	 * If the -i option is specified, the user can skip on the pre-order
10245769Sbostic 	 * visit.  The fts_number field flags skipped directories.
10345769Sbostic 	 */
10445769Sbostic #define	SKIPPED	1
10545769Sbostic 
10645769Sbostic 	if (!(fts = fts_open(argv,
10745769Sbostic 	    needstat ? FTS_PHYSICAL : FTS_PHYSICAL|FTS_NOSTAT,
10845769Sbostic 	    (int (*)())NULL))) {
10945769Sbostic 		(void)fprintf(stderr, "rm: %s.\n", strerror(errno));
11045769Sbostic 		exit(1);
1111083Sbill 	}
11245769Sbostic 	while (p = fts_read(fts)) {
11345769Sbostic 		switch(p->fts_info) {
11445769Sbostic 		case FTS_DNR:
11545769Sbostic 		case FTS_ERR:
11645769Sbostic 			error(p->fts_path, errno);
11745769Sbostic 			exit(1);
11845769Sbostic 		/*
11945769Sbostic 		 * FTS_NS: assume that if can't stat the file, it can't be
120*47195Sbostic 		 * unlinked.
12145769Sbostic 		 */
12245769Sbostic 		case FTS_NS:
12345769Sbostic 			if (!needstat)
12445769Sbostic 				break;
12545769Sbostic 			if (!fflag || errno != ENOENT)
12645769Sbostic 				error(p->fts_path, errno);
12745769Sbostic 			continue;
12845769Sbostic 		/* Pre-order: give user chance to skip. */
12945769Sbostic 		case FTS_D:
13045769Sbostic 			if (iflag && !check(p->fts_path, p->fts_accpath,
13145769Sbostic 			    &p->fts_statb)) {
13245769Sbostic 				(void)fts_set(fts, p, FTS_SKIP);
13345769Sbostic 				p->fts_number = SKIPPED;
13418327Sralph 			}
13545769Sbostic 			continue;
13645769Sbostic 		/* Post-order: see if user skipped. */
13745769Sbostic 		case FTS_DP:
13845769Sbostic 			if (p->fts_number == SKIPPED)
13945769Sbostic 				continue;
14045769Sbostic 			break;
14118277Sralph 		}
14245769Sbostic 
14345769Sbostic 		if (!fflag &&
14445769Sbostic 		    !check(p->fts_path, p->fts_accpath, &p->fts_statb))
14545769Sbostic 			continue;
14645769Sbostic 
14745769Sbostic 		/*
14845769Sbostic 		 * If we can't read or search the directory, may still be
14945769Sbostic 		 * able to remove it.  Don't print out the un{read,search}able
15045769Sbostic 		 * message unless the remove fails.
15145769Sbostic 		 */
152*47195Sbostic 		if (p->fts_info == FTS_DP || p->fts_info == FTS_DNR) {
15345769Sbostic 			if (!rmdir(p->fts_accpath))
15418277Sralph 				continue;
15545769Sbostic 			if (errno == ENOENT) {
15645769Sbostic 				if (fflag)
15745769Sbostic 					continue;
15845769Sbostic 			} else if (p->fts_info != FTS_DP)
159*47195Sbostic 				(void)fprintf(stderr,
160*47195Sbostic 				    "rm: unable to read %s.\n", p->fts_path);
16145769Sbostic 		} else if (!unlink(p->fts_accpath) || fflag && errno == ENOENT)
162*47195Sbostic 			continue;
16345769Sbostic 		error(p->fts_path, errno);
1641083Sbill 	}
16545769Sbostic }
1661083Sbill 
16745769Sbostic rmfile(argv)
16845769Sbostic 	char **argv;
16945769Sbostic {
17045769Sbostic 	register int df;
17145769Sbostic 	register char *f;
17245769Sbostic 	struct stat sb;
17345769Sbostic 
17445769Sbostic 	df = dflag;
17545769Sbostic 	/*
17645769Sbostic 	 * Remove a file.  POSIX 1003.2 states that, by default, attempting
17745769Sbostic 	 * to remove a directory is an error, so must always stat the file.
17845769Sbostic 	 */
17945769Sbostic 	while (f = *argv++) {
18045769Sbostic 		/* Assume if can't stat the file, can't unlink it. */
18145769Sbostic 		if (lstat(f, &sb)) {
18245769Sbostic 			if (!fflag || errno != ENOENT)
18345769Sbostic 				error(f, errno);
18445769Sbostic 			continue;
1851083Sbill 		}
18645769Sbostic 		if (S_ISDIR(sb.st_mode) && !df) {
187*47195Sbostic 			(void)fprintf(stderr, "rm: %s: is a directory\n", f);
18845769Sbostic 			retval = 1;
18945769Sbostic 			continue;
19018327Sralph 		}
19145769Sbostic 		if (!fflag && !check(f, f, &sb))
19245769Sbostic 			continue;
19345769Sbostic 		if ((S_ISDIR(sb.st_mode) ? rmdir(f) : unlink(f)) &&
19445769Sbostic 		    (!fflag || errno != ENOENT))
19545769Sbostic 			error(f, errno);
1961083Sbill 	}
1971083Sbill }
1981083Sbill 
19945769Sbostic check(path, name, sp)
20045769Sbostic 	char *path, *name;
20145769Sbostic 	struct stat *sp;
2021083Sbill {
20345769Sbostic 	register int first, ch;
20445769Sbostic 	char modep[15], *user_from_uid(), *group_from_gid();
2051083Sbill 
20645769Sbostic 	/* Check -i first. */
20745769Sbostic 	if (iflag)
20845769Sbostic 		(void)fprintf(stderr, "remove %s? ", path);
20945769Sbostic 	else {
21045769Sbostic 		/*
21145769Sbostic 		 * If it's not a symbolic link and it's unwritable and we're
21245769Sbostic 		 * talking to a terminal, ask.  Symbolic links are excluded
213*47195Sbostic 		 * because their permissions are meaningless.
21445769Sbostic 		 */
21545769Sbostic 		if (S_ISLNK(sp->st_mode) || !stdin_ok || !access(name, W_OK))
21645769Sbostic 			return(1);
21745769Sbostic 		strmode(sp->st_mode, modep);
21845769Sbostic 		(void)fprintf(stderr, "override %s%s%s/%s for %s? ",
21945769Sbostic 		    modep + 1, modep[9] == ' ' ? "" : " ",
22045769Sbostic 		    user_from_uid(sp->st_uid, 0),
22145769Sbostic 		    group_from_gid(sp->st_gid, 0), path);
22245769Sbostic 	}
22345769Sbostic 	(void)fflush(stderr);
22445769Sbostic 
22545769Sbostic 	first = ch = getchar();
22645769Sbostic 	while (ch != '\n' && ch != EOF)
22745769Sbostic 		ch = getchar();
22845769Sbostic 	return(first == 'y');
2291083Sbill }
23018277Sralph 
23145769Sbostic #define ISDOT(a)	((a)[0] == '.' && (!(a)[1] || (a)[1] == '.' && !(a)[2]))
23245769Sbostic checkdot(argv)
23345769Sbostic 	char **argv;
23418277Sralph {
23545769Sbostic 	register char *p, **t, **save;
23645769Sbostic 	int complained;
23718277Sralph 
23845769Sbostic 	complained = 0;
23945769Sbostic 	for (t = argv; *t;) {
24045769Sbostic 		if (p = rindex(*t, '/'))
24145769Sbostic 			++p;
24245769Sbostic 		else
24345769Sbostic 			p = *t;
24445769Sbostic 		if (ISDOT(p)) {
24545769Sbostic 			if (!complained++)
24645769Sbostic 			    (void)fprintf(stderr,
24745769Sbostic 				"rm: \".\" and \"..\" may not be removed.\n");
24845769Sbostic 			retval = 1;
24945769Sbostic 			for (save = t; t[0] = t[1]; ++t);
25045769Sbostic 			t = save;
25145769Sbostic 		} else
25245769Sbostic 			++t;
25345769Sbostic 	}
25418277Sralph }
25534044Sbostic 
25645769Sbostic error(name, val)
25745769Sbostic 	char *name;
25845769Sbostic 	int val;
25945769Sbostic {
26045769Sbostic 	(void)fprintf(stderr, "rm: %s: %s.\n", name, strerror(val));
26145769Sbostic 	retval = 1;
26245769Sbostic }
26345769Sbostic 
26434044Sbostic usage()
26534044Sbostic {
26645769Sbostic 	(void)fprintf(stderr, "usage: rm [-dfiRr] file ...\n");
26734044Sbostic 	exit(1);
26834044Sbostic }
269