home *** CD-ROM | disk | FTP | other *** search
- /*
- * 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);
- }
-