home *** CD-ROM | disk | FTP | other *** search
- /* CmdSweep.c
- *
- * Author: Bill DuPree (with thanks to Jeff Lydiatt for PatMatch.c)
- * Language: Lattice C version 5.04
- * Date: January 16, 1990
- * Version: 1.00
- *
- * Usage: CmdSweep [options] <starting_directory> "Command"
- *
- * CmdSweep executes a given command in every subdirectory under the
- * specified starting directory including the starting directory. It
- * can also be used to execute the command for every matching file
- * encountered in the directory tree. If the current directory is the
- * desired starting directory, then it may be specified by coding the
- * argument as "".
- *
- * The "Command" argument specifies a template for the command you wish
- * to execute. A favorite application of mine is to check the contents
- * of a diskette for viral infestations by using CmdSweep with the command
- * template "kv *", i.e. the full entry on the command line is:
- *
- * CmdSweep df0: "kv *"
- *
- * The command template may contain the escape sequences %p and %f. These
- * will be replaced with the current directory path and current file
- * respectively, in command generation. If you need to use the % character
- * in the command, then escape it with an additional % character.
- *
- * The "-A" option will force CmdSweep to process directories (and files)
- * in alphabetical order. While this may cause a slight performance penalty,
- * it is useful in that you have some indication of how much processing
- * remains to be done by the visual inspection of the current directory
- * and/or file name.
- *
- * The use of the "-F" (Files) option will cause CmdSweep to execute the
- * command for every file it encounters in the directory tree. If coded
- * as "-F=pattern" then the command is executed only for files matching
- * the AmigaDOS pattern.
- *
- * Deferred execution may be had through the use of the "-S" (Script) option
- * which will cause CmdSweep to generate an AmigaDOS script file. This
- * option is coded as "-S=script_file_name" where script_file_name is the
- * name of the file to receive the list of script commands. If coded as
- * "-SE=script_file_name" then ECHO commands will be placed into the
- * script to provide a visible progress report when executing the script.
- *
- * You may use the "-X=pattern" (eXclude) option to exclude from
- * consideration all files that match the supplied AmigaDOS pattern.
- *
- * The "-H" (Here) option will inhibit the directory tree traversal causing
- * CmdSweep to operate only in the supplied directory. None of its
- * subdirectories will be entered.
- *
- * To compile and link:
- *
- * lc -b -r -v CmdSweep.c
- * lc -b -r -v PatMatch.c
- * blink lib:c.o,CmdSweep.o,PatMatch.o to CmdSweep lib lib:lc.lib SD SC ND
- *
- * Examples:
- *
- * (1) To browse every C source and header file on a diskette in drive df1:
- *
- * CmdSweep -f=#?.((c)|(h)) df1: "More %f"
- *
- * (2) To create a script file in RAM: to do the same as example one:
- *
- * CmdSweep -s=ram:Browse.Script -f=#?.((c)|(h)) df1: "More %f"
- *
- * (3) To delete all files ending in the ".bak" extension on dh0:
- *
- * CmdSweep -f=#?.bak dh0: "delete %f"
- * or
- * CmdSweep dh0: "delete #?.bak"
- *
- * (4) To rid a diskette in drive df1: from viruses using the public domain
- * KV program:
- *
- * CmdSweep df1: "kv *"
- * or
- * CmdSweep df1: "kv %p/"
- * or
- * CmdSweep -f df1: "kv %p/%f"
- *
- * I'm sure that you will find many other uses.
- *
- *--------------------------------------------------------------------------
- * DISCLAIMER:
- *
- * THIS PROGRAM AND ITS ASSOCIATED DOCUMENTATION ARE PROVIDED "AS IS"
- * WITHOUT REPRESENTATION OR WARRANTY OF ANY KIND, EITHER EXPRESS OR
- * IMPLIED, INCLUDING WITHOUT LIMITATION, ANY REPRESENTATIONS OR
- * ENDORSEMENTS REGARDING THE USE OF, THE RESULTS OF, OR PERFORMANCE OF
- * THE PROGRAM OR ITS ASSOCIATED DOCUMENTATION, ITS APPROPRIATENESS,
- * ACCURACY, RELIABILTY, OR CURRENTNESS. THE ENTIRE RISK AS TO THE USE OF
- * THIS PROGRAM OR ITS ASSOCIATED DOCUMENTATION IS ASSUMED BY THE USER.
- *
- * IN NO EVENT WILL BILL DUPREE, NOR ANY OTHER PARTY INVOLVED IN THE
- * CREATION OR DISSEMINATION OF THIS PROGRAM AND ITS ASSOCIATED
- * DOCUMENTATION, BE LIABLE FOR ANY DAMAGES, DIRECT, INDIRECT,
- * INCIDENTAL OR CONSEQUENTIAL, RESULTING FROM ANY DEFECT IN THIS PROGRAM,
- * EVEN IF ANY OF THE AFOREMENTIONED PARTIES HAS BEEN ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGES.
- *
- * THIS DISCLAIMER SHALL SUPERCEDE ANY VERBAL OR WRITTEN STATEMENT TO
- * THE CONTRARY.
- *
- * Enough of the CYA stuff already. This program is hereby placed into the
- * public domain. Please use it freely for whatever purposes you deem
- * necessary. However, if you incorporate it into any wondrous creations
- * of your own, give credit where credit is due, i.e. leave the names of
- * the author(s) intact in the code. We all want our moments in the sun.
- * It would probably also be wise to continue including the disclaimer given
- * the state of affairs in our litigious society. If you feel compelled to
- * contact me for any reason, or if you want to pass on constructive
- * criticism, suggestions, ideas for new features, or monetary rewards
- * (not necessary), or if you are a young, pretty, and single female Amiga
- * enthusiast in the Chicagoland area (I'm single), you can contact me at:
- *
- * Bill DuPree
- * 7449 W. Washington #402
- * Forest Park, IL. 60130
- */
-
- #include "libraries/dos.h"
- #include "libraries/dosextens.h"
- #include "exec/memory.h"
- #include "string.h"
- #include "stdio.h"
- #include "stdlib.h"
- #include "proto/dos.h"
- #include "proto/exec.h"
-
- #define FALSE 0
- #define TRUE 1
- #define _MAX_PATH 256
- #define ERROR 10
-
- /* These two arrays are required for AmigaDOS style regular */
- /* expression pattern matching. */
- static WORD Aux[128], xAux[128];
- static char *Pat, *xPat;
-
- /* These declarations are for the pattern matching routines */
- extern int CmplPat(char *, WORD *);
- extern int Match(char *, WORD *, char *);
-
- /* A couple of ANSI prototypes for our own routines */
- static char *show_locked_dir(BPTR, char *);
- static int traverse_tree(char *, BPTR);
- static void my_exit(int);
-
- struct dir_entry {
- struct dir_entry *next; /* pointer to next instance */
- char *name; /* pointer to a string containing dir name */
- };
-
- /* Some global flags which tell what options were selected */
- static int do_files, x_files, here_only, sorted, echo;
-
- /* The script file stream */
- static FILE *script;
- char *ScriptName;
-
- /* We only need one FileInfoBlock, not one for every level of recursion */
- /* like most tree traversal algorithms. This is because we defer */
- /* recursion until we have exhausted the current directory contents. */
- /* Subdirectories are saved in an ordered, dynamically allocated, */
- /* linked list for use later. See traverse_tree(). */
-
- struct FileInfoBlock *myfib;
-
- /* This program is VERY!!! loosely based on a non-working example given in */
- /* the book "Programmer's Guide to the Amiga" authored by Rob Peck and */
- /* published by Sybex. (See example opta.c on pp. 34) */
-
- int main(int argc, char **argv)
- {
- int i, rc, got_dir, batch;
- BPTR oldlock, newlock;
- char *whichdir, *command;
- static char work[_MAX_PATH];
- static char *signon =
- "\x1b[33mCmdSweep\x1b[31m v. 1.00 - January 16, 1990\n"
- "Author: Bill DuPree";
- static char *usage =
- "Usage: CmdSweep [options] <starting_dir> \"command\"\n"
- "Enter \"CmdSweep -help\" for more information";
-
- /* Start variables off with known values */
- batch = rc = 0;
- sorted = got_dir = do_files = x_files = here_only = echo = FALSE;
- oldlock = NULL;
- myfib = NULL;
- script = NULL;
- command = whichdir = "";
- *work = (char) 0;
- Pat = "#?";
-
- puts(signon);
-
- /* make sure command line is worth parsing */
- if (argc < 2) {
- puts("Command line error");
- puts(usage);
- my_exit(ERROR);
- }
-
- /* allocate space for a FileInfoBlock */
-
- if ((myfib = (struct FileInfoBlock *)
- AllocMem(sizeof(struct FileInfoBlock), MEMF_CLEAR)) == NULL) {
- fputs("\n\aAlloc of FileInfoBlock failed\n", stderr);
- my_exit(ERROR);
- }
-
- /* Usage and/or help requested? */
- if (argc == 2)
- if (!strcmp(argv[1], "?")) {
- puts(usage);
- my_exit(0);
- }
- else if (!stricmp(argv[1], "-HELP")) {
- puts("\fUsage: CmdSweep [options] <starting_dir> \"command\"");
- puts("where options are:\n");
-
- puts("\t-A : Process directories (and files) in alphabetical order.\n");
-
- puts("\t-F : will generate the given command for every file in the");
- puts("\t traversed directory tree and \"-F=pattern\" will generate.");
- puts("\t the command for every file that matches the AmigaDOS pattern.\n");
-
- puts("\t-S : coded as \"-S=script_file_name\", where script_file_name");
- puts("\t is any valid AmigaDOS file name. The given file name");
- puts("\t will be opened to receive the list of commands that would");
- puts("\t have otherwise been executed immediately. The -SE variant");
- puts("\t will place ECHO commands in the script for a visible progress");
- puts("\t report.\n");
-
- puts("\t-X : coded as \"-X=pattern\" excludes files matching the pattern.\n");
-
- puts("\t-H : causes CmdSweep to operate only in the specified directory.\n");
-
- fputs("Press return to continue:", stdout);
- fflush(stdout);
- fgets(work, 1, stdin);
-
- puts("\fCmdSweep help continued:\n");
- puts("Options are not case sensitive and can be used in any combination to");
- puts("achieve the desired effect. All command line options and arguments must");
- puts("be separated by space(s). If they contain embedded space(s), then they");
- puts("must be surrounded by quotes.\n");
-
- puts("The <starting_dir> argument is not optional and must be a valid directory");
- puts("on a mounted device. If the current directory is desired then it should be");
- puts("coded as \"\".\n");
-
- puts("The \"command\" argument is also mandatory. If you embed the escape");
- puts("sequences \"%p\" or \"%f\" (no quotes) in the command, then the current");
- puts("path and file will replace them in the resulting command, respectively.");
- puts("The sequence \"%%\" escapes the '%' character in the generated commands.\n");
- my_exit(0);
- }
-
- /* process command line */
- for (i = 1; i < argc; i++) {
- if (*argv[i] == (char) '-') {
- if ((stricmp(argv[i], "-A") == 0) && !sorted)
- sorted = TRUE;
- else if ((stricmp(argv[i], "-F") == 0) && !do_files)
- do_files = TRUE;
- else if ((strnicmp(argv[i], "-F=", 3) == 0) && !do_files) {
- do_files = TRUE;
- strcpy(Pat, &argv[i][3]);
- }
- else if ((stricmp(argv[i], "-H") == 0) && !here_only)
- here_only = TRUE;
- else if ((strnicmp(argv[i], "-S=", 3) == 0) && !batch)
- batch = i;
- else if ((strnicmp(argv[i], "-SE=", 4) == 0) && !batch) {
- echo = TRUE;
- batch = i;
- }
- else if ((strnicmp(argv[i], "-X=", 3) == 0) && !x_files) {
- x_files = TRUE;
- xPat = &argv[i][3];
- }
- else {
- fputs("\n\aExtraneous options\n", stderr);
- my_exit(ERROR);
- }
- }
- else {
- if ((*whichdir == (char) 0) && !got_dir) {
- got_dir = TRUE;
- whichdir = argv[i];
- }
- else if (*command == (char) 0)
- command = argv[i];
- else {
- fputs("\n\aExtraneous command line args\n", stderr);
- my_exit(ERROR);
- }
- }
- }
-
- /* we have to examine files if we are excluding them */
- do_files |= x_files;
-
- /* make sure command was specified */
- if (*command == (char) 0) {
- fputs("\n\aCommand argument is required\n", stderr);
- my_exit(ERROR);
- }
-
- /* Compile and validate the inclusive pattern */
- if (do_files && !CmplPat(Pat, Aux)) {
- fputs("\n\aBad wild card expression in inclusion\n", stderr);
- my_exit(ERROR);
- }
-
- /* Compile and validate the pattern for exclusion */
- if (x_files && !CmplPat(xPat, xAux)) {
- fputs("\n\aBad wild card expression in exclusion\n", stderr);
- my_exit(ERROR);
- }
-
- if (batch) {
- if ((script = fopen(ScriptName = &argv[batch][echo ? 4 : 3], "w")) == NULL) {
- fputs("\n\aCannot open script file\n", stderr);
- my_exit(ERROR);
- }
- }
-
- /* get a lock on the specified directory */
- newlock = Lock(whichdir, ACCESS_READ);
- if (newlock != 0) {
- Examine(newlock, myfib);
- if (myfib->fib_DirEntryType > 0) {
- /* get lock on current dir so we can come back to it*/
- oldlock = CurrentDir(newlock);
-
- /* process new dir and subtree recursively */
- rc = (traverse_tree(command, newlock) == FALSE) ? ERROR : 0;
-
- /* return to original directory */
- (void) CurrentDir(oldlock);
- if (batch) {
- fputs("\nCD ", script);
- fputs(show_locked_dir(oldlock, work), script);
- fputs("\n", script);
- }
-
- /* unlock what we locked */
- UnLock(newlock);
- }
- else {
- fputs("\n\aError - \"", stderr);
- fputs(whichdir, stderr);
- fputs("\" is not a directory\n", stderr);
- my_exit(ERROR);
- }
- }
- else {
- fputs("\n\aCan't lock selected dir\n", stderr);
- my_exit(ERROR);
- }
- fputs("\n", stdout);
- my_exit(rc);
- }
-
- /*
- * Function to cleanup and exit.
- */
- static void my_exit(int rc)
- {
- if (script) fclose(script);
-
- /* Set script attribute if we generated a script */
- if (script && (rc == 0))
- (void) SetProtection(ScriptName, (FIBF_SCRIPT | FIBF_EXECUTE));
-
- if (myfib) FreeMem(myfib, sizeof(struct FileInfoBlock));
- exit(rc);
- }
-
- /*
- * This routine will construct the full path for a given lock.
- */
- static char *show_locked_dir(BPTR lock, char *cd)
- {
- BPTR mylock, oldlock;
- struct FileInfoBlock *finfo;
- static char work[_MAX_PATH];
-
- /* allocate space for a FileInfoBlock */
-
- if ((finfo = (struct FileInfoBlock *)
- AllocMem(sizeof(struct FileInfoBlock), MEMF_CLEAR)) == NULL) {
- fputs("\n\aAlloc of FileInfoBlock failed\n", stderr);
- return NULL;
- }
-
- /* construct the path by examining all parent directories */
- mylock = lock;
- *cd = *work = (char) 0;
- while (mylock) {
- (void)Examine(mylock, finfo);
- strcpy(work, cd);
- strcpy(cd, finfo->fib_FileName);
- oldlock = mylock;
- mylock = ParentDir(oldlock);
-
- /* This is not well documented, but you have to unlock */
- /* the locks obtained from ParentDir(). */
- if (oldlock != lock)
- UnLock(oldlock);
-
- if (mylock == 0) /* did we hit the root? */
- strcat(cd,":");
-
- if (work[0]) { /* concatenate names */
- if (mylock != 0)
- strcat(cd, "/");
- strcat(cd, work);
- }
- }
-
- /* return used resources to OS */
- FreeMem(finfo, sizeof(struct FileInfoBlock));
- return cd;
- }
-
- /*
- * This routine expands the command template by replacing %p escape
- * sequences with the path and %f escape sequences with the file.
- */
- static int expand_cmd(char *new, char *old, char *path, char *file)
- {
- char *percent, *lastpos;
-
- *new = (char) 0;
- lastpos = old;
- while (percent = strchr(lastpos, '%')) {
- *percent = (char) 0;
- strcat(new, lastpos);
- *percent = (char) '%';
- percent++;
- if (toupper(*percent) == 'F') {
- percent++;
- strcat(new, file);
- }
- else if (toupper(*percent) == 'P') {
- percent++;
- strcat(new, path);
- }
- else if (*percent == '%') {
- strcat(new, "%");
- percent++;
- }
- else
- return FALSE;
-
- lastpos = percent;
- }
- strcat(new, lastpos);
- return TRUE;
- }
-
- struct dir_entry *make_entry(char *name)
- {
- struct dir_entry *new;
-
- /* allocate a dir_entry structure for deferred processing */
- if ((new = malloc(sizeof(struct dir_entry))) == NULL) {
- fputs("\n\aAlloc of dir_entry failed\n", stderr);
- return NULL;
- }
-
- /* fill in name */
- if ((new->name = strdup(name)) == NULL) {
- fputs("\n\aOut of memory\n", stderr);
- free(new);
- return NULL;
- }
- return new;
- }
-
-
- /* Now follow the tree... might hit a directory, might hit a file. */
- /* If a directory, list it and then follow it down (recursively). */
-
- static int traverse_tree(char *cmd, BPTR lock)
- {
- int rc;
- BPTR newlock, oldlock;
- struct dir_entry *dirlist, *filelist, *new, *current, *prev;
- static char path[_MAX_PATH];
- static char command[_MAX_PATH];
-
- /* start with empty list of files and directories */
- filelist = dirlist = NULL;
-
- rc = TRUE;
-
- /* The first call to Examine fills in the FileInfoBlock */
- /* with information about the directory. If it is */
- /* called at the root level, it contains the volume */
- /* name of the disk. */
-
- Examine(lock, myfib);
- if (show_locked_dir(lock, path) == NULL) {
- rc = FALSE;
- goto clean_exit;
- }
- fputs("\n\n\x1b[32;41mCmdSweep: Directory ==> ", stdout);
- fputs(path, stdout);
- fputs("\x1b[31;40m\n", stdout);
- fflush(stdout);
-
- /* Expand command if we are executing directory by directory */
- if (!do_files && !expand_cmd(command, cmd, path, "")) {
- fputs("\n\aCommand format is invalid\n", stderr);
- rc = FALSE;
- goto clean_exit;
- }
-
- /* Write out appropriate script commands if necessary */
- if (script) {
- if (echo) {
- fputs("\nECHO \"*e[32;41mDirectory ==> ", script);
- fputs(path, script);
- fputs("*e[31;40m\"\n", script);
- }
- else
- fputs("\n", script);
-
- fputs("CD ", script);
- fputs(path, script);
- fputs("\n", script);
- if (!do_files) {
- if (echo) {
- fputs("ECHO \"*e[32;41mCommand: ", script);
- fputs(command, script);
- fputs("*e[31;40m\"\n", script);
- }
- fputs(command, script);
- fputs("\n", script);
- }
- }
- else if (!do_files) {
- fputs("\x1b[32;41mCommand: ", stdout);
- fputs(command, stdout);
- fputs("\x1b[31;40m\n", stdout);
- fflush(stdout);
-
- if (Execute(command, 0, 0) == FALSE) {
- fputs("\n\aSystem command processor failure\n", stderr);
- rc = FALSE;
- goto clean_exit;
- }
- }
-
- /* loop until last directory entry */
- while (ExNext(lock, myfib)) {
- new = NULL;
- if (myfib->fib_DirEntryType > 0) {
- /* got a subdirectory */
- if (!here_only) {
- if ((new = make_entry(myfib->fib_FileName)) == NULL)
- goto clean_exit;
- current = dirlist;
- prev = (struct dir_entry *) &dirlist;
- }
- }
- else { /* process matching files if needed */
- if ((do_files && Match(Pat, Aux, myfib->fib_FileName)) &&
- (!x_files || !Match(xPat, xAux, myfib->fib_FileName))) {
- if ((new = make_entry(myfib->fib_FileName)) == NULL)
- goto clean_exit;
- current = filelist;
- prev = (struct dir_entry *) &filelist;
- }
- }
-
- if (!sorted && new) {
- prev->next = new;
- new->next = current;
- }
- else while (new) { /* maintain list in alpha order */
- if ((current == NULL) ||
- (stricmp(new->name, current->name) < 0)) {
- prev->next = new;
- new->next = current;
- break;
- }
- else {
- prev = current;
- current = current->next;
- }
- }
- } /* while (ExNext()) */
-
- /* Process list of files */
- while (filelist) {
-
- /* Format the current command */
- if (!expand_cmd(command, cmd, path, filelist->name)) {
- fputs("\n\aCommand format is invalid\n", stderr);
- rc = FALSE;
- goto clean_exit;
- }
-
- /* write the command to the AmigaDOS script if necessary */
- if (script) {
- if (echo) {
- fputs("ECHO \"*e[32;41mCommand: ", script);
- fputs(command, script);
- fputs("*e[31;40m\"\n", script);
- }
- fputs(command, script);
- fputs("\n", script);
- }
- else { /* not a script -- execute the command immediately */
- fputs("\x1b[32;41mCommand: ", stdout);
- fputs(command, stdout);
- fputs("\x1b[31;40m\n", stdout);
- fflush(stdout);
-
- if (Execute(command, 0, 0) == FALSE) {
- fputs("\n\aSystem command processor failure\n", stderr);
- rc = FALSE;
- goto clean_exit;
- }
- }
- /* Get next file to process and free processed element */
- free(filelist->name);
- prev = filelist;
- filelist = filelist->next;
- free(prev);
- }
-
- /* recursively process subdirectories */
- while (dirlist) {
-
- /* Get a lock on the directory and */
- /* go into it to process its contents. */
- newlock = Lock(dirlist->name, ACCESS_READ);
- oldlock = CurrentDir(newlock);
-
- /* Recursively follow the tree down every branch */
- if (!(rc = traverse_tree(cmd, newlock))) break;
-
- /* After processing the contents of the new directory */
- /* return to old parent directory. */
- (void) CurrentDir(oldlock);
- UnLock(newlock);
-
- /* Get next directory in list and free processed element */
- free(dirlist->name);
- prev = dirlist;
- dirlist = dirlist->next;
- free(prev);
- }
-
- /* free all used resources */
- clean_exit:
- while (filelist) {
- free(filelist->name);
- prev = filelist;
- filelist = filelist->next;
- free(prev);
- }
-
- while (dirlist) {
- free(dirlist->name);
- prev = dirlist;
- dirlist = dirlist->next;
- free(prev);
- }
- return rc;
- }
-