home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / magazine / drdobbs / 1989 / 06 / cawk.lst < prev    next >
File List  |  1989-05-27  |  25KB  |  910 lines

  1. _WRITING AWK-LIKE EXTENSIONS TO C_
  2. by Jim Mischel
  3.  
  4.  
  5. [LISTING ONE]
  6. /*
  7.  * qstring.c - finds and outputs all quoted strings in a C source file.
  8.  *
  9.  * Copyright 1988, Jim Mischel
  10.  */
  11. #include <stdio.h>
  12. #include <string.h>
  13. #include "awklib.h"
  14.  
  15. void main (void) {
  16.     char pat[MAXPAT];
  17.     char buff[MAXSTR];
  18.     char s[MAXSTR];
  19.     char *c;
  20.  
  21.     awk_init ();
  22.     if (makepat("\"[^\"]*\"", pat) == NULL) {
  23.     fprintf (stderr, "Error compiling pattern string.\n");
  24.     return;
  25.     }
  26.  
  27.     while (gets (buff) != NULL) {
  28.     c = buff;
  29.     while ((c = re_match (c, pat)) != NULL) {
  30.         strncpy (s, c, RLENGTH);
  31.         s[RLENGTH] = '\0';
  32.         puts (s);
  33.         c += RLENGTH;
  34.     }
  35.     }
  36. }
  37.  
  38.  
  39. [LISTING TWO]
  40.  
  41. /*
  42.  * awklib.h - defines, global variables, and function prototypes for
  43.  * AWKLIB routines.
  44.  *
  45.  * Copyright 1988, Jim Mischel.  All rights reserved.
  46.  *
  47.  */
  48. extern int RSTART;
  49. extern int RLENGTH;
  50. extern int NF;
  51. extern char *FS;
  52. extern char *FS_PAT;
  53. extern char *FIELDS[];
  54.  
  55. #define MAXSTR    1024
  56. #define MAXPAT    2*MAXSTR
  57.  
  58. void    pascal awk_init (void);
  59. char *    pascal setfs     (char *fs);
  60. char *     pascal match     (char *s, char *re);
  61. char *     pascal re_match (char *s, char *pat);
  62. char *     pascal makepat     (char *re, char *pat);
  63. int     pascal split     (char *s, char **a, char *fs);
  64. int    pascal re_split (char *s, char **a, char *pat);
  65. int    pascal getline     (char *s, int nchar, FILE *infile);
  66. int    pascal sub     (char *re, char *replace, char *str);
  67. int    pascal re_sub     (char *pat, char *replace, char *str);
  68. int    pascal gsub     (char *re, char *replace, char *str);
  69. int    pascal re_gsub     (char *pat, char *replace, char *str);
  70. char *     pascal strins     (char *s, char *i, int pos);
  71. char *     pascal strcins     (char *s, int ch, int pos);
  72. char *     pascal strdel     (char *s, int pos, int n);
  73. char *     pascal strcdel     (char *s, int pos);
  74. char *     pascal strccat     (char *s, int c);
  75.  
  76.  
  77. [LISTING THREE]
  78. /*
  79.  * awklib.c - C callable routines that provide field splitting and regular
  80.  *  expression matching functions much like those found in AWK.
  81.  *
  82.  * Copyright 1988, Jim Mischel.  All rights reserved.
  83.  */
  84. #include <stdio.h>
  85. #include <string.h>
  86. #include <alloc.h>
  87. #include <process.h>
  88.  
  89. #define    TRUE        -1
  90. #define    FALSE        0
  91. #define ALMOST        1    /* returned when a closure matches a NULL */
  92.  
  93. #define ENDSTR        '\0'
  94. #define EOL        '$'
  95. #define BOL        '^'
  96. #define NEGATE        '^'
  97. #define CCL        '['
  98. #define NCCL        ']'
  99. #define CCLEND        ']'
  100. #define ANY        '.'
  101. #define DASH        '-'
  102. #define OR        '|'
  103. #define ESCAPE        '\\'
  104. #define LPAREN        '('
  105. #define RPAREN        ')'
  106. #define CLOSURE        '*'
  107. #define POS_CLO     '+'
  108. #define ZERO_ONE     '?'
  109. #define LITCHAR     'c'
  110. #define END_TERM     'e'
  111. #define FS_DEFAULT     "[ \t]+"
  112.  
  113. #define MAXSTR        1024
  114. #define MAXPAT        2*MAXSTR
  115. #define MAXFIELD     128
  116.  
  117. /*
  118.  * AWKLIB global variables.  These variables are defined in AWKLIB.H and may
  119.  * be accessed by the application.
  120.  */
  121. int     RSTART;            /* start of matched substring */
  122. int    RLENGTH;        /* length of matched substring */
  123. int        NF;             /* number of fields from most current split */
  124. char *    FS;            /* global field separator */
  125. char *    FS_PAT;                 /* compiled field separator */
  126. char *  FIELDS[MAXFIELD];       /* contents of fields from most current split */
  127.  
  128. /*
  129.  * Internal function prototypes.
  130.  */
  131. char *     pascal re_match (char *s, char *pat);
  132. char *     pascal makepat     (char *re, char *pat);
  133. int    pascal re_split (char *s, char **a, char *pat);
  134. char *     pascal do_sub (char *str, int inx, int len, char *replace);
  135. char     parse_escape (void);
  136.  
  137. /*
  138.  * These string routines, while designed specifically for this application,
  139.  * may be useful to other programs.  Their prototypes are included in the
  140.  * AWKLIB.H file.
  141.  */
  142. char *     pascal strins     (char *s, char *i, int pos);
  143. char *     pascal strcins     (char *s, int ch, int pos);
  144. char *     pascal strdel     (char *s, int pos, int n);
  145. char *     pascal strcdel     (char *s, int pos);
  146. char *     pascal strccat     (char *s, int c);
  147.  
  148. /*
  149.  * Initialize AWKLIB global variables.  This routine MUST be called before
  150.  * using the AWKLIB routines.  Failure to do so may produce some strange
  151.  * results.
  152.  */
  153. void    pascal awk_init (void) {
  154.     int x;
  155.     char * pascal setfs (char *fs);
  156.  
  157.     FS = FS_PAT = NULL;
  158.     setfs (FS_DEFAULT);
  159.     RSTART = RLENGTH = NF = 0;
  160.     for (x = 0; x < MAXFIELD; x++)
  161.     FIELDS[x] = NULL;
  162. } /* awk_init */
  163.  
  164. /*
  165.  * Sets the field separator to the regular expression fs.  The regular
  166.  * expression is compiled into FS_PAT.  FS_PAT is returned.  NULL is returned
  167.  * on error and neither FS or FS_PAT is modified.
  168.  */
  169. char *    pascal setfs (char *fs) {
  170.     char pat[MAXPAT];
  171.  
  172.     pat[0] = ENDSTR;
  173.     if (makepat (fs_DEFAULT, pat) == NULL)
  174.     return (NULL);
  175.     if (FS != NULL)
  176.         free (FS);
  177.     if (FS_PAT != NULL)
  178.     free (FS_PAT);
  179.     FS = strdup (fs);
  180.     FS_PAT = strdup (pat);
  181.     return (FS_PAT);
  182. } /* setfs */
  183.  
  184. /*
  185.  * makepat() - "compile" the regular expression re into pat and return a
  186.  * a pointer to the compiled string, or NULL if the compile fails.
  187.  *
  188.  * Performs a recursive descent parse of the expression.
  189.  */
  190.  
  191. char *_re_ptr;        /* global for pattern building */
  192.  
  193. char * pascal makepat (char *re, char *pat) {
  194.     char *t;
  195.     char * parse_expression (void);
  196.  
  197.     _re_ptr = re;
  198.     if ((t = parse_expression ()) == NULL)
  199.     return (NULL);
  200.     else if (*_re_ptr != ENDSTR) {
  201.     free (t);
  202.     return (NULL);
  203.     }
  204.     else {
  205.     strcpy (pat, t);
  206.     free (t);
  207.     return (pat);
  208.     }
  209. } /* makepat */
  210.  
  211. /*
  212.  * parse_expression() - Parse and translate an expression.  Returns a pointer
  213.  * to the compiled expression, or NULL on error.
  214.  */
  215. char * parse_expression (void) {
  216.     char pat[MAXPAT];
  217.     char *arg1;
  218.  
  219.     char * parse_term (void);
  220.  
  221.     pat[0] = ENDSTR;
  222.     if ((arg1 = parse_term ()) == NULL)    /* get the first term */
  223.     return (NULL);
  224.  
  225.     while (*_re_ptr == OR) {        /* parse all subsequent terms */
  226.         strccat (pat, OR);
  227.     strcat (pat, arg1);
  228.         strccat (pat, END_TERM);
  229.     free (arg1);
  230.     _re_ptr++;
  231.         if ((arg1 = parse_term ()) == NULL)
  232.         return (NULL);
  233.     }
  234.     strcat (pat, arg1);
  235.     strccat (pat, END_TERM);
  236.     free (arg1);
  237.     return (strdup (pat));
  238. } /* parse_expression */
  239.  
  240. /*
  241.  * parse_term() - parse and translate a term.  Returns a pointer to the
  242.  * compiled term or NULL on error.
  243.  */
  244. char * parse_term (void) {
  245.     char *t;
  246.     char pat[MAXPAT];
  247.  
  248.     int isfactor (char c);
  249.     char * parse_factor (void);
  250.  
  251.     pat[0] = ENDSTR;
  252.     if (*_re_ptr == BOL)
  253.         strccat (pat, *_re_ptr++);
  254.     do {
  255.         if ((t = parse_factor ()) == NULL)
  256.         return (NULL);
  257.     else {
  258.         strcat (pat, t);
  259.         free (t);
  260.     }
  261.     } while (isfactor (*_re_ptr));    /* parse all factors of this term */
  262.     return (strdup (pat));
  263. } /* parse_term */
  264.  
  265. /*
  266.  * isfactor() - returns TRUE if c is a valid factor character
  267.  */
  268. int     isfactor (char c) {
  269.     static char nfac_chars[] = "^|)]+?*";
  270.     return (strchr (nfac_chars, c) == NULL) ? TRUE : FALSE;
  271. } /* isfactor */
  272.  
  273. /*
  274.  * parse_factor() - parse and translate a factor.  Returns a pointer to the
  275.  * compiled factor or NULL on error.
  276.  */
  277. char * parse_factor (void) {
  278.     char pat[MAXPAT];
  279.     char *t;
  280.  
  281.     char * parse_expression (void);
  282.     int parse_closure (char *pat, char c);
  283.     char * parse_ccl (void);
  284.  
  285.     pat[0] = ENDSTR;
  286.     switch (*_re_ptr) {
  287.     case LPAREN    :        /* parenthesised expression */
  288.         _re_ptr++;
  289.         t = parse_expression ();
  290.         strcat (pat, t);
  291.         free (t);
  292.         if (*_re_ptr++ != RPAREN)
  293.         return (NULL);
  294.         break;
  295.         case CCL    :        /* character class */
  296.         _re_ptr++;
  297.         t = parse_ccl ();
  298.         strcat (pat, t);
  299.         free (t);
  300.         if (*_re_ptr++ != CCLEND)
  301.         return (NULL);
  302.         break;
  303.     case ANY    :        /* '.' or '$' operators */
  304.     case EOL    :
  305.         strccat (pat, *_re_ptr++);
  306.         break;
  307.         case ESCAPE    :        /* ESCAPE character */
  308.         _re_ptr++;
  309.             strccat (pat, LITCHAR);
  310.             strccat (pat, parse_escape ());
  311.         break;
  312.     case CLOSURE     :
  313.     case POS_CLO     :
  314.     case ZERO_ONE     :
  315.         case NEGATE    :
  316.     case CCLEND    :
  317.     case RPAREN    :
  318.     case OR        :         /* not valid characters */
  319.          return (NULL);
  320.     default          :        /* literal character */
  321.             strccat (pat, LITCHAR);
  322.         strccat (pat, *_re_ptr++);
  323.         break;
  324.     }
  325.     /*
  326.      * check for closure
  327.      */
  328.     if (*_re_ptr == CLOSURE || *_re_ptr == ZERO_ONE || *_re_ptr == POS_CLO)
  329.     if (parse_closure (pat, *_re_ptr++) == FALSE)
  330.         return (NULL);
  331.     return (strdup (pat));
  332. } /* parse_factor */
  333.  
  334. /*
  335.  * parse_escape () - returns ASCII value of character(s) following ESCAPE
  336.  */
  337. char     parse_escape (void) {
  338.     unsigned char ch;
  339.     switch (*_re_ptr) {
  340.     case 'b'  : _re_ptr++; return ('\b');    /* backspace */
  341.     case 't'  : _re_ptr++; return ('\t');    /* tab */
  342.     case 'f'  : _re_ptr++; return ('\f');    /* formfeed */
  343.     case 'n'  : _re_ptr++; return ('\n');    /* linefeed */
  344.     case 'r'  : _re_ptr++; return ('\r');    /* carriage return */
  345.         case '0'  :                /* 0-7 is octal constant */
  346.     case '1'  :
  347.     case '2'  :
  348.     case '3'  :
  349.     case '4'  :
  350.     case '5'  :
  351.     case '6'  :
  352.     case '7'  :
  353.             ch = *_re_ptr++ - '0';
  354.         if (*_re_ptr >= '0' && *_re_ptr < '8') {
  355.             ch <<= 3;
  356.         ch += *_re_ptr++ - '0';
  357.         }
  358.         if (*_re_ptr >= '0' && *_re_ptr < '8') {
  359.             ch <<= 3;
  360.             ch += *_re_ptr++ - '0';
  361.         }
  362.         return (ch);
  363.         default      :            /* otherwise, just that char */
  364.         return (*_re_ptr++);
  365.     }
  366. } /* parse_escape */
  367.  
  368. /*
  369.  * parse_closure() - place closure character and size before the factor
  370.  * in the compiled string.
  371.  */
  372. int parse_closure (char *pat, char c) {
  373.     int len;
  374.  
  375.     memmove (pat+2, pat, strlen (pat) + 1);
  376.     pat[0] = c;
  377.     len = strlen (pat + 2);
  378.     if (len > 255)
  379.     return (FALSE);            /* closure expression too large */
  380.     else {
  381.         pat[1] = len;
  382.     return (TRUE);
  383.     }
  384. } /* parse_closure */
  385.  
  386. /*
  387.  * parse_ccl() - parse and translate a character class.  Return pointer to the
  388.  * compiled class or NULL on error.
  389.  */
  390. char * parse_ccl (void) {
  391.     char pat[MAXPAT];
  392.     int first = TRUE;
  393.     int len;
  394.  
  395.     char * parse_dash (char *pat, char ch);
  396.  
  397.     strcpy (pat, "[ ");
  398.     if (*_re_ptr == NEGATE) {        /* if first character is NEGATE */
  399.     pat[0] = NCCL;            /* then we have a negated */
  400.     _re_ptr++;            /* character class */
  401.     }
  402.  
  403.     /*
  404.      * parse all characters up to the closing bracket or end of string marker
  405.      */
  406.     while (*_re_ptr != CCLEND && *_re_ptr != ENDSTR) {
  407.         if (*_re_ptr == DASH && first == FALSE) {    /* DASH, check for range */
  408.             if (*++_re_ptr == NCCL)
  409.         strccat (pat, DASH);        /* not range, literal DASH */
  410.         else
  411.             parse_dash (pat, *_re_ptr++);
  412.     }
  413.     else {
  414.         if (*_re_ptr == ESCAPE) {
  415.             _re_ptr++;
  416.                 strccat (pat, parse_escape ());
  417.         }
  418.         else
  419.             strccat (pat, *_re_ptr++);
  420.     }
  421.     first = FALSE;
  422.     }
  423.     len = strlen (pat+2);
  424.     if (len > 255)
  425.     return (NULL);            /* character class too large */
  426.     else {
  427.         pat[1] = len;            /* store CCL length at pat[1] */
  428.     return (strdup (pat));
  429.     }
  430. } /* parse_ccl */
  431.  
  432. /*
  433.  * parse_dash() - fill in range characters.
  434.  */
  435. char * parse_dash (char *pat, char ch) {
  436.     int ch1;
  437.  
  438.     for (ch1 = pat[strlen (pat) - 1] + 1; ch1 <= ch; ch1++)
  439.         strccat (pat, ch1);
  440.     return (pat);
  441. } /* parse_dash */
  442.  
  443. /*
  444.  * match() - Return a pointer to the first character of the left-most longest
  445.  * substring of s that matches re or NULL if no match is found.  Sets
  446.  * RSTART and RLENGTH.  This routine compiles the regular expression re and
  447.  * then calls re_match to perform the actual matching.
  448.  */
  449. char * pascal match (char *s, char *re) {
  450.     char pat[MAXPAT];
  451.  
  452.     pat[0] = ENDSTR;
  453.     if (makepat (re, pat) == NULL)
  454.     return (NULL);
  455.     return (re_match (s, pat));
  456. } /* match */
  457.  
  458. /*
  459.  * re_match() - Return a pointer to the first character of the left-most
  460.  * longest substring of s that matches pat, or NULL if no match is found.
  461.  * Sets RSTART and RLENGTH.  The != FALSE test below must NOT be changed
  462.  * to == TRUE.  match_term() can return TRUE, FALSE, or ALMOST.  Both TRUE
  463.  * and ALMOST are considered TRUE by this routine.
  464.  */
  465. char *_s_end;            /* global points to last character matched */
  466.  
  467. char * pascal re_match (char *s, char *pat) {
  468.     char *c = s;
  469.     int pascal match_term (int inx, char *s, char *pat);
  470.  
  471.     _s_end = NULL;
  472.     while (*c != ENDSTR) {
  473.     if (match_term (c-s, c, pat) != FALSE) {
  474.         RSTART = c-s;
  475.         RLENGTH = _s_end - c;
  476.         return (c);
  477.     }
  478.     c++;
  479.     }
  480.     RSTART = RLENGTH = 0;
  481.     return (NULL);
  482. } /* re_match */
  483.  
  484. /*
  485.  * Match a compiled term.  Returns TRUE, FALSE, or ALMOST.
  486.  */
  487. int pascal match_term (int inx, char *s, char *pat) {
  488.     int pascal match_or (int inx, char *s, char *pat);
  489.     int pascal match_ccl (char c, char *pat);
  490.     int pascal match_closure (int inx, char *s, char *pat, char *clopat);
  491.     int pascal match_0_1 (int inx, char *s, char *pat);
  492.  
  493.     _s_end = s;
  494.     if (*pat == ENDSTR)
  495.     return (FALSE);
  496.     do {
  497.     switch (*pat) {
  498.         case BOL      :            /* match beginning of line */
  499.         if (inx != 0)
  500.             return (FALSE);
  501.         pat++;
  502.         break;
  503.         case LITCHAR  :            /* match literal character */
  504.         if (*s++ != *++pat)
  505.             return (FALSE);
  506.         pat++;
  507.         break;
  508.         case END_TERM : pat++;  break;    /* skip end-of-term character */
  509.         case ANY      :            /* match any character ... */
  510.         if (*s++ == ENDSTR)        /* ... except end of string */
  511.             return (FALSE);
  512.         pat++;
  513.         break;
  514.         case OR      : return (match_or (inx, s, pat));
  515.         case CCL       :            /* character class requires */
  516.         case NCCL      :                     /* special processing */
  517.         if (*s == ENDSTR)
  518.             return (FALSE);
  519.         if (!match_ccl (*s++, pat++))
  520.             return (FALSE);
  521.         pat += *pat + 1;
  522.         break;
  523.         case EOL      :            /* match end of string */
  524.         if (*s != ENDSTR)
  525.             return (FALSE);
  526.         pat++;
  527.         break;
  528.         case ZERO_ONE : return (match_0_1 (inx, s, pat));
  529.         case CLOSURE  :
  530.         case POS_CLO  : {
  531.         char clopat[MAXPAT];
  532.         strncpy (clopat, pat+2, *(pat+1));
  533.         clopat[*(pat+1)] = ENDSTR;
  534.         return (match_closure (inx, s, pat, clopat));
  535.         break;
  536.         }
  537.         default      :
  538.         /*
  539.          * If we get to this point, then something has gone very wrong.
  540.          * Most likely, someone has tried to match with an invalid
  541.          * compiled pattern.  Whatever the case, the only thing to do
  542.          * is abort the program.
  543.          */
  544.         fputs ("In match_term:  can't happen", stderr);
  545.         exit (1);
  546.         break;
  547.     } /* switch */
  548.     _s_end = s;
  549.     } while (*pat != ENDSTR);
  550.     return (TRUE);
  551. } /* match_term */
  552.  
  553. /*
  554.  * match_or() - Handles selection processing.
  555.  */
  556. int pascal match_or (int inx, char *s, char *pat) {
  557.     char workpat[MAXPAT];
  558.     char *t1, *t2, *junk;
  559.  
  560.     int pascal match_term (int inx, char *s, char *pat);
  561.     char * pascal skip_term (char *pat);
  562.  
  563.     /*
  564.      * The first case is build into workpat.  Second case is already there.
  565.      * Both patterns are searched to determine the longest matched substring.
  566.      */
  567.     workpat[0] = ENDSTR;
  568.     pat++;
  569.     junk = skip_term (pat);
  570.     strncat (workpat, pat, junk-pat);
  571.     strcat (workpat, skip_term (junk));
  572.     t1 = (match_term (inx, s, workpat) != FALSE) ? _s_end : NULL;
  573.     /*
  574.      * The second pattern need not be searched if the first pattern results
  575.      * in a match through to the end of the string, since the longest possible
  576.      * match has already been found.
  577.      */
  578.     if (t1 == NULL || *_s_end != ENDSTR) {
  579.     t2 = (match_term (inx, s, junk) != FALSE) ? _s_end : NULL;
  580.     /*
  581.      * determine which matched the longest substring
  582.      */
  583.     if (t1 != NULL && (t2 == NULL || t1 > t2))
  584.         _s_end = t1;
  585.     }
  586.     return (t1 == NULL && t2 == NULL) ? FALSE : TRUE;
  587. } /* match_or */
  588.  
  589. /*
  590.  * Skip over the current term and return a pointer to the next term in
  591.  * the pattern.
  592.  */
  593. char * pascal skip_term (char *pat) {
  594.     register int nterm = 1;
  595.  
  596.     while (nterm > 0) {
  597.         switch (*pat) {
  598.         case OR    : nterm++; break;
  599.         case CCL    :
  600.         case NCCL    :
  601.         case CLOSURE:
  602.         case ZERO_ONE:
  603.         case POS_CLO:
  604.         pat++;
  605.                 pat += *pat;
  606.         break;
  607.         case END_TERM: nterm--; break;
  608.         case LITCHAR: pat++; break;
  609.     }
  610.     pat++;
  611.     }
  612.     return (pat);
  613. } /* skip_term */
  614.  
  615. /*
  616.  * Match the ZERO_ONE operator.  First, this routine attempts to match the
  617.  * entire pattern with the input string.  If that fails, it skips over
  618.  * the closure pattern and attempts to match the rest of the pattern.
  619.  */
  620. int pascal match_0_1 (int inx, char *s, char *pat) {
  621.     char *save_s = s;
  622.  
  623.     if (match_term (inx, s, pat+2) == TRUE)
  624.     return (TRUE);
  625.     else if (match_term (inx, save_s, pat+2+*(pat+1)) == FALSE)
  626.     return (FALSE);
  627.     else
  628.     return (ALMOST);
  629. } /* match_0_1 */
  630.  
  631. /*
  632.  * Match CLOSURE and POS_CLO.
  633.  * Match as many of the closure patterns as possible, then attempt to match
  634.  * the remaining pattern with what's left of the input string.  Backtrack
  635.  * until we've either matched the remaing pattern or we arrive back at where
  636.  * we started.
  637.  */
  638. int pascal match_closure (int inx, char *s, char *pat, char *clopat) {
  639.     char *save_s = s;
  640.  
  641.     if (match_term (inx, s, clopat) == TRUE) {
  642.     save_s = _s_end;
  643.     if (match_closure (inx, save_s, pat, clopat) == TRUE)
  644.         return (TRUE);
  645.     else
  646.         return (match_term (inx, save_s, pat+2+*(pat+1)));
  647.     }
  648.     else if (*pat != CLOSURE)
  649.     return (FALSE);          /* POS_CLO requires at least one match */
  650.     else if (match_term (inx, save_s, pat+2+*(pat+1)) == TRUE)
  651.     return (ALMOST);
  652.     else
  653.     return (FALSE);
  654. } /* match_closure */
  655.  
  656. /*
  657.  * Match a character class or negated character class
  658.  */
  659. int pascal match_ccl (char c, char *pat) {
  660.     register int x;
  661.     char ccl = *pat++;
  662.  
  663.     for (x = *pat; x > 0; x--)
  664.         if (c == pat[x])
  665.         return (ccl == CCL);
  666.     return (ccl != CCL);
  667. } /* match_ccl */
  668.  
  669. /*
  670.  * Substitue 'replace' for the leftmost longest substring of str matched by
  671.  * the regular expression re.
  672.  * Return number of substitutions made (which in this case will be 0 or 1).
  673.  */
  674. int pascal sub (char *re, char *replace, char *str) {
  675.  
  676.     if (match (str, re) != NULL) {
  677.     free (do_sub (str, RSTART, RLENGTH, replace));
  678.     return (1);
  679.     }
  680.     else
  681.     return (0);
  682. } /* sub */
  683.  
  684. /*
  685.  * Substitue 'replace' for the leftmost longest substring of str matched by
  686.  * the compiled regular expression pat.
  687.  * Return number of substitutions made (which in this case will be 0 or 1).
  688.  */
  689. int pascal re_sub (char *pat, char *replace, char *str) {
  690.     int    pascal re_sub     (char *pat, char *replace, char *str);
  691.  
  692.     if (re_match (str, pat) != NULL) {
  693.     free (do_sub (str, RSTART, RLENGTH, replace));
  694.     return (1);
  695.     }
  696.     else
  697.     return (0);
  698. } /* re_sub */
  699.  
  700. /*
  701.  * Substitute 'replace' globally for all substrings in str matched by the
  702.  * regular expression re.
  703.  * Return number of substitutions made.
  704.  *
  705.  * This routine uses makepat() to compile the regular expression, then calls
  706.  * re_gsub() to do the actual replacement.
  707.  *
  708.  * NOTE:  gsub() makes only 1 pass through the string.  Replaced strings
  709.  * cannot themselves be replaced.
  710.  */
  711. int pascal gsub (char *re, char *replace, char *str) {
  712.     int    pascal re_gsub (char *pat, char *replace, char *str);
  713.  
  714.     char pat[MAXPAT];
  715.  
  716.     pat[0] = ENDSTR;
  717.     if (makepat (re, pat) == NULL)
  718.     return (0);
  719.     return (re_gsub (pat, replace, str));
  720. } /* gsub */
  721.  
  722. /*
  723.  * Substitute 'replace' globally for all substrings in str matched by the
  724.  * compiled regular expression pat.
  725.  * Return number of substitutions made.
  726.  *
  727.  * NOTE:  gsub() makes only 1 pass through the string.  Replaced strings
  728.  * cannot themselves be replaced.
  729.  */
  730. int pascal re_gsub (char *pat, char *replace, char *str) {
  731.     char *m = str;
  732.     int nsub = 0;
  733.     char *p;
  734.  
  735.     while ((m = re_match (m, pat)) != NULL) {
  736.     p = do_sub (m, 0, RLENGTH, replace);
  737.     nsub++;
  738.     m += strlen (p);
  739.     free (p);
  740.     }
  741.     return (nsub);
  742. } /* re_gsub */
  743.  
  744. /*
  745.  * remove 'len' characters from 'str' starting at position 'inx'.  Then insert
  746.  * the replacement string at position 'inx'.
  747.  */
  748. char * pascal do_sub (char *str, int inx, int len, char *replace) {
  749.     char *p;
  750.     char * pascal makesub (char *replace, char *found, int len);
  751.  
  752.     p = makesub (replace, &str[inx], len);
  753.     strdel (str, inx, len);
  754.     strins (str, p, inx);
  755.     return (p);
  756. } /* do_sub */
  757.  
  758. /*
  759.  * Make a substitution string.
  760.  */
  761. char * pascal makesub (char *replace, char *found, int len) {
  762.     char news[MAXSTR];
  763.     char *c = replace;
  764.     int x;
  765.  
  766.     news[0] = ENDSTR;
  767.     while (*c != ENDSTR) {
  768.     if (*c == '&')
  769.         for (x = 0; x < len; x++)
  770.         strccat (news, found[x]);
  771.     else if (*c == '\\') {
  772.         _re_ptr = c+1;
  773.         strccat (news, parse_escape ());
  774.         c = _re_ptr - 1;
  775.     }
  776.     else
  777.         strccat (news, *c);
  778.     c++;
  779.     }
  780.     return (strdup (news));
  781. } /* makesub */
  782.  
  783. /*
  784.  * split - split the string s into fields in the array a on field separator fs.
  785.  * fs is a regular expression.  Returns number of fields.  Also sets the global
  786.  * variable NF.  This routine compiles fs into a pattern and then calls
  787.  * re_split() to do the work.
  788.  */
  789. int pascal split (char *s, char **a, char *fs) {
  790.     char pat[MAXPAT];
  791.  
  792.     pat[0] = ENDSTR;
  793.     makepat (fs, pat);
  794.     return re_split (s, a, pat);
  795. } /* split */
  796.  
  797. /*
  798.  * re_split() - split the string s into fields in the array on field seperator
  799.  * pat.  pat is a compiled regular expression (built by makepat()).  Returns
  800.  * number of fields.  Also sets the global variable NF.
  801.  */
  802. int pascal re_split (char *s, char **a, char *pat) {
  803.     int rstart = RSTART;        /* save RSTART and RLENGTH */
  804.     int rlength = RLENGTH;
  805.     char *c = s;
  806.     char *oldc = s;
  807.  
  808.     NF = 0;
  809.     if (a[0] != NULL)
  810.     free (a[0]);
  811.     a[0] = strdup (s);
  812.  
  813.     while (*oldc != ENDSTR) {
  814.     while ((c = re_match (oldc, pat)) == oldc)
  815.         oldc += RLENGTH;
  816.     if (*oldc != ENDSTR) {
  817.         if (c == NULL)
  818.         c = &oldc[strlen (oldc)];
  819.         if (a[++NF] != NULL)
  820.         a[NF] = realloc (a[NF], c-oldc+1);
  821.             else
  822.                 a[NF] = malloc (c-oldc+1);
  823.             memcpy (a[NF], oldc, c-oldc);
  824.         a[NF][c-oldc] = ENDSTR;
  825.         oldc = c;
  826.     }
  827.     }
  828.     RSTART = rstart;            /* restore globals */
  829.     RLENGTH = rlength;
  830.     return (NF);
  831. } /* re_split */
  832.  
  833. /*
  834.  * Reads a line from infile and splits it into FIELDS.  Returns EOF on
  835.  * end-of-file or error.
  836.  */
  837. int pascal getline (char *s, int nchar, FILE *infile) {
  838.     char *c;
  839.  
  840.     if (fgets (s, nchar, infile) == NULL)
  841.     return (EOF);
  842.     if ((c = strchr (s, '\n')) != NULL)
  843.     *c = ENDSTR;                        /* look for and replace newline */
  844.     re_split (s, FIELDS, FS_PAT);
  845.     return (0);
  846. } /* getline */
  847.  
  848. /*
  849.  * add a character to the end of a string
  850.  */
  851. char * pascal strccat (char *s, int ch) {
  852.     register int len = strlen (s);
  853.     s[len++] = ch;
  854.     s[len] = ENDSTR;
  855.     return (s);
  856. }
  857.  
  858. /*
  859.  * removes the character at pos from the string.
  860.  */
  861. char * pascal strcdel (char *s, int pos) {
  862.     memcpy (s+pos, s+pos+1, strlen (s) - pos);
  863.     return (s);
  864. } /* strcdel */
  865.  
  866. /*
  867.  * inserts the character ch into the string at position pos.  Assumes there
  868.  * is room enough in the string for the character.
  869.  */
  870. char * pascal strcins (char *s, int ch, int pos) {
  871.     memmove (s+pos+1, s+pos, strlen (s) - pos + 1);
  872.     s[pos] = ch;
  873.     return (s);
  874. }
  875.  
  876. /*
  877.  * removes n characters from s starting at pos
  878.  */
  879. char * pascal strdel (char *s, int pos, int n) {
  880.     memcpy (s+pos, s+pos+n, strlen(s)-pos-n+1);
  881.     return (s);
  882. }
  883.  
  884. /*
  885.  * inserts the string i into the string s at position pos.  Assumes there
  886.  * is sufficient memory in s to hold i.
  887.  */
  888. char * pascal strins (char *s, char *i, int pos) {
  889.     char *p = s+pos;
  890.     int ilen = strlen (i);
  891.  
  892.     memmove (p+ilen, p, strlen (s) - pos + 1);
  893.     memcpy (p, i, ilen);
  894.     return (s);
  895. }
  896.  
  897.  
  898. [EXAMPLE 1]
  899.  
  900. Program A                                    Program B
  901.  
  902.  ...                                       ...
  903. while (getline(line,80,f) != EOF) {       makepat("a+bc",pat);
  904.   if (match(line,"a+bc") != NULL)         while (getline(line,80,f) != EOF) {
  905.     puts(line);                             if (re_match(line,pat) != NULL)
  906. }                                             puts(line);
  907.  ...                                      }
  908.                                            ...
  909.  
  910.