|
|
DataMuseum.dkPresents historical artifacts from the history of: Commodore CBM-900 |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about Commodore CBM-900 Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - download
Length: 9223 (0x2407)
Types: TextFile
Notes: UNIX file
Names: »rm.c«
└─⟦f27320a65⟧ Bits:30001972 Commodore 900 hard disk image with partial source code
└─⟦f4b8d8c84⟧ UNIX Filesystem
└─⟦this⟧ »cmd/rm.c«
/*
* Rec'd from Lauren Weinstein, 7-16-84.
* rm -- remove files or directories and their contents.
* 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 <dir.h>
#include <access.h>
#include <signal.h>
#define NFNAME 1000 /* Largest filename expansion in `-r' option */
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 */
char *save();
extern int errno;
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)
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))) {
iflag = 0;
ntflag = 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;
register int abortf = 0;
char c;
if (stat(fname, &sb))
return (didnt(NULL));
if (isdir = ((sb.st_mode & S_IFMT) == S_IFDIR)) {
if (!rflag)
return (didnt("%s: directory\n"));
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("%s: current directory\n"));
if (sb.st_dev == root_sb.st_dev && sb.st_ino == root_sb.st_ino)
return (didnt("%s: root directory|n"));
} else if (accparent()<0)
return (didnt(NULL));
if (iflag) {
if (!query("%s? "))
return (report(1));
} else if (!fflag && access(fname, AWRITE)<0) {
if (ntflag) /* stdin not a terminal? */
fprintf(stderr, "%sno write permission", cmd, fname);
fprintf(stderr, "%soverride protection %o for %s? ",
cmd, (sb.st_mode & 0777), fname);
if ((c = getchar()) != 'y')
abortf++; /* flag abort */
while (c != EOF && c != '\n') /* flush remaining input */
c = getchar();
if (abortf) /* abort delete? */
return(report(1)); /* yes */
}
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("%s: out of memory\n"));
}
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.
*/
query(question)
char *question;
{
register int c;
register int answer = 0;
fputs(cmd, stderr);
fprintf(stderr, question, fname);
if ((c = getchar())=='y')
answer = 1;
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 [-frivt] file ...\n");
exit(1);
}
/*
* Report reason for failure.
*/
didnt(reason)
char *reason;
{
if (fflag)
return;
if (reason == NULL)
lerror(fname);
else
{ fputs(cmd, stderr);
fprintf(stderr, reason, fname);
}
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);
}
/*
* Compare strings for equality.
*/
equals(s1, s2)
register char *s1, *s2;
{
while (*s1++ == *s2)
if (*s2++ == '\0')
return (1);
return (0);
}
/*
* 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);
}