home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power Programming
/
powerprogramming1994.iso
/
progtool
/
dirutl
/
mds_rm.arc
/
RM.C
< prev
next >
Wrap
C/C++ Source or Header
|
1988-08-30
|
14KB
|
492 lines
/*
* RM.C Version 4.1
*
* Description:
* This program simulates the UNIX "rm" command. It allows one to remove
* files and/or directory structures with ease. It will also find and allow
* you to remove files with special attributes set. The options are:
*
* -i Inquire before delete mode.
* -r Recursively delete directory structures.
* -a Inquire before deleting files that have the archive bit set.
* -v Verbose mode, list files and directories as they are removed.
* -f Remove without prompting or complaining.
*
* The option flags apply only to those items that follow them on the
* command line. For instance:
*
* rm dumb.txt -r dumbdir -i a:gooddir
*
* will delete "dumb.txt", then recursively delete "dumbdir" and all it's
* contents, then ask before recursively deleting "a:gooddir". If you say
* "yes", it will then stop on each item in "a:gooddir" and ask before
* deleting it. If you leave anything un-deleted (i.e. the directory is
* not empty) then the directory chain down to the leftover items will
* not be removed even if you asked it to do so. If you respond "no" to
* removing a directory, that directory and all it's contents are left
* alone.
*
* It will always ask before deleting files with special attributes, such
* as a READONLY marked file, unless the -f option is used. The -i, -v and -a
* options are mutually exclusive with the -f option (i.e. they will cancel
* the -f option and -f will cancel both -a, -v and -i options for those items
* following them on the command line).
*
* Command line style arguments may also be passed through the environment
* variable RMOPTS. For example, "set RMOPTS = -i" will cause "rm" to always
* prompt before deleting. File names can also be given in RMOPTS and will
* make "rm" always try to delete the named files. NOTE: arguments given in
* RMOPTS will always be executed first. True command line arguments will
* follow those given in RMOPTS.
*
* UNIX is a trademark of AT&T.
*
* Use at your own risk!
*
* Environment:
* MS/PC-DOS V2.0 or higher.
*
* Compile:
* Using Microsoft C V4.0 -- "cl -F F000 rm.c -o rm"
* (-F is used to expand the stack size)
*
* Bugs (features):
* 1) It will not delete the current directory or the root directory.
* 2) It will not delete directories with anything left in them.
* 3) To remove everything, you must type "*.*" not just "*".
*
* Copyright: August 1988 By: Mark D. Salzman
*
* Permission is granted to distribute this program or any derivative work
* by any means as long as the source is included in the distribution and
* this copyright notice remains intact.
*/
#include <dos.h>
#include <stdio.h>
#include <stdlib.h>
#include <direct.h>
#include <string.h>
#include <process.h>
#define TRUE 1
#define FALSE 0
/* define the attribute bit masks */
#define A_READONLY 0x01
#define A_HIDDEN 0x02
#define A_SYSTEM 0x04
#define A_VOLUME 0x08
#define A_SUBDIR 0x10
#define A_ARCHIVE 0x20
/* define the default search mask */
#define S_MASK (A_HIDDEN | A_SYSTEM | A_SUBDIR)
#define DOSLINE 128 /* Length of the DOS command line */
typedef struct /* DOS Data Transfer Area table */
{
unsigned char used1[21]; /* Used in FIND NEXT operation */
unsigned char attrib; /* File attributes byte */
unsigned int tstamp; /* File time stamp */
unsigned int dstamp; /* File date stamp */
unsigned long fsize; /* File size in bytes */
unsigned char fname[13]; /* File name and extension (string) */
unsigned char used2[85]; /* Reserved for future use */
} dosdta;
int inquire, recurse, archive, quiet, verbose; /* Global option flags */
int name; /* Flag to see if a name was ever given. */
int err; /* Variable to hold the exit error state */
/*
* The main part initializes the option flags, checks for the environment
* variable RMOPTS, parses it's contents if it exsists, parses the command
* line, and passes the whole mess to "checkargs" for processing.
*/
main (argc, argv)
int argc;
char *argv[];
{
char envline[DOSLINE], *envargs[DOSLINE/2], *env;
int envcnt;
inquire = FALSE; /* Initialize flags */
recurse = FALSE;
archive = FALSE;
verbose = FALSE;
quiet = FALSE;
name = FALSE;
envcnt = 0;
err = 0;
if ((env = getenv("RMOPTS")) != NULL) /* Does RMOPTS exist? */
{
strcpy(envline, env); /* If so, copy it's value */
envcnt = parse(envline, envargs); /* and parse it like the */
if (envcnt != 0) /* command line. */
checkargs(envcnt, envargs, 0);
}
if ((argc < 2) && (envcnt == 0)) /* If no arguments, print usage. */
usage();
checkargs(argc, argv, 1); /* Parse the real command line */
if (name == FALSE) usage(); /* If no names given, print usage */
exit(err);
}
/*
* This procedure checks the arguments for option flags and item names,
* sets the options as they come up, and passes the item names to the
* delete function.
*/
checkargs(ac, av, start)
int ac;
char *av[];
int start;
{
int cnt, cn;
for (cnt = start ; cnt < ac ; ++cnt) /* Check the arguments */
{ /* If it is an option flag */
if((av[cnt][0] == '-') || (av[cnt][0] == '/'))
for(cn = 1 ; cn < strlen(av[cnt]) ; ++cn)
switch (av[cnt][cn])
{
case 'i' : inquire = TRUE;
quiet = FALSE;
break;
case 'r' : recurse = TRUE;
break;
case 'a' : archive = TRUE;
quiet = FALSE;
break;
case 'v' : verbose = TRUE;
break;
case 'f' : quiet = TRUE;
inquire = FALSE;
archive = FALSE;
break;
default :
fprintf(stderr,"Unknown Option Flag '%s'\n",av[cnt]);
usage();
}
else
{ /* Otherwise treat it as a name */
name = TRUE;
delete(av[cnt]);
}
}
}
/*
* This routine expands a item name (possibly containing "*" or "?") and calls
* "rmitem" to delete all matching files (or directories if -r is given).
*/
delete(arg)
char *arg;
{
char dirspec[DOSLINE]; /* String used to contain the directory path */
char *tmp;
dosdta dta;
/* Determine the path by stripping off the file name part */
strcpy(dirspec, arg);
if((tmp = strrchr(dirspec,'\\')) == NULL)
{
if((tmp = strrchr(dirspec,':')) == NULL) strset(dirspec,'\0');
else strset(++tmp,'\0');
}
else strset(++tmp,'\0');
/* Now expand the file name and delete all matching items */
if(find_first(arg, &dta, S_MASK) != 0)
{
if(quiet == FALSE)
{
fprintf(stderr,"'%s' NOT FOUND\n",arg);
err = -1;
}
}
else
{
rmitem(dirspec, &dta);
while( find_next(&dta) == 0 )
rmitem(dirspec, &dta);
}
}
/*
* This routine takes a DTA for an item and determines how to remove
* that item from the directory specified by "dirspec".
*/
rmitem(dirspec,dta)
char *dirspec;
dosdta *dta;
{
char filespec[DOSLINE];
/* Don't try to delete the DOS special files "." & ".." */
if ((strcmp(dta->fname, ".") != 0) && (strcmp(dta->fname, "..") != 0))
{
strcpy(filespec, dirspec); /* Append the name to the path */
strcat(filespec, dta->fname);
if (dta->attrib & A_SUBDIR) /* if it is a directory ... */
{
if (recurse) /* and we want to remove it ... */
{
if (inquire) /* and we want to ask first ... */
{
fprintf(stderr,"Remove directory '%s' (y/n)? ",filespec);
if (yesno()) deldir(filespec);
}
else deldir(filespec);
}
else
if(quiet == FALSE)
fprintf(stderr,"'%s' is a directory\n",filespec);
}
else /* assume it is a normal file */
{
if (inquire)
{
fprintf(stderr,"Remove file '%s' (y/n)? ",filespec);
if (yesno()) rmfile(filespec, dta);
}
else rmfile(filespec, dta);
}
}
}
/*
* This routine attempts to recursively delete a directory.
*/
deldir(arg)
char *arg; /* The full path name of the directory */
{
char tempspec[DOSLINE];
if (verbose) fprintf(stderr,"Removing directory %s\n",arg);
if (rmdir(arg) != 0) /* If not empty ... */
{
strcpy(tempspec, arg); /* Create new search string */
strcat(tempspec,"\\*.*");
delete(tempspec); /* Then recurse into it to empty it */
if ((rmdir(arg) != 0) && (quiet == FALSE))
{
fprintf(stderr,"'%s' NOT REMOVED\n",arg);
err = -1;
}
}
}
/*
* This routine attempts to remove a file. It checks to see if any of the
* special file attributes are set and if so, it checks to see if you still
* want to delete it.
*/
rmfile(arg, dta)
char *arg; /* The full path name of the file */
dosdta *dta; /* Pointer to the DTA for the file */
{
int killit, override;
if(quiet) /* If in quiet mode, kill everything reguardless */
{
killit = TRUE;
override = TRUE;
}
else /* Otherwise check special conditions first */
{
killit = TRUE;
override = FALSE;
if ((dta->attrib & A_READONLY) != 0)
{
fprintf(stderr,"'%s' is marked READONLY, delete anyway? (y/n) ",arg);
killit = yesno();
override = killit;
}
if ((archive) && ((dta->attrib & A_ARCHIVE) != 0) && (killit))
{
fprintf(stderr,"'%s' has not been backed up, delete anyway? (y/n) ",arg);
killit = yesno();
}
if (((dta->attrib & A_SYSTEM) != 0) && (killit))
{
fprintf(stderr,"'%s' is a SYSTEM file, delete anyway? (y/n) ",arg);
killit = yesno();
}
if (((dta->attrib & A_HIDDEN) != 0) && (killit))
{
fprintf(stderr,"'%s' is a HIDDEN file, delete anyway? (y/n) ",arg);
killit = yesno();
}
}
if (killit) /* Do we still want to remove it? */
{
if(verbose) fprintf(stderr,"Removing file %s\n",arg);
if((override) && ((dta->attrib & A_READONLY) != 0))
chmod(arg,(dta->attrib & !(A_READONLY)));
if ((remove(arg) != 0) && (quiet == FALSE))
{
fprintf(stderr,"'%s' NOT REMOVED\n",arg);
err = -1;
}
}
}
/*
* Find the first entry in the directory specified by the "path".
*/
find_first(path, dta, mask)
char *path; /* Path name to start search */
dosdta *dta; /* Location of the DTA to use */
unsigned int mask; /* File search attributes mask */
{
union REGS ir, or;
ir.x.ax = 0x1A00; /* DOS function 1A */
ir.x.dx = (unsigned)(dta);
int86(0x21, &ir, &or); /* Set the DTA address */
ir.x.ax = 0x4E00; /* DOS function 4E */
ir.x.cx = mask;
ir.x.dx = (unsigned)(path);
int86(0x21, &ir, &or); /* Do the search & fill the DTA */
return(or.x.ax); /* Return errors if any */
}
/*
* Find the next file in the directory. Must be used only after Find_first.
*/
find_next(dta)
dosdta *dta; /* Location of the DTA */
{
union REGS ir, or;
ir.x.ax = 0x1A00; /* DOS function 1A */
ir.x.dx = (unsigned)(dta);
int86(0x21, &ir, &or); /* Set the DTA address */
ir.x.ax = 0x4F00; /* DOS function 4F */
int86(0x21, &ir, &or); /* Do the search & fill the DTA */
return(or.x.ax); /* Return errors if any */
}
/*
* This routine is used to change file attributes.
*/
chmod(arg, mask)
char *arg;
unsigned int mask;
{
union REGS ir, or;
ir.h.ah = 0x43; /* DOS function 43 */
ir.h.al = 0x01; /* Set (not get) attribute bits */
ir.x.cx = mask;
ir.x.dx = (unsigned)(arg);
int86(0x21, &ir, &or);
return(or.x.ax); /* Return errors if any */
}
/* Parse: This function takes an input line and breakes it up into an
* array of lines. Normal whitespace chars are used to divide
* the line. The number of lines is returned. Zero is returned
* if there were no non-whitespace chars or if there was an
* unmatched quote situation.
*/
parse(in,v)
char *in,*v[];
{
char last_quote;
int c = 0;
int newword = TRUE;
int literal = FALSE;
int quote = FALSE;
while (*in!='\0')
{
switch(*in) /* if its a space and a quote is not open */
{
case '\n': *in='\0';
newword=TRUE;
break;
case '\'':
case '"':
if (((last_quote==*in)&&(literal==TRUE)) || (literal==FALSE))
{
literal=(literal==FALSE)?TRUE:FALSE;
last_quote=(literal==TRUE)?*in:'\0';
*in='\0';
newword=TRUE;
break;
}
case ' ':
case '\t':
{ /* convert all unquoted whitespace into null */
if(literal == FALSE)
{
*in='\0';
newword=TRUE;
break;
}
}
default:
if (newword==TRUE)
{
v[c++]=in;
newword=FALSE;
}
}
in++;
}
if (literal==TRUE)
{
printf("mismatched %c.\n",last_quote);
c=0;
}
return(c);
}
/*
* Get a Yes/No response from the terminal and clear the input line.
*/
yesno()
{
switch(getchar())
{
case 'y' :
case 'Y' : while(getchar() != '\n');
return(TRUE);
break;
case '\n': return(FALSE);
break;
default : while(getchar() != '\n');
return(FALSE);
break;
}
}
/*
* Print a program usage message, and exit.
*/
usage()
{
printf("\nUSAGE: rm [-i] [-a] [-v] [-f] [-r] file_or_directory_name(s)\n\n");
printf(" -i Inquire before delete mode (default no)\n");
printf(" -a Inquire before deleting non-archived files\n");
printf(" -v List files and directories as they are removed\n");
printf(" -f Remove without prompting or complaining\n");
printf(" -r Recursively delete directory structures\n");
exit(-1);
}