rm.c revision 122409
11556Srgrimes/*-
21556Srgrimes * Copyright (c) 1990, 1993, 1994
31556Srgrimes *	The Regents of the University of California.  All rights reserved.
41556Srgrimes *
51556Srgrimes * Redistribution and use in source and binary forms, with or without
61556Srgrimes * modification, are permitted provided that the following conditions
71556Srgrimes * are met:
81556Srgrimes * 1. Redistributions of source code must retain the above copyright
91556Srgrimes *    notice, this list of conditions and the following disclaimer.
101556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111556Srgrimes *    notice, this list of conditions and the following disclaimer in the
121556Srgrimes *    documentation and/or other materials provided with the distribution.
131556Srgrimes * 3. All advertising materials mentioning features or use of this software
141556Srgrimes *    must display the following acknowledgement:
151556Srgrimes *	This product includes software developed by the University of
161556Srgrimes *	California, Berkeley and its contributors.
171556Srgrimes * 4. Neither the name of the University nor the names of its contributors
181556Srgrimes *    may be used to endorse or promote products derived from this software
191556Srgrimes *    without specific prior written permission.
201556Srgrimes *
211556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
221556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
231556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
241556Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
251556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
261556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
271556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
281556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
291556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
301556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
311556Srgrimes * SUCH DAMAGE.
321556Srgrimes */
331556Srgrimes
34114433Sobrien#if 0
351556Srgrimes#ifndef lint
3627959Sstevestatic const char copyright[] =
371556Srgrimes"@(#) Copyright (c) 1990, 1993, 1994\n\
381556Srgrimes	The Regents of the University of California.  All rights reserved.\n";
3927964Ssteve#endif /* not lint */
4027964Ssteve
4127964Ssteve#ifndef lint
4227964Sstevestatic char sccsid[] = "@(#)rm.c	8.5 (Berkeley) 4/18/94";
43114433Sobrien#endif /* not lint */
4427964Ssteve#endif
4599110Sobrien#include <sys/cdefs.h>
4699110Sobrien__FBSDID("$FreeBSD: head/bin/rm/rm.c 122409 2003-11-10 09:40:18Z guido $");
471556Srgrimes
481556Srgrimes#include <sys/stat.h>
4947584Skris#include <sys/param.h>
5047584Skris#include <sys/mount.h>
511556Srgrimes
521556Srgrimes#include <err.h>
531556Srgrimes#include <errno.h>
541556Srgrimes#include <fcntl.h>
551556Srgrimes#include <fts.h>
5690644Simp#include <grp.h>
5790644Simp#include <pwd.h>
581556Srgrimes#include <stdio.h>
591556Srgrimes#include <stdlib.h>
601556Srgrimes#include <string.h>
6150539Smharo#include <sysexits.h>
621556Srgrimes#include <unistd.h>
631556Srgrimes
6450872Smharoint dflag, eval, fflag, iflag, Pflag, vflag, Wflag, stdin_ok;
657798Sacheuid_t uid;
661556Srgrimes
6790110Simpint	check(char *, char *, struct stat *);
6890110Simpvoid	checkdot(char **);
6990110Simpvoid	rm_file(char **);
70122409Sguidoint	rm_overwrite(char *, struct stat *);
7190110Simpvoid	rm_tree(char **);
7290110Simpvoid	usage(void);
731556Srgrimes
741556Srgrimes/*
751556Srgrimes * rm --
761556Srgrimes *	This rm is different from historic rm's, but is expected to match
771556Srgrimes *	POSIX 1003.2 behavior.  The most visible difference is that -f
781556Srgrimes *	has two specific effects now, ignore non-existent files and force
791556Srgrimes * 	file removal.
801556Srgrimes */
811556Srgrimesint
8290110Simpmain(int argc, char *argv[])
831556Srgrimes{
841556Srgrimes	int ch, rflag;
8554895Ssheldonh	char *p;
861556Srgrimes
8754895Ssheldonh	/*
8854895Ssheldonh	 * Test for the special case where the utility is called as
8954895Ssheldonh	 * "unlink", for which the functionality provided is greatly
9054895Ssheldonh	 * simplified.
9154895Ssheldonh	 */
9254895Ssheldonh	if ((p = rindex(argv[0], '/')) == NULL)
9354895Ssheldonh		p = argv[0];
9454895Ssheldonh	else
9554895Ssheldonh		++p;
9654895Ssheldonh	if (strcmp(p, "unlink") == 0) {
9797533Stjr		while (getopt(argc, argv, "") != -1)
9854895Ssheldonh			usage();
9997533Stjr		argc -= optind;
10097533Stjr		argv += optind;
10199858Stjr		if (argc != 1)
10297533Stjr			usage();
10397533Stjr		rm_file(&argv[0]);
10497533Stjr		exit(eval);
10554895Ssheldonh	}
10654895Ssheldonh
10720421Ssteve	Pflag = rflag = 0;
10850872Smharo	while ((ch = getopt(argc, argv, "dfiPRrvW")) != -1)
1091556Srgrimes		switch(ch) {
1101556Srgrimes		case 'd':
1111556Srgrimes			dflag = 1;
1121556Srgrimes			break;
1131556Srgrimes		case 'f':
1141556Srgrimes			fflag = 1;
1151556Srgrimes			iflag = 0;
1161556Srgrimes			break;
1171556Srgrimes		case 'i':
1181556Srgrimes			fflag = 0;
1191556Srgrimes			iflag = 1;
1201556Srgrimes			break;
1211556Srgrimes		case 'P':
1221556Srgrimes			Pflag = 1;
1231556Srgrimes			break;
1241556Srgrimes		case 'R':
1251556Srgrimes		case 'r':			/* Compatibility. */
1261556Srgrimes			rflag = 1;
1271556Srgrimes			break;
12850872Smharo		case 'v':
12950872Smharo			vflag = 1;
13050872Smharo			break;
13120421Ssteve		case 'W':
13220421Ssteve			Wflag = 1;
13320421Ssteve			break;
1341556Srgrimes		default:
1351556Srgrimes			usage();
1361556Srgrimes		}
1371556Srgrimes	argc -= optind;
1381556Srgrimes	argv += optind;
1391556Srgrimes
14044282Sjkh	if (argc < 1) {
14144282Sjkh		if (fflag)
142122409Sguido			return (0);
1431556Srgrimes		usage();
14444282Sjkh	}
1451556Srgrimes
1461556Srgrimes	checkdot(argv);
1477798Sache	uid = geteuid();
1481556Srgrimes
14920421Ssteve	if (*argv) {
15020421Ssteve		stdin_ok = isatty(STDIN_FILENO);
15120421Ssteve
15220421Ssteve		if (rflag)
15320421Ssteve			rm_tree(argv);
15420421Ssteve		else
15520421Ssteve			rm_file(argv);
15620421Ssteve	}
15720421Ssteve
1581556Srgrimes	exit (eval);
1591556Srgrimes}
1601556Srgrimes
1611556Srgrimesvoid
16290110Simprm_tree(char **argv)
1631556Srgrimes{
1641556Srgrimes	FTS *fts;
1651556Srgrimes	FTSENT *p;
1661556Srgrimes	int needstat;
16720421Ssteve	int flags;
1687798Sache	int rval;
1691556Srgrimes
1701556Srgrimes	/*
1711556Srgrimes	 * Remove a file hierarchy.  If forcing removal (-f), or interactive
1721556Srgrimes	 * (-i) or can't ask anyway (stdin_ok), don't stat the file.
1731556Srgrimes	 */
17420421Ssteve	needstat = !uid || (!fflag && !iflag && stdin_ok);
1751556Srgrimes
1761556Srgrimes	/*
1771556Srgrimes	 * If the -i option is specified, the user can skip on the pre-order
1781556Srgrimes	 * visit.  The fts_number field flags skipped directories.
1791556Srgrimes	 */
1801556Srgrimes#define	SKIPPED	1
1811556Srgrimes
18251230Sbde	flags = FTS_PHYSICAL;
18320421Ssteve	if (!needstat)
18420421Ssteve		flags |= FTS_NOSTAT;
18520421Ssteve	if (Wflag)
18620421Ssteve		flags |= FTS_WHITEOUT;
18778170Sru	if (!(fts = fts_open(argv, flags, NULL)))
18899744Sdillon		err(1, "fts_open");
1891556Srgrimes	while ((p = fts_read(fts)) != NULL) {
1901556Srgrimes		switch (p->fts_info) {
1911556Srgrimes		case FTS_DNR:
1921556Srgrimes			if (!fflag || p->fts_errno != ENOENT) {
1931556Srgrimes				warnx("%s: %s",
1941556Srgrimes				    p->fts_path, strerror(p->fts_errno));
1951556Srgrimes				eval = 1;
1961556Srgrimes			}
1971556Srgrimes			continue;
1981556Srgrimes		case FTS_ERR:
1991556Srgrimes			errx(1, "%s: %s", p->fts_path, strerror(p->fts_errno));
2001556Srgrimes		case FTS_NS:
2011556Srgrimes			/*
2021556Srgrimes			 * FTS_NS: assume that if can't stat the file, it
2031556Srgrimes			 * can't be unlinked.
2041556Srgrimes			 */
2051556Srgrimes			if (!needstat)
2061556Srgrimes				break;
2071556Srgrimes			if (!fflag || p->fts_errno != ENOENT) {
2081556Srgrimes				warnx("%s: %s",
2091556Srgrimes				    p->fts_path, strerror(p->fts_errno));
2101556Srgrimes				eval = 1;
2111556Srgrimes			}
2121556Srgrimes			continue;
2131556Srgrimes		case FTS_D:
2141556Srgrimes			/* Pre-order: give user chance to skip. */
21520421Ssteve			if (!fflag && !check(p->fts_path, p->fts_accpath,
2161556Srgrimes			    p->fts_statp)) {
2171556Srgrimes				(void)fts_set(fts, p, FTS_SKIP);
2181556Srgrimes				p->fts_number = SKIPPED;
2191556Srgrimes			}
2207798Sache			else if (!uid &&
2217798Sache				 (p->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) &&
2227798Sache				 !(p->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)) &&
2237798Sache				 chflags(p->fts_accpath,
2247798Sache					 p->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)) < 0)
2257798Sache				goto err;
2261556Srgrimes			continue;
2271556Srgrimes		case FTS_DP:
2281556Srgrimes			/* Post-order: see if user skipped. */
2291556Srgrimes			if (p->fts_number == SKIPPED)
2301556Srgrimes				continue;
2311556Srgrimes			break;
23220421Ssteve		default:
23320421Ssteve			if (!fflag &&
23420421Ssteve			    !check(p->fts_path, p->fts_accpath, p->fts_statp))
23520421Ssteve				continue;
2361556Srgrimes		}
2371556Srgrimes
2387798Sache		rval = 0;
2397798Sache		if (!uid &&
2407798Sache		    (p->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) &&
2417798Sache		    !(p->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)))
2427798Sache			rval = chflags(p->fts_accpath,
2437798Sache				       p->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE));
24453819Smharo		if (rval == 0) {
2457798Sache			/*
2467798Sache			 * If we can't read or search the directory, may still be
2477798Sache			 * able to remove it.  Don't print out the un{read,search}able
2487798Sache			 * message unless the remove fails.
2497798Sache			 */
25020421Ssteve			switch (p->fts_info) {
25120421Ssteve			case FTS_DP:
25220421Ssteve			case FTS_DNR:
25353819Smharo				rval = rmdir(p->fts_accpath);
25453819Smharo				if (rval == 0 || (fflag && errno == ENOENT)) {
25553819Smharo					if (rval == 0 && vflag)
25650872Smharo						(void)printf("%s\n",
25770219Sobrien						    p->fts_path);
2581556Srgrimes					continue;
25950539Smharo				}
26020421Ssteve				break;
26120421Ssteve
26220421Ssteve			case FTS_W:
26353819Smharo				rval = undelete(p->fts_accpath);
26453819Smharo				if (rval == 0 && (fflag && errno == ENOENT)) {
26553819Smharo					if (vflag)
26650872Smharo						(void)printf("%s\n",
26770219Sobrien						    p->fts_path);
26820421Ssteve					continue;
26950539Smharo				}
27020421Ssteve				break;
27120421Ssteve
27220421Ssteve			default:
2737798Sache				if (Pflag)
274122409Sguido					if (!rm_overwrite(p->fts_accpath, NULL))
275122409Sguido						continue;
27653819Smharo				rval = unlink(p->fts_accpath);
27753819Smharo				if (rval == 0 || (fflag && errno == ENOENT)) {
27853819Smharo					if (rval == 0 && vflag)
27950872Smharo						(void)printf("%s\n",
28070219Sobrien						    p->fts_path);
2817798Sache					continue;
28250539Smharo				}
2837798Sache			}
2841556Srgrimes		}
2857798Sacheerr:
2861556Srgrimes		warn("%s", p->fts_path);
2871556Srgrimes		eval = 1;
2881556Srgrimes	}
2891556Srgrimes	if (errno)
2901556Srgrimes		err(1, "fts_read");
2911556Srgrimes}
2921556Srgrimes
2931556Srgrimesvoid
29490110Simprm_file(char **argv)
2951556Srgrimes{
2961556Srgrimes	struct stat sb;
29720421Ssteve	int rval;
2981556Srgrimes	char *f;
2991556Srgrimes
3001556Srgrimes	/*
3011556Srgrimes	 * Remove a file.  POSIX 1003.2 states that, by default, attempting
3021556Srgrimes	 * to remove a directory is an error, so must always stat the file.
3031556Srgrimes	 */
3041556Srgrimes	while ((f = *argv++) != NULL) {
3051556Srgrimes		/* Assume if can't stat the file, can't unlink it. */
3061556Srgrimes		if (lstat(f, &sb)) {
30720421Ssteve			if (Wflag) {
30820421Ssteve				sb.st_mode = S_IFWHT|S_IWUSR|S_IRUSR;
30920421Ssteve			} else {
31020421Ssteve				if (!fflag || errno != ENOENT) {
31120421Ssteve					warn("%s", f);
31220421Ssteve					eval = 1;
31320421Ssteve				}
31420421Ssteve				continue;
3151556Srgrimes			}
31620421Ssteve		} else if (Wflag) {
31720421Ssteve			warnx("%s: %s", f, strerror(EEXIST));
31820421Ssteve			eval = 1;
3191556Srgrimes			continue;
3201556Srgrimes		}
32120421Ssteve
32220421Ssteve		if (S_ISDIR(sb.st_mode) && !dflag) {
3231556Srgrimes			warnx("%s: is a directory", f);
3241556Srgrimes			eval = 1;
3251556Srgrimes			continue;
3261556Srgrimes		}
32720421Ssteve		if (!fflag && !S_ISWHT(sb.st_mode) && !check(f, f, &sb))
3281556Srgrimes			continue;
3297798Sache		rval = 0;
3307798Sache		if (!uid &&
3317798Sache		    (sb.st_flags & (UF_APPEND|UF_IMMUTABLE)) &&
3327798Sache		    !(sb.st_flags & (SF_APPEND|SF_IMMUTABLE)))
3337798Sache			rval = chflags(f, sb.st_flags & ~(UF_APPEND|UF_IMMUTABLE));
33453819Smharo		if (rval == 0) {
33520421Ssteve			if (S_ISWHT(sb.st_mode))
33620421Ssteve				rval = undelete(f);
33720421Ssteve			else if (S_ISDIR(sb.st_mode))
3387798Sache				rval = rmdir(f);
3397798Sache			else {
3407798Sache				if (Pflag)
341122409Sguido					if (!rm_overwrite(f, &sb))
342122409Sguido						continue;
3437798Sache				rval = unlink(f);
3447798Sache			}
3451556Srgrimes		}
3461556Srgrimes		if (rval && (!fflag || errno != ENOENT)) {
3471556Srgrimes			warn("%s", f);
3481556Srgrimes			eval = 1;
3491556Srgrimes		}
35053819Smharo		if (vflag && rval == 0)
35150539Smharo			(void)printf("%s\n", f);
3521556Srgrimes	}
3531556Srgrimes}
3541556Srgrimes
3551556Srgrimes/*
3561556Srgrimes * rm_overwrite --
3571556Srgrimes *	Overwrite the file 3 times with varying bit patterns.
3581556Srgrimes *
3591556Srgrimes * XXX
3601556Srgrimes * This is a cheap way to *really* delete files.  Note that only regular
3611556Srgrimes * files are deleted, directories (and therefore names) will remain.
362102230Strhodes * Also, this assumes a fixed-block file system (like FFS, or a V7 or a
363102230Strhodes * System V file system).  In a logging file system, you'll have to have
3641556Srgrimes * kernel support.
3651556Srgrimes */
366122409Sguidoint
36790110Simprm_overwrite(char *file, struct stat *sbp)
3681556Srgrimes{
3691556Srgrimes	struct stat sb;
37047584Skris	struct statfs fsb;
3711556Srgrimes	off_t len;
37247584Skris	int bsize, fd, wlen;
37347584Skris	char *buf = NULL;
3741556Srgrimes
3751556Srgrimes	fd = -1;
3761556Srgrimes	if (sbp == NULL) {
3771556Srgrimes		if (lstat(file, &sb))
3781556Srgrimes			goto err;
3791556Srgrimes		sbp = &sb;
3801556Srgrimes	}
3811556Srgrimes	if (!S_ISREG(sbp->st_mode))
382122409Sguido		return (1);
3831556Srgrimes	if ((fd = open(file, O_WRONLY, 0)) == -1)
3841556Srgrimes		goto err;
38547584Skris	if (fstatfs(fd, &fsb) == -1)
38647584Skris		goto err;
38747584Skris	bsize = MAX(fsb.f_iosize, 1024);
38847584Skris	if ((buf = malloc(bsize)) == NULL)
389122304Sbde		err(1, "%s: malloc", file);
3901556Srgrimes
3911556Srgrimes#define	PASS(byte) {							\
39247584Skris	memset(buf, byte, bsize);					\
3931556Srgrimes	for (len = sbp->st_size; len > 0; len -= wlen) {		\
39447584Skris		wlen = len < bsize ? len : bsize;			\
3951556Srgrimes		if (write(fd, buf, wlen) != wlen)			\
3961556Srgrimes			goto err;					\
3971556Srgrimes	}								\
3981556Srgrimes}
3991556Srgrimes	PASS(0xff);
4001556Srgrimes	if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET))
4011556Srgrimes		goto err;
4021556Srgrimes	PASS(0x00);
4031556Srgrimes	if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET))
4041556Srgrimes		goto err;
4051556Srgrimes	PASS(0xff);
40647584Skris	if (!fsync(fd) && !close(fd)) {
40747584Skris		free(buf);
408122409Sguido		return (1);
40947584Skris	}
4101556Srgrimes
4111556Srgrimeserr:	eval = 1;
41247584Skris	if (buf)
41347584Skris		free(buf);
414122304Sbde	if (fd != -1)
415122304Sbde		close(fd);
4161556Srgrimes	warn("%s", file);
417122409Sguido	return (0);
4181556Srgrimes}
4191556Srgrimes
4201556Srgrimes
4211556Srgrimesint
42290110Simpcheck(char *path, char *name, struct stat *sp)
4231556Srgrimes{
4241556Srgrimes	int ch, first;
42561749Sjoe	char modep[15], *flagsp;
4261556Srgrimes
4271556Srgrimes	/* Check -i first. */
4281556Srgrimes	if (iflag)
4291556Srgrimes		(void)fprintf(stderr, "remove %s? ", path);
4301556Srgrimes	else {
4311556Srgrimes		/*
4321556Srgrimes		 * If it's not a symbolic link and it's unwritable and we're
4331556Srgrimes		 * talking to a terminal, ask.  Symbolic links are excluded
4341556Srgrimes		 * because their permissions are meaningless.  Check stdin_ok
4351556Srgrimes		 * first because we may not have stat'ed the file.
436122409Sguido		 * Also skip this check if the -P option was specified because
437122409Sguido		 * we will not be able to overwrite file contents and will
438122409Sguido		 * barf later.
4391556Srgrimes		 */
440122409Sguido		if (!stdin_ok || S_ISLNK(sp->st_mode) || Pflag ||
44120421Ssteve		    (!access(name, W_OK) &&
4427798Sache		    !(sp->st_flags & (SF_APPEND|SF_IMMUTABLE)) &&
44320421Ssteve		    (!(sp->st_flags & (UF_APPEND|UF_IMMUTABLE)) || !uid)))
4441556Srgrimes			return (1);
4451556Srgrimes		strmode(sp->st_mode, modep);
44661749Sjoe		if ((flagsp = fflagstostr(sp->st_flags)) == NULL)
44799744Sdillon			err(1, "fflagstostr");
44861749Sjoe		(void)fprintf(stderr, "override %s%s%s/%s %s%sfor %s? ",
4491556Srgrimes		    modep + 1, modep[9] == ' ' ? "" : " ",
4501556Srgrimes		    user_from_uid(sp->st_uid, 0),
4517798Sache		    group_from_gid(sp->st_gid, 0),
45261749Sjoe		    *flagsp ? flagsp : "", *flagsp ? " " : "",
4537798Sache		    path);
45461749Sjoe		free(flagsp);
4551556Srgrimes	}
4561556Srgrimes	(void)fflush(stderr);
4571556Srgrimes
4581556Srgrimes	first = ch = getchar();
4591556Srgrimes	while (ch != '\n' && ch != EOF)
4601556Srgrimes		ch = getchar();
46114409Swosch	return (first == 'y' || first == 'Y');
4621556Srgrimes}
4631556Srgrimes
4642927Sphk#define ISDOT(a)	((a)[0] == '.' && (!(a)[1] || ((a)[1] == '.' && !(a)[2])))
4651556Srgrimesvoid
46690110Simpcheckdot(char **argv)
4671556Srgrimes{
4681556Srgrimes	char *p, **save, **t;
4691556Srgrimes	int complained;
4701556Srgrimes
4711556Srgrimes	complained = 0;
4721556Srgrimes	for (t = argv; *t;) {
4731556Srgrimes		if ((p = strrchr(*t, '/')) != NULL)
4741556Srgrimes			++p;
4751556Srgrimes		else
4761556Srgrimes			p = *t;
4771556Srgrimes		if (ISDOT(p)) {
4781556Srgrimes			if (!complained++)
4791556Srgrimes				warnx("\".\" and \"..\" may not be removed");
4801556Srgrimes			eval = 1;
48120421Ssteve			for (save = t; (t[0] = t[1]) != NULL; ++t)
48220421Ssteve				continue;
4831556Srgrimes			t = save;
4841556Srgrimes		} else
4851556Srgrimes			++t;
4861556Srgrimes	}
4871556Srgrimes}
4881556Srgrimes
4891556Srgrimesvoid
49090110Simpusage(void)
4911556Srgrimes{
49253819Smharo
49354895Ssheldonh	(void)fprintf(stderr, "%s\n%s\n",
49454895Ssheldonh	    "usage: rm [-f | -i] [-dPRrvW] file ...",
49554895Ssheldonh	    "       unlink file");
49650539Smharo	exit(EX_USAGE);
4971556Srgrimes}
498