home *** CD-ROM | disk | FTP | other *** search
/ The CDPD Public Domain Collection for CDTV 2 / CDPD_II_2352.bin / scope / 101-125 / scopedisk111 / ff15 / ff.c < prev    next >
C/C++ Source or Header  |  1992-10-27  |  16KB  |  579 lines

  1. /*
  2.  
  3.                          FileFind Amiga Version 1.5
  4.  
  5.     FF will search all directories on a filing device for occurances of a
  6.           particular file.  Supports both AmigaDOS and MS-DOS wildcards
  7.           and is PURE - can be made resident.
  8.  
  9.  
  10.                                   History:
  11.  
  12.   Version 1.1: Now will support the MS-DOS wildcard characters: "*" and "?"
  13.           Also: smaller code and a little quicker execution.
  14.  
  15.   Version 1.2: Uses AmigaDOS' Lock(), Examine() & ExNext() instead of
  16.           Lattice's dfind() & dnext().  AmigaDOS' routines are
  17.           much faster and can be used recursively so it is not
  18.           neccesary to use the heap to make a list of directories.
  19.           Lattice's stcpm() is used to compare filenames that are
  20.           found to the user-supplied wildcard.  If stcpm() says that
  21.           a filename matches, then the name is displayed.  This
  22.           change increases the speed of this routine by an average
  23.           of 55%!!!  One additional modification allows the user to
  24.           specify a directory to look in.  This effectively defines
  25.           a branch of the directory tree to search in, rather than
  26.           always starting at the root and scanning all directories
  27.           on the disk.  4-10-88   Finished....  4-22-88
  28.  
  29.   Version 1.3: Added Ctrl-CDEF handling.  Re-compiled to take advantage of
  30.           Lattice Version 5.02 direct shared library calls - this
  31.           eliminates the need to link in the amiga.lib stub routines
  32.           thereby shortening the executable code size (as well as
  33.           providing a tiny speed increase).  Added ANSI-C prototyping
  34.           function calls.  Pruned the code a little more.  Took advantage
  35.           of Lattice 5.02 registerized parameters for some functions
  36.           and register type auto variables within functions.  Removed all
  37.           global variables to allow for pure code (Resident-able).  Compiled
  38.           with stack checking turned off (-v) and "merge duplicate strings"
  39.           on (-cs) for smaller code size.  Added printing of path names
  40.           that are being searched.  Added support for quoted arguments
  41.           which may contain spaces.
  42.           Finished: 07-26-89
  43.  
  44.   Version 1.4: Replaced Lattice's slightly inappropriate pattern matching
  45.           routines with one of my own.  It supports the following wildcard
  46.           characters:
  47.             ?   - matches one of any character.
  48.             #x  - matches one or more occurances of "x", which can be
  49.                   any valid character.
  50.             *   - matches any number of any characters.
  51.             #?  - (really an instance of "#x") works same as "*".
  52.           My routine was written specifically for filename pattern matching
  53.           which makes it more appropriate for use in this program.  It is
  54.           a little larger than Lattice's but it appears to be faster, and
  55.           it behaves like a filename wildcard pattern matching routine
  56.           is supposed to, unlike Lattice's which is really an all-purpose
  57.           regular expression parser.  For example, with Lattice's routine,
  58.           if the user entered "FF foo", Lattice would actually match "foo*",
  59.           which probably not what the user really wanted to see.  My routine
  60.           would literally match "foo" if that is what was entered, or "foo*"
  61.           if that is what was entered.
  62.           Finished: 08/12/89
  63.  
  64.   Version 1.5: Added the Quiet (-q) and the Verbose (-v) switches.
  65.           Corrected potential problem in the SKIP_NON_WHITE() macro which
  66.           did not look-out for the end of the string.  Changed MAX_PATH_LEN
  67.           to 257 to be consistant with AmigaDOS' path length limit of 256
  68.           characters - no room here for artificial constraints!
  69.           Finished: 10/23/89
  70.  
  71.  
  72.     Copyright © 1988, 1989  Ray Lambert
  73.  
  74. */
  75.  
  76. #include <stdio.h>
  77. #include <stdlib.h>
  78. #include <string.h>
  79. #include <ctype.h>
  80. #include <dos.h>
  81. #include <libraries/dos.h>
  82. #include <libraries/dosextens.h>
  83. #include <exec/memory.h>
  84. #include <proto/dos.h>
  85. #include <proto/exec.h>
  86.  
  87. #define VERSION       "1.5 (pure!)\n"
  88. #define MAX_PATH_LEN  257
  89.  
  90. #define SKIP_WHITE(s)     while(*(s)==' ') (s)++
  91. #define SKIP_NON_WHITE(s) while((*(s)!=' ')&&(*(s)!='\0')) (s)++
  92. #define CLREOL            conwrite("›K")
  93.  
  94. UBYTE bequiet; /* this global data is ok with Lattice's cres.o residentable
  95.                   startup module */
  96.  
  97. /****/
  98.  
  99. char * __asm strsrch(register __a0 char *str, register __a1 char *sstr)
  100. {
  101.   register char *a;
  102.   register char *b;
  103.   register char *z;
  104.   int slen=strlen(str);
  105.   int sslen=strlen(sstr);
  106.   if (sslen>slen) return(NULL);
  107.   z=(char *)(str+(slen-sslen+1));
  108.   for(; str<=z; str++)
  109.     {
  110.       if (*str==*sstr)
  111.         {
  112.           a=str;
  113.           b=sstr;
  114.           do
  115.             {
  116.               a++;
  117.               b++;
  118.             }
  119.           while( (*b) && (*b==*a) );
  120.           if (!*b) return(str);   /* end of search string reached - its a match! */
  121.         }
  122.     }
  123.   return(NULL);
  124. }
  125.  
  126. /****/
  127.  
  128. int __asm wcmatch(register __a0 char *name, register __a1 char *pattern)
  129. {
  130.   register char *wc;
  131.   register char *ss;
  132.   register char *a;
  133.   register char *b;
  134.   register char *temp;
  135.   register int result=-1;
  136.  
  137.  
  138. /* So you say goto's are a no-no eh?  Well let's see you code the
  139.     following more efficiently... */
  140.  
  141.   temp=malloc(strlen(name)+1);
  142.   if (!temp) goto wcabort1;
  143.  
  144.   wc=strdup(pattern);
  145.   if (!wc) goto wcabort2;
  146.   strupr(wc);
  147.  
  148.   ss=strdup(name);
  149.   if (!ss) goto wcabort3;
  150.   strupr(ss);
  151.  
  152.  
  153. /*
  154.   I know the following is cheating a little but for the sake of simplicity
  155.   I'm gonna do it anyway!  Any occurance of "#?" in the pattern will be
  156.   changed to "*".  The two patterns do the same thing, and now we'll
  157.   only have to parse for one of them.
  158. */
  159.   do
  160.     {
  161.       a=strsrch(wc,"#?");
  162.       if (a)
  163.         {
  164.           *a='*';
  165.           a++;
  166.           b=(a+1);
  167.           while(*b)
  168.             {
  169.               *a=*b;
  170.               a++;
  171.               b++;
  172.             }
  173.           *a='\0';
  174.         }
  175.     }
  176.   while(a);
  177.  
  178.  
  179. /* now to get down to work... */
  180.   a=wc;     /* "a" tracks the wildcard pattern          */
  181.   b=ss;     /* "b" tracks the source string (filename?) */
  182.   for(; result==-1; )
  183.     {
  184.  
  185.     /* check for premature end of source string */
  186.       if ( (*b=='\0') && (*a!='\0') )
  187.         {   /* if end of source string but only one char left in
  188.               pattern and its a '*' then it is a match */
  189.           if ( (*a=='*') || (*(a+1)=='\0') )
  190.             {
  191.               result=1;   /* a match */
  192.             }
  193.           else
  194.             {
  195.               result=0;   /* no match */
  196.             }
  197.           continue;
  198.         }
  199.  
  200.     /* parse next wildcard character */
  201.       switch(*a)
  202.         {
  203.           case '\0':  /* end of pattern string */
  204.             {
  205.               if (*b=='\0')
  206.                 {
  207.                   result=1; /* exact match */
  208.                   continue;
  209.                 }
  210.               result=0;   /* no match */
  211.               continue;
  212.             }
  213.           case '?':
  214.             /* matches any character */
  215.             {
  216.               a++;
  217.               b++;
  218.               break;
  219.             }
  220.           case '#':
  221.             /* matches any number of occurances of the following character */
  222.             {
  223.               a++;
  224.               while( (*b) && (*b==*a) ) b++;
  225.               a++;
  226.               continue;
  227.             }
  228.           case '*':
  229.             /* matches any number of occurances of any character(s) */
  230.             {
  231.               register char *c;
  232.               a++;
  233.  
  234.             /* is "*" the last character in pattern? If so then match */
  235.               if (!*a)
  236.                 {
  237.                   result=1;   /* match */
  238.                   continue;
  239.                 }
  240.  
  241.             /* extract the sub-string existing after the "*" */
  242.               c=temp;
  243.               while(!strchr("?#*",*a))
  244.                 {
  245.                   *c=*a;
  246.                   c++;
  247.                   a++;
  248.                 }
  249.               *c='\0';
  250.               if (!*temp) /* no sub-string exists - ignore this wc char */
  251.                 {
  252.                   continue;
  253.                 }
  254.  
  255.             /* locate the extracted sub-string in the source string */
  256.               c=strsrch(b,temp);
  257.               if (!c)
  258.                 {
  259.                   result=0;   /* no match */
  260.                   continue;
  261.                 }
  262.  
  263.             /* at this point we have:
  264.                 a: points to first char beyond sub-string following "*" in pattern
  265.                 b: points to chars to be matched to "*" (to be tossed)
  266.                 c: points to temp sub-string in search string (beyond b)
  267.                we must: advance b beyond the substring pointed to by c
  268.             */
  269.               b=(char *)(c+strlen(temp));
  270.  
  271.               continue; /* and that's it! */
  272.             }
  273.           default:  /* must match exactly! (another easy one...) */
  274.             {
  275.               if (*a!=*b)
  276.                 {
  277.                   result=0;   /* no match */
  278.                   continue;
  279.                 }
  280.               a++;
  281.               b++;
  282.               break;
  283.             }
  284.         }
  285.     }
  286.   free(ss);
  287. wcabort3:
  288.   free(wc);
  289. wcabort2:
  290.   free(temp);
  291. wcabort1:
  292.   return(result);
  293. }
  294.  
  295. /****/
  296.  
  297. void __asm conwrite(register __a0 char *str)
  298. {
  299.   Write(Output(),str,strlen(str));
  300. }
  301.  
  302. /****/
  303.  
  304. void __asm conwrite_int(register __d0 int num)
  305. {
  306.   char str[10];
  307.   register int i;
  308.   register char *p=str;
  309.  
  310. /* Find beginning divisor */
  311.   for(i=10000; ( (i>num) && (i>1) ); i/=10);
  312.  
  313.   do
  314.     {
  315.       *p=(char)((num/i)+'0');
  316.       num%=i;
  317.       i/=10;
  318.       p++;
  319.     }
  320.   while(i>=1);
  321.   *p='\0';
  322.   conwrite(str);
  323. }
  324.  
  325. /****/
  326.  
  327. int chk4brk(void)
  328. {
  329.   if (  SetSignal(0L,0L) &
  330.             ( SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D |
  331.               SIGBREAKF_CTRL_E | SIGBREAKF_CTRL_F )
  332.      ) return(1);
  333.   return(0);
  334. }
  335.  
  336. /****/
  337.  
  338. void __asm report_searching(  register __a0 char *root,
  339.                               register __d0 int level,
  340.                               register __a1 char *mask  )
  341. {
  342.   if (bequiet) return;
  343.   CLREOL;
  344.   conwrite("Searching: ");
  345.   conwrite(root);
  346.   if (level>0) conwrite("/");
  347.   conwrite(mask);
  348.   conwrite("\x0d"); /* move cursor to column 1 */
  349. }
  350.  
  351. /****/
  352.  
  353. int __asm dtree(  register __a0 char *root,
  354.                   register __a1 char *mask,
  355.                   register __d0 int level   )
  356. {
  357.   char temp_str[MAX_PATH_LEN];
  358.   register int cnt=0;
  359.   register BPTR lock;
  360.   register struct FileInfoBlock *info;
  361.   register int brk=0;
  362.   if ( (lock=Lock(root,ACCESS_READ))==NULL ) return(0);
  363.   info=(struct FileInfoBlock *)
  364.       AllocMem(sizeof(struct FileInfoBlock),MEMF_CLEAR);
  365.       /* AllocMem() is used to guarantee long-word alignment */
  366.   if (!info)
  367.     {
  368.       UnLock(lock);
  369.       conwrite("›33;42m Memory is low! ›0m\n");
  370.       return(0);
  371.     }
  372.   if (!bequiet) report_searching(root,level,mask);
  373.   if (!Examine(lock,info))
  374.     {
  375.       UnLock(lock);
  376.       FreeMem(info,sizeof(struct FileInfoBlock));
  377.       return(0);
  378.     }
  379.   while( (brk==0) && (ExNext(lock,info)!=0) )
  380.     {
  381.       if (brk=chk4brk()) continue;
  382.       strcpy(temp_str,root);
  383.       if (level>0) strcat(temp_str,"/");
  384.       strcat(temp_str,info->fib_FileName);
  385.       if (info->fib_DirEntryType>0)
  386.         {   /* it's a directory - scan it */
  387.           register int result;
  388.           level++;
  389.           result=dtree(temp_str,mask,level);
  390.           level--;
  391.           if (result==-1)
  392.             {
  393.               brk=1;
  394.               continue;
  395.             }
  396.           cnt+=result;
  397.         }
  398.       else
  399.         {   /* it is a normal file - compare it */
  400.           strupr(info->fib_FileName);
  401.           if (wcmatch(info->fib_FileName,mask))
  402.             {
  403.               if (!bequiet) CLREOL;
  404.               conwrite("›33mFound: ");
  405.               conwrite(temp_str);
  406.               conwrite("\n›0m");
  407.               if (!bequiet) report_searching(root,level,mask);
  408.               cnt++;
  409.             }
  410.         }
  411.     }
  412.   UnLock(lock);
  413.   FreeMem(info,sizeof(struct FileInfoBlock));
  414.   return( (int)((brk!=0)?(-1):(cnt)) );
  415. }
  416.  
  417. /****/
  418.  
  419. void usage(void)
  420. {
  421.   conwrite("\nUsage: FF [-q] [-v] [DRIVE:][PATH/]FILENAME [...]\n\n");
  422.   conwrite("    [-q]     = Quiet mode - supresses \"searching...\" messages\n");
  423.   conwrite("    [-v]     = Verbose mode - opposite of \"Quiet\" (default)\n");
  424.   conwrite("    [DRIVE:] = Optional drive to scan\n");
  425.   conwrite("    [PATH/]  = Optional path to begin scan at\n");
  426.   conwrite("    FILENAME = File to search for (wildcards allowed)\n");
  427.   conwrite("    [...]    = Optional multiple additional arguments\n\n");
  428.   exit(5);
  429. }
  430.  
  431. /****/
  432.  
  433. char * __regargs strdcpy(char *to, char *from, char delimiter)
  434. {
  435.   while( (*from!=delimiter) && (*from!='\0') )
  436.     {
  437.       *to=*from;
  438.       to++;
  439.       from++;
  440.     }
  441.   *to='\0';
  442.   if (*from==delimiter) from++;
  443.   return(from);
  444. }
  445.  
  446. /****/
  447.  
  448. void _main(char *args)
  449. {
  450.   register char *ptr;
  451.   register int match_cnt;
  452.   char
  453.     drive[MAX_PATH_LEN],
  454.     file_mask[MAX_PATH_LEN];
  455.   conwrite("\n›33;40mFileFind Amiga! Version ");
  456.   conwrite(VERSION);
  457.   conwrite("›0mCopyright © 1988, 1989 by Ray Lambert\n");
  458.  
  459.   bequiet = 0;  /* initialize to Verbose mode */
  460.  
  461. /* Change terminating newline to a NULL */
  462.   ptr=(char *)(args+strlen(args)-1);
  463.   if (*ptr=='\n') *ptr='\0';
  464.  
  465. /* Skip over command "FF " */
  466.   SKIP_WHITE(args);
  467.   SKIP_NON_WHITE(args);
  468.  
  469. /* check for no args */
  470.   SKIP_WHITE(args);
  471.   if (!*args) usage();
  472.  
  473.   conwrite("(Ctrl-C to abort)\n\n");
  474.  
  475. /* process all args */
  476.   do
  477.     {
  478.  
  479.     /* get next arg */
  480.       if (*args=='-') /* a switch */
  481.         {
  482.           args++;
  483.           switch(toupper(*args))
  484.             {
  485.               case 'Q':  /* quiet switch */
  486.                 {
  487.                   bequiet = 1;
  488.                   conwrite("›32;40m(Quiet mode enabled)›0m\n");
  489.                   break;
  490.                 }
  491.               case 'V':  /* verbose switch */
  492.                 {
  493.                   bequiet = 0;
  494.                   conwrite("›32;40m(Verbose mode enabled)›0m\n");
  495.                   break;
  496.                 }
  497.             }
  498.           SKIP_NON_WHITE(args);
  499.           SKIP_WHITE(args);
  500.           continue;
  501.         }
  502.  
  503.       if (*args=='\"')      /* quoted argument */
  504.         {                   /* copy till closing quote or null */
  505.           args=strdcpy(drive,(++args),'\"');
  506.         }
  507.       else      /* normal argument         */
  508.         {       /* copy till space or null */
  509.           args=strdcpy(drive,args,' ');
  510.         }
  511.  
  512.     /* Skip over leading spaces for next pass */
  513.       SKIP_WHITE(args);
  514.  
  515.     /* Check for user drive spec */
  516.       ptr=strchr(drive,':');
  517.       if (ptr)  /* Drive was supplied by user */
  518.         {
  519.         /* Get pointer to last xter (actually to null) */
  520.           while(*ptr!='\0') ptr++;
  521.         /* Find beginning of file mask */
  522.           while( (*ptr!='/') && (*ptr!=':') ) ptr--;
  523.         /* copy file mask to "file_mask" variable */
  524.           strcpy(file_mask,(ptr+1));
  525.         /* truncate file mask from "drive" variable */
  526.           *(ptr+1)='\0';
  527.         /* don't forget to trunc the "/" */
  528.           if (*ptr=='/') *ptr='\0';
  529.         }
  530.       else  /* Get drive from system */
  531.         {
  532.           strcpy(file_mask,drive);
  533.           if (getcd(0,drive)!=0) exit(50);  /* die silently */
  534.         /* Cut path off - we only want the volume name */
  535.           ptr=strchr(drive,':');
  536.           if (ptr) *(++ptr)='\0';
  537.         }
  538.  
  539.     /* be a little user friendly */
  540.       conwrite("\nSearching for file \"");
  541.       conwrite(file_mask);
  542.       conwrite("\" on drive \"");
  543.       conwrite(drive);
  544.       conwrite("\"\n\n");
  545.  
  546.     /* actually do the search */
  547.       ptr=strchr(drive,':');
  548.       match_cnt=dtree(drive,file_mask, ( (*(++ptr)!='\0') ? (1) : (0) ) );
  549.  
  550.     /* clear the last searching message */
  551.       if (!bequiet) CLREOL;
  552.  
  553.     /* process the result */
  554.       switch(match_cnt)
  555.         {
  556.           case -1:  /* a ctrl-? break occured */
  557.             {
  558.               conwrite("**break\n\n");
  559.               exit(0);
  560.             }
  561.           case 0:
  562.             {
  563.               conwrite("No matches found.\n\n");
  564.               break;
  565.             }
  566.           default:
  567.             {
  568.               conwrite("\nFound ");
  569.               conwrite_int(match_cnt);
  570.               conwrite(" match");
  571.               conwrite( ( (match_cnt==1) ? (".\n\n") : ("es.\n\n") ) );
  572.               break;
  573.             }
  574.         }
  575.     }
  576.   while(*args!='\0');
  577. }
  578.  
  579.