home *** CD-ROM | disk | FTP | other *** search
/ Club Amiga de Montreal - CAM / CAM_CD_1.iso / files / 345.lha / CmdSweep_v1.0 / CmdSweep.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-01-29  |  24.1 KB  |  683 lines

  1. /* CmdSweep.c
  2.  *
  3.  * Author:   Bill DuPree (with thanks to Jeff Lydiatt for PatMatch.c)
  4.  * Language: Lattice C version 5.04
  5.  * Date:     January 16, 1990
  6.  * Version:  1.00
  7.  *
  8.  * Usage:  CmdSweep [options] <starting_directory> "Command"
  9.  *
  10.  * CmdSweep executes a given command in every subdirectory under the
  11.  * specified starting directory including the starting directory.  It
  12.  * can also be used to execute the command for every matching file
  13.  * encountered in the directory tree.  If the current directory is the
  14.  * desired starting directory, then it may be specified by coding the
  15.  * argument as "".
  16.  *
  17.  * The "Command" argument specifies a template for the command you wish
  18.  * to execute.  A favorite application of mine is to check the contents
  19.  * of a diskette for viral infestations by using CmdSweep with the command
  20.  * template "kv *", i.e. the full entry on the command line is:
  21.  *
  22.  *      CmdSweep df0: "kv *"
  23.  *
  24.  * The command template may contain the escape sequences %p and %f.  These
  25.  * will be replaced with the current directory path and current file 
  26.  * respectively, in command generation.  If you need to use the % character
  27.  * in the command, then escape it with an additional % character.
  28.  *
  29.  * The "-A" option will force CmdSweep to process directories (and files)
  30.  * in alphabetical order.  While this may cause a slight performance penalty,
  31.  * it is useful in that you have some indication of how much processing
  32.  * remains to be done by the visual inspection of the current directory
  33.  * and/or file name.
  34.  *
  35.  * The use of the "-F" (Files) option will cause CmdSweep to execute the
  36.  * command for every file it encounters in the directory tree.  If coded
  37.  * as "-F=pattern"  then the command is executed only for files matching
  38.  * the AmigaDOS pattern.
  39.  *
  40.  * Deferred execution may be had through the use of the "-S" (Script) option
  41.  * which will cause CmdSweep to generate an AmigaDOS script file.  This
  42.  * option is coded as "-S=script_file_name" where script_file_name is the
  43.  * name of the file to receive the list of script commands.  If coded as
  44.  * "-SE=script_file_name" then ECHO commands will be placed into the
  45.  * script to provide a visible progress report when executing the script.
  46.  *
  47.  * You may use the "-X=pattern" (eXclude) option to exclude from 
  48.  * consideration all files that match the supplied AmigaDOS pattern.
  49.  *
  50.  * The "-H" (Here) option will inhibit the directory tree traversal causing
  51.  * CmdSweep to operate only in the supplied directory.  None of its
  52.  * subdirectories will be entered.
  53.  *
  54.  * To compile and link:
  55.  *
  56.  *      lc -b -r -v CmdSweep.c
  57.  *      lc -b -r -v PatMatch.c
  58.  *      blink lib:c.o,CmdSweep.o,PatMatch.o to CmdSweep lib lib:lc.lib SD SC ND
  59.  *
  60.  * Examples:
  61.  *
  62.  * (1) To browse every C source and header file on a diskette in drive df1:
  63.  *
  64.  *      CmdSweep -f=#?.((c)|(h)) df1: "More %f"
  65.  *
  66.  * (2) To create a script file in RAM: to do the same as example one:
  67.  *
  68.  *      CmdSweep -s=ram:Browse.Script -f=#?.((c)|(h)) df1: "More %f"
  69.  *
  70.  * (3) To delete all files ending in the ".bak" extension on dh0:
  71.  *
  72.  *      CmdSweep -f=#?.bak dh0: "delete %f"
  73.  * or
  74.  *      CmdSweep dh0: "delete #?.bak"
  75.  *
  76.  * (4) To rid a diskette in drive df1: from viruses using the public domain
  77.  *     KV program:
  78.  *
  79.  *      CmdSweep df1: "kv *"
  80.  * or
  81.  *      CmdSweep df1: "kv %p/"
  82.  * or
  83.  *      CmdSweep -f df1: "kv %p/%f"
  84.  *
  85.  * I'm sure that you will find many other uses.
  86.  * 
  87.  *--------------------------------------------------------------------------
  88.  * DISCLAIMER:
  89.  *
  90.  * THIS PROGRAM AND ITS ASSOCIATED DOCUMENTATION ARE PROVIDED "AS IS"
  91.  * WITHOUT REPRESENTATION OR WARRANTY OF ANY KIND, EITHER EXPRESS OR
  92.  * IMPLIED, INCLUDING WITHOUT LIMITATION, ANY REPRESENTATIONS OR
  93.  * ENDORSEMENTS REGARDING THE USE OF, THE RESULTS OF, OR PERFORMANCE OF
  94.  * THE PROGRAM OR ITS ASSOCIATED DOCUMENTATION, ITS APPROPRIATENESS,
  95.  * ACCURACY, RELIABILTY, OR CURRENTNESS.  THE ENTIRE RISK AS TO THE USE OF
  96.  * THIS PROGRAM OR ITS ASSOCIATED DOCUMENTATION IS ASSUMED BY THE USER.
  97.  *
  98.  * IN NO EVENT WILL BILL DUPREE, NOR ANY OTHER PARTY INVOLVED IN THE
  99.  * CREATION OR DISSEMINATION OF THIS PROGRAM AND ITS ASSOCIATED
  100.  * DOCUMENTATION, BE LIABLE FOR ANY DAMAGES, DIRECT, INDIRECT,
  101.  * INCIDENTAL OR CONSEQUENTIAL, RESULTING FROM ANY DEFECT IN THIS PROGRAM,
  102.  * EVEN IF ANY OF THE AFOREMENTIONED PARTIES HAS BEEN ADVISED OF THE
  103.  * POSSIBILITY OF SUCH DAMAGES.
  104.  *
  105.  * THIS DISCLAIMER SHALL SUPERCEDE ANY VERBAL OR WRITTEN STATEMENT TO
  106.  * THE CONTRARY.
  107.  *
  108.  * Enough of the CYA stuff already.  This program is hereby placed into the
  109.  * public domain.  Please use it freely for whatever purposes you deem
  110.  * necessary.  However, if you incorporate it into any wondrous creations
  111.  * of your own, give credit where credit is due, i.e. leave the names of
  112.  * the author(s) intact in the code.  We all want our moments in the sun.
  113.  * It would probably also be wise to continue including the disclaimer given
  114.  * the state of affairs in our litigious society.  If you feel compelled to
  115.  * contact me for any reason, or if you want to pass on constructive
  116.  * criticism, suggestions, ideas for new features, or monetary rewards
  117.  * (not necessary), or if you are a young, pretty, and single female Amiga
  118.  * enthusiast in the Chicagoland area (I'm single), you can contact me at:
  119.  *
  120.  *      Bill DuPree
  121.  *      7449 W. Washington #402
  122.  *      Forest Park, IL. 60130
  123.  */
  124.  
  125. #include "libraries/dos.h"
  126. #include "libraries/dosextens.h"
  127. #include "exec/memory.h"
  128. #include "string.h"
  129. #include "stdio.h"
  130. #include "stdlib.h"
  131. #include "proto/dos.h"
  132. #include "proto/exec.h"
  133.  
  134. #define FALSE 0
  135. #define TRUE  1
  136. #define _MAX_PATH 256
  137. #define ERROR 10
  138.  
  139. /* These two arrays are required for AmigaDOS style regular */
  140. /* expression pattern matching.                             */
  141. static WORD Aux[128], xAux[128];
  142. static char *Pat, *xPat;
  143.  
  144. /* These declarations are for the pattern matching routines */
  145. extern int CmplPat(char *, WORD *);
  146. extern int Match(char *, WORD *, char *);
  147.  
  148. /* A couple of ANSI prototypes for our own routines */
  149. static char *show_locked_dir(BPTR, char *);
  150. static int traverse_tree(char *, BPTR);
  151. static void my_exit(int);
  152.  
  153. struct dir_entry {
  154.         struct dir_entry *next; /* pointer to next instance */
  155.         char *name;             /* pointer to a string containing dir name */
  156. };
  157.  
  158. /* Some global flags which tell what options were selected */
  159. static int do_files, x_files, here_only, sorted, echo;
  160.  
  161. /* The script file stream */
  162. static FILE *script;
  163. char *ScriptName;
  164.  
  165. /* We only need one FileInfoBlock, not one for every level of recursion */
  166. /* like most tree traversal algorithms.  This is because we defer       */
  167. /* recursion until we have exhausted the current directory contents.    */
  168. /* Subdirectories are saved in an ordered, dynamically allocated,       */
  169. /* linked list for use later.  See traverse_tree().                     */
  170.  
  171. struct FileInfoBlock *myfib;
  172.  
  173. /* This program is VERY!!! loosely based on a non-working example given in */
  174. /* the book "Programmer's Guide to the Amiga" authored by Rob Peck and     */
  175. /* published by Sybex.  (See example opta.c on pp. 34)                     */
  176.  
  177. int main(int argc, char **argv)
  178. {
  179.         int i, rc, got_dir, batch;
  180.         BPTR oldlock, newlock;
  181.         char *whichdir, *command;
  182.         static char work[_MAX_PATH];
  183.         static char *signon =
  184.                 "\x1b[33mCmdSweep\x1b[31m v. 1.00 - January 16, 1990\n"
  185.                 "Author:  Bill DuPree";
  186.         static char *usage =
  187.                 "Usage: CmdSweep [options] <starting_dir> \"command\"\n"
  188.                 "Enter \"CmdSweep -help\" for more information";
  189.  
  190.         /* Start variables off with known values */
  191.     batch = rc = 0;
  192.     sorted = got_dir = do_files = x_files = here_only = echo = FALSE;
  193.         oldlock = NULL;
  194.         myfib = NULL;
  195.     script = NULL;
  196.         command = whichdir = "";
  197.         *work = (char) 0;
  198.         Pat = "#?";
  199.  
  200.         puts(signon);
  201.  
  202.         /* make sure command line is worth parsing */
  203.         if (argc < 2) {
  204.                 puts("Command line error");
  205.                 puts(usage);
  206.                 my_exit(ERROR);
  207.         }
  208.  
  209.         /* allocate space for a FileInfoBlock */
  210.  
  211.         if ((myfib = (struct FileInfoBlock *)
  212.            AllocMem(sizeof(struct FileInfoBlock), MEMF_CLEAR)) == NULL) {
  213.                 fputs("\n\aAlloc of FileInfoBlock failed\n", stderr);
  214.                 my_exit(ERROR);
  215.         }
  216.  
  217.         /* Usage and/or help requested? */
  218.         if (argc == 2)
  219.                 if (!strcmp(argv[1], "?")) {
  220.                         puts(usage);
  221.                         my_exit(0);
  222.                 }
  223.         else if (!stricmp(argv[1], "-HELP")) {
  224.                         puts("\fUsage: CmdSweep [options] <starting_dir> \"command\"");
  225.             puts("where options are:\n");
  226.  
  227.             puts("\t-A : Process directories (and files) in alphabetical order.\n");
  228.  
  229.             puts("\t-F : will generate the given command for every file in the");
  230.                         puts("\t     traversed directory tree and \"-F=pattern\" will generate.");
  231.             puts("\t     the command for every file that matches the AmigaDOS pattern.\n");
  232.  
  233.                         puts("\t-S : coded as \"-S=script_file_name\", where script_file_name");
  234.                         puts("\t     is any valid AmigaDOS file name.  The given file name");
  235.                         puts("\t     will be opened to receive the list of commands that would");
  236.             puts("\t     have otherwise been executed immediately.  The -SE variant");
  237.             puts("\t     will place ECHO commands in the script for a visible progress");
  238.             puts("\t     report.\n");
  239.  
  240.             puts("\t-X : coded as \"-X=pattern\" excludes files matching the pattern.\n");
  241.  
  242.             puts("\t-H : causes CmdSweep to operate only in the specified directory.\n");
  243.  
  244.             fputs("Press return to continue:", stdout);
  245.             fflush(stdout);
  246.             fgets(work, 1, stdin);
  247.  
  248.             puts("\fCmdSweep help continued:\n");
  249.             puts("Options are not case sensitive and can be used in any combination to");
  250.                         puts("achieve the desired effect.  All command line options and arguments must");
  251.                         puts("be separated by space(s).  If they contain embedded space(s), then they");
  252.             puts("must be surrounded by quotes.\n");
  253.  
  254.             puts("The <starting_dir> argument is not optional and must be a valid directory");
  255.                         puts("on a mounted device.  If the current directory is desired then it should be");
  256.             puts("coded as \"\".\n");
  257.  
  258.             puts("The \"command\" argument is also mandatory.  If you embed the escape");
  259.                         puts("sequences \"%p\" or \"%f\" (no quotes) in the command, then the current");
  260.                         puts("path and file will replace them in the resulting command, respectively.");
  261.                         puts("The sequence \"%%\" escapes the '%' character in the generated commands.\n");
  262.                         my_exit(0);
  263.                 }
  264.  
  265.         /* process command line */
  266.         for (i = 1; i < argc; i++) {
  267.                 if (*argv[i] == (char) '-') {
  268.             if ((stricmp(argv[i], "-A") == 0) && !sorted)
  269.                 sorted = TRUE;
  270.             else if ((stricmp(argv[i], "-F") == 0) && !do_files)
  271.                 do_files = TRUE;
  272.                         else if ((strnicmp(argv[i], "-F=", 3) == 0) && !do_files) { 
  273.                                 do_files = TRUE;
  274.                                 strcpy(Pat, &argv[i][3]);
  275.                         }
  276.                         else if ((stricmp(argv[i], "-H") == 0) && !here_only) 
  277.                                 here_only = TRUE;
  278.                         else if ((strnicmp(argv[i], "-S=", 3) == 0) && !batch)
  279.                                 batch = i;
  280.                         else if ((strnicmp(argv[i], "-SE=", 4) == 0) && !batch) {
  281.                 echo = TRUE;
  282.                                 batch = i;
  283.             }
  284.                         else if ((strnicmp(argv[i], "-X=", 3) == 0) && !x_files) { 
  285.                                 x_files = TRUE;
  286.                                 xPat = &argv[i][3];
  287.                         }
  288.                         else {
  289.                                 fputs("\n\aExtraneous options\n", stderr);
  290.                                 my_exit(ERROR);
  291.                         }
  292.                 }
  293.                 else {
  294.                         if ((*whichdir == (char) 0) && !got_dir) {
  295.                                 got_dir = TRUE;
  296.                                 whichdir = argv[i];
  297.                         }
  298.                         else if (*command == (char) 0)
  299.                                 command = argv[i];
  300.                         else {
  301.                                 fputs("\n\aExtraneous command line args\n", stderr);
  302.                                 my_exit(ERROR);
  303.                         }
  304.                 }
  305.         }
  306.  
  307.         /* we have to examine files if we are excluding them */
  308.         do_files |= x_files;
  309.  
  310.         /* make sure command was specified */
  311.         if (*command == (char) 0) {
  312.                 fputs("\n\aCommand argument is required\n", stderr);
  313.                 my_exit(ERROR);
  314.         }
  315.  
  316.         /* Compile and validate the inclusive pattern */
  317.         if (do_files && !CmplPat(Pat, Aux)) {
  318.                 fputs("\n\aBad wild card expression in inclusion\n", stderr);
  319.                 my_exit(ERROR);
  320.         }
  321.  
  322.         /* Compile and validate the pattern for exclusion */
  323.         if (x_files && !CmplPat(xPat, xAux)) {
  324.                 fputs("\n\aBad wild card expression in exclusion\n", stderr);
  325.                 my_exit(ERROR);
  326.         }
  327.  
  328.         if (batch) {
  329.         if ((script = fopen(ScriptName = &argv[batch][echo ? 4 : 3], "w")) == NULL) {
  330.                         fputs("\n\aCannot open script file\n", stderr);
  331.                         my_exit(ERROR);
  332.                 }
  333.         }
  334.  
  335.         /* get a lock on the specified directory */
  336.         newlock = Lock(whichdir, ACCESS_READ);
  337.     if (newlock != 0) {
  338.         Examine(newlock, myfib);
  339.         if (myfib->fib_DirEntryType > 0) {
  340.             /* get lock on current dir so we can come back to it*/
  341.             oldlock = CurrentDir(newlock);
  342.  
  343.             /* process new dir and subtree recursively */
  344.             rc = (traverse_tree(command, newlock) == FALSE) ? ERROR : 0;
  345.  
  346.             /* return to original directory */
  347.             (void) CurrentDir(oldlock);
  348.             if (batch) {
  349.                 fputs("\nCD ", script);
  350.                 fputs(show_locked_dir(oldlock, work), script);
  351.                 fputs("\n", script);
  352.             }
  353.  
  354.             /* unlock what we locked */
  355.             UnLock(newlock);
  356.         }
  357.         else {
  358.             fputs("\n\aError - \"", stderr);
  359.             fputs(whichdir, stderr);
  360.             fputs("\" is not a directory\n", stderr);
  361.             my_exit(ERROR);
  362.         }
  363.     }
  364.         else {
  365.                 fputs("\n\aCan't lock selected dir\n", stderr);
  366.         my_exit(ERROR);
  367.         }
  368.         fputs("\n", stdout);
  369.         my_exit(rc);
  370. }
  371.  
  372. /*
  373.  * Function to cleanup and exit.
  374.  */
  375. static void my_exit(int rc)
  376. {
  377.     if (script) fclose(script);
  378.  
  379.     /* Set script attribute if we generated a script */
  380.     if (script && (rc == 0))
  381.         (void) SetProtection(ScriptName, (FIBF_SCRIPT | FIBF_EXECUTE));
  382.  
  383.     if (myfib) FreeMem(myfib, sizeof(struct FileInfoBlock));
  384.         exit(rc);
  385. }
  386.  
  387. /*
  388.  * This routine will construct the full path for a given lock.
  389.  */
  390. static char *show_locked_dir(BPTR lock, char *cd)
  391. {
  392.         BPTR mylock, oldlock;
  393.         struct FileInfoBlock *finfo;
  394.         static char work[_MAX_PATH];
  395.  
  396.         /* allocate space for a FileInfoBlock */
  397.  
  398.         if ((finfo = (struct FileInfoBlock *)
  399.            AllocMem(sizeof(struct FileInfoBlock), MEMF_CLEAR)) == NULL) {
  400.                 fputs("\n\aAlloc of FileInfoBlock failed\n", stderr);
  401.                 return NULL;
  402.         }
  403.  
  404.         /* construct the path by examining all parent directories */
  405.         mylock = lock;
  406.         *cd = *work = (char) 0;
  407.         while (mylock) {
  408.                 (void)Examine(mylock, finfo);
  409.                 strcpy(work, cd);
  410.                 strcpy(cd, finfo->fib_FileName);
  411.                 oldlock = mylock;
  412.                 mylock = ParentDir(oldlock);
  413.  
  414.                 /* This is not well documented, but you have to unlock */
  415.                 /* the locks obtained from ParentDir().                */
  416.                 if (oldlock != lock)
  417.                         UnLock(oldlock);
  418.  
  419.                 if (mylock == 0)        /* did we hit the root? */
  420.                         strcat(cd,":");
  421.  
  422.                 if (work[0]) {          /* concatenate names */
  423.                         if (mylock != 0)                        
  424.                                 strcat(cd, "/");
  425.                         strcat(cd, work);
  426.                 }
  427.         }
  428.  
  429.         /* return used resources to OS */
  430.         FreeMem(finfo, sizeof(struct FileInfoBlock));
  431.         return cd;
  432. }
  433.  
  434. /*
  435.  * This routine expands the command template by replacing %p escape
  436.  * sequences with the path and %f escape sequences with the file.
  437.  */
  438. static int expand_cmd(char *new, char *old, char *path, char *file)
  439. {
  440.         char *percent, *lastpos;
  441.  
  442.         *new = (char) 0;
  443.         lastpos = old;
  444.         while (percent = strchr(lastpos, '%')) {
  445.                 *percent = (char) 0;
  446.                 strcat(new, lastpos);
  447.                 *percent = (char) '%';
  448.                 percent++;
  449.                 if (toupper(*percent) == 'F') {
  450.                         percent++;
  451.                         strcat(new, file);
  452.                 }
  453.                 else if (toupper(*percent) == 'P') {
  454.                         percent++;
  455.                         strcat(new, path);
  456.                 }
  457.                 else if (*percent == '%') {
  458.                         strcat(new, "%");
  459.                         percent++;
  460.                 }
  461.                 else
  462.                         return FALSE;
  463.  
  464.                 lastpos = percent;
  465.         }
  466.         strcat(new, lastpos);
  467.         return TRUE;
  468. }
  469.  
  470. struct dir_entry *make_entry(char *name)
  471. {
  472.     struct dir_entry *new;
  473.  
  474.     /* allocate a dir_entry structure for deferred processing */
  475.     if ((new = malloc(sizeof(struct dir_entry))) == NULL) {
  476.         fputs("\n\aAlloc of dir_entry failed\n", stderr);
  477.         return NULL;
  478.     }
  479.  
  480.     /* fill in name */
  481.     if ((new->name = strdup(name)) == NULL) {
  482.         fputs("\n\aOut of memory\n", stderr);
  483.         free(new);
  484.         return NULL;
  485.     }
  486.     return new;
  487. }
  488.  
  489.  
  490. /* Now follow the tree... might hit a directory, might hit a file.   */
  491. /* If a directory, list it and then follow it down (recursively).    */
  492.  
  493. static int traverse_tree(char *cmd, BPTR lock)
  494. {
  495.         int rc;
  496.         BPTR newlock, oldlock;
  497.         struct dir_entry *dirlist, *filelist, *new, *current, *prev;
  498.         static char path[_MAX_PATH];
  499.         static char command[_MAX_PATH];
  500.  
  501.         /* start with empty list of files and directories */
  502.         filelist = dirlist = NULL;
  503.  
  504.         rc = TRUE;
  505.  
  506.         /* The first call to Examine fills in the FileInfoBlock */
  507.         /* with information about the directory.  If it is      */
  508.         /* called at the root level, it contains the volume     */
  509.         /* name of the disk.                                    */
  510.  
  511.         Examine(lock, myfib);
  512.         if (show_locked_dir(lock, path) == NULL) {
  513.                 rc = FALSE;
  514.                 goto clean_exit;
  515.         }               
  516.         fputs("\n\n\x1b[32;41mCmdSweep:  Directory ==> ", stdout);
  517.         fputs(path, stdout);
  518.         fputs("\x1b[31;40m\n", stdout);
  519.         fflush(stdout);
  520.  
  521.         /* Expand command if we are executing directory by directory */
  522.         if (!do_files && !expand_cmd(command, cmd, path, "")) {
  523.                 fputs("\n\aCommand format is invalid\n", stderr);
  524.                 rc = FALSE;
  525.                 goto clean_exit;
  526.         }
  527.  
  528.         /* Write out appropriate script commands if necessary */
  529.         if (script) {
  530.         if (echo) {
  531.                     fputs("\nECHO \"*e[32;41mDirectory ==> ", script);
  532.                     fputs(path, script);
  533.                     fputs("*e[31;40m\"\n", script);
  534.         }
  535.         else
  536.             fputs("\n", script);
  537.  
  538.                 fputs("CD ", script);
  539.                 fputs(path, script);
  540.                 fputs("\n", script);
  541.                 if (!do_files) {
  542.             if (echo) {
  543.                             fputs("ECHO \"*e[32;41mCommand:   ", script);
  544.                             fputs(command, script);
  545.                             fputs("*e[31;40m\"\n", script);
  546.             }
  547.                         fputs(command, script);
  548.                         fputs("\n", script);
  549.                 }
  550.         }
  551.         else if (!do_files) {
  552.                 fputs("\x1b[32;41mCommand:   ", stdout);
  553.                 fputs(command, stdout);
  554.                 fputs("\x1b[31;40m\n", stdout);
  555.                 fflush(stdout);
  556.  
  557.                 if (Execute(command, 0, 0) == FALSE) {
  558.                         fputs("\n\aSystem command processor failure\n", stderr);
  559.                         rc = FALSE;
  560.                         goto clean_exit;
  561.                 }
  562.         }
  563.  
  564.         /* loop until last directory entry */
  565.         while (ExNext(lock, myfib)) {
  566.         new = NULL;
  567.                 if (myfib->fib_DirEntryType > 0) {
  568.                         /* got a subdirectory */
  569.                         if (!here_only) {
  570.                 if ((new = make_entry(myfib->fib_FileName)) == NULL)
  571.                     goto clean_exit;
  572.                 current = dirlist;
  573.                                 prev = (struct dir_entry *) &dirlist;
  574.                         }
  575.                 }
  576.                 else {  /* process matching files if needed */
  577.                         if ((do_files && Match(Pat, Aux, myfib->fib_FileName)) &&
  578.                            (!x_files || !Match(xPat, xAux, myfib->fib_FileName))) {
  579.                 if ((new = make_entry(myfib->fib_FileName)) == NULL)
  580.                     goto clean_exit;
  581.                                 current = filelist;
  582.                                 prev = (struct dir_entry *) &filelist;
  583.                         }
  584.                 }
  585.  
  586.         if (!sorted && new) {
  587.             prev->next = new;
  588.             new->next = current;
  589.         }
  590.         else while (new) { /* maintain list in alpha order */
  591.                         if ((current == NULL) ||
  592.                            (stricmp(new->name, current->name) < 0)) {
  593.                                 prev->next = new;
  594.                                 new->next = current;
  595.                                 break;
  596.                         }
  597.                         else {
  598.                                 prev = current;
  599.                                 current = current->next;
  600.                         }
  601.                 }
  602.     } /* while (ExNext()) */
  603.  
  604.     /* Process list of files */
  605.     while (filelist) {
  606.  
  607.         /* Format the current command */
  608.         if (!expand_cmd(command, cmd, path, filelist->name)) {
  609.                         fputs("\n\aCommand format is invalid\n", stderr);
  610.                         rc = FALSE;
  611.                         goto clean_exit;
  612.                         }
  613.  
  614.         /* write the command to the AmigaDOS script if necessary */
  615.         if (script) {
  616.             if (echo) {
  617.                             fputs("ECHO \"*e[32;41mCommand:   ", script);
  618.                             fputs(command, script);
  619.                             fputs("*e[31;40m\"\n", script);
  620.             }
  621.                         fputs(command, script);
  622.                         fputs("\n", script);
  623.                 }
  624.         else {  /* not a script -- execute the command immediately */
  625.                         fputs("\x1b[32;41mCommand:   ", stdout);
  626.                         fputs(command, stdout);
  627.                         fputs("\x1b[31;40m\n", stdout);
  628.                         fflush(stdout);
  629.  
  630.                         if (Execute(command, 0, 0) == FALSE) {
  631.                                 fputs("\n\aSystem command processor failure\n", stderr);
  632.                                 rc = FALSE;
  633.                                 goto clean_exit;
  634.                         }
  635.                 }
  636.         /* Get next file to process and free processed element */
  637.         free(filelist->name);
  638.         prev = filelist;
  639.         filelist = filelist->next;
  640.         free(prev);
  641.         }
  642.  
  643.         /* recursively process subdirectories */
  644.     while (dirlist) {
  645.  
  646.         /* Get a lock on the directory and     */
  647.         /* go into it to process its contents. */
  648.         newlock = Lock(dirlist->name, ACCESS_READ);
  649.         oldlock = CurrentDir(newlock);
  650.  
  651.                 /* Recursively follow the tree down every branch */
  652.                 if (!(rc = traverse_tree(cmd, newlock))) break;
  653.  
  654.         /* After processing the contents of the new directory */
  655.         /* return to old parent directory.                    */
  656.         (void) CurrentDir(oldlock);
  657.                 UnLock(newlock);
  658.  
  659.         /* Get next directory in list and free processed element */
  660.         free(dirlist->name);
  661.         prev = dirlist;
  662.         dirlist = dirlist->next;
  663.         free(prev);
  664.         }
  665.  
  666.         /* free all used resources */
  667. clean_exit:
  668.         while (filelist) {
  669.                 free(filelist->name);
  670.         prev = filelist;
  671.                 filelist = filelist->next;
  672.         free(prev);
  673.         }
  674.  
  675.         while (dirlist) {
  676.                 free(dirlist->name);
  677.         prev = dirlist;
  678.                 dirlist = dirlist->next;
  679.         free(prev);
  680.         }
  681.         return rc;
  682. }
  683.