File:  [MW Coherent from dump] / coherent / d / bin / rm.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Wed May 29 04:56:39 2019 UTC (7 years, 2 months ago) by root
Branches: MarkWilliams, MAIN
CVS tags: relic, HEAD
coherent

/*
 * rm.c
 * 6/4/91
 * Remove files or directories and their contents.
 * Usage: rm [-firtv] file ...
 *
 * Rec'd from Lauren Weinstein, 7-16-84.
 * This command is setuid to root to allow directory unlinks.
 * Interrupts are handled to prevent mangled directories.
 * Exit status of 1 indicates an error:
 *	couldn't find current directory,
 *	couldn't find root directory,
 *	pathname too long,
 *	couldn't find parent directory,
 *	couldn't stat argument,
 *	directory argument without -r flag,
 *	no permission on directory,
 *	directory is current directory,
 *	directory is root directory,
 *	unlink failed,
 *	couldn't open directory,
 *	couldn't read directory,
 *	ran out of memory saving directory entries.
 * Failure to delete a directory because it is not empty due to interactive
 *	file selection is not an error.
 * All the synonyms for pwd and root are identified by letting the system
 *	map them into stat buffers and comparing device x inode pairs.
 */

#include <stdio.h>
#include <sys/stat.h>
#include <sys/dir.h>
#include <access.h>
#include <signal.h>

extern	int errno;

char	*save();

#define	NFNAME	1000		/* Largest filename expansion in `-r' option */
#define	equals(s1, s2)	(strcmp((s1), (s2)) == 0)

char	fname[NFNAME];		/* current argument name */
struct	stat	sb;		/* current argument status buffer */
FILE	*dfp;			/* directory read stream */
char	iobuf[BUFSIZ];		/* stdio buffer */
struct	direct	db;		/* directory entry buffer */
struct	stat	dot_sb;		/* current directory status buffer */
struct	stat	root_sb;	/* root status buffer */
int	interrupted;		/* interrupt flag */
char 	*dot = ".";
char	*dotdot = "..";
char	*root = "/";
char	*cmd = "rm: ";		/* for messages */

int	fflag;			/* Force removal */
int	iflag;			/* Interactive removal */
int	rflag;			/* Recursive removal of directory */
int	tflag;			/* Test, do not perform removes */
int	vflag;			/* Verbose report */
int 	ntflag;			/* Non-zero if stdin not a terminal */

main(argc, argv) int argc; char *argv[];
{
	register char *ap;
	register int i;
	register int estat = 0;

	while (argc>1 && *argv[1]=='-') {
		for (ap=&argv[1][1]; *ap != '\0'; ap++)
			switch (*ap) {
			case 'f':	fflag = 1;	break;
			case 'i':	iflag = 1;	break;
			case 'r':	rflag = 1;	break;
			case 't':	tflag = 1;	break;
			case 'v':	vflag = 1;	break;
			default:
				usage();
			}
		argc--;
		argv++;
	}
	if (argc < 2)
		usage();
	if (!isatty(fileno(stdin))) {
		ntflag = 1;
		if (iflag) {
			fprintf(stderr, "rm: -i illegal with redirection\n");
			exit(1);
		}
	}		
	if (stat(dot, &dot_sb)) {
		lerror(dot);
		exit(1);
	}
	if (stat(root, &root_sb)) {
		lerror(root);
		exit(1);
	}

	catch(SIGINT);
	catch(SIGHUP);
	signal(SIGQUIT, SIG_IGN);

	for (i=1; i<argc; i++) {
		if (copy(fname, argv[i], NFNAME-2)) {
			toolong(argv[i]);
			estat = 1;
		} else
			estat |= (remove() == -1);
	}
	exit(estat);
}

/*
 * Remove the entry with name fname.
 * Check for all flags and permissions.
 * Return: -1 for errors; 1 for entries remain; 0 for removal.
 */
remove()
{
	register int isdir;

	if (stat(fname, &sb))
		return ((fflag) ? 0 : didnt(NULL));
	if (isdir = ((sb.st_mode & S_IFMT) == S_IFDIR)) {
		if (!rflag)
			return (didnt("directory"));
		if (access(fname, ALIST|ADEL|ASRCH) < 0)
			return (didnt(NULL));
		if (sb.st_dev == dot_sb.st_dev && sb.st_ino == dot_sb.st_ino)
			return (didnt("current directory"));
		if (sb.st_dev == root_sb.st_dev && sb.st_ino == root_sb.st_ino)
			return (didnt("root directory"));
	} else if (accparent() < 0)
		return (didnt(NULL));
	if (iflag) {
		if (!query("%s?", fname))
			return (report(1));
	} else if (!fflag && access(fname, AWRITE) < 0) {
		if (ntflag)	/* stdin not a terminal? */
			fprintf(stderr,"%sno write permission for %s",
				cmd, fname);
		else if (!query("override protection %o for %s?", 
			(sb.st_mode & 0777), fname))
				return(report(1));	/* abort */
	}
	if (isdir) {
		if (isdir = rmdir())
			return (report(isdir));
	} else {
		if (rmfile() < 0)
			return (didnt(NULL));
	}
	return (report(0));
}

/*
 * Check that the parent of this file has delete permission.
 * Rmdir checks parents of directories.
 */
accparent()
{
	register char *sp;
	register int c;
	register int accpar;

	for (sp = fname; *sp; sp += 1)		/* find end */
		;
	while (sp > fname && sp[-1] != '/')	/* find last / */
		sp -= 1;
	if (sp > fname) {			/* a real name */
		c = *sp;
		*sp = '\0';
		accpar = access(fname, ADEL);
		*sp = c;
	} else					/* simple name */
		accpar = access(dot, ADEL);
	return (accpar);
}

/*
 * Recursive removal of directories.
 * Scan the directory entries and append them to fname successively.
 * Call remove to remove everything except original fname, fname/., and
 * fname/.. which are rmfiled directly.
 */
rmdir()
{
	register char *cp, *np;
	int limit;
	int rmstat = 0;
	char *nbase = NULL, *ntops = NULL;

	/* save directory name */
	cp = fname;
	while (*cp++)
		;
	cp[-1] = '/';
	*cp = '\0';
	limit = NFNAME - 2 - (cp - fname);

	/* get the directory */
	if ((dfp = fopen(fname, "r")) == NULL)
		return (didnt(NULL));
	setbuf(dfp, iobuf);

	/* read and save file names in directory */
	while (fread(&db, sizeof(db), 1, dfp) == 1) {
		if (db.d_ino == 0
		   || equals(db.d_name, dot)
		   || equals(db.d_name, dotdot))
			continue;
		if ((ntops = save(db.d_name)) == NULL) {
			fclose(dfp);
			return (didnt("out of memory"));
		}
		if (nbase == NULL)
			nbase = ntops;
	}
	fclose(dfp);

	/* rescan names, form destinations, and remove */
	if (nbase != NULL) {
		for (np = nbase; np <= ntops; ) {
			if (copy(cp, np, limit)) {
				toolong(np);
				rmstat = -1;
				continue;
			}
			switch (remove()) {
			case -1:
				rmstat = -1;
				break;
			case 0:
				break;
			case 1:	
				rmstat = rmstat == -1 ? -1 : 1;
				break;
			default:
				botch("bad return from return");
				break;
			}
			while (*np++);
		}
		forget(nbase);
	}

	/* Now delete the directory, if it's empty */
	if (rmstat) {
		cp[-1] = '\0';
		return (rmstat);
	}
	if (copy(cp, dotdot, limit)) {
		toolong(dotdot);
		cp[-1] = '\0';
		return (-1);
	}
	if (access(fname, ADEL) < 0) {
		lerror(fname);
		cp[-1] = '\0';
		return (-1);
	}
	if (rmfile()
	   || copy(cp, dot, limit)
	   || rmfile()
	   || (cp[-1] = '\0')
	   || rmfile())
		botch("directory unlink error");
	return (rmstat);
}

/*
 * Unlink a single file if tflag is reset.
 */
rmfile()
{
	return (tflag ? 0 : unlink(fname));
}

/*
 * Ask a question about the current file,
 * return one if the answer begins with y or Y.
 */
/* VARARGS */
query(question) char *question;
{
	register int c;
	register int answer;

	fprintf(stderr, "%s%r [y/n] ", cmd, &question);
	answer = ((c = getchar()) == 'y' || c == 'Y');
	while (c != EOF && c != '\n')
		c = getchar();
	return (answer);
}

/*
 * Report that fname concatenated with the argument string is too long.
 */
toolong(cp) char *cp;
{
	if (!fflag)
		fprintf(stderr, "%s%s%s: too long\n", cmd, fname, cp);
}

/*
 * Copy src string to dst observing that no more than lim characters can fit.
 * Clean up on error.
 */
copy(dst, src, lim) char *dst, *src; int lim;
{
	register char *dp, *sp, *ep;

	dp = dst;
	sp = src;
	ep = dp + lim;

	while ((dp < ep) && (*dp++ = *sp++))
		;
	if (dp == ep) {
		*dst = '\0';
		return (-1);
	}
	return (0);
}

usage()
{
	fprintf(stderr, "Usage: rm [-firtv] file ...\n");
	exit(1);
}

/*
 * Report reason for failure.
 */
didnt(reason) char *reason;
{
	if (fflag)
		return -1;
	if (reason == NULL)
 		lerror(fname);
	else
		fprintf(stderr, "%s%s: %s\n", cmd, fname, reason);
	return (report(-1));
}

/*
 * Report non-removal/removal and return error code.
 * Since we come here after each file or directory is done,
 *	exit if an interrupt was detected.
 */
report(err)
int err;
{
	if (vflag)
		fprintf(stderr, "%s%s: %sremoved\n", cmd, fname,
			err ? "not ":"");
	if (interrupted)
		exit(1);
	return (err);
}

onintr()
{
	signal(SIGINT, SIG_IGN);
	signal(SIGHUP, SIG_IGN);
	++interrupted;
}

catch(sig)
{
	if (signal(sig, SIG_IGN) == SIG_DFL)
		signal(sig, onintr);
}

/*
 * Simplified heap.
 */
char *membase = NULL;
char *curbase = NULL;
char *curtops = NULL;

char *
save(name) register char *name;
{
	register char *saved;
	register int ntosave;
	extern char *sbrk();

	if (membase == NULL)
		membase = curbase = curtops = sbrk(0);

	saved = curbase;
	ntosave = 14;
	do {
		if (saved == curtops) {
			if (saved != sbrk(01000))
				return (NULL);
			curtops += 01000;
		}
	} while (ntosave-- && (*saved++ = *name++));
	if (ntosave < 0)
		*saved++ = '\0';
	name = curbase;
	curbase = saved;
	return (name);
}

forget(names) register char *names;
{
	if (names < membase || names >= curtops)
		botch("memory deallocation");
	curbase = names;
}

botch(msg) char *msg;
{
	fprintf(stderr, "%sbotched: %s at %s\n", cmd, msg, fname);
	exit(1);
}

lerror(msg) char *msg;
{
	register int err;

	err = errno;  		/* save error code for perror */
	fputs(cmd, stderr);	/* command name */
	errno = err;		/* restore error code */
	perror(msg);
}

/* end of rm.c */

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.