home *** CD-ROM | disk | FTP | other *** search
/ Shareware Overload / ShartewareOverload.cdr / progm / cpgms.zip / CHG.C < prev    next >
C/C++ Source or Header  |  1985-12-11  |  26KB  |  954 lines

  1. /*
  2.  *      This program is based on GREP from the following sources:
  3.  *
  4.  * The  information  in  this  document  is  subject  to  change
  5.  * without  notice  and  should not be construed as a commitment
  6.  * by Digital Equipment Corporation or by DECUS.
  7.  *
  8.  * Neither Digital Equipment Corporation, DECUS, nor the authors
  9.  * assume any responsibility for the use or reliability of  this
  10.  * document or the described software.
  11.  *
  12.  *      Copyright (C) 1980, DECUS
  13.  *
  14.  *
  15.  * General permission to copy or modify, but not for profit,  is
  16.  * hereby  granted,  provided that the above copyright notice is
  17.  * included and reference made to  the  fact  that  reproduction
  18.  * privileges were granted by DECUS.
  19.  *
  20.  */
  21.  
  22. #include "stdio.h"
  23. /*
  24.  * change.
  25.  *
  26.  */
  27.  
  28. #define QUOTE '@'
  29. #define BLANK '#'
  30. #define ESCAPE '\\'
  31.  
  32. char    *documentation[] = {
  33. "change searches a file for a given pattern and replaces it with ",
  34. " another pattern.  Execute by",
  35. "   chg [flags] regular_expression substitue_expression file_list",
  36. "",
  37. "Flags are single characters preceeded by '-':",
  38. "    -e  delete empty lines",
  39. "    -o  only output lines with matches",
  40. "",
  41. "The file_list is a list of files (wildcards are acceptable).",
  42. "If mulitple files are given, all are concatenated on the std output.",
  43. "If no files are given the std input is read.",
  44. "",
  45. 0 };
  46.  
  47.  
  48. char    *patdoc[] = {
  49. "The regular_expression defines the pattern to search for.  Upper- and",
  50. "lower-case are always ignored.  Blank lines never match.  The expression",
  51. "should be quoted to prevent file-name translation.",
  52. "x      An ordinary character (not mentioned below) matches that character.",
  53. "'\\'   The backslash escapes tab, backspace, formfeed, newline, and",
  54. "'          carriage return with \\t, \\b, \\f, \\n, and \\r.",
  55. "'@'    The at-sign quotes any character.  \"@$\" matches a dollar-sign.",
  56. "'^'    A circumflex at the beginning of an expression matches the",
  57. "       beginning of a line.",
  58. "'$'    A dollar-sign at the end of an expression matches the end of a line.",
  59. "'.'    A period matches any character except \"new-line\".",
  60. "'#'    An \"pound-sign\" matches one blank.",
  61. "':a'   A colon matches a class of characters described by the following",
  62. "':d'     character.  \":a\" matches any alphabetic, \":d\" matches digits,",
  63. "':n'     \":n\" matches alphanumerics, \": \" matches spaces, tabs, and",
  64. "':#'     other control characters, such as new-line.",
  65. "'*'    An expression followed by an asterisk matches zero or more",
  66. "       occurrances of that expression: \"fo*\" matches \"f\", \"fo\"",
  67. "       \"foo\", etc.",
  68. "'+'    An expression followed by a plus sign matches one or more",
  69. "       occurrances of that expression: \"fo+\" matches \"fo\", etc.",
  70. "'-'    An expression followed by a minus sign optionally matches",
  71. "       the expression.",
  72. "'[]'   A string enclosed in square brackets matches any character in",
  73. "       that string, but no others.  If the first character in the",
  74. "       string is a circumflex, the expression matches any character",
  75. "       except \"new-line\" and the characters in the string.  For",
  76. "       example, \"[xyz]\" matches \"xx\" and \"zyx\", while \"[^xyz]\"",
  77. "       matches \"abc\" but not \"axb\".  A range of characters may be",
  78. "       specified by two characters separated by \"-\".  Note that,",
  79. "       [a-z] matches alphabetics, while [z-a] never matches.",
  80. "'{}'   Group regular expressions into one pattern element: f{oo}+",
  81. "       will only match an 'f' followed by an even number of 'o's.",
  82. "       Braces may be nested.  They are not allowed in square brackets.",
  83. "The concatenation of regular expressions is a regular expression.",
  84. "",
  85. "",
  86. 0};
  87.  
  88. char    *subdoc[] = {
  89. "The substitution_expression which which to replace the matched pattern.",
  90. "Upper- and lower-case are significant in substitution.",
  91. "x      An ordinary character (not mentioned below) is inserted as-is.",
  92. "'\\'   The backslash escapes tab, backspace, formfeed, newline, and",
  93. "'          carriage return with \\t, \\b, \\f, \\n, and \\r.",
  94. "'@'    The at-sign quotes any character.  \"@&\" matches a 'and-sign'.",
  95. "'.'    Represents the null string.  Used to delete the whole pattern.",
  96. "'#'    An \"pound-sign\" matches one blank.",
  97. "'&'    Represents the entire matched pattern.  If the pattern was",
  98. "       change f.o x& , then an x would be added prior to the area ",
  99. "       matched (before the f).  It is the same as &0 (see below).",
  100. "'&1' through '&80'   These represent the nth field in braces.  &0 is ",
  101. "       a special case which represents the whole matched pattern.  For",
  102. "       example:  chg {:d:d}{:a} &2&1 , would move the alpha character",
  103. "       in front of the numbers.  A brace in a *, +, or - is counted as",
  104. "       one field at its maximum match.  It may be empty.  To figure the ",
  105. "       number for a field, start at the left of the pattern with 0 and ",
  106. "       add one for each brace encountered. (&0 is the same as & alone).",
  107. ""
  108. 0};
  109.  
  110. #define MSDOS 1
  111. #define DeSmet 1
  112.  
  113.  
  114. #ifndef stdin
  115. #define stdin STDIN
  116. #define stdout STDOUT
  117. #define stderr STDERR
  118. #endif
  119.  
  120. #define LMAX    512
  121. #define PMAX    256
  122. #define OMAX    512
  123.  
  124. #define CHAR    1
  125. #define BOL     2
  126. #define EOL     3
  127. #define ANY     4
  128. #define CLASS   5
  129. #define NCLASS  6
  130. #define STAR    7
  131. #define PLUS    8
  132. #define MINUS   9
  133. #define ALPHA   10
  134. #define DIGIT   11
  135. #define NALPHA  12
  136. #define PUNCT   13
  137. #define RANGE   14
  138. #define ENDPAT  15
  139. #define BRACE   16
  140. #define ENDBRACE  17
  141.  
  142. #define ANDMAX 80       /* may only be two digits long */
  143. #define AND    18
  144.  
  145. struct {               /* store & matches here */
  146.     char *bstart;
  147.     char *bend;
  148. } braces[ANDMAX+1];
  149. int brndx = 0;         /* index to braces arrary */
  150.  
  151. int     nfile;
  152. int     eflag = 0;
  153. int     oflag = 0;
  154.  
  155. int     debug   =       0;         /* Set for debug code      */
  156.  
  157. char    *pp, *subp;     /* pattern pointers */
  158.  
  159. char    file_name[81];
  160.  
  161. char    lbuf[LMAX];
  162. char    pbuf[PMAX];
  163. char    sbuf[PMAX];
  164. char    obuf[OMAX];
  165. char    wrkbuf[80];
  166.  
  167. /*******************************************************/
  168.  
  169. main(argc, argv)
  170. char *argv[];
  171. {
  172.    register char   *p;
  173.    register int    c, i;
  174.    int             gotpattern, gotsubpat;
  175.    int             gotcha;
  176.    char  *fexpnd();
  177.  
  178.    FILE            *f;
  179.  
  180.    if (argc <= 1)
  181.       usage("No arguments");
  182.    if (argc == 2 && argv[1][0] == '?' && argv[1][1] == 0) {
  183.       help(documentation);
  184.       help(patdoc);
  185.       help(subdoc);
  186.       return;
  187.       }
  188.    nfile = argc-1;
  189.    gotsubpat = gotpattern = 0;
  190.    for (i=1; i < argc; ++i) {
  191.       p = argv[i];
  192.       if (*p == '-') {
  193.          ++p;
  194.          while (c = *p++) {
  195.             switch(tolower(c)) {
  196.  
  197.             case '?':
  198.                help(documentation);
  199.                break;
  200.  
  201.             case 'D':
  202.             case 'd':
  203.                ++debug;
  204.                break;
  205.  
  206.             case 'E':
  207.             case 'e':
  208.                ++eflag;
  209.                break;
  210.  
  211.             case 'O':
  212.             case 'o':
  213.                ++oflag;
  214.                break;
  215.  
  216.             default:
  217.                usage("Unknown flag");
  218.             }
  219.          }
  220.          argv[i] = 0;
  221.          --nfile;
  222.       } else if (!gotpattern) {
  223.          compile(p);
  224.          argv[i] = 0;
  225.          ++gotpattern;
  226.          --nfile;
  227.       } else if (!gotsubpat) {
  228.          compsub(p);
  229.          argv[i] = 0;
  230.          ++gotsubpat;
  231.          --nfile;
  232.       }
  233.    }
  234.    if (!gotpattern)
  235.       usage("No search pattern");
  236.    if (!gotsubpat)
  237.       usage("No substitution pattern");
  238.    if (nfile == 0)
  239.       change(stdin, 0);
  240.    else {
  241.       for (i=1; i < argc; ++i) {
  242.          if (argv[i])
  243.              while (p = fexpnd(argv[i])) 
  244.                  if (p = argv[i]) 
  245.                     if ((f=fopen(p, "r")) == NULL)
  246.                        cant(p);
  247.                     else {
  248.                        change(f, p);
  249.                        fclose(f);
  250.                     }
  251.        }
  252.     }
  253. }
  254.  
  255. /*******************************************************/
  256.  
  257. usage(s)
  258. char    *s;
  259. {
  260.    puts("?CHANGE-E-");
  261.    puts(s);
  262.    puts("\n");
  263.    puts("Usage: chg [-eo] pattern subst [file ...].  chg ? for help\n");
  264.    exit(1);
  265. }
  266.  
  267.  
  268.  
  269. /*******************************************************/
  270.  
  271.  
  272. compile(source)
  273. char       *source;   /* Pattern to compile         */
  274. /*
  275.  * Compile the pattern into global pbuf[]
  276.  */
  277. {
  278.    register char  *s;         /* Source string pointer     */
  279.    register int   c;          /* Current character         */
  280.    int            o;          /* Temp                      */
  281.    char           *pcompil();  /* Compile braced group     */
  282.  
  283.    pp = pbuf;
  284.    s = source;
  285.    if (debug) {
  286.       puts("Pattern = \"");
  287.       puts(s);
  288.       puts("\"\n");
  289.      }
  290.    s = pcompil(s);
  291.    if (*(--s) == '}')
  292.         badpat("Unmatched close brace",source,s);
  293. }
  294.  
  295. /*******************************************************/
  296.  
  297. char *
  298. pcompil(source)
  299. char       *source;   /* Pattern to compile         */
  300. /*
  301.  * Compile (recursively) the pattern into global pbuf[]
  302.  */
  303. {
  304.    register char  *s;         /* Source string pointer     */
  305.    char           *lp;        /* Last pattern pointer      */
  306.    register int   c;          /* Current character         */
  307.    int            o;          /* Temp                      */
  308.    char           *spp;       /* Save beginning of pattern */
  309.    char           *cclass();  /* Compile class routine     */
  310.    char           *sbrace();  /* Compile braced group      */
  311.    char           spchr();     /* Translate char to escaped char */
  312.  
  313.    s = source;
  314.    while (c = *s++) {
  315.       /*
  316.        * STAR, PLUS and MINUS are special.
  317.        */
  318.       if (c == '*' || c == '+' || c == '-') {
  319.          if (pp == pbuf ||
  320.               (o=pp[-1]) == BOL ||
  321.               o == EOL ||
  322.               o == STAR ||
  323.               o == PLUS ||
  324.               o == MINUS)
  325.             badpat("Illegal occurrance op.", source, s);
  326.          store(ENDPAT);
  327.          store(ENDPAT);
  328.          spp = pp;               /* Save pattern end     */
  329.          while (--pp > lp)       /* Move pattern down    */
  330.             *pp = pp[-1];        /* one byte             */
  331.          *pp =   (c == '*') ? STAR :
  332.             (c == '-') ? MINUS : PLUS;
  333.          pp = spp;               /* Restore pattern end  */
  334.          continue;
  335.       }
  336.       /*
  337.        * All the rest.
  338.        */
  339.       lp = pp;         /* Remember start       */
  340.       switch(c) {
  341.  
  342.       case BLANK:
  343.          store(CHAR);
  344.         store(' ');
  345.          break;
  346.  
  347.       case '^':
  348.          store(BOL);
  349.          break;
  350.  
  351.       case '$':
  352.          store(EOL);
  353.          break;
  354.  
  355.       case '.':
  356.          store(ANY);
  357.          break;
  358.  
  359.       case '}':         /* brace pairs group pattern strings */
  360.          return(s);
  361.  
  362.       case '{':         /* brace pairs group pattern strings */
  363.          s = sbrace(source, s);
  364.          break;
  365.  
  366.       case '[':
  367.          s = cclass(source, s);
  368.          break;
  369.  
  370.       case ':':
  371.          if (*s) {
  372.             c = *s++;
  373.             switch(tolower(c)) {
  374.  
  375.             case 'a':
  376.             case 'A':
  377.                store(ALPHA);
  378.                break;
  379.  
  380.             case 'd':
  381.             case 'D':
  382.                store(DIGIT);
  383.                break;
  384.  
  385.             case 'n':
  386.             case 'N':
  387.                store(NALPHA);
  388.                break;
  389.  
  390.             case BLANK:
  391.                store(PUNCT);
  392.                break;
  393.  
  394.             default:
  395.                badpat("Unknown : type", source, s);
  396.  
  397.             }
  398.             break;
  399.          }
  400.          else    badpat("No : type", source, s);
  401.  
  402.       case QUOTE:
  403.          if (*s) {
  404.                 c = *s++;
  405.                 store(CHAR);
  406.                 store(tolower(c));
  407.         }
  408.         break;
  409.  
  410.       case ESCAPE:
  411.          if (*s) {
  412.                 store(CHAR);
  413.                 store(spchr(tolower(c=*s++)));
  414.          }
  415.         break;
  416.  
  417.       default:
  418.          store(CHAR);
  419.          store(tolower(c));
  420.       }
  421.    }
  422.    store(ENDPAT);
  423.    store(0);                /* Terminate string     */
  424.    if (debug) {
  425.       for (lp = pbuf; lp < pp;) {
  426.          if ((c = (*lp++ & 0377)) < ' ') {
  427.             puts(itoa(c, wrkbuf));
  428.          }
  429.          else {
  430.             wrkbuf[0] = c;
  431.             wrkbuf[1] = '\0';
  432.             puts(wrkbuf);
  433.         }
  434.      }
  435.      puts("\n");
  436.    }
  437.    return(s);
  438. }
  439.  
  440. /*******************************************************/
  441.  
  442. compsub(source)
  443. char       *source;   /* Pattern to compile         */
  444. /*
  445.  * Compile the substitution patter into global sbuf[]
  446.  */
  447. {
  448.    register char  *s;         /* Source string pointer     */
  449.    register int   c, c2;      /* Current character         */
  450.    char            *lp;       /* temp string pointer       */
  451.    int            o;          /* Temp                      */
  452.    char           spchr();     /* Translate char to escaped char */
  453.  
  454.    s = source;
  455.    subp = sbuf;
  456.  
  457.    while (c = *s++) {
  458.  
  459.       switch(c) {
  460.  
  461.       case BLANK:
  462.          stores(CHAR);
  463.          stores(' ');
  464.          break;
  465.  
  466.       case '.':
  467.          break;         /* ignore it */
  468.  
  469.       case '&':
  470.          if ((o = atoi(s)) > brndx)     /* brndx is set by pcompil */
  471.               badpat("&nn > max", source, s);
  472.          stores(AND);
  473.          stores(o);
  474.          while (isdigit(*s))
  475.              s++;               /* skip over number */
  476.          break;
  477.  
  478.       case QUOTE:
  479.          if (*s) {
  480.                 c = *s++;
  481.                 stores(CHAR);
  482.                 stores(tolower(c));
  483.         }
  484.         break;
  485.  
  486.       case ESCAPE:
  487.          if (*s) {
  488.                 stores(CHAR);
  489.                 stores(spchr(tolower(c=*s++)));
  490.         }
  491.         break;
  492.  
  493.       default:
  494.          stores(CHAR);
  495.          stores(tolower(c));
  496.       }
  497.    }
  498.    stores(ENDPAT);
  499.    stores(0);                /* Terminate string     */
  500.    if (debug) {
  501.       puts("sub = ");
  502.       puts(itoa(subp - sbuf, wrkbuf));
  503.       puts("\n");
  504.       for (lp = sbuf; lp < subp;) {
  505.          if ((c = (*lp++ & 0377)) < ' ') 
  506.             puts(itoa(c, wrkbuf));
  507.          else {
  508.             wrkbuf[0] = c;
  509.             wrkbuf[1] = '\0';
  510.             puts(wrkbuf);
  511.         }
  512.      }
  513.      puts("\n");
  514.    }
  515.    return(s);
  516. }
  517.  
  518. /*******************************************************/
  519.  
  520. char spchr(inchar)
  521. char inchar;
  522. {
  523.         switch(inchar) {
  524.  
  525.                 case 't':
  526.                         return('\t');
  527.                 case 'b':
  528.                         return('\b');
  529.                 case 'f':
  530.                         return('\f');
  531.                 case 'n':
  532.                         return('\n');
  533.                 case 'r':
  534.                         return('\r');
  535.                 default:
  536.                         return(inchar);
  537.                 }
  538. }
  539.  
  540. /*******************************************************/
  541.  
  542. char *
  543. cclass(source, src)
  544. char       *source;   /* Pattern start -- for error msg.      */
  545. char       *src;      /* Class start           */
  546. /*
  547.  * Compile a class (within [])
  548.  */
  549. {
  550.    register char   *s;        /* Source pointer    */
  551.    register char   *cp;       /* Pattern start     */
  552.    register int    c;         /* Current character */
  553.    int             o;         /* Temp              */
  554.    char           spchr();     /* Translate char to escaped char */
  555.  
  556.    s = src;
  557.    o = CLASS;
  558.    if (*s == '^') {
  559.       ++s;
  560.       o = NCLASS;
  561.    }
  562.    store(o);
  563.    cp = pp;
  564.    store(0);                          /* Byte count      */
  565.    while ((c = *s++) && c!=']') {
  566.       if (c == QUOTE) {                /* Store quoted char    */
  567.          if ((c = *s++) == '\0')      /* Gotta get something  */
  568.             badpat("Class terminates badly", source, s);
  569.          else    store(tolower(c));
  570.       }
  571.       else if (c == ESCAPE) {
  572.                 if ((c = *s++) == '\0')
  573.                         badpat("Invalid Escape sequence", source, s);
  574.                 else
  575.                         store(spchr(c));
  576.         }
  577.       else if (c == '-' &&
  578.             (pp - cp) > 1 && *s != ']' && *s != '\0') {
  579.          c = pp[-1];             /* Range start     */
  580.          pp[-1] = RANGE;         /* Range signal    */
  581.          store(c);               /* Re-store start  */
  582.          c = *s++;               /* Get end char and*/
  583.          store(tolower(c));      /* Store it        */
  584.       }
  585.       else {
  586.          store(tolower(c));      /* Store normal char */
  587.       }
  588.    }
  589.    if (c != ']')
  590.       badpat("Unterminated class", source, s);
  591.    if ((c = (pp - cp)) >= 256)
  592.       badpat("Class too large", source, s);
  593.    if (c == 0)
  594.       badpat("Empty class", source, s);
  595.    *cp = c;
  596.    return(s);
  597. }
  598.  
  599.  
  600. /*******************************************************/
  601.  
  602. char *
  603. sbrace(source, src)
  604. char       *source;   /* Pattern start -- for error msg.      */
  605. char       *src;      /* Bracketed area start  */
  606. /*
  607.  * Compile a pattern withing {} (a grouping)
  608.  */
  609. {
  610.    register char   *s;        /* Source pointer    */
  611.    register int    c;         /* Current character */
  612.    int             o;         /* Temp              */
  613.  
  614.    s = src;
  615.    o = BRACE;
  616.    store(o);
  617.  
  618.    if (++brndx > ANDMAX)      /* save max used for compsub */
  619.        badpat("Too many braces",source,s);
  620.    s = pcompil(s);         /* call compile to finish pattern */
  621.    if (s[-1] == '}')    /* set up end of group */
  622.         store(ENDBRACE);
  623.    else
  624.         badpat("Unmatched brace",source,s);
  625.    return(s);
  626. }
  627.  
  628. /*******************************************************/
  629.  
  630. store(op)
  631. {
  632.    if (pp >= &pbuf[PMAX])
  633.       error("Pattern too complex\n","");
  634.    *pp++ = op;
  635. }
  636.  
  637. /*******************************************************/
  638.  
  639. stores(op)
  640. {
  641.    if (subp >= &sbuf[PMAX])
  642.       error("Pattern too complex\n","");
  643.    *subp++ = op;
  644. }
  645.  
  646.  
  647. /*******************************************************/
  648.  
  649. badpat(message, source, stop)
  650. char  *message;       /* Error message */
  651. char  *source;        /* Pattern start */
  652. char  *stop;          /* Pattern end   */
  653. {
  654.    register int    c, lp;
  655.  
  656.    if (debug) {
  657.       for (lp = pbuf; lp < pp;) {
  658.          if ((c = (*lp++ & 0377)) < ' ') 
  659.             puts(itoa(c, wrkbuf));
  660.          else {
  661.             wrkbuf[0] = c;
  662.             wrkbuf[1] = '\0';
  663.             puts(wrkbuf);
  664.         }
  665.      }
  666.      puts("\n");
  667.    }
  668.    puts("-CHG-E-");
  669.    puts(message);
  670.    puts(" pattern is\"");
  671.    puts(source);
  672.    puts("\"\n");
  673.    puts("-CHG-E-Stopped at byte ");
  674.    puts(itoa(stop-source, wrkbuf));
  675.    puts(" '");
  676.    wrkbuf[0] = c;
  677.    wrkbuf[1] = '\0';
  678.    puts(wrkbuf);
  679.    puts("' \n");
  680.    error("?CHG-E-Bad pattern\n","");
  681. }
  682.  
  683.  
  684.  
  685. /*******************************************************/
  686.  
  687. change(fp, fn)
  688. FILE       *fp;       /* File to process            */
  689. char       *fn;       /* File name (for -f option)  */
  690. /*
  691.  * Scan the file for the pattern in pbuf[]
  692.  */
  693. {
  694.    register int lno, indx;
  695.    int c, m;
  696.    char *l, *match(), *subst();
  697.  
  698.    lno = 0;
  699.    while (fgets(lbuf, LMAX, fp)) {
  700.       ++lno;
  701.       m = 0;
  702.       l = lbuf;
  703.       while (l != 0)
  704.           if (l = match(l)) {
  705.               m++;
  706.               l = subst();    /* copies to obuf with substitution */
  707.               strncpy(lbuf,obuf,OMAX); /* and replace for multiple subs */
  708.           }
  709.       if (m || !oflag)                     /* write line? */
  710.          if ((lbuf[0] != '\n') || !eflag)  /* skip empty lines? */
  711.              puts(lbuf);
  712.    }
  713. }
  714.  
  715.  
  716. /*******************************************************/
  717.  
  718. char *
  719. match(lstart)
  720. char *lstart;
  721. /*
  722.  * Match the current line (in lbuf[]), return l if it does.
  723.  */
  724. {
  725.    register char   *l;        /* Line pointer       */
  726.    char *e;                   /* end of pattern pointer */
  727.    int i;
  728.    char *pmatch();
  729.  
  730.    for (l = lstart; *l; l++) {
  731.       brndx = 0;
  732.       if (e = pmatch(l, pbuf)) {
  733.          braces[0].bstart = l;  /* record match range for subst */
  734.          braces[0].bend = e;
  735.          return(e);
  736.       }
  737.    }
  738.    return(0);
  739. }
  740.  
  741. /*******************************************************/
  742.  
  743. char *
  744. pmatch(line, pattern)
  745. char               *line;     /* (partial) line to match      */
  746. char               *pattern;  /* (partial) pattern to match   */
  747. {
  748.    char   *l;        /* Current line pointer         */
  749.    char   *p;        /* Current pattern pointer      */
  750.    char   c;         /* Current character            */
  751.    char   *e;        /* End for STAR and PLUS match  */
  752.    int    op;        /* Pattern operation            */
  753.    int    n;         /* Class counter                */
  754.    int    savbrace;  /* Brace reset point            */
  755.    char   *are;      /* Start of STAR match          */
  756.  
  757.    l = e = line;
  758.    if (debug > 1) {
  759.       puts("pmatch(\"");
  760.       puts(line);
  761.       puts("\")\n");
  762.    }
  763.    p = pattern;
  764.    while ((op = *p++) != ENDPAT && op != ENDBRACE) {
  765.       if (debug > 1) {
  766.          puts("byte[");
  767.          puts(itoa(l-lbuf, wrkbuf));
  768.          puts("] = 0");
  769.          puts(itoa(*l, wrkbuf));
  770.          puts(" '");
  771.          wrkbuf[0] = *l;
  772.          wrkbuf[1] = '\0';
  773.          puts("', op = 0");
  774.          puts(itoa(op, wrkbuf));
  775.          puts("\n");
  776.       }
  777.       switch(op) {
  778.  
  779.       case CHAR:
  780.          if (tolower(*l++) != *p++)
  781.             return(0);
  782.          break;
  783.  
  784.       case BOL:
  785.          if (l != lbuf)
  786.             return(0);
  787.          break;
  788.  
  789.       case EOL:
  790.          if (*l != '\0')
  791.             return(0);
  792.          break;
  793.  
  794.       case ANY:
  795.          if (*l++ == '\0')
  796.             return(0);
  797.          break;
  798.  
  799.       case DIGIT:
  800.          if ((c = *l++) < '0' || (c > '9'))
  801.             return(0);
  802.          break;
  803.  
  804.       case ALPHA:
  805.          c = tolower(*l++);
  806.          if (c < 'a' || c > 'z')
  807.             return(0);
  808.          break;
  809.  
  810.       case NALPHA:
  811.          c = tolower(*l++);
  812.          if (c >= 'a' && c <= 'z')
  813.             break;
  814.          else if (c < '0' || c > '9')
  815.             return(0);
  816.          break;
  817.  
  818.       case PUNCT:
  819.          c = *l++;
  820.          if (c == 0 || c > ' ')
  821.             return(0);
  822.          break;
  823.  
  824.       case BRACE:
  825.          savbrace = brndx;      /* save for backout */
  826.          if (++brndx <= ANDMAX)
  827.              braces[brndx].bstart = l;  /* record start of match */
  828.           else
  829.              error("braces overflowed\n","");
  830.  
  831.          e = pmatch(l, p);      /* match braced group */
  832.  
  833.          for(n=1; n; p++)       /* skip matched group */
  834.              if (*p == ENDBRACE)
  835.                  n--;           /* end of some group */
  836.              else if (*p == BRACE)
  837.                  n++;           /* start of another group */
  838.  
  839.          if (e) {
  840.              braces[savbrace+1].bend = e;   /* update end of match */
  841.              l = e;      /* if match, update area */
  842.          }
  843.          else {
  844.             brndx = savbrace;   /* ignore inner matched braces */
  845.             return(0);  /* else, return no match */
  846.          }
  847.          break;
  848.  
  849.       case CLASS:
  850.       case NCLASS:
  851.          c = tolower(*l++);
  852.          n = *p++ & 0377;
  853.          do {
  854.             if (*p == RANGE) {
  855.                p += 3;
  856.                n -= 2;
  857.                if (c >= p[-2] && c <= p[-1])
  858.                   break;
  859.             }
  860.             else if (c == *p++)
  861.                break;
  862.          } while (--n > 1);
  863.          if ((op == CLASS) == (n <= 1))
  864.             return(0);
  865.          if (op == CLASS)
  866.             p += n - 2;
  867.          break;
  868.  
  869.       case MINUS:
  870.          e = pmatch(l, p);       /* Look for a match    */
  871.          while (*p++ != ENDPAT); /* Skip over pattern   */
  872.          if (e)                  /* Got a match?        */
  873.             l = e;               /* Yes, update string  */
  874.          break;                  /* Always succeeds     */
  875.  
  876.       case PLUS:                 /* One or more ...     */
  877.          if ((l = pmatch(l, p)) == 0)
  878.             return(0);           /* Gotta have a match  */
  879.       case STAR:                 /* Zero or more ...    */
  880.          are = l;                /* Remember line start */
  881.          while (*l && (e = pmatch(l, p)))
  882.             l = e;               /* Get longest match   */
  883.          while (*p++ != ENDPAT); /* Skip over pattern   */
  884.          while (l >= are) {      /* Try to match rest   */
  885.             if (e = pmatch(l, p))
  886.                return(e);
  887.             --l;                 /* Nope, try earlier   */
  888.          }
  889.          return(0);              /* Nothing else worked */
  890.  
  891.       default:
  892.          puts("Bad op code ");
  893.          puts(itoa(op, wrkbuf));
  894.          puts("\n");
  895.          error("Cannot happen -- match\n","");
  896.       }
  897.    }
  898.    return(l);
  899. }
  900.  
  901. /*******************************************************/
  902.  
  903. char *
  904. subst()
  905. {
  906.    char   *l;        /* Current input line pointer   */
  907.    char   *o;        /* Current output line pointer  */
  908.    char   *t;        /* Temporary pattern pointer    */
  909.    char   *p;        /* Current pattern pointer      */
  910.    char   *le;       /* Save area for return         */
  911.    char   c;         /* Current character            */
  912.    int    op;        /* Pattern operation            */
  913.    int    ndx;       /* Matched area index           */
  914.  
  915.    l = lbuf;
  916.    o = obuf;
  917.    p = sbuf;
  918.  
  919.    while (l < braces[0].bstart)
  920.        *o++ = *l++;
  921.  
  922.    while ((op = *p++) != ENDPAT) {
  923.  
  924.       switch(op) {
  925.  
  926.       case CHAR:
  927.          *o++ = *p++;   /* use next char */
  928.          break;
  929.  
  930.       case AND:
  931.          ndx = (int)*p++;
  932.          if (ndx <= brndx)  /* if not matched, use null string */
  933.              for(t = braces[ndx].bstart; t < braces[ndx].bend; )
  934.                 *o++ = *t++;
  935.          break;
  936.  
  937.  
  938.       default:
  939.          puts("Bad op code ");
  940.          puts(itoa(op, wrkbuf));
  941.          puts("\n");
  942.          error("Cannot happen -- subst\n","");
  943.       }
  944.    }
  945.  
  946.    l = braces[0].bend;  /*  skip matched area on input stream */
  947.    le = o - obuf + lbuf;  /* save scan start in input buffer for return */
  948.    while (*o++ = *l++)  /*  and copy rest of input to output */
  949.            ;
  950.  
  951.    return(le);
  952. }
  953.  
  954.