home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / magazine / drdobbs / c_spec / sources / match.c < prev    next >
C/C++ Source or Header  |  1986-02-20  |  18KB  |  799 lines

  1. #include <stdio.h>
  2. #include <ctype.h>
  3.  
  4. /*    MATCH.C        (c) Copyright 1985, Allen I. Holub,
  5.  *                All Rights Reserved
  6.  *
  7.  *    The pattern matching routines used by grep.
  8.  *
  9.  *    Revision History:
  10.  *
  11.  *    10/20/84  fixed bug in amatch
  12.  *    10/29/84  fixed bug in dodash
  13.  *    11/03/84  explicitly declared stoupper() as returning char *
  14.  *    11/21/84  dodash and makepat:
  15.  *            - test error returns from malloc() calls.
  16.  *          - changed dodash to use a bit map
  17.  *    11/24/85  Added the + operator to makepat. Added the match()
  18.  *          subroutine.
  19.  */
  20.  
  21. #define    NUL    0x00    /* ^@    */
  22. #define    CR    0x0d    /* ^M    */
  23. #define    SUB    0x1a    /* ^Z    */
  24. #define CPMEOF    SUB
  25.  
  26. #define getpat(pat)    makepat(pat,0)
  27. #define max(a,b)    ((a) > (b) ? (a) : (b))
  28.  
  29. /*    HEX_L(c) converts c (a hex digit in ASCII) to a number.
  30.  *    HEX_H(c) does the same but puts the result in the high nibble.
  31.  */
  32.  
  33. #define HEX_L(c)    ( isdigit(c) ?  (c)-'0' : ((toupper(c))-'A')+10)
  34. #define HEX_H(c)    ( HEX_L(c) << 4 )
  35.  
  36. /*    Definitions of meta-characters used in pattern matching routines.
  37.  *    LITCHAR & NCCL are only used as token identifiers; all the others
  38.  *    are also both token identifiers and the actual symbol used in
  39.  *    the regular expression
  40.  */
  41.  
  42. #define    BOL    '^'
  43. #define EOL    '$'
  44. #define ANY    '.'
  45. #define LITCHAR    'L'
  46. #define ESCAPE    '\\'
  47. #define    CCL    '['        /* Character class:    [...]        */
  48. #define CCLEND    ']'
  49. #define NEGATE    '^'
  50. #define    NCCL    '!'        /* Negative character class [^...]    */
  51. #define PCLOSE    '+'        /* 1 or more times            */
  52. #define CLOSURE '*'        /* 0 or more times            */
  53. #define OR_SYM    '|'
  54.  
  55. /*
  56.  *    Tokens are used to hold pattern templates. (see makepat() in
  57.  *    tools.h
  58.  */
  59.  
  60. typedef struct token
  61. {
  62.     char        tok;
  63.     char        lchar;
  64.     char        *bitmap;
  65.     struct token    *next;
  66. }TOKEN;
  67.  
  68. #define TOKSIZE sizeof(TOKEN)
  69.  
  70. /*----------------------------------------------------------------------*/
  71.  
  72. static char    *amatch( lin, pat, boln )
  73. char    *lin, *boln;
  74. TOKEN    *pat;
  75. {
  76.     /*    Scans through the pattern template looking for a match
  77.      * with lin. Each element of lin is compared with the template
  78.      * until either a mis-match is found or the end of the template
  79.      * is reached. In the former case a 0 is returned; in the latter,
  80.      * a pointer into lin (pointing to the last character in the
  81.      * matched pattern) is returned.
  82.      *
  83.      *    "lin"    is a pointer to the line being searched.
  84.      *    "pat"    is a pointer to a template made by makepat().
  85.      *    "boln"    is a pointer into "lin" which points at the
  86.      *            character at the beginning of line.
  87.      */
  88.  
  89.     register char    *bocl, *rval, *strstart;
  90.  
  91.     if (pat == 0)
  92.         return( (char *)0 );
  93.  
  94.     strstart = lin;
  95.  
  96.     while ( pat )
  97.     {
  98.         if (pat->tok == CLOSURE  &&   pat->next)
  99.         {
  100.             /*
  101.              *    Process a closure:
  102.              *    First skip over the closure token to the
  103.              *    object to be repeated. This object can be
  104.              *    a character class.
  105.              */
  106.  
  107.             pat = pat->next;
  108.  
  109.             /*    Now match as many occurrences of the
  110.              *    closure pattern as possible.
  111.              */
  112.  
  113.             bocl = lin;
  114.  
  115.             while ( *lin  &&  omatch(&lin, pat, boln)  )
  116.                 ;
  117.  
  118.  
  119.             /*     'Lin' now points to the character that made
  120.              *    made us fail. Now go on to process the
  121.              *    rest of the string. A problem here is
  122.              *    a character following the closure which
  123.              *    could have been in the closure.
  124.              *    For example, in the pattern "[a-z]*t" (which
  125.              *    matches any lower-case word ending in a t),
  126.              *    the final 't' will be sucked up in the while
  127.              *    loop. So, if the match fails, we back up a
  128.              *    notch and try to match the rest of the
  129.              *    string again, repeating this process
  130.              *    recursively until we get back to the
  131.              *    beginning of the closure. The recursion
  132.              *    goes, at most, two levels deep.
  133.              */
  134.  
  135.             if (pat = pat->next)
  136.             {
  137.                 while ( bocl <= lin )
  138.                 {
  139.                     if (rval = amatch(lin, pat, boln) )
  140.                     {
  141.                         /* success */
  142.                         return(rval);
  143.                     }
  144.                     else
  145.                         --lin;
  146.                 }
  147.                 return( (char *)0 );    /* match failed    */
  148.             }
  149.         }
  150.         else if ( omatch(&lin, pat, boln) )
  151.             pat = pat->next;
  152.         else
  153.             return( (char *) 0);
  154.     }
  155.     
  156.     /*
  157.      *    Note that omatch() advances lin to point at the next
  158.      *    character to be matched. Consequently, when we reach
  159.      *    the end of the template, lin will be pointing at the
  160.      *    character following the last character matched.
  161.      *    The exceptions are templates containing only a
  162.      *    BOLN or EOLN token. In these cases omatch doesn't
  163.      *    advance.
  164.      *
  165.      *    So, decrement lin to make it point at the end of the
  166.      *    matched string. Then, check to make sure that we haven't
  167.      *    decremented past the beginning of the string.
  168.      *
  169.      *    A philosophical point should be mentioned here. Is $
  170.      *    a position or a character? (Ie. does $ mean the EOL
  171.      *    character itself or does it mean the character at the end of
  172.      *    the line.) I decided here to make it mean the former, in
  173.      *    order to make the behavior of amatch() consistent. If you
  174.      *    give amatch the pattern ^$ (match all lines consisting only
  175.      *    of an end of line) then, since something has to be returned,
  176.      *    a pointer to the end of line character itself is returned.
  177.      *
  178.      *    The --lin is done outside the return statement because max()
  179.      *    is often a macro (which has side-effects).
  180.      */
  181.  
  182.     --lin;
  183.     return( (char *) max(strstart, lin) );
  184. }
  185.  
  186. /* ---------------------------------------------------------------------- */
  187.  
  188. static setbit( c, field )
  189. int    c;
  190. char    field[];
  191. {
  192.     /*    Set a bit in the bit ASCII bit map corresponding to the
  193.      *    character c. Field must be at least 16 bytes long.
  194.      */
  195.  
  196.     field[ (c & 0x7f) >> 3 ]  |=  1 << (c & 0x07) ;
  197. }
  198.  
  199. /* ---------------------------------------------------------------------- */
  200.  
  201. static testbit( c, field )
  202. int    c;
  203. char    *field;
  204. {
  205.     /*    See if the bit corresponding to c in field is set.
  206.      */
  207.  
  208.     return (   field[ (c & 0x7f)>>3 ]  &  (1 << (c & 0x07))   );
  209. }
  210.  
  211. /* ---------------------------------------------------------------------- */
  212.  
  213.  
  214. static  char    *dodash( delim, src, map )
  215. int    delim;
  216. char    *src, *map;
  217. {
  218.     /*     Expand the set pointed to by "*src" into the bitmap "map."
  219.      *    Stop at delim or end of string.  Update *src to point
  220.      *    at terminator. A set can have one element {x} or several 
  221.      *    elements ( {abcdefghijklmnopqrstuvwxyz} and {a-z}
  222.      *    are equivalent ). Note that the dash notation is expanded
  223.      *    as sequential numbers. This means (since we are using the
  224.      *    ASCII character set) that a-Z will contain the entire alphabet
  225.      *    plus the symbols:  [\]^_`
  226.      *
  227.      *    The character classes are stored in a 16 byte wide bit field
  228.      *    where each bit corresponds to an ASCII character.
  229.      */
  230.  
  231.  
  232.     register int    first, last;
  233.     char         *start;
  234.  
  235.     start = src;
  236.  
  237.     while( *src  &&  *src != delim )
  238.     {
  239.         if( *src != '-')
  240.             setbit( esc( &src ), map );
  241.  
  242.         else if( src == start  ||  *(src+1) == delim )
  243.             setbit( '-', map );
  244.  
  245.         else
  246.         {
  247.             src++;
  248.  
  249.             if( *src < *(src - 2) )
  250.             {
  251.                 first = *src;
  252.                 last  = *(src-2);
  253.             }
  254.             else
  255.             {
  256.                 first = *(src - 2);
  257.                 last  = *src;
  258.             }
  259.  
  260.             while( ++first <= last )
  261.                 setbit( first, map );
  262.  
  263.             src++;
  264.         }
  265.     }
  266.  
  267.     return( src );
  268. }
  269.  
  270. /* ---------------------------------------------------------------------- */
  271.  
  272. static    int    esc(s)
  273. char    **s;
  274. {
  275.     /* Map escape sequences into their equivalent symbols. Returns the
  276.      * Correct ASCII character. c is the character following the \.
  277.      */
  278.  
  279.     register int    rval;
  280.  
  281.     if( **s != ESCAPE )
  282.         rval = *( (*s)++ );
  283.     else
  284.     {
  285.         (*s)++;
  286.  
  287.         switch( toupper(**s) )
  288.         {
  289.         case '\0':    rval = ESCAPE;        break;
  290.         case 'B':    rval = '\b' ;        break;
  291.         case 'F':    rval = '\f' ;        break;
  292.         case 'N':    rval = '\n' ;        break;
  293.         case 'R':    rval = '\r' ;        break;
  294.         case 'S':    rval = ' '  ;        break;
  295.         case 'T':    rval = '\t' ;        break;
  296.         case 'E':    rval = '\033';        break;
  297.         default:    rval = **s  ;        break;
  298.         case 'X':    (*s)++;
  299.                 rval  = HEX_H( **s );
  300.                 (*s)++;
  301.                 rval |= HEX_L( **s );
  302.                 break;
  303.         }
  304.  
  305.         (*s)++;
  306.     }
  307.  
  308.     return (rval);
  309. }
  310.  
  311. /* ---------------------------------------------------------------------- */
  312.  
  313. static TOKEN    *addnode( headp, ntok )
  314. TOKEN        **headp, *ntok;
  315. {
  316.     /*  Called by makepat, adds one node to the pattern template.
  317.     */
  318.  
  319.     static     TOKEN    *tail, **nextlast;
  320.  
  321.     if( *headp == NULL )               /* Chain is empty    */
  322.     {
  323.         nextlast = headp;
  324.         *headp = tail = ntok ;
  325.     }
  326.     else if( ntok->tok != CLOSURE )        /* Insert at end of list */
  327.     {
  328.         tail->next = ntok;
  329.         nextlast   = &(tail->next) ;
  330.         tail       = ntok;
  331.     }
  332.     else                    /* Insert before end    */
  333.     {
  334.         ntok->next = *nextlast ;
  335.         *nextlast  = ntok ;
  336.     }
  337.  
  338.     tail->next = NULL ;
  339.  
  340. #ifdef DEBUG
  341.     pr_tok( *headp );
  342. #endif
  343.  
  344.     return( tail );
  345. }
  346.  
  347. /* ---------------------------------------------------------------------- */
  348.  
  349. TOKEN    *makepat(arg, delim)
  350. char    *arg;
  351. int    delim;
  352. {
  353.     /*    Make a pattern template from the string pointed to by arg.
  354.      *    Stop when delim or '\000' or '\n' is found in arg. 
  355.      *    Return a pointer to the pattern template.
  356.      *
  357.      *    The pattern templates used here are somewhat different
  358.      *    than those used in the book; each token is a structure
  359.      *    of the form TOKEN (see tools.h). A token consists of
  360.      *    an identifier, a pointer to a string, a literal 
  361.      *    character and a pointer to another token. This last is 0 if
  362.      *    there is no subsequent token.
  363.      *
  364.      *    The one strangeness here is caused (again) by CLOSURE which
  365.      *    has to be put in front of the previous token. To make this
  366.      *    insertion a little easier, the 'next' field of the last
  367.      *    token in the chain (the one pointed to by 'tail') is made
  368.      *    to point at the previous node. When we are finished,
  369.      *    tail->next is set to 0.
  370.      */
  371.  
  372.     TOKEN        *head = NULL, *anothertok ;
  373.     register TOKEN    *ntok;
  374.     register TOKEN    *tail;
  375.  
  376.     /*    Check for characters that aren't legal at the beginning
  377.      *    of a template.
  378.      */
  379.  
  380.     if(!*arg || *arg==delim || *arg=='\n'|| *arg==CLOSURE || *arg==PCLOSE)
  381.         return( NULL );
  382.  
  383.     for(; *arg  &&  *arg != delim && *arg != '\n' ; arg++ )
  384.     {
  385.         if( !(ntok = (TOKEN *) malloc(TOKSIZE)) ) 
  386.         {
  387.             fprintf(stderr, "Out of memory\n");
  388.             goto fatal_error;
  389.         }
  390.  
  391.         switch( *arg )
  392.         {
  393.         case ANY:
  394.             ntok->tok = ANY;
  395.             break;
  396.  
  397.         case BOL:
  398.             if (head != NULL)
  399.                 goto fatal_error;
  400.  
  401.             ntok->tok = BOL;
  402.             break;
  403.  
  404.         case EOL: 
  405.  
  406.             if( *(arg+1) == delim   || *(arg+1) == '\0'
  407.                         || *(arg+1) == '\n'  )
  408.                 ntok->tok = EOL;
  409.             else
  410.                 goto fatal_error;
  411.             break;
  412.  
  413.         case CCL:
  414.  
  415.             if( *(arg+1) == NEGATE )
  416.             {
  417.                 ntok->tok = NCCL;
  418.                 arg += 2;
  419.             }
  420.             else
  421.             {
  422.                 ntok->tok = CCL;
  423.                 arg++;
  424.             }
  425.  
  426.             if( ntok->bitmap = (char *) calloc( 16, 1 ) )
  427.                 arg = dodash(CCLEND, arg, ntok->bitmap );
  428.             else
  429.             {
  430.                 fprintf(stderr,"Out of memory\n");
  431.                 goto fatal_error;
  432.             }
  433.  
  434.             break;
  435.  
  436.         case PCLOSE:
  437.  
  438.             if( !(anothertok = (TOKEN *) malloc(TOKSIZE)) ) 
  439.             {
  440.                 fprintf(stderr,"Out of memory\n");
  441.                 goto fatal_error;
  442.             }
  443.  
  444.             memcpy( anothertok, tail, sizeof(TOKEN) );
  445.             tail = addnode( &head, anothertok );
  446.  
  447.             /* Fall through to closure case */
  448.  
  449.         case CLOSURE:
  450.  
  451.             switch( tail->tok )
  452.             {
  453.             case BOL:
  454.             case EOL:
  455.             case PCLOSE:
  456.             case CLOSURE:     goto fatal_error;
  457.             default:     ntok->tok = CLOSURE;
  458.             }
  459.  
  460.             break;
  461.  
  462.         default:
  463.             ntok->tok = LITCHAR;
  464.             ntok->lchar = esc( &arg );
  465.             --arg;    /* esc advances us past the character */
  466.         }
  467.  
  468.         tail = addnode( &head, ntok );
  469.     }
  470.  
  471.     return (head);
  472.  
  473. fatal_error:
  474.  
  475.     unmakepat( head );
  476.     return( NULL );
  477. }
  478.  
  479. /* ---------------------------------------------------------------------- */
  480.  
  481. char    *matchs(line, pat, ret_endp)
  482. char    *line;
  483. TOKEN    *pat;
  484. int    ret_endp;
  485. {
  486.     /*
  487.      *    Compares line and pattern. Line is a character string while
  488.      *    pat is a pattern template made by getpat().
  489.      *    Returns:
  490.      *        1. A zero if no match was found.
  491.      *        2. A pointer the last character
  492.      *           satisfying the match if ret_endp is non-zero.
  493.      *        3. A pointer to the beginning of the matched string
  494.      *           if ret_endp is 0;
  495.      *
  496.      *    For example:
  497.      *
  498.      *        matchs ("1234567890", getpat("4[0-9]*7"), 0);
  499.      *
  500.      *    will return a pointer to the '4', while
  501.      *
  502.      *        matchs ("1234567890", getpat("4[0-9]*7"), 1);
  503.      *
  504.      *    will return a pointer to the '7'. Note that success
  505.      *    is returned when BOL or EOL (by itself ) is being requested
  506.      *    on a null string. In this case a pointer to the null is
  507.      *    returned.
  508.      */
  509.  
  510.     char     *bptr, *rval = (char *)0;
  511.  
  512.     if( !*line )
  513.     {
  514.         if((pat->tok == EOL) ||
  515.            (pat->tok == BOL && (!pat->next || pat->next->tok == EOL)))
  516.             rval = line;
  517.     }
  518.     else
  519.     {
  520.         bptr = line;
  521.  
  522.         while (*line)
  523.         {
  524.             if ( (rval = amatch(line, pat, bptr)) == 0 )
  525.                 line++;
  526.             else
  527.             {
  528.                 rval = ret_endp ? rval : line ;
  529.                 break;
  530.             }
  531.         }
  532.     }
  533.  
  534.     return (rval);
  535. }
  536.  
  537. /* ---------------------------------------------------------------------- */
  538.  
  539. static    int    omatch( linp, pat, boln )
  540. char    **linp, *boln;
  541. TOKEN    *pat;
  542. {
  543.     /*    Match one pattern element, pointed at by pat, with the
  544.      *    character at **linp. Return non-zero on match.
  545.      *    Otherwise, return 0. *Linp is advanced to skip over the
  546.      *    matched character; it is not advanced on failure. The
  547.      *    amount of the advance is 0 for patterns that match null
  548.      *    strings, 1 otherwise. "boln" should point at the position
  549.      *    that will match a BOL token.
  550.      */
  551.  
  552.     register int    advance;
  553.     
  554.     advance = -1;
  555.     
  556.     if( **linp == '\0' )
  557.     {
  558.         /*    This clause takes care of EOL matching a terminating
  559.          *    null. The case of EOL matching a '\n' is handled
  560.          *    in the following else clause.
  561.          */
  562.  
  563.         if( pat->tok == EOL )
  564.             advance = 0;
  565.     }
  566.     else
  567.     {
  568.         switch ( pat->tok )
  569.         {
  570.         case LITCHAR:
  571.             if ( **linp == pat->lchar )
  572.                 advance = 1;
  573.             break;
  574.  
  575.         case BOL:
  576.             if ( *linp == boln )
  577.                 advance = 0;
  578.             break;
  579.  
  580.         case ANY:
  581.             if ( **linp != '\n' )
  582.                 advance = 1;
  583.             break;
  584.  
  585.         case EOL:
  586.             if ( **linp == '\n' )
  587.                 advance = 0;
  588.             break;
  589.  
  590.         case CCL:
  591.             if( testbit( **linp, pat->bitmap ) )
  592.                 advance = 1;
  593.             break;
  594.  
  595.         case NCCL:
  596.             if( !testbit( **linp, pat->bitmap) )
  597.                 advance = 1;
  598.             break;
  599.  
  600.         default:
  601.             printf("omatch: can't happen\n");
  602.         }
  603.     }
  604.  
  605.     if( advance > 0 )
  606.         *linp += advance;
  607.  
  608.     return( ++advance );
  609. }
  610.  
  611. /* --------------------------------------------------------------- */
  612.  
  613. unmakepat(head)
  614. TOKEN    *head;
  615. {
  616.     /*    Free up the memory used for the token string    */
  617.  
  618.     register TOKEN    *old_head;
  619.  
  620.     while (head)
  621.     {
  622.         switch (head->tok)
  623.         {
  624.         case CCL:
  625.         case NCCL:
  626.             free( head->bitmap );
  627.             /* no break, fall through to default */
  628.  
  629.         default:
  630.             old_head = head;
  631.             head = head->next;
  632.             free( old_head );
  633.             break;
  634.         }
  635.     }
  636. }
  637.  
  638. /*----------------------------------------------------------------------*/
  639.  
  640. char    *match( look_for, in_this_string)
  641. char    *look_for, *in_this_string;
  642. {
  643.     /*    Return a pointer to the characters matching look_for
  644.      *    if it's in the string, else return 0.
  645.      */
  646.  
  647.     register TOKEN    *p;
  648.     register char    *rval;
  649.  
  650.     rval = matchs( in_this_string, p = getpat(look_for), 0 );
  651.     unmakepat( p );
  652.  
  653.     return( rval );
  654. }
  655.  
  656. /* ---------------------------------------------------------------------- */
  657.  
  658. #ifdef DEBUG
  659.  
  660. pr_tok( head )
  661. TOKEN    *head;
  662. {
  663.     /*    Print out the pattern template (linked list of TOKENs)
  664.      *    pointed to by head. This is a useful debugging aid. Note
  665.      *    that pr_tok() just scans along the linked list, terminating
  666.      *    on a null pointer; so, you can't use pr_tok from inside
  667.      *    makepat() because tail->next points to the previous
  668.      *    node instead of being null. Note that NEGATE and OR_SYM
  669.      *    are not listed because they won't occur in a template.
  670.      */
  671.  
  672.     register char    *str;
  673.     register int    i;
  674.  
  675.     for (; head ; head = head->next )
  676.     {
  677.         switch (head->tok)
  678.         {
  679.         case BOL:
  680.             str = "BOL";
  681.             break;
  682.  
  683.         case EOL:
  684.             str = "EOL";
  685.             break;
  686.  
  687.         case ANY:
  688.             str = "ANY";
  689.             break;
  690.  
  691.         case LITCHAR:
  692.             str = "LITCHAR";
  693.             break;
  694.  
  695.         case ESCAPE:
  696.             str = "ESCAPE";
  697.             break;
  698.  
  699.         case CCL:
  700.             str = "CCL";
  701.             break;
  702.  
  703.         case CCLEND:
  704.             str = "CCLEND";    
  705.             break;
  706.  
  707.         case NCCL:
  708.             str = "NCCL";
  709.             break;
  710.  
  711.         case CLOSURE:
  712.             str = "CLOSURE";
  713.             break;
  714.  
  715.         default:
  716.             str = "**** unknown ****";
  717.         }
  718.  
  719.         printf("%-8s at: 0x%x, ", str, head);
  720.  
  721.         if (head->tok == CCL || head->tok == NCCL)
  722.         {
  723.             printf("string (at 0x%x) =<", head->bitmap );
  724.  
  725.             for( i = 0; i < 0x7f ; i++)
  726.                 if( testbit(i, head->bitmap) )
  727.                     putchar(i);
  728.  
  729.             printf(">, ");
  730.         }
  731.  
  732.         else if (head->tok == LITCHAR)
  733.             printf("lchar = %c, ", head->lchar);
  734.  
  735.         printf("next = 0x%x\n", head->next);
  736.     }
  737.  
  738.     putchar('\n');
  739. }
  740.  
  741. /*----------------------------------------------------------------------*/
  742.  
  743. main()
  744. {
  745.     static     char    ibuf[80], search_this[80], for_this[80];
  746.     register char    *p, *start, *end;
  747.     register TOKEN    *tp;
  748.  
  749.     while(1)
  750.     {
  751.         printf("----------------------------------------------\n");
  752.         printf("Str to search (CR=<%s>): ", search_this );
  753.  
  754.         gets(ibuf);
  755.         if( *ibuf )
  756.             strcpy(search_this, ibuf);
  757.  
  758.         printf("Pattern to search for  : ");
  759.         gets(for_this);
  760.  
  761.         printf("\nlooking for <%s> in <%s>\n", for_this, search_this);
  762.  
  763.         if( p = match( for_this, search_this ) )
  764.         {
  765.             printf("FOUND at <%s>\n\n", p );
  766.  
  767.             tp    = getpat(for_this);
  768.             start = matchs(search_this , tp, 0 );
  769.             end   = matchs(search_this , tp, 1 );
  770.             printf("tp = 0x%x, start = 0x%x, end = 0x%x\n",
  771.                         tp, start, end );
  772.             unmakepat( tp );
  773.  
  774.             if( !*search_this  )
  775.                 printf("search_this (at 0x%x) is empty\n",
  776.                             search_this  );
  777.             else
  778.             { 
  779.                 putchar('\n');
  780.  
  781.                 for( p = search_this ; *p ; p++ )
  782.                 {
  783.                     if( p == start )
  784.                         putchar('<');
  785.  
  786.                     putchar( *p );
  787.  
  788.                     if( p == end )
  789.                         puts(">");
  790.                 }
  791.             }
  792.         }
  793.         else
  794.             printf("NOT FOUND\n\n");
  795.     }
  796. }
  797.  
  798. #endif
  799.