home *** CD-ROM | disk | FTP | other *** search
/ Frozen Fish 1: Amiga / FrozenFish-Apr94.iso / bbs / alib / d1xx / d102 / match_stuff.lha / Match_Stuff / Mat.c < prev    next >
C/C++ Source or Header  |  1987-09-06  |  17KB  |  545 lines

  1. /* Mat -- string and file matching filter  87:8:15 */
  2. /* Copyright (C) 1987 Peter Goodeve */
  3.  
  4.  
  5. #include <stdio.h>
  6. #include "exec/types.h"
  7. #include "exec/memory.h"
  8. #include "libraries/DOS.h"
  9. #include "libraries/DOSextens.h"
  10.  
  11. #include "splicer.h"
  12.  
  13. /* CmplPat() returns this bit set if pattern is "sliced",
  14.  * and therefore template is needed: */
  15. #define CODE_SLICE 128
  16.  
  17. /* This structure is used to keep track of subdirectory and file names
  18.  * and patterns as they are scanned for matches: */
  19. struct FileRef {
  20.     struct FileInfoBlock fileblock; /* declared first so it will be LW-A */
  21.     ULONG   currlock,       /* lock for PARENT directory to this segment */
  22.             flags;
  23.     char    *pathname,      /* remainder of path name from this point */
  24.             chunkpat[64],   /* pattern specified for this file or dir */
  25.             chunkaux[64];   /* auxiliary vector for pattern match */
  26.     struct FileRef *nextref, *prevref;  /* list pointers */
  27. };
  28.  
  29. #define HAVELOCK 1
  30. #define NEWLOCK 2
  31. #define DONETOP 4
  32. #define ISDEVICE 16
  33. #define ISPATTERN 256
  34.  
  35. #define MATCH_NODIRS 256
  36. #define MATCH_NEEDPAT 128
  37. #define MATCH_NEEDTMPLT 64
  38. #define MATCH_TEXT (0 | MATCH_NODIRS |MATCH_NEEDPAT)
  39. #define MATCH_NAMES (1 | MATCH_NEEDTMPLT)
  40. #define MATCH_IMMED (2 | MATCH_NEEDPAT)
  41. #define JOIN_FILES (3 | MATCH_NODIRS)
  42.  
  43.  
  44. int casesens = TRUE, newlines = TRUE, firstonly = FALSE;
  45. int matchmode = MATCH_TEXT;
  46. int success = FALSE;
  47.  
  48.  
  49. ULONG maindir;
  50.  
  51. /* current file references (for sliceset): */
  52. char LocalName[64], FileDir[120];
  53.  
  54. UBYTE filecuts[33]; /* only need one of these */
  55.  
  56. /* Pointers to ends of File reference list
  57.  * (full Kernel list structure wasn't worth it here...)
  58.  */
  59. struct FileRef *firstref, *endref;
  60.  
  61. int builderr = 0; /* set if bad directory reference pattern */
  62.  
  63. /*
  64.  * buildrefs -- procedure to allocate FileRefs for the segments
  65.  *              of a file path pattern.
  66.  *              Calls itself recursively until the complete path
  67.  *              specification has been processed.
  68.  */
  69. struct FileRef *buildrefs(pathname, prev)
  70.  char *pathname; struct FileRef *prev;
  71. {
  72.     char *pp, *cp;
  73.     struct FileRef *tref;
  74.     int res;
  75.     tref = AllocMem(sizeof(struct FileRef),MEMF_PUBLIC | MEMF_CLEAR);
  76.     if (!tref) {
  77.         freerefs(firstref);
  78.         exit(55);
  79.     }
  80.     endref = tref;
  81.     if (!(tref->prevref = prev)) {
  82.         *filecuts = builderr = 0;
  83.         firstref = tref;
  84.     }
  85.     pp = tref->pathname = pathname;
  86.     cp = tref->chunkpat;
  87.     while (*pp && *pp != '/' && *pp != ':')
  88.         *cp++ = (*pp >= 'a' && *pp <= 'z') ? *pp++ + ('A' - 'a') : *pp++;
  89.     if (*pp == ':') {
  90.         tref->flags |= ISDEVICE;
  91.         *cp++ = ':';
  92.         if (tref->prevref /* not first segment!*/) {
  93.             builderr = 2;
  94.             return tref;
  95.         }
  96.     }
  97.     else if (pp == pathname)
  98.         *cp++ = '/';
  99.     *cp = '\0';
  100.     res = CmplPat(tref->chunkpat, tref->chunkaux);
  101.     if (res > 1) tref->flags |= ISPATTERN;
  102.     if (!res || ((res & CODE_SLICE) && tref->nextref)) {
  103.         builderr = 1;
  104.         return tref;
  105.     }
  106.     if (*pp)
  107.         tref->nextref = buildrefs(++pp, tref);
  108.     return tref;
  109. }
  110.  
  111. /* freerefs --  procedure to return the memory used by the
  112.  *              chain of FileRefs to the free list.
  113.  *              Calls itself recursively until all items
  114.  *              in the chain have been freed.
  115.  */
  116. freerefs(ref) struct FileRef *ref;
  117. {
  118.     if (!ref) return;
  119.     firstref = endref = NULL;
  120.     if (ref->flags & NEWLOCK) UnLock(ref->currlock);
  121.     freerefs(ref->nextref);
  122.     FreeMem(ref,sizeof(struct FileRef));
  123. }
  124.  
  125. /*
  126.  * nextlock --  procedure to get a lock for the next higher level;
  127.  *              on the first call, it calls itself recursively until
  128.  *              the top level of the File Reference chain is reached,
  129.  *              at which point it obtains a lock for the current parent,
  130.  *              then returns a lock either for the specific name or
  131.  *              for the first match it finds to the specified pattern;
  132.  *              each level then repeats the process until a final
  133.  *              filename is reached.
  134.  *              Subsequent calls will return a new match if there is
  135.  *              one, otherwise will make recursive calls as far as
  136.  *              necessary to get a new branch of the pattern tree.
  137.  *              [Note that the bottom level match MUST be a file
  138.  *               -- it will not return a directory name.]
  139.  */
  140. ULONG nextlock(ref) struct FileRef *ref;
  141. {
  142.     struct FileInfoBlock *fbp;
  143.     char matchstr[108], *pp, *cp;
  144.     ULONG lk, succ, thislock;
  145.     fbp = &ref->fileblock;
  146.     do {
  147.       if (ref->flags & HAVELOCK) {
  148.        CurrentDir(ref->currlock);
  149.        if (!(ref->flags & ISPATTERN)) {
  150.            thislock = Lock(ref->chunkpat, ACCESS_READ);
  151.            ref->flags ^= HAVELOCK; /* this way only once..*/
  152.            if (thislock && Examine(thislock,fbp)) {
  153.                return thislock;
  154.            }
  155.        }
  156.        else {
  157.            lk = ref->currlock;
  158.            while (succ = ExNext(lk, fbp)) {
  159.              if( ref->nextref /* not bottom level*/ ?
  160.                        fbp->fib_EntryType < 0 /* ignore files */
  161.                   : ((matchmode & MATCH_NODIRS) &&
  162.                        fbp->fib_EntryType > 0 /* ignore dirs */))
  163.                       continue;
  164.              pp = fbp->fib_FileName;
  165.              cp = matchstr;
  166.              while (*cp++ = (*pp >= 'a' && *pp <= 'z') ?
  167.                            *pp++ + ('A' - 'a') : *pp++) /* loop */;
  168.              if (SMatch(ref->chunkpat, ref->chunkaux,
  169.                          matchstr, filecuts) )
  170.                 break;
  171.             }    ; /* loop until found or exhausted */
  172.            if (succ) {
  173.                return Lock(fbp->fib_FileName, ACCESS_READ);
  174.            }
  175.            else {
  176.            ref->flags ^= HAVELOCK; /* lock no longer valid ..*/
  177.            }
  178.        }
  179.       } /**[END if (..HAVELOCK) ]**/
  180.  
  181.       /* continues if no current valid lock */
  182.       if (ref->prevref) {
  183.           if (ref->flags & NEWLOCK) UnLock(ref->currlock);
  184.           ref->flags &= ~(HAVELOCK | NEWLOCK);
  185.           if (thislock = nextlock(ref->prevref)) {
  186.               ref->currlock = thislock;
  187.               ref->flags |= HAVELOCK | NEWLOCK;
  188.           }
  189.       }
  190.       else if (!(ref->flags & DONETOP)) {
  191.           ref->currlock = CurrentDir(0);
  192.           CurrentDir(ref->currlock);
  193.           ref->flags |= (HAVELOCK | DONETOP);
  194.       }
  195.       if (ref->flags & HAVELOCK) {
  196.           Examine(ref->currlock, fbp);
  197.       }
  198.     } while (ref->flags & HAVELOCK);
  199.     return NULL;
  200. }
  201.  
  202. /*
  203.  * findfile --  procedure to scan the File Reference chain
  204.  *              to set a parent directory and get the name of
  205.  *              the next file to be accessed.
  206.  *              Calls nextlock to do most of the work.
  207.  *              Also sets up Directory and Filename strings
  208.  *              for use by the splicing section.
  209.  */
  210. char *findfile(ref) struct FileRef *ref;
  211. {
  212.     ULONG thislock;
  213.     char *thisname = endref->fileblock.fib_FileName;
  214.     char *cp, *pp;
  215.     struct FileRef *rp, *nrp;
  216.     thislock = nextlock(endref); /* go up chain to find a matching file */
  217.     if (!thislock)
  218.         return NULL;
  219.  
  220.     UnLock(thislock);
  221.     strcpy(LocalName, thisname);
  222.     cp = FileDir;
  223.     for (rp = ref; nrp = rp->nextref; rp = nrp) {
  224.         pp = rp->fileblock.fib_FileName;
  225.         while (*cp = *pp++) cp++;
  226.         *cp++ = (rp->flags & ISDEVICE) ? ':'
  227.                 : nrp->nextref ? '/' : '\0';
  228.     }
  229.     *cp = '\0';
  230.     return thisname;
  231. }
  232.  
  233. /*
  234.  * openfile --  procedure to open the file found by findfile
  235.  */
  236. FILE *openfile(fname) char *fname;
  237. {
  238.     FILE *fp;
  239.     fp = fopen(fname,"r");
  240.     if (!fp) {
  241.         fputs(fname,stderr);
  242.         fputs(" -- Couldn't open file!\n",stderr);
  243.     }
  244.     return fp; /* regardless */
  245. }
  246.  
  247. /***************************************************************/
  248.  
  249. main(argc,argv) int argc; char **argv;
  250. {
  251.     char *pat = NULL, aux[256], *splice = NULL, *failspl = NULL,
  252.          *origpat = NULL, upperpat[256];
  253.     int cmplcode = 0;
  254.     char *fname;
  255.     struct FileRef *ref;
  256.     FILE *fp;
  257.  
  258. /*    extern int DEBmode;
  259.     DEBmode = 0;          */
  260.  
  261.     maindir = CurrentDir(0);
  262.     CurrentDir(maindir); /* there must be a neater way to do this ? */
  263.  
  264.     if (argc < 3) {
  265.         fputs( "format is:   MAT <pattern> [<template>] filenames...\n",
  266.               stderr);
  267.         exit(0);
  268.     }
  269.  
  270.     ++argv; /* skip over program name */
  271.     argc--;
  272.     while (checkarg(&argc, &argv)) /* loop */;
  273.     if (matchmode & MATCH_NEEDPAT) {
  274.         origpat = *argv++;
  275.         argc--;
  276.         if (!(cmplcode = CmplPat(origpat,aux))) {
  277.             fputs("badly formed pattern\n",stderr);
  278.             exit(20);
  279.         }
  280.         uppercase(origpat, upperpat); /* best to do it now -- in case...*/
  281.     }
  282.  
  283.     if ((cmplcode & CODE_SLICE) || (matchmode & MATCH_NEEDTMPLT)) {
  284.         if (argc-- < 2) {
  285.             fputs( "this format requires a template argument...\n",
  286.                   stderr);
  287.             exit(20);
  288.         }
  289.         splice = *argv++;
  290.         for (failspl = splice; *failspl;)
  291.             if (*failspl++ == '^' && *failspl++ == '|') break;
  292.         if(*failspl) *(failspl-2) = '\0';
  293.         else failspl = NULL;
  294.     }
  295.  
  296.     for ( ;  argc;  argc--, argv++) {
  297.         while (checkarg(&argc, &argv)); /* loop */
  298.         if (!argc /* might get keyw. at end of string */) break;
  299.         if (casesens || !origpat) pat = origpat;
  300.         else pat = upperpat;
  301.         if (matchmode == MATCH_IMMED) {
  302.             matchnames(*argv,pat,aux,splice,failspl);
  303.             continue; /* go into short loop! */
  304.         }
  305.         ref = buildrefs(*argv, NULL);
  306.         if (builderr)
  307.             fputs("Bad Filename Pattern\n", stderr);
  308.         else while (fname = findfile(ref)) {
  309.             if (matchmode == MATCH_NAMES) {
  310.                 if (!splice) {
  311.                     fputs("MUST have a template!\n", stderr);
  312.                     CurrentDir(maindir);
  313.                     exit(20);
  314.                 }
  315.                 matchnames(fname,pat,aux,splice,failspl);
  316.             }
  317.             else {
  318.                 fp = openfile(fname);
  319.                 if (matchmode == JOIN_FILES)
  320.                     copyfile(fp);
  321.                 else
  322.                     matchfile(fp, pat, aux, splice, failspl);
  323.                 fclose(fp);
  324.             }
  325.         }
  326.         freerefs(ref);
  327.         CurrentDir(maindir);
  328.     }
  329.     exit(success ? 0 : 5); /* returns 0 if success, otherwise WARN */
  330. }
  331.  
  332. /* numeric string used to maintain sequential index number of matches: */
  333. char findxnum[6] = "00000",
  334.      *endfindx = &findxnum[4];
  335.  
  336.  
  337. /*
  338.  * matchfile -- procedure to search for matching lines in the currently
  339.  *              open file.  If there is no template, any matching line is
  340.  *              sent to the standard output, otherwise the pieces specified
  341.  *              by the template are spliced together into a new output line.
  342.  */
  343. matchfile(fp, pat, aux, template, ftemplate)
  344.    FILE *fp; char *pat, *aux, *template, *ftemplate;
  345. {
  346.     char inline[257], upperline[256], cuts[33],
  347.          linenum[10], *endnum = &linenum[4], *lnp,
  348.          pieces[256], compline[300],
  349.          *linep, *matchp;
  350.     struct CutsRef sliceset[15], *fsliceset; /* probably should be static */
  351.     char *Splice();
  352.     int p;
  353.     strcpy(linenum, "00000");
  354.     for (p = 0; p < 15; p++) sliceset[p].Mode = 0;
  355.     sliceset[5].ID = 'O';
  356.     sliceset[5].Segment = inline;
  357.     sliceset[6].ID = 'N';
  358.     sliceset[6].Segment = linenum;
  359.     sliceset[7].ID = 'F';
  360.     sliceset[7].Segment = LocalName;
  361.     sliceset[8].ID = 'D';
  362.     sliceset[8].Segment = FileDir;
  363.     sliceset[9].ID = 'I';
  364.     sliceset[9].Segment = findxnum;
  365.     sliceset[10].ID = 'B';
  366.     sliceset[10].Segment = "\n";
  367.     sliceset[11].ID = 'Q';
  368.     sliceset[11].Segment = "\"";
  369.     sliceset[12].ID = 0; /* until we add more stuff.. */
  370.     fsliceset = &sliceset[5]; /* this set may be used if no match */
  371.     while (fgets(matchp = inline, 256, fp)) {
  372.         p = strlen(inline);
  373.         for (lnp = endnum; lnp >= linenum && ++(*lnp) > '9';)
  374.              *lnp-- = '0';
  375.         if (inline[--p] == '\n') inline[p] = '\0'; /* note we move back */
  376.         if (!casesens)
  377.             uppercase(inline, matchp = upperline);
  378.         if (SMatch(pat, aux, matchp, cuts)) {
  379.             success = TRUE;
  380.             for (lnp = endfindx; lnp >= findxnum && ++(*lnp) > '9';)
  381.                  *lnp-- = '0';
  382.             if (template) {
  383.                 Slice(inline, cuts, pieces, sliceset);
  384.                 linep = Splice(sliceset, template, compline);
  385.             }
  386.             else linep = inline;
  387.             fputs(linep, stdout);
  388.             if (newlines) fputc('\n',stdout);
  389.             if (firstonly) break;
  390.         }
  391.         else
  392.             if (ftemplate) { /* may specify output if match fails, too */
  393.                 fputs( Splice(fsliceset, ftemplate, compline), stdout);
  394.                 if (newlines) fputc('\n', stdout);
  395.             }
  396.     }
  397. }
  398.  
  399.  
  400. /*
  401.  * matchnames -- procedure to match literal strings -- either filenames
  402.  *              or literal arguments in the command line. Action is
  403.  *              similar to matchfile, but slicing information can be
  404.  *              obtained from the filename pattern rather than a
  405.  *              separately specified one and splicing may occur without
  406.  *              any slices (using implicitly defined pieces).
  407.  */
  408. matchnames(fname, pat, aux, template, ftemplate)
  409.    char *fname, *pat, *aux, *template, *ftemplate;
  410. {
  411.     char cuts[33], pieces[256], compline[300], *linep, *matchp;
  412.     struct CutsRef sliceset[15], *fsliceset; /* probably should be static */
  413.     char uppername[64];
  414.     char *Splice(), *lnp;
  415.     int p;
  416.     for (p = 0; p < 15; p++) sliceset[p].Mode = 0;
  417.     sliceset[5].ID = 'O';
  418.     sliceset[5].Segment = fname;
  419.     sliceset[6].ID = 'N';
  420.     sliceset[6].Segment = findxnum; /* for convenience (?!) */
  421.     sliceset[7].ID = 'F';
  422.     sliceset[7].Segment = LocalName;
  423.     sliceset[8].ID = 'D';
  424.     sliceset[8].Segment = FileDir;
  425.     sliceset[9].ID = 'I';
  426.     sliceset[9].Segment = findxnum;
  427.     sliceset[10].ID = 'B';
  428.     sliceset[10].Segment = "\n";
  429.     sliceset[11].ID = 'Q';
  430.     sliceset[11].Segment = "\"";
  431.     sliceset[12].ID = 0; /* until we add more stuff.. */
  432.     fsliceset = &sliceset[5];
  433.     *cuts = '\0';
  434.     if (!casesens)
  435.             uppercase(fname, matchp = uppername);
  436.     else matchp = fname;
  437.     if (!pat /* may be missing */
  438.         || SMatch(pat, aux, matchp, cuts)) {
  439.         success = TRUE;
  440.         for (lnp = endfindx; lnp >= findxnum && ++(*lnp) > '9';)
  441.              *lnp-- = '0';
  442.         if (template) {
  443.             Slice(fname, *cuts ? cuts : filecuts, pieces, sliceset);
  444.             linep = Splice(sliceset, template, compline);
  445.         }
  446.         else linep = fname;
  447.         fputs(linep, stdout);
  448.         if (newlines) fputc('\n',stdout);
  449.     }
  450.     else
  451.         if (ftemplate) {
  452.             fputs( Splice(fsliceset, ftemplate, compline), stdout);
  453.             if (newlines) fputc('\n', stdout);
  454.         }
  455. }
  456.  
  457. /*
  458.  * copyfile --  simply copies current file to the standard output
  459.  */
  460.  
  461. #define BUFLEN 512
  462.  
  463. copyfile(fp) FILE *fp;
  464. {
  465.     UBYTE buffer[BUFLEN];
  466.     ULONG fhin, fhout;
  467.     int xfbytes;
  468.     fhin = fileno(fp);
  469.     fhout = fileno(stdout);
  470.     while ((xfbytes = read(fhin, buffer, BUFLEN)) > 0)
  471.         write(fhout, buffer, xfbytes);
  472.     success = TRUE;
  473. }
  474.  
  475.  
  476. /*
  477.  * Special Argument Recognition Section
  478.  *
  479.  * checkarg --  matches the argument against argpat, and takes the
  480.  *              appropriate action if there is a match.
  481.  *              Note how it uses the slicing information returned
  482.  *              by SMatch as a convenient way of determining the
  483.  *              actual meaning of the argument.
  484.  */
  485.  
  486. char argpat[] =
  487.    "NOCASE^|CASE^|(FILES|F)^|STRING^|(JOIN|J)^|FIRST^|ALL^|NOLINES^|LINE^";
  488. #define NOCASE 7
  489. #define CASE 13
  490. #define FILES 24
  491. #define STRING 32
  492. #define JOIN 42
  493. #define FIRST 49
  494. #define ALL 54
  495. #define NOLINES 63
  496. #define LINE 69
  497. UBYTE argaux[72]; /* lengthen as necessary! */
  498.  
  499. checkarg(pargc,pargv) int *pargc; char **pargv[];
  500. {
  501.     char argcuts[33], upperarg[256], *cutp = argcuts;
  502.     if (!*pargc) return FALSE; /* don't run off the end... */
  503.     CmplPat(argpat, argaux); /* ...pure laziness */
  504.     uppercase(**pargv, upperarg);
  505.     if (SMatch(argpat, argaux, upperarg, argcuts)) {
  506.         while (!cutp[1]) cutp += 2; /* skip phony subpatterns (e.g. "F") */
  507.         switch((int)(*cutp)) {
  508.         case NOCASE: casesens = FALSE;
  509.                      break;
  510.         case CASE:   casesens = TRUE;
  511.                      break;
  512.         case FILES:  matchmode = MATCH_NAMES;
  513.                      break;
  514.         case STRING: matchmode = MATCH_IMMED;
  515.                      break;
  516.         case JOIN:   matchmode = JOIN_FILES;
  517.                      break;
  518.         case FIRST:  firstonly = TRUE;
  519.                      break;
  520.         case ALL:    firstonly = FALSE;
  521.                      break;
  522.         case NOLINES: newlines = FALSE;
  523.                      break;
  524.         case LINE:   newlines = TRUE;
  525.                      fputc('\n', stdout);
  526.                      break;
  527.         }
  528.         (*pargv)++;
  529.         (*pargc)--;
  530.         return TRUE;
  531.     }
  532.     return FALSE;
  533. }
  534.  
  535. /*
  536.  * uppercase -- translates supplied source string to upper case
  537.  *              (yes, I know there is a library function...)
  538.  */
  539. uppercase(src,dest) char *src, *dest;
  540. {
  541.     while (*dest++ = (*src >= 'a' && *src <= 'z') ?
  542.             *src++ + ('A' - 'a') : *src++) /* loop */;
  543. }
  544.  
  545.