home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Shareware Supreme Volume 6 #1
/
swsii.zip
/
swsii
/
126
/
SPRDPR20.ZIP
/
RM.C
< prev
next >
Wrap
Text File
|
1989-10-21
|
20KB
|
713 lines
#pragma inline
/*
RM.C Copyright (C) 1988 Mark Adler Pasadena, CA
All rights reserved.
Version history -
1.0 4 Jun 1988 First public version
1.1 4 Nov 1988 Make rm as fast as del (use FCB delete)
1.2 18 Nov 1988 Fixed for Turbo C 2.0 (label needs statement)
1.3 3 Feb 1989 Display files and directories in lower case
1.4 21 Oct 1989 Catch dangling options and ignore command
RM is an extended delete command. It can delete all the files in a
directory and its subdirectories, etc. and remove all the directories.
Alternatively, it can delete all the files in any directories that match
the specified pattern. Since RM is a more powerful delete command, it
can be that much more dangerous. So the following warning is in order:
WARNING: RM IS A VERY DESTRUCTIVE COMMAND. It will NOT ask for
verification of a particularily destructive argument like the DOS DEL
command does. What you ask it is what it does. BE CAREFUL.
Normally, RM acts like the DEL command, deleting any files that match
the pattern, except that it does not verify the argument *.*---it just
does it. For example:
rm *.bak
will delete all the *.bak files in the current directory. The command:
rm *.*
will delete all the files in the current directory without asking first.
RM can also delete the contents of subdirectories and then remove the
subdirectories. The option for this is "/S". For example:
rm/s \tex
will delete all the files and subdirectories contained in \tex and
finally remove the directory \tex. The command:
rm/s \tex\*.*
will do the same thing, but not remove the directory tex, just
everything in it.
Alternatively, RM can delete any files that match the pattern in all
subdirectories. The option for this is "/F":
rm/f \*.bak
will delete all *.bak files on the current drive, no matter where they
are. No subdirectories will be removed.
If you think that's dangerous, RM can also remove hidden, system, or
read-only files if asked to. The options are "/H", "/Y", and "/R".
For example:
rm/shyr \*.* DON'T EVEN THINK OF TYPING THIS COMMAND.
will remove every single file and directory on the current drive,
including the system---leaving only the drive label, a lot of empty
space, and a user in a state of panic.
RM can take multiple arguments, processing each in turn. For example:
rm *.obj *.lst *.map
will delete all the files specified.
RM does have one option that makes it a little less dangerous; "/P".
This option causes RM to prompt you for every file or directory it tries
to get rid of. For example:
rm/p *.*
might ask you:
Delete file test.c (y/n)? _
or
Remove directory \tmp (y/n)? _
In either case, you must respond with either an upper or lower case "Y"
or "N". Any other response is ignored.
When it is done, RM will tell you how many files it deleted and how many
directories it removed. It might say, for example:
13 files deleted
or
129 files deleted and 4 directories removed
If RM is, for some reason, unable to delete a file or remove a
directory, it will say:
Could not delete file test.c
or
Could not remove directory \tmp - part of current path
or
Could not remove directory \tmp - unable to empty it
It should not be possible to get the first error. The second error
occurs when the directory RM tried to remove is part of the current path
for that drive. DOS does not allow pulling the rug out from under your
feet by removing a directory you are in. The remedy is to change out of
the directory you wish to remove and repeating the RM command.
The third error can happen when RM tries to remove a directory with
read-only, hidden, or system files in it, and no options were specified
to allow RM to remove those. The remedy is to repeat the RM command
with the necessary options to delete the files desired.
RM takes options that are preceded by a slash (/). The command line is
read from left to right, processing options and file/path names as they
appear. However, options that are appended to a file/path name take
effect before that name is processed. For example, this command would
do the same thing as "rm/p *.c":
rm *.c/p
But the command "rm *.c /p" (notice the extra space before the /p) would
not do the same thing. In this case, RM notices the "dangling" option
before it does anything and simply ignores the entire command for
safety, printing:
Dangling option---entire command ignored
Since there are options to change the defaults along a command line,
there are options to change them back for processing the next thing
on the command line. All the options are listed here:
/P - Prompt for each deletion or removal.
/Q - Quiet---don't prompt (default).
/S - Remove matching subdirectories and all of their contents.
/N - Ignore subdirectories (default).
/H - Remove hidden files.
/V - Visible---ignore hidden files (default).
/Y - Remove system files.
/T - Typical---ignore system files (default).
/R - Remove read-only files!
/W - Only delete writable files (default).
/F - Remove all files that match pattern, and no subdirs.
/A - Remove all files in subdirectories, period (default).
/? - Display version number and list of options.
For example, the command:
rm /p *.lst /q *.obj
will ask before deleting each file that matches *.lst, and then delete
all the files that match *.obj without asking.
Options can be combined with or without additional slashes. For
example, these commands do the same thing:
rm *.lst/pr
rm/p/r *.lst
rm /p /r *.lst
rm /p *.lst/r
Once again---BE CAREFUL WITH THIS COMMAND. Thanks to Kurt Schmidt for
suggesting that RM be changed to notice a dangling option.
*/
typedef unsigned long ulong;
/* Option flags, assigned to default values */
char ask = 0, /* Prompt for each deletion */
sub = 0, /* Include subdirectories */
att = 0, /* Attribute for hidden, system */
rof = 0, /* Remove read-only files also */
fin = 0; /* Find---use same name in subdirs */
ulong fls, drs; /* Number of files, directories removed */
/* Structure for fnd1st(), fndnxt() */
struct find {
char rsvd[21]; /* What DOS needs to keep track of find */
char attr; /* File attribute */
unsigned time; /* Time stamp */
unsigned date; /* Date stamp */
ulong size; /* File size in bytes */
char name[13]; /* FIle name as a zero terminated string */
};
/* Send character 'c' to stdout */
void pputc(char c)
{
asm mov DL,c
asm mov AH,2
asm int 21h
}
/* Send string 's' to stdout */
void pputs(char *s)
{
asm mov SI,s
asm cld
plp:
asm lodsb
asm test AL,AL
asm jz pfin
asm mov DL,AL
asm mov AH,2
asm int 21h
asm jmp short plp
pfin: ;
}
/* Convert the string s from upper case to lower case */
char *strlow(char *s)
{
asm mov AX,DS
asm mov ES,AX
asm cld
asm mov SI,s
asm lea DI,[SI-1]
llp:
asm inc DI
llpnoi:
asm lodsb
asm test AL,AL
asm jz lfin
asm cmp AL,'A'
asm jb llp
asm cmp AL,'Z'
asm ja llp
asm add AL,'a'-'A'
asm stosb
asm jmp short llpnoi
lfin: ;
return s;
}
/* Copy string s to string d, return end of d */
char *scpy(char *d, char *s)
{
asm mov SI,s
asm mov DI,d
asm cld
asm mov AX,DS
asm mov ES,AX
slp:
asm lodsb
asm stosb
asm test AL,AL
asm jnz slp
asm mov AX,DI
asm dec AX
return (char *) _AX;
}
/* Convert unsigned long 'n' to decimal in string 's' */
char *ultod(ulong n, char *s)
{
/* Load n into BX:AX, s into SI and DI, and the radix into CX */
asm mov DI,s
asm mov SI,DI
asm mov AX,n
asm mov BX,n+2
asm mov CX,10
/* Convert n into a digit string, least significant digit first */
dlp:
/* Divide BX:AX by CX, quotient to BX:AX, remainder to DX */
asm xchg AX,BX /* BX = low n */
asm sub DX,DX /* DX:AX = high n */
asm div CX /* AX = high q, DX = temporary r */
asm xchg AX,BX /* BX = high q, DX:AX = temp r:low q */
asm div CX /* BX:AX = q, DX = r */
/* Put digit in string */
asm add DL,'0'
asm mov [DI],DL
asm inc DI
/* Do until BX:AX is zero */
asm mov DX,AX
asm or DX,BX
asm jnz dlp
/* Terminate string */
asm mov [DI],AL
/* Reverse the string, putting most significant digit first */
rlp:
asm dec DI
asm cmp DI,SI
asm jna rfin
asm mov AL,[SI]
asm xchg AL,[DI]
asm mov [SI],AL
asm inc SI
asm jmp short rlp
rfin:
/* Return pointer to start of string */
asm mov AX,s
return (char *) _AX;
}
/* Ask user yes/no question */
int query(char *s, char *t)
{
pputs(s);
pputs(t);
pputs(" (y/n)? ");
qlp:
asm mov AH,8 /* Get console input without echo */
asm int 21h
asm test AL,AL
asm jnz got
asm int 21h /* If extended code, ignore it */
asm jmp short qlp
got:
asm mov DL,AL /* Save character typed */
asm and AL,5Fh /* Convert lower to upper case */
asm sub SI,SI /* Return 0 if no */
asm cmp AL,'N'
asm je qfin
asm inc SI /* Return 1 if yes */
asm cmp AL,'Y'
asm jne qlp /* Do until we get a proper answer */
qfin:
pputc(_DX); /* Echo y, n, Y, or N */
pputs("\r\n");
return _SI;
}
/* Find first match to 'p', attribute 'a', results in 'f' */
int fnd1st(char *p, struct find *f, int a)
{
/* Set DTA to f */
asm mov DX,f
asm mov AH,1Ah
asm int 21h
/* Do find first */
asm mov DX,p
asm mov CX,a
asm mov AH,4Eh
asm int 21h
asm sbb AX,AX
return _AX;
}
/* Find next match for fnd1st() done on 'f' */
int fndnxt(struct find *f)
{
/* Set DTA to f */
asm mov DX,f
asm mov AH,1Ah
asm int 21h
/* Do find next */
asm mov AH,4Fh
asm int 21h
asm sbb AX,AX
return _AX;
}
/* Parse file name n into FCB f */
int parse(char *n, char *f)
{
asm mov AX,DS
asm mov ES,AX
asm mov SI,n
asm mov DI,f
asm mov AX,2901h
asm int 21h
asm cbw
return _AX;
}
/* Get current directory on drive d into string p */
int curdir(int d, char *p)
{
asm mov DL,d
asm mov SI,p
asm mov AH,47h
asm int 21h
asm jb cerr
asm sub AX,AX
cerr:
return _AX;
}
/* Set new directory p */
int newdir(char *p)
{
asm mov DX,p
asm mov AH,3Bh
asm int 21h
asm jb nerr
asm sub AX,AX
nerr:
return _AX;
}
/* Count the matches for q on drive e (0=default) with attributes */
/* att/rof and adjust the attribute if needed for the global delete */
ulong count(int e, char *q)
{
int r;
char *p;
ulong n;
struct find d;
char m[16];
p = m;
if (e)
{
*p++ = e + 'A' - 1;
*p++ = ':';
}
scpy(p, q);
n = 0;
r = fnd1st(m, &d, att);
while (!r)
{
if (rof || !(d.attr & 1)) /* Ignore read-only unless specified */
{
n++;
if (d.attr & 7) /* Adjust attribute if needed */
{
scpy(p, d.name);
asm lea DX,m
asm sub CX,CX
asm mov AX,4301h
asm int 21h
}
}
r = fndnxt(&d);
}
return n;
}
void remove(char *a)
{
register char *p, *q;
int e;
struct find d;
char s[128];
char t[128];
/* Setup */
q = s;
p = scpy(q, a) - 1; /* Copy path being searched */
while (p >= q && *p != '\\' && *p != ':')
p--; /* Scan back for path delimiter */
p++; /* Point past delimiter */
if (sub || fin)
scpy(t, p); /* Save name to match */
/* First try global delete since it is faster */
if (!ask) /* Of course, skip if prompting */
do { /* (use "do" so can use "break") */
char *r, f[44], u[128], v[128];
/* Set up to do global delete using FCB calls */
f[0] = 0; /* Not an extended FCB */
if (parse(p, f+7) != 1) /* Make an FCB out of the name */
break; /* If not global, skip all this */
if (q[0] && q[1] == ':') /* Get drive */
e = (q[0] & 0x5f) - 'A' + 1;
else
e = 0; /* Use default drive */
f[7] = e; /* Set drive */
*v = '\0'; /* No current path yet */
if (p - q && *(p-1) != ':') /* Change path if needed */
{
r = v; /* Construct old path */
if (e)
{
*r++ = e + 'A' - 1; /* Put in drive if needed, */
*r++ = ':';
}
*r++ = '\\'; /* and backslash to start at root */
if (curdir(e, r)) /* Get current directory */
break; /* If can't get it, skip rest */
scpy(u, q); /* Copy and terminate new path */
u[p - q - (p - q != 1 && u[p - q - 1] != ':')] = '\0';
if (newdir(u))
break; /* If no such path, skip rest */
}
/* Do global delete and count how many files were deleted */
fls += count(e, p); /* Adjust attributes if neeeded */
asm lea DX,f+7
asm mov AH,13h
asm int 21h
fls -= count(e, p);
/* Change path on that drive back to what it was */
if (*v)
newdir(v);
} while (0); /* (not really a loop) */
/* Find matching files */
e = fnd1st(q, &d, att); /* Search allowed attributes */
while (!e)
{
if (rof || !(d.attr & 1)) /* Ignore read-only unless specified */
{
scpy(p, d.name);
if (!ask || query("Delete file ", strlow(q)))
{
if (d.attr & 1) /* If read-only and got here, */
{ /* then change the attribute */
asm mov DX,q
asm sub CX,CX
asm mov AX,4301h
asm int 21h
}
asm mov DX,q /* Delete file */
asm mov AH,41h
asm int 21h
asm jnb delok
pputs("Could not delete file ");
pputs(strlow(q));
pputs("\r\n");
asm jmp short delbad
delok:
fls++;
delbad: ;
}
}
e = fndnxt(&d);
}
/* Find matching subdirectories, if requested */
if (sub || fin)
{
scpy(p, fin ? "*.*" : t); /* What directories to match */
e = fnd1st(q, &d, 0x17); /* Look at everything except labels */
while (!e) /* Do all subdirectories */
{
if ((d.attr & 0x10) &&
(d.name[0] != '.' || (d.name[1] && d.name[1] != '.')))
{ /* Is subdir and not "." or ".." */
q = scpy(p, d.name); /* Overlay wildcard with found name */
*q++ = '\\'; /* Append path delimiter */
scpy(q, fin ? t : "*.*"); /* New wildcard */
remove(s); /* Do subdirectory */
if (!fin) /* Remove directory if not in find */
{
*--q = '\0';
if (!ask || query("Remove directory ", strlow(s)))
{
asm lea DX,s /* Remove directory */
asm mov AH,3Ah
asm int 21h
asm jnb rdok
e = _AX; /* Save error code */
pputs("Could not remove directory ");
pputs(strlow(s));
if (e == 0x10) /* See if current directory */
pputs(" - part of current path");
else
pputs(" - unable to empty it");
pputs("\r\n");
asm jmp short rdbad
rdok:
drs++;
rdbad: ;
}
}
}
e = fndnxt(&d);
}
}
}
void main(int argc, char *argv[])
{
register char *p;
register int k;
int i, m;
char *a[64]; /* Tokens and options */
char t[64]; /* Token/~option flags */
/* Parse line into tokens and options */
m = k = 0;
for (i = 1; i < argc; i++) /* Do command line */
{
p = argv[i]; /* Next argument */
if (*p != '/') /* See if token */
{
a[k] = p++;
t[k++] = 1; /* Token */
m = 1; /* Token flag */
while (*p && *p != '/')
p++;
if (*p) /* See if options on token */
{
*p++ = '\0'; /* Terminate token string */
a[k] = a[k-1]; /* Put options appended to token */
t[k] = 1; /* BEFORE that token. */
a[k-1] = p;
t[k-1] = 0; /* Option(s) */
k++;
}
}
else
{
a[k] = ++p; /* Options start after the slash */
t[k++] = 0; /* Option(s) */
if (m)
m = -1; /* Dangling option flag */
}
}
if (m < 0)
{
pputs("Dangling option---entire command ignored\r\n");
asm mov AX,4C01h /* Exit with error */
asm int 21h
}
m = k; /* Number of tokens and options */
/* Process tokens and options */
fls = drs = 0; /* Initialize files, dirs removed */
for (i = 0; i < m; i++)
if (t[i]) /* Token */
remove(a[i]);
else /* Options */
for (p = a[i]; (k = *p) != 0; p++)
if (k == '/') {} /* Ignore extra /'s */
else if ((k &= 0x5f) == 'Q') ask = 0; /* Don't prompt */
else if (k == 'P') ask = 1; /* Prompt */
else if (k == 'N') sub = 0; /* No subdirs */
else if (k == 'S') sub = 1; /* Subdirs */
else if (k == 'V') att &= ~2; /* Ignore hidden */
else if (k == 'H') att |= 2; /* Delete hidden */
else if (k == 'T') att &= ~4; /* Ignore system */
else if (k == 'Y') att |= 4; /* Delete system */
else if (k == 'W') rof = 0; /* Ignore read-only */
else if (k == 'R') rof = 1; /* Delete read-only */
else if (k == 'A') fin = 0; /* Use *.* in subs */
else if (k == 'F') fin = 1; /* Find file */
else /* Invalid option */
{
if (*p != '?')
pputs("Invalid option\r\n");
pputs("\
RM 1.4 Copyright (C) 1988,1989 Mark Adler All rights reserved.\r\n\
Valid options are (*=default):\r\n\
/P\t- Prompt for each deletion\r\n\
/Q\t- No prompting *\r\n\
/S\t- Remove subdirectories\r\n\
/N\t- Ignore subdirectories *\r\n\
/H\t- Delete hidden files\r\n\
/V\t- Ignore hidden files *\r\n\
/Y\t- Delete system files\r\n\
/T\t- Ignore system files *\r\n\
/R\t- Delete read-only files!\r\n\
/W\t- Ignore read-only files *\r\n\
/F\t- Find files\r\n\
/A\t- All files *\r\n\
/?\t- Display this list\r\n");
asm mov AX,4C01h /* Exit with error */
asm int 21h
}
pputs(ultod(fls, t));
pputs(" file");
if (fls != 1)
pputc('s');
pputs(" deleted");
if (drs)
{
pputs(" and ");
pputs(ultod(drs, t));
pputs(" director");
if (drs != 1)
pputs("ies");
else
pputc('y');
pputs(" removed");
}
pputs("\r\n");
}