home *** CD-ROM | disk | FTP | other *** search
- /* RM - Remove files and/or directory subtrees
-
- Copyright (C) 1989, 1990, 1991 Brian B. McGuinness
- 15 Kevin Road
- Scotch Plains, NJ 07076
-
- This program is free software; you can redistribute it and/or modify it under
- the terms of the GNU General Public License as published by the Free Software
- Foundation; either version 1, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- details.
-
- You should have received a copy of the GNU General Public License along with
- this program; if not, write to the Free Software Foundation, Inc., 675 Mass
- Ave, Cambridge, MA 02139, USA.
-
- This is an extended MS-DOS implementation of the *NIX RM utility.
-
- Syntax: RM [/AEFIRSV?] {name | @}...
-
- /A = Detect all files, even hidden and system files, which are not normally
- detected. If RM is used to remove files in the root directory of a
- bootable disk, especially a hard disk, /A should not be used with /F
- as there is a risk of removing the system files IBMBIO.COM (IO.SYS) and
- IBMDOS.COM (MSDOS.SYS). This would make it impossible to boot the system
- from that disk.
-
- /E = Only delete matching files whose file size is zero. Normally, we delete
- matching files regardless of their size. This option is useful for
- disposing of empty "junk" files produced by programs or by failed
- attempts at I/O redirection. For example, to remove ALL empty files from
- drive C:, use the command RM /ES C:\*.* (/S is explained below). Note
- that empty files use up no disk space but they do use up a directory entry.
-
- /F = Force deletion of files without asking. Normally, we prompt the user for
- confirmation before removing read-only, hidden, or system files or before
- assuming "\*.*" at the end of a directory name (see /R below). If the /F
- switch is used, these files are removed without any prompt being given.
-
- /I = Interactive mode: the user is prompted for confirmation before each file
- is removed, regardless of the file's attributes.
-
- /R = Recursively delete entire subtrees. Normally, when a directory name
- appears on the command line it is treated as if "\*.*" was appended to
- it. If the /R switch is used, RM will recursively descend through any
- directory whose name is given and will remove the entire subtree that
- begins with that directory.
-
- /S = Delete matching files in entire subtree. For example, entering
- RM \UTIL\*.BAK will delete all files in the \UTIL directory which have
- the extension .BAK. Typing RM /S \UTIL\*.BAK will delete all .BAK
- files in the \UTIL directory and all subdirectories of \UTIL. Typing
- RM /S *.DOC will remove all .DOC files in the current default directory
- and all of its subdirectories. /S ignores directory names: e.g., if you
- have a directory called \ASM, then typing RM /S \ASM will do nothing. To
- remove all files in \ASM and its subdirectories, type RM /S \ASM\*.*.
- This will only remove files, not directories. Use /R to remove a whole
- subtree.
-
- /V = Verbose: display the name of each file or directory as it is removed.
-
- /? = Display instructions for using this program
-
- Note: Switches become active as they are encountered on the command line. If
- a switch is to affect a given file, it must appear before the file's
- name on the command line.
-
- name = name of a file or directory to be removed. Wildcards may be used.
-
- @ = Read a list of names and/or switches from the standard input device and
- treat each of these as if it had been typed on the command line as an
- argument of RM. More than one name and/or switch may appear on each
- line of input. When an EOF is encountered, processing of the command
- line resumes.
-
- This feature is useful when used with a file containing a lengthy list
- of names of files to be deleted. It may also be used with any program
- which writes a list of file names to standard output: e.g. a program
- which lists all files in a given directory which have not been modified
- since a certain date.
-
- If the environment string VERBOSE exists and has the value ON, then RM will
- act as though the /V switch is in effect even if /V is not included on the
- command line.
-
- 1.00 August 1989 - Original version.
- 1.01 August 1989 - If the user presses a special key (e.g. F10) after being
- prompted for a yes or no answer, don't echo a weird char.
- Also speed up character string output.
- 1.02 September 1989 - Allow the use of Esc to exit from the program.
- 1.03 May 1990 - Minor bug fixes and code cleanup.
- 1.10 July 1991 - /E switch added, messages improved.
-
- Version 1.10 C language
- ----------------------------------------------------------------------------- */
-
- #include <stdio.h>
- #include <direct.h> /* for remove() */
- #include <io.h> /* for chmod() */
-
- /* IF THE FOLLOWING KEYSTROKE IS READ IN RESPONSE TO A YES/NO PROMPT, THIS
- PROGRAM WILL IMMEDIATELY TERMINATE EXECUTION */
-
- #define ESCAPE 27
-
- /* DEFINE ERROR CODES */
-
- #define SUCCESS 0
- #define BADSYNTAX 11
- #define BADSWITCH 13
-
- /* DEFINE FILE ATTRIBUTES */
-
- #define NORMAL 0
- #define READONLY 1
- #define HIDDEN 2
- #define SYSTEM 4
- #define DIRECTORY 16
- #define ARCHIVE 32
-
- /* DEFINE FLAG VALUES FOR COMMAND LINE SWITCHES */
-
- #define EMPTY 1 /* /E */
- #define FORCED 2 /* /F */
- #define INTERACTIVE 4 /* /I */
- #define RECURSIVE 8 /* /R */
- #define GLOBAL 16 /* /S */
- #define VERBOSE 32 /* /V */
-
- /* DEFINE DATA STRUCTURE RETURNED FROM findfile() */
- /* Note: In the time, S = seconds / 2; in the date, Y = year - 1980 */
-
- struct DIRINFO {
- char reserved[21]; /* Undocumented (varies with DOS version) */
- unsigned char attrib; /* Attribute byte: 00ADVSHR */
- unsigned short time; /* Time file was saved: HHHHHMMMMMMSSSSS */
- unsigned short date; /* Date file was saved: YYYYYYYMMMMDDDDD */
- unsigned long size; /* File size in bytes */
- char name[13]; /* File name */
- };
-
- /*----------------------------------------------------------------------------*/
- main (int argc, char **argv) {
-
- void delete (char *pathname, struct DIRINFO *file, int flags); /* Remove a file */
- int delglobal (char *fname, int attributes, int flags, char *end); /* Delete file in all subdirectories */
- int delmatch (char *fname, int attributes, int flags, char *end); /* Delete matching files */
-
- int fexpand (char *relname, char *absname); /* Fully qualify file name */
- int findfile (char *fname, int srchattr, struct DIRINFO *); /* Search directory for a file */
- int getkeyn(); /* Read keystroke and print newline */
- short getmode (char *fname); /* Get file attributes */
- char *nexttok (int *argc, char ***argv, int *inpflag); /* Get next argument */
- void recurse (char *path, int attributes, int flags); /* Remove a subtree */
- void usage (char switchar); /* Display instructions */
-
- char switchar; /* Current MS-DOS switch character */
- int attrib; /* Attributes to use to search for matching files */
- int flags; /* Keeps track of command line switches */
- int filecnt; /* Keeps track of how many names we've found on cmd line */
- int inpflag; /* 0 = get tokens from cmd line, otherwise get them from stdin */
- char *token; /* Command line token */
- int i;
-
- char fullname[84]; /* Buffer for fully qualified file/directory name */
- char *end; /* Location of end of fully qualified file name */
-
- char defdir[80]; /* Buffer for default directory name */
- int cdlen; /* Length of name of current default directory */
-
- /* SET UP DEFAULT PARAMETERS */
- attrib=ARCHIVE | READONLY; /* Only search for normal & read-only files */
- flags=0; /* No switches found yet */
- inpflag=0; /* Read tokens from command line */
- filecnt=0; /* No file names found yet */
-
- token=getenv ("VERBOSE"); /* If VERBOSE=ON, set flag accordingly */
- if (token != NULL && !stricmp (token, "ON")) flags=VERBOSE;
-
- switchar=getswchr(); /* Get the current DOS switch char */
- fexpand (".", defdir); /* Get current default directory, fully qualified */
- for (cdlen=0; defdir[cdlen]; cdlen++);
-
- /* Process each token on the command line */
-
- while ((token=nexttok (&argc, &argv, &inpflag)) != NULL) {
-
- if (*token == switchar) { /* It's a switch */
- while (*++token) switch (toupper (*token)) {
- case 'A': attrib |= HIDDEN | SYSTEM;
- break;
-
- case 'E': flags |= EMPTY;
- break;
-
- case 'F': flags |= FORCED;
- flags &= ~INTERACTIVE;
- break;
-
- case 'I': flags |= INTERACTIVE;
- flags &= ~FORCED;
- break;
-
- case 'R': flags |= RECURSIVE;
- break;
-
- case 'S': flags |= GLOBAL;
- break;
-
- case 'V': flags |= VERBOSE;
- break;
-
- case '?': usage (switchar);
- break;
-
- default: iprintf ("rm: Unknown switch: %c%c\n", switchar, *token);
- exit (BADSWITCH);
- }
- }
-
- else { /* It's a filespec */
- filecnt++;
- if (fexpand (token, fullname)) { /* Expand token to fully-qualified name */
- iprintf ("rm: Invalid name: %s\n", token);
- continue;
- }
- for (end=fullname; *end; end++); /* Find the end of the name */
-
- if (end[-2] == ':') { /* DOS doesn't recognize root as a directory */
- i=DIRECTORY;
- end--;
- } else {
- if (end[-1] == '\\') *--end = '\0';
- i=getmode (fullname);
- }
-
- if (i != -1 && i & DIRECTORY) { /* DEAL WITH A DIRECTORY */
-
- if (flags & RECURSIVE) { /* DELETE ENTIRE SUBTREE */
- i=end - fullname;
- if (cdlen >= i && !strnicmp (defdir, fullname, i)) {
- iprintf ("rm: Can't remove tree containing current directory:\n %s\n ",
- fullname);
- }
- else recurse (fullname, attrib, flags);
- }
-
- else { /* DELETE ALL FILES IN THE DIRECTORY */
- if (!(flags & FORCED)) {
- iprintf ("Delete all files in directory %s [n]? ", fullname);
- i=toupper(getkeyn());
- if (i == ESCAPE) exit (SUCCESS);
- if (i != 'Y') continue;
- }
- strcpy (end++, "\\*.*");
- delmatch (fullname, attrib, flags, end);
- }
- }
- else { /* IT'S NOT A DIRECTORY, SO DELETE ALL MATCHING FILES */
- for (; end > fullname && end[-1] != '\\' && end[-1] != ':'; end--);
-
- if (flags & GLOBAL) i=delglobal (fullname, attrib, flags, end);
- else i=delmatch (fullname, attrib, flags, end);
- if (!i) iprintf ("rm: Not found: %s\n", token);
- }
- } /* end of "arg is a pathname" */
- } /* end of "while more cmd line args" */
-
- /* IF NO PATHNAMES WERE GIVEN, PRINT SYNTAX AND EXIT WITH ERROR CODE */
- if (!filecnt) {
- usage (switchar);
- exit (BADSYNTAX);
- }
- exit (SUCCESS);
- }
-
- /*----------------------------------------------------------------------------*/
- /* delete() - Delete a specified file */
- /* */
- /* filename = pathname of file to be deleted */
- /* attrib = attributes of file to be deleted */
- /* flags = flags set by command line switches */
- /*----------------------------------------------------------------------------*/
- void delete (char *pathname, struct DIRINFO *file, int flags) {
- int i;
- static char attr[]="RHSVDA";
- static char *month[] = {"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
-
- /* FOR SPECIAL FILES, OR IF IN INTERACTIVE MODE, MAKE SURE THAT THE USER
- REALLY WANTS TO DELETE THE FILE */
-
- if ((flags & INTERACTIVE) ||
- ((file->attrib & (HIDDEN | SYSTEM | READONLY)) && !(flags & FORCED))) {
- iprintf ("Delete %s %ld %d %s %04d %d:%02d ", pathname, file->size,
- file->date & 0x001F, month[((file->date & 0x01E0) >> 5) - 1],
- 1980 + (file->date >> 9), file->time >> 11, (file->time & 0x07E0) >> 5);
- for (i=5; i >= 0; i--) iprintf ("%c", (file->attrib & (1 << i)) ? attr[i] : '-');
- iprintf (" [N]? ");
- i=toupper(getkeyn());
- if (i == ESCAPE) exit (SUCCESS);
- if (i != 'Y') return;
- }
-
- #ifdef DEBUG
- iprintf ("remove (%s)\n", pathname);
- #else
-
- /* MODIFY ATTRIBUTES, IF NECESSARY, SO WE CAN DELETE THE FILE */
- if (file->attrib & (HIDDEN | SYSTEM | READONLY)) chmod (pathname, NORMAL);
-
- if (remove (pathname)) iprintf ("rm: Can't delete %s\n", pathname);
- else if (flags & VERBOSE) iprintf ("%s\n", pathname);
- #endif
- }
-
- /*----------------------------------------------------------------------------*/
- /* delglobal() - Delete all matching files in a subtree */
- /* */
- /* name = file specification for files to be removed */
- /* attributes = attributes to be used in file search */
- /* flags = flags set by command line switches */
- /* end = pointer to beginning of file name (just beyond path) */
- /* */
- /* We return the number of matching files found. */
- /*----------------------------------------------------------------------------*/
-
- int delglobal (char *fname, int attributes, int flags, char *end) {
-
- struct DIRINFO dir;
- int i, nfiles;
- char *cp, filespec[13];
-
- strcpy (filespec, end); /* Save pattern to match in file search */
-
- /* DELETE MATCHING FILES IN CURRENT DIRECTORY */
- nfiles=delmatch (fname, attributes, flags, end);
-
- /* DELETE MATCHING FILES IN ALL SUBDIRECTORIES */
- attributes |= DIRECTORY;
- strcpy (end,"*.*");
- i=findfile (fname, attributes, &dir);
- attributes &= ~DIRECTORY; /* Restore attributes to their original state */
- while (!i) {
- /* IGNORE THE "." AND ".." FILES */
- if ((dir.name[0] != '.') || (dir.name[1] && (dir.name[1] != '.' || dir.name[2]))) {
- if (dir.attrib & DIRECTORY) {
- strcpy (end, dir.name); /* Append directory name to the path */
- for (cp=end; *cp; cp++); /* Append file spec to the directory name */
- *cp++='\\';
- strcpy (cp, filespec);
- nfiles += delglobal (fname, attributes, flags, cp);
- }
- }
- i=findfile (NULL, 0, &dir);
- }
- end[-1]='\0'; /* Restore path to its original state */
- return nfiles;
- }
-
- /*----------------------------------------------------------------------------*/
- /* delmatch() - Delete all files matching a given file specification */
- /* */
- /* name = file specification for files to be removed */
- /* attributes = attributes to be used in file search */
- /* flags = flags set by command line switches */
- /* end = pointer to beginning of file name (just beyond path) */
- /* */
- /* We return the number of matching files found. */
- /*----------------------------------------------------------------------------*/
-
- int delmatch (char *fname, int attributes, int flags, char *end) {
-
- struct DIRINFO file;
- int i, nfiles;
-
- nfiles=0; /* Keep track of how many matching files we find */
-
- i=findfile (fname, attributes, &file);
- while (!i) {
- nfiles++;
- strcpy (end, file.name);
- if (!(file.size && (flags & EMPTY))) delete (fname, &file, flags);
- i=findfile (NULL, 0, &file);
- }
- return nfiles;
- }
-
- /*----------------------------------------------------------------------------*/
- /* recurse() - Recursively delete a subtree */
- /* */
- /* path = full path of directory to be removed */
- /* attributes = attributes to be used in file search */
- /* flags = flags set by command line switches */
- /*----------------------------------------------------------------------------*/
-
- void recurse (char *path, int attributes, int flags) {
-
- char *pathend;
- int i;
- struct DIRINFO file;
-
- /* FOR INTERACTIVE MODE, CHECK IF WE'RE TO REMOVE THIS DIRECTORY */
- if (flags & INTERACTIVE) {
- iprintf ("Remove directory %s [N]? ", path);
- i=toupper(getkeyn());
- if (i == ESCAPE) exit (SUCCESS);
- if (i != 'Y') return;
- }
-
- /* FIND END OF PATH STRING AND SAVE ITS LOCATION */
- for (pathend=path; *pathend; pathend++);
-
- strcpy (pathend++, "\\*.*"); /* Append '\*.*' to the path. */
-
- attributes |= DIRECTORY; /* Search for directories as well as files */
-
- /* REMOVE EACH FILE AND SUBTREE IN THIS DIRECTORY */
- i=findfile (path, attributes, &file);
- while (!i) {
- /* IGNORE THE "." AND ".." FILES */
- if ((file.name[0] != '.') || (file.name[1] && (file.name[1] != '.' || file.name[2]))) {
-
- strcpy (pathend, file.name); /* Append current file name to the path */
-
- if (file.attrib & DIRECTORY) recurse (path, attributes, flags);
- else if (!(file.size && (flags & EMPTY))) delete (path, &file, flags);
-
- }
- i=findfile (NULL, 0, &file);
- }
- pathend[-1]='\0'; /* Restore path to its original state */
- attributes &= ~DIRECTORY; /* Restore attributes to their original state */
-
- #ifdef DEBUG
- iprintf ("rmdir (%s)\n", path);
- #else
- if (rmdir (path)) { /* Remove the directory */
- iprintf ("rm: Can't remove directory: %s\n", path);
- }
- else if (flags & VERBOSE) iprintf ("[%s]\n", path);
- #endif
- }
-
- /*------------------------------------------------------------------------------
- usage() - Display instructions for using this program
- ------------------------------------------------------------------------------*/
- void usage (char c) {
- static int displayed=0; /* Only display instructions once */
-
- if (!displayed++) {
- iprintf ("RM - Remove files and/or directory subtrees\n"
- "Copyright (C) 1989, 1990, 1991 Brian B. McGuinness\n\n"
- "Syntax: RM [%cAEFIRSV?] {name | @}...\n"
- " %cA = Remove ALL matching files, even hidden & system files (careful!)\n"
- " %cE = Only remove empty (size zero) files\n"
- " %cF = Force removal even if hidden or read-only (don't ask user)\n"
- " %cI = Interactive: ask user to confirm each removal\n"
- " %cR = Recursively remove entire subtrees\n", c, c, c, c, c, c);
- iprintf (" %cS = Remove matching files from subtrees\n"
- " %cV = Verbose: display the name of each object as it is removed\n"
- " %c? = Display instructions\n\n"
- " @ = Read pathnames and switches from standard input until end-of-file is\n"
- " encountered. Then continue processing the command line.\n\n", c, c, c);
- iprintf ("Setting the environment variable VERBOSE=ON is equivalent to %cV\n\n"
- "This program is free software; you can redistribute it and/or modify it under\n"
- "the terms of the GNU General Public License as published by the Free Software\n"
- "Foundation; either version 1, or (at your option) any later version.\n", c);
- }
- }
-