home *** CD-ROM | disk | FTP | other *** search
/ Garbo / Garbo.cdr / pc / memacs / ue311c.arc / SEARCH.C < prev    next >
C/C++ Source or Header  |  1991-02-17  |  43KB  |  1,808 lines

  1. /*
  2.  * The functions in this file implement commands that search in the forward
  3.  * and backward directions.
  4.  *
  5.  * (History comments formerly here have been moved to history.c)
  6.  *
  7.  */
  8.  
  9. #include <stdio.h>
  10. #include "estruct.h"
  11. #include "eproto.h"
  12. #include "edef.h"
  13. #include "elang.h"
  14.  
  15. static int patlenadd;
  16. static int deltaf[HICHAR], deltab[HICHAR];
  17. static int lastchfjump, lastchbjump;
  18. static short int gr_closure;
  19. static char *gr_match[MAXGROUPS];
  20.  
  21. /*
  22.  * forwsearch -- Search forward.  Get a search string from the user, and
  23.  *    search for the string.  If found, reset the "." to be just after
  24.  *    the match string, and (perhaps) repaint the display.
  25.  */
  26. int PASCAL NEAR forwsearch(f, n)
  27. int f, n;    /* default flag / numeric argument */
  28. {
  29.     register int    status;
  30.  
  31.     /* If n is negative, search backwards.
  32.      * Otherwise proceed by asking for the search string.
  33.      */
  34.     if (n < 0)
  35.         return(backsearch(f, -n));
  36.  
  37.     /* Ask the user for the text of a pattern.  If the
  38.      * response is TRUE (responses other than FALSE are
  39.      * possible), search for the pattern for up to n
  40.      * times, as long as the pattern is there to be
  41.      * found.
  42.      */
  43.     if ((status = readpattern(TEXT78, &pat[0], TRUE)) == TRUE)
  44.         status = forwhunt(f, n);
  45. /*                                "Search" */
  46.  
  47.     return(status);
  48. }
  49.  
  50. /*
  51.  * forwhunt -- Search forward for a previously acquired search string.
  52.  *    If found, reset the "." to be just after the match string,
  53.  *    and (perhaps) repaint the display.
  54.  */
  55. int PASCAL NEAR forwhunt(f, n)
  56. int f, n;    /* default flag / numeric argument */
  57. {
  58.     register int    spoint = PTEND;
  59.     register int    status;
  60.  
  61.     if (n < 0)        /* search backwards */
  62.         return(backhunt(f, -n));
  63.  
  64.     /* Make sure a pattern exists, or that we didn't switch
  65.      * into MAGIC mode after we entered the pattern.
  66.      */
  67.     if (pat[0] == '\0')
  68.     {
  69.         mlwrite(TEXT80);
  70. /*                      "No pattern set" */
  71.         return FALSE;
  72.     }
  73.  
  74. #if    MAGIC
  75.     if ((curwp->w_bufp->b_mode & MDMAGIC) != 0 &&
  76.          mcpat[0].mc_type == MCNIL)
  77.     {
  78.         if (!mcstr())
  79.             return FALSE;
  80.     }
  81. #endif
  82.  
  83.     /*
  84.      * Do one extra search to get us past our current
  85.      * match, if the search type has us at the start
  86.      * of a match, instead of after a match.
  87.      */
  88.     if ((searchtype == SRBEGIN))
  89.     {
  90.         spoint = PTBEG;
  91.         if (lastflag & CFSRCH)
  92.             n = (n > 2)? (n + 1): 2;
  93.     }
  94.  
  95. #if    MAGIC
  96.     if ((magical && curwp->w_bufp->b_mode & MDMAGIC) != 0)
  97.         status = mcscanner(FORWARD, spoint, n);
  98.     else
  99. #endif
  100.         status = scanner(FORWARD, spoint, n);
  101.  
  102.     /* Complain if not there.
  103.      */
  104.     if (status != TRUE)
  105.         mlwrite(TEXT79);
  106. /*                      "Not found" */
  107.  
  108.     thisflag |= CFSRCH;
  109.     return(status);
  110. }
  111.  
  112. /*
  113.  * backsearch -- Reverse search.  Get a search string from the user, and
  114.  *    search, starting at "." and proceeding toward the front of the buffer.
  115.  *    If found "." is left pointing at the first character of the pattern
  116.  *    (the last character that was matched).
  117.  */
  118. int PASCAL NEAR backsearch(f, n)
  119. int f, n;    /* default flag / numeric argument */
  120. {
  121.     register int    status;
  122.  
  123.     /* If n is negative, search forwards.
  124.      * Otherwise proceed by asking for the search string.
  125.      */
  126.     if (n < 0)
  127.         return(forwsearch(f, -n));
  128.  
  129.     /* Ask the user for the text of a pattern.  If the
  130.      * response is TRUE (responses other than FALSE are
  131.      * possible), search for the pattern for up to n
  132.      * times, as long as the pattern is there to be
  133.      * found.
  134.      */
  135.     if ((status = readpattern(TEXT81, &pat[0], TRUE)) == TRUE)
  136.         status = backhunt(f, n);
  137. /*                                "Reverse search" */
  138.  
  139.     return(status);
  140. }
  141.  
  142. /*
  143.  * backhunt -- Reverse search for a previously acquired search string,
  144.  *    starting at "." and proceeding toward the front of the buffer.
  145.  *    If found "." is left pointing at the first character of the pattern
  146.  *    (the last character that was matched).
  147.  */
  148. int PASCAL NEAR backhunt(f, n)
  149. int f, n;    /* default flag / numeric argument */
  150. {
  151.     register int    spoint = PTBEG;
  152.     register int    status;
  153.  
  154.     if (n < 0)
  155.         return(forwhunt(f, -n));
  156.  
  157.     /* Make sure a pattern exists, or that we didn't switch
  158.      * into MAGIC mode after we entered the pattern.
  159.      */
  160.     if (tap[0] == '\0')
  161.     {
  162.         mlwrite(TEXT80);
  163. /*                      "No pattern set" */
  164.         return FALSE;
  165.     }
  166. #if    MAGIC
  167.     if ((curwp->w_bufp->b_mode & MDMAGIC) != 0 &&
  168.          tapcm[0].mc_type == MCNIL)
  169.     {
  170.         if (!mcstr())
  171.             return FALSE;
  172.     }
  173. #endif
  174.  
  175.     /*
  176.      * Do one extra search to get us past our current
  177.      * match, if the search type has us at the start
  178.      * of a match, instead of after a match.
  179.      */
  180.     if ((searchtype == SREND))
  181.     {
  182.         spoint = PTEND;
  183.         if (lastflag & CFSRCH)
  184.             n = (n > 2)? (n + 1): 2;
  185.     }
  186.  
  187. #if    MAGIC
  188.     if ((magical && curwp->w_bufp->b_mode & MDMAGIC) != 0)
  189.         status = mcscanner(REVERSE, spoint, n);
  190.     else
  191. #endif
  192.         status = scanner(REVERSE, spoint, n);
  193.  
  194.     /* Complain if not there.
  195.      */
  196.     if (status != TRUE)
  197.         mlwrite(TEXT79);
  198. /*                      "Not found" */
  199.  
  200.     thisflag |= CFSRCH;
  201.     return(status);
  202. }
  203.  
  204. #if    MAGIC
  205. /*
  206.  * mcscanner -- Search for a meta-pattern in either direction.  If found,
  207.  *    reset the "." to be at the start or just after the match string,
  208.  *    and (perhaps) repaint the display.
  209.  */
  210. int PASCAL NEAR mcscanner(direct, beg_or_end, repeats)
  211. int    direct;        /* which way to go.*/
  212. int    beg_or_end;    /* put point at beginning or end of pattern.*/
  213. int    repeats;    /* search repetitions.*/
  214. {
  215.     MC    *mcpatrn;    /* pointer into pattern */
  216.     LINE    *curline;    /* current line during scan */
  217.     int    curoff;        /* position within current line */
  218.  
  219.     /* If we are going in reverse, then the 'end' is actually
  220.      * the beginning of the pattern.  Toggle it.
  221.      */
  222.     beg_or_end ^= direct;
  223.  
  224.     /* Another directional problem: if we are searching
  225.      * forwards, use the pattern in mcpat[].  Otherwise,
  226.      * use the reversed pattern in tapcm[].
  227.      */
  228.     mcpatrn = (direct == FORWARD)? &mcpat[0]: &tapcm[0];
  229.  
  230.     /*
  231.      * Save the old matchlen length, in case it is
  232.      * very different (closure) from the old length.
  233.      * This is important for query-replace undo
  234.      * command.
  235.      */
  236.     mlenold = matchlen;
  237.  
  238.     /* Setup local scan pointers to global ".".
  239.      */
  240.     curline = curwp->w_dotp;
  241.     curoff  = curwp->w_doto;
  242.  
  243.     /* Scan each character until we hit the head link record.
  244.      */
  245.     while (!boundry(curline, curoff, direct))
  246.     {
  247.         /* Save the current position in case we need to
  248.          * restore it on a match, and initialize matchlen to
  249.          * zero in case we are doing a search for replacement.
  250.          */
  251.         matchline = curline;
  252.         matchoff = curoff;
  253.         matchlen = 0;
  254.  
  255.         if (amatch(mcpatrn, direct, &curline, &curoff))
  256.         {
  257.             /* A SUCCESSFULL MATCH!!!
  258.              * reset the global "." pointers.
  259.              */
  260.             if (beg_or_end == PTEND)    /* at end of string */
  261.             {
  262.                 curwp->w_dotp = curline;
  263.                 curwp->w_doto = curoff;
  264.             }
  265.             else        /* at beginning of string */
  266.             {
  267.                 curwp->w_dotp = matchline;
  268.                 curwp->w_doto = matchoff;
  269.             }
  270.  
  271.             curwp->w_flag |= WFMOVE; /* flag that we have moved */
  272.             savematch();
  273.  
  274.             /*
  275.              * Continue scanning if we haven't found
  276.              * the nth match.
  277.              */
  278.             if (--repeats <= 0)
  279.                 return TRUE;
  280.         }
  281.  
  282.         /* Advance the cursor.
  283.          */
  284.         nextch(&curline, &curoff, direct);
  285.     }
  286.  
  287.     return FALSE;    /* We could not find a match.*/
  288. }
  289.  
  290. /*
  291.  * amatch -- Search for a meta-pattern in either direction.  Based on the
  292.  *    recursive routine amatch() (for "anchored match") in
  293.  *    Kernighan & Plauger's "Software Tools".
  294.  */
  295. int PASCAL NEAR    amatch(mcptr, direct, pcwline, pcwoff)
  296. register MC    *mcptr;        /* string to scan for */
  297. int        direct;        /* which way to go.*/
  298. LINE        **pcwline;    /* current line during scan */
  299. int        *pcwoff;    /* position within current line */
  300. {
  301.     register int    c;    /* character at current position */
  302.  
  303.     LINE    *curline;    /* current line during scan */
  304.     int    curoff;        /* position within current line */
  305.     int    n_chars;    /* number of chars matched in closure.*/
  306.     int    cl_min;        /* minimum number of chars matched in closure.*/
  307.     int    cl_type;    /* Which closure type?.*/
  308.  
  309.     /* Set up local scan pointers to ".", and get
  310.      * the current character.  Then loop around
  311.      * the pattern pointer until success or failure.
  312.      */
  313.     curline = *pcwline;
  314.     curoff = *pcwoff;
  315.  
  316.     /* The beginning-of-line and end-of-line metacharacters
  317.      * do not compare against characters, they compare
  318.      * against positions.
  319.      * BOL is guaranteed to be at the start of the pattern
  320.      * for forward searches, and at the end of the pattern
  321.      * for reverse searches.  The reverse is true for EOL.
  322.      * So, for a start, we check for them on entry.
  323.      */
  324.     if (mcptr->mc_type == BOL)
  325.     {
  326.         if (curoff != 0)
  327.             return FALSE;
  328.         mcptr++;
  329.     }
  330.  
  331.     if (mcptr->mc_type == EOL)
  332.     {
  333.         if (curoff != llength(curline))
  334.             return FALSE;
  335.         mcptr++;
  336.     }
  337.  
  338.     while (mcptr->mc_type != MCNIL)
  339.     {
  340.         c = nextch(&curline, &curoff, direct);
  341.  
  342.         /* Is the current meta-character modified
  343.          * by a closure?
  344.          */
  345.         if (cl_type = (mcptr->mc_type & ALLCLOS))
  346.         {
  347.             n_chars = 0;
  348.             if (cl_type == ZEROONE)
  349.             {
  350.                 if (mceq(c, mcptr))
  351.                 {
  352.                     c = nextch(&curline, &curoff, direct);
  353.                     n_chars++;
  354.                 }
  355.  
  356.                 cl_min = 0;
  357.             }
  358.             else
  359.             {
  360.                 /* Match as many characters as possible
  361.                  * against the current meta-character.
  362.                  */
  363.                 while (mceq(c, mcptr))
  364.                 {
  365.                     c = nextch(&curline, &curoff, direct);
  366.                     n_chars++;
  367.                 }
  368.  
  369.                 /* Minimum number of characters that may
  370.                  * match is 0 or 1.
  371.                  */
  372.                 cl_min = (cl_type == CLOSURE_1);
  373.             }
  374.  
  375.             /* We are now at the character that made us
  376.              * fail.  Try to match the rest of the pattern.
  377.              * Shrink the closure by one for each failure.
  378.              */
  379.             mcptr++;
  380.             for (;;)
  381.             {
  382.                 if (n_chars < cl_min)
  383.                     return FALSE;
  384.  
  385.                 nextch(&curline, &curoff, direct ^ REVERSE);
  386.  
  387.                 if (amatch(mcptr, direct, &curline, &curoff))
  388.                 {
  389.                     matchlen += n_chars;
  390.                     goto success;
  391.                 }
  392.                 n_chars--;
  393.             }
  394.         }
  395.         else            /* Not closure.*/
  396.         {
  397.             /* The only way we'd get a BOL metacharacter
  398.              * at this point is at the end of the reversed pattern.
  399.              * The only way we'd get an EOL metacharacter
  400.              * here is at the end of a regular pattern.
  401.              * So if we have one or the other, and are at
  402.              * the appropriate position, we are guaranteed success
  403.              * (since the next pattern character has to be MCNIL).
  404.              * Before we report success, however, we back up by
  405.              * one character, so as to leave the cursor in the
  406.              * correct position.  For example, a search for "X$"
  407.              * will leave the cursor at the end of the line, while
  408.              * a search for "X<NL>" will leave the cursor at the
  409.              * beginning of the next line.  This follows the
  410.              * notion that the meta-character '$' (and likewise
  411.              * '^') matches positions, not characters.
  412.              */
  413.             if (mcptr->mc_type == BOL)
  414.                 if (curoff == llength(curline))
  415.                 {
  416.                     nextch(&curline, &curoff,
  417.                            direct ^ REVERSE);
  418.                     goto success;
  419.                 }
  420.                 else
  421.                     return FALSE;
  422.  
  423.             if (mcptr->mc_type == EOL)
  424.                 if (curoff == 0)
  425.                 {
  426.                     nextch(&curline, &curoff,
  427.                            direct ^ REVERSE);
  428.                     goto success;
  429.                 }
  430.                 else
  431.                     return FALSE;
  432.  
  433.             /* Neither BOL nor EOL, so go through
  434.              * the meta-character equal function.
  435.              */
  436.             if (!mceq(c, mcptr))
  437.                 return FALSE;
  438.         }
  439.  
  440.         /* Increment the length counter and
  441.          * advance the pattern pointer.
  442.          */
  443.         matchlen++;
  444.         mcptr++;
  445.     }            /* End of mcptr loop.*/
  446.  
  447.     /* A SUCCESSFULL MATCH!!!
  448.      * Reset the "." pointers.
  449.      */
  450. success:
  451.     *pcwline = curline;
  452.     *pcwoff  = curoff;
  453.  
  454.     return TRUE;
  455. }
  456. #endif
  457.  
  458. /*
  459.  * scanner -- Search for a pattern in either direction.  If found,
  460.  *    reset the "." to be at the start or just after the match string,
  461.  *    and (perhaps) repaint the display.
  462.  *    Fast version using simplified version of Boyer and Moore
  463.  *    Software-Practice and Experience, vol 10, 501-506 (1980)
  464.  */
  465. int PASCAL NEAR scanner(direct, beg_or_end, repeats)
  466. int    direct;        /* which way to go.*/
  467. int    beg_or_end;    /* put point at beginning or end of pattern.*/
  468. int    repeats;    /* search repetitions.*/
  469. {
  470.     register int    c;        /* character at current position */
  471.     register char    *patptr;    /* pointer into pattern */
  472.     char    *patrn;            /* string to scan for */
  473.     LINE    *curline;        /* current line during scan */
  474.     int    curoff;            /* position within current line */
  475.     LINE    *scanline;        /* current line during scanning */
  476.     int    scanoff;        /* position in scanned line */
  477.     int    jump;            /* next offset */
  478.  
  479.     /* If we are going in reverse, then the 'end' is actually
  480.      * the beginning of the pattern.  Toggle it.
  481.      */
  482.     beg_or_end ^= direct;
  483.  
  484.     /* Another directional problem: if we are searching
  485.      * forwards, use the pattern in pat[].  Otherwise,
  486.      * use the reversed pattern in tap[].
  487.      */
  488.     patrn = (direct == FORWARD)? &pat[0]: &tap[0];
  489.  
  490.     /* Set up local pointers to global ".".
  491.      */
  492.     curline = curwp->w_dotp;
  493.     curoff = curwp->w_doto;
  494.  
  495.     /* Scan each character until we hit the head link record.
  496.      * Get the character resolving newlines, offset
  497.      * by the pattern length, i.e. the last character of the
  498.      * potential match.
  499.      */
  500.     jump = patlenadd;
  501.  
  502.     while (!fbound(jump, &curline, &curoff, direct))
  503.     {
  504.         /* Set up the scanning pointers, and save
  505.          * the current position in case we match
  506.          * the search string at this point.
  507.          */
  508.         scanline = matchline = curline;
  509.         scanoff  = matchoff  = curoff;
  510.  
  511.         patptr = patrn;
  512.  
  513.         /* Scan through the pattern for a match.
  514.          */
  515.         while ((c = *patptr++) != '\0')
  516.             if (!eq((int) c, nextch(&scanline, &scanoff, direct)))
  517.             {
  518.                 jump = (direct == FORWARD)
  519.                     ? lastchfjump
  520.                     : lastchbjump;
  521.                 goto fail;
  522.             }
  523.  
  524.         /* A SUCCESSFULL MATCH!!!
  525.          * reset the global "." pointers
  526.          */
  527.         if (beg_or_end == PTEND)    /* at end of string */
  528.         {
  529.             curwp->w_dotp = scanline;
  530.             curwp->w_doto = scanoff;
  531.         }
  532.         else        /* at beginning of string */
  533.         {
  534.             curwp->w_dotp = matchline;
  535.             curwp->w_doto = matchoff;
  536.         }
  537.  
  538.         curwp->w_flag |= WFMOVE; /* Flag that we have moved.*/
  539.         savematch();
  540.  
  541.         /*
  542.          * Continue scanning if we haven't found
  543.          * the nth match.
  544.          */
  545.         if (--repeats <= 0)
  546.             return TRUE;
  547.         else
  548.         {
  549.             curline = scanline;
  550.             curoff = scanoff;
  551.         }
  552.  
  553. fail:;            /* continue to search */
  554.     }
  555.  
  556.     return FALSE;    /* We could not find a match */
  557. }
  558.  
  559. /*
  560.  * fbound -- Return information depending on whether we have hit a boundry
  561.  *    (and may therefore search no further) or if a trailing character
  562.  *    of the search string has been found.  See boundry() for search
  563.  *    restrictions.
  564.  */
  565. int PASCAL NEAR    fbound(jump, pcurline, pcuroff, dir)
  566. LINE    **pcurline;
  567. int    *pcuroff, dir, jump;
  568. {
  569.     register int    spare, curoff;
  570.     register LINE    *curline;
  571.  
  572.     curline = *pcurline;
  573.     curoff = *pcuroff;
  574.  
  575.     if (dir == FORWARD)
  576.     {
  577.         while (jump != 0)
  578.         {
  579.             curoff += jump;
  580.             spare = curoff - llength(curline);
  581.  
  582.             if (curline == curbp->b_linep)
  583.                 return TRUE;    /* hit end of buffer */
  584.  
  585.             while (spare > 0)
  586.             {
  587.                 curline = lforw(curline);/* skip to next line */
  588.                 curoff = spare - 1;
  589.                 spare = curoff - llength(curline);
  590.                 if (curline == curbp->b_linep)
  591.                     return TRUE;    /* hit end of buffer */
  592.             }
  593.  
  594.             if (spare == 0)
  595.                 jump = deltaf[(int) '\r'];
  596.             else
  597.                 jump = deltaf[(int) lgetc(curline, curoff)];
  598.         }
  599.  
  600.         /* The last character matches, so back up to start
  601.          * of possible match.
  602.          */
  603.         curoff -= patlenadd;
  604.  
  605.         while (curoff < 0)
  606.         {
  607.             curline = lback(curline);/* skip back a line */
  608.             curoff += llength(curline) + 1;
  609.         }
  610.  
  611.     }
  612.     else            /* Reverse.*/
  613.     {
  614.         jump++;        /* allow for offset in reverse */
  615.         while (jump != 0)
  616.         {
  617.             curoff -= jump;
  618.             while (curoff < 0)
  619.             {
  620.                 curline = lback(curline);    /* skip back a line */
  621.                 curoff += llength(curline) + 1;
  622.                 if (curline == curbp->b_linep)
  623.                     return TRUE;    /* hit end of buffer */
  624.             }
  625.  
  626.             if (curoff == llength(curline))
  627.                 jump = deltab[(int) '\r'];
  628.             else
  629.                 jump = deltab[(int) lgetc(curline, curoff)];
  630.         }
  631.  
  632.         /* The last character matches, so back up to start
  633.          * of possible match.
  634.          */
  635.         curoff += matchlen;
  636.         spare = curoff - llength(curline);
  637.         while (spare > 0)
  638.         {
  639.             curline = lforw(curline);/* skip back a line */
  640.             curoff = spare - 1;
  641.             spare = curoff - llength(curline);
  642.         }
  643.     }
  644.  
  645.     *pcurline = curline;
  646.     *pcuroff = curoff;
  647.     return FALSE;
  648. }
  649.  
  650. /*
  651.  * setjtable -- Settting up search delta forward and delta backward
  652.  *    tables.  The reverse search string and string lengths are
  653.  *    set here, for table initialization and for substitution
  654.  *    purposes.  The default for any character to jump is the
  655.  *    pattern length.
  656.  */
  657. PASCAL NEAR setjtable()
  658. {
  659.     int    i;
  660.     
  661.     rvstrcpy(tap, pat);
  662.     patlenadd = (mlenold = matchlen = strlen(pat)) - 1;
  663.  
  664.     for (i = 0; i < HICHAR; i++)
  665.     {
  666.         deltaf[i] = matchlen;
  667.         deltab[i] = matchlen;
  668.     }
  669.  
  670.     /* Now put in the characters contained
  671.      * in the pattern, duplicating the CASE
  672.      */
  673.     for (i = 0; i < patlenadd; i++)
  674.     {
  675. #if    0
  676.         /*
  677.          * Debugging & tracing information.
  678.          */
  679.         mlwrite(TEXT82, (unsigned int) pat[i], patlenadd - i);
  680. /*                      "Considering %d with jump %d" */
  681.         tgetc();
  682.         if (isletter(pat[i]))
  683.         {
  684.             mlwrite(TEXT83, chcase((unsigned int) pat[i]));
  685. /*                              "Its other case is %d" */
  686.             tgetc();
  687.         }
  688. #endif
  689.         if (isletter(pat[i]))
  690.             deltaf[(unsigned int) chcase((unsigned int) pat[i])]
  691.              = patlenadd - i;
  692.         deltaf[(unsigned int) pat[i]] = patlenadd - i;
  693.  
  694.         if (isletter(tap[i]))
  695.             deltab[(unsigned int) chcase((unsigned int) tap[i])]
  696.              = patlenadd - i;
  697.         deltab[(unsigned int) tap[i]] = patlenadd - i;
  698.     }
  699.  
  700.     /* The last character will have the pattern length
  701.      * unless there are duplicates of it.  Get the number to
  702.      * jump from the arrays delta, and overwrite with zeroes
  703.      * in delta duplicating the CASE.
  704.      */
  705.     lastchfjump = patlenadd + deltaf[(unsigned int) pat[patlenadd]];
  706.     lastchbjump = patlenadd + deltab[(unsigned int) pat[0]];
  707.  
  708.     if (isletter(pat[patlenadd]))
  709.         deltaf[(unsigned int) chcase(pat[patlenadd])] = 0;
  710.     deltaf[(int) pat[patlenadd]] = 0;
  711.  
  712.     if (isletter(pat[0]))
  713.         deltab[(unsigned int) chcase(pat[0])] = 0;
  714.     deltab[(int) pat[0]] = 0;
  715. }
  716.  
  717. /*
  718.  * eq -- Compare two characters.  The "bc" comes from the buffer, "pc"
  719.  *    from the pattern.  If we are not in EXACT mode, fold out the case.
  720.  */
  721. int PASCAL NEAR eq(bc, pc)
  722. register int    bc;
  723. register int    pc;
  724. {
  725.     if ((curwp->w_bufp->b_mode & MDEXACT) == 0)
  726.     {
  727.         if (islower(bc))
  728.             bc = chcase(bc);
  729.  
  730.         if (islower(pc))
  731.             pc = chcase(pc);
  732.     }
  733.  
  734.     return(bc == pc);
  735. }
  736.  
  737. /*
  738.  * readpattern -- Read a pattern.  Stash it in apat.  If it is the
  739.  *    search string (which means that the global variable pat[]
  740.  *    has been passed in), create the reverse pattern and the magic
  741.  *    pattern, assuming we are in MAGIC mode (and #defined that way).
  742.  *
  743.  *    Apat is not updated if the user types in an empty line.  If
  744.  *    the user typed an empty line, and there is no old pattern, it is
  745.  *    an error.  Display the old pattern, in the style of Jeff Lomicka.
  746.  *    There is some do-it-yourself control expansion.  Change to using
  747.  *    <META> to delimit the end-of-pattern to allow <NL>s in the search
  748.  *    string. 
  749.  */
  750. int PASCAL NEAR readpattern(prompt, apat, srch)
  751. char    *prompt;
  752. char    apat[];
  753. int    srch;
  754. {
  755.     register int    status;
  756.     char        tpat[NPAT+20];
  757.  
  758.     strcpy(tpat, prompt);    /* copy prompt to output string */
  759.     strcat(tpat, " [");    /* build new prompt string */
  760.     expandp(&apat[0], &tpat[strlen(tpat)], NPAT/2);    /* add old pattern */
  761.     strcat(tpat, "]<META>: ");
  762.  
  763.     /* Read a pattern.  Either we get one,
  764.      * or we just get the META charater, and use the previous pattern.
  765.      * Then, if it's the search string, make a reversed pattern.
  766.      * *Then*, make the meta-pattern, if we are defined that way.
  767.      */
  768.     if ((status = mltreply(tpat, tpat, NPAT, sterm)) == TRUE)
  769.     {
  770.         lastflag &= ~CFSRCH;
  771.         strcpy(apat, tpat);
  772.  
  773.         /* If we are doing the search string,
  774.          * set the delta tables.
  775.          */
  776.         if (srch)
  777.             setjtable();
  778.  
  779.     }
  780.     else if (status == FALSE && apat[0] != 0)    /* Old one */
  781.         status = TRUE;
  782.  
  783. #if    MAGIC
  784.     /* Only make the meta-pattern if in magic mode,
  785.      * since the pattern in question might have an
  786.      * invalid meta combination.
  787.      */
  788.     if (status == TRUE)
  789.         if ((curwp->w_bufp->b_mode & MDMAGIC) == 0)
  790.         {
  791.             mcclear();
  792.             rmcclear();
  793.         }
  794.         else
  795.             status = srch? mcstr(): rmcstr();
  796. #endif
  797.     return(status);
  798. }
  799.  
  800. /*
  801.  * savematch -- We found the pattern?  Let's save it away.
  802.  */
  803. int PASCAL NEAR savematch()
  804. {
  805.     register char    *ptr;        /* pointer to last match string */
  806.     register int    j;
  807.     LINE        *curline;    /* line of last match */
  808.     int        curoff;        /* offset "      "    */
  809.  
  810.     /* Free any existing match string, then
  811.      * attempt to allocate a new one.
  812.      */
  813.     if (patmatch != NULL)
  814.         free(patmatch);
  815.  
  816.     ptr = patmatch = malloc(matchlen + 1);
  817.  
  818.     if (ptr != NULL)
  819.     {
  820.         curoff = matchoff;
  821.         curline = matchline;
  822.  
  823.         for (j = 0; j < matchlen; j++)
  824.             *ptr++ = nextch(&curline, &curoff, FORWARD);
  825.  
  826.         *ptr = '\0';
  827.     }
  828. }
  829.  
  830. /*
  831.  * rvstrcpy -- Reverse string copy.
  832.  */
  833. PASCAL NEAR rvstrcpy(rvstr, str)
  834. register char    *rvstr, *str;
  835. {
  836.     register int i;
  837.  
  838.     str += (i = strlen(str));
  839.  
  840.     while (i-- > 0)
  841.         *rvstr++ = *--str;
  842.  
  843.     *rvstr = '\0';
  844. }
  845.  
  846. /*
  847.  * sreplace -- Search and replace.
  848.  */
  849. int PASCAL NEAR sreplace(f, n)
  850. int f;        /* default flag */
  851. int n;        /* # of repetitions wanted */
  852. {
  853.     return(replaces(FALSE, f, n));
  854. }
  855.  
  856. /*
  857.  * qreplace -- search and replace with query.
  858.  */
  859. int PASCAL NEAR qreplace(f, n)
  860. int f;        /* default flag */
  861. int n;        /* # of repetitions wanted */
  862. {
  863.     return(replaces(TRUE, f, n));
  864. }
  865.  
  866. /*
  867.  * replaces -- Search for a string and replace it with another
  868.  *    string.  Query might be enabled (according to kind).
  869.  */
  870. int PASCAL NEAR    replaces(kind, f, n)
  871. int    kind;    /* Query enabled flag */
  872. int    f;    /* default flag */
  873. int    n;    /* # of repetitions wanted */
  874. {
  875.     register int status;    /* success flag on pattern inputs */
  876.     register int rlength;    /* length of replacement string */
  877.     register int numsub;    /* number of substitutions */
  878.     register int nummatch;    /* number of found matches */
  879.     int nlflag;        /* last char of search string a <NL>? */
  880.     int nlrepl;        /* was a replace done on the last line? */
  881.     char c;            /* input char for query */
  882.     char tpat[NPAT];    /* temporary to hold search pattern */
  883.     LINE *origline;        /* original "." position */
  884.     int origoff;        /* and offset (for . query option) */
  885.     LINE *lastline;        /* position of last replace and */
  886.     int lastoff;        /* offset (for 'u' query option) */
  887.  
  888.     if (curbp->b_mode & MDVIEW)    /* don't allow this command if    */
  889.         return(rdonly());    /* we are in read only mode    */
  890.  
  891.     /* Check for negative repetitions.
  892.      */
  893.     if (f && n < 0)
  894.         return(FALSE);
  895.  
  896.     /* Ask the user for the text of a pattern.
  897.      */
  898.     if ((status = readpattern(
  899.         (kind == FALSE ? TEXT84 : TEXT85), &pat[0], TRUE)) != TRUE)
  900. /*                           "Replace" */
  901. /*                                    "Query replace" */
  902.         return(status);
  903.  
  904.     /* Ask for the replacement string.
  905.      */
  906.     if ((status = readpattern(TEXT86, &rpat[0], FALSE)) == ABORT)
  907. /*                                "with" */
  908.         return(status);
  909.  
  910.     /* Find the length of the replacement string.
  911.      */
  912.     rlength = strlen(&rpat[0]);
  913.  
  914.     /* Set up flags so we can make sure not to do a recursive
  915.      * replace on the last line.
  916.      */
  917.     nlflag = (pat[matchlen - 1] == '\r');
  918.     nlrepl = FALSE;
  919.  
  920.     if (kind)
  921.     {
  922.         /* Build query replace question string.
  923.          */
  924.         strcpy(tpat, TEXT87);
  925. /*                           "Replace '" */
  926.         expandp(&pat[0], &tpat[strlen(tpat)], NPAT/3);
  927.         strcat(tpat, TEXT88);
  928. /*                           "' with '" */
  929.         expandp(&rpat[0], &tpat[strlen(tpat)], NPAT/3);
  930.         strcat(tpat, "'? ");
  931.  
  932.         /* Initialize last replaced pointers.
  933.          */
  934.         lastline = NULL;
  935.         lastoff = 0;
  936.     }
  937.  
  938.     /* Save original . position, init the number of matches and
  939.      * substitutions, and scan through the file.
  940.      */
  941.     origline = curwp->w_dotp;
  942.     origoff = curwp->w_doto;
  943.     numsub = 0;
  944.     nummatch = 0;
  945.  
  946.     while ( (f == FALSE || n > nummatch) &&
  947.         (nlflag == FALSE || nlrepl == FALSE) )
  948.     {
  949.         /* Search for the pattern.
  950.          * If we search with a regular expression,
  951.          * matchlen is reset to the true length of
  952.          * the matched string.
  953.          */
  954. #if    MAGIC
  955.         if ((magical && curwp->w_bufp->b_mode & MDMAGIC) != 0)
  956.         {
  957.             if (!mcscanner(FORWARD, PTBEG, 1))
  958.                 break;
  959.         }
  960.         else
  961. #endif
  962.             if (!scanner(FORWARD, PTBEG, 1))
  963.                 break;        /* all done */
  964.  
  965.         ++nummatch;    /* Increment # of matches */
  966.  
  967.         /* Check if we are on the last line.
  968.          */
  969.         nlrepl = (lforw(curwp->w_dotp) == curwp->w_bufp->b_linep);
  970.  
  971.         /* Check for query.
  972.          */
  973.         if (kind)
  974.         {
  975.             /* Get the query.
  976.              */
  977. pprompt:        mlwrite(&tpat[0], &pat[0], &rpat[0]);
  978. qprompt:
  979.             update(TRUE);  /* show the proposed place to change */
  980.             c = tgetc();            /* and input */
  981.             mlerase();            /* and clear it */
  982.  
  983.             /* And respond appropriately.
  984.              */
  985.             switch (c)
  986.             {
  987. #if    FRENCH
  988.                 case 'o':    /* oui, substitute */
  989.                 case 'O':
  990. #endif
  991.                 case 'y':    /* yes, substitute */
  992.                 case 'Y':
  993.                 case ' ':
  994.                     break;
  995.  
  996.                 case 'n':    /* no, onward */
  997.                 case 'N':
  998.                     forwchar(FALSE, 1);
  999.                     continue;
  1000.  
  1001.                 case '!':    /* yes/stop asking */
  1002.                     kind = FALSE;
  1003.                     break;
  1004.  
  1005.                 case 'u':    /* undo last and re-prompt */
  1006.                 case 'U':
  1007.                     /* Restore old position.
  1008.                      */
  1009.                     if (lastline == NULL)
  1010.                     {
  1011.                         /* There is nothing to undo.
  1012.                          */
  1013.                         TTbeep();
  1014.                         goto pprompt;
  1015.                     }
  1016.                     curwp->w_dotp = lastline;
  1017.                     curwp->w_doto = lastoff;
  1018.                     lastline = NULL;
  1019.                     lastoff = 0;
  1020.  
  1021.                     /* Delete the new string.
  1022.                      */
  1023.                     backchar(FALSE, rlength);
  1024.                     status = delins(rlength, patmatch, FALSE);
  1025.                     if (status != TRUE)
  1026.                         return(status);
  1027.  
  1028.                     /* Record one less substitution,
  1029.                      * backup, save our place, and
  1030.                      * reprompt.
  1031.                      */
  1032.                     --numsub;
  1033.                     backchar(FALSE, mlenold);
  1034.                     matchline = curwp->w_dotp;
  1035.                     matchoff  = curwp->w_doto;
  1036.                     goto pprompt;
  1037.  
  1038.                 case '.':    /* abort! and return */
  1039.                     /* restore old position */
  1040.                     curwp->w_dotp = origline;
  1041.                     curwp->w_doto = origoff;
  1042.                     curwp->w_flag |= WFMOVE;
  1043.  
  1044.                 case BELL:    /* abort! and stay */
  1045.                     mlwrite(TEXT89);
  1046. /*                                              "Aborted!" */
  1047.                     return(FALSE);
  1048.  
  1049.                 default:    /* bitch and beep */
  1050.                     TTbeep();
  1051.  
  1052.                 case '?':    /* help me */
  1053.                     mlwrite(TEXT90);
  1054. /*"(Y)es, (N)o, (!)Do rest, (U)ndo last, (^G)Abort, (.)Abort back, (?)Help: "*/
  1055.                     goto qprompt;
  1056.  
  1057.             }    /* end of switch */
  1058.         }    /* end of "if kind" */
  1059.  
  1060.         /* if this is the point origin, flag so we a can reset it */
  1061.         if (curwp->w_dotp == origline) {
  1062.             origline = NULL;
  1063.             lastline = curwp->w_dotp->l_bp;
  1064.         }
  1065.  
  1066.         /*
  1067.          * Delete the sucker, and insert its
  1068.          * replacement.
  1069.          */
  1070.         status = delins(matchlen, &rpat[0], TRUE);
  1071.         if (origline == NULL) {
  1072.             origline = lastline->l_fp;
  1073.             origoff = 0;
  1074.         }
  1075.  
  1076.         if (status != TRUE)
  1077.             return(status);
  1078.  
  1079.         /* Save our position, since we may undo this.
  1080.          * If we are not querying, check to make sure
  1081.          * that we didn't replace an empty string
  1082.          * (possible in MAGIC mode), because we'll
  1083.          * infinite loop.
  1084.          */
  1085.         if (kind)
  1086.         {
  1087.             lastline = curwp->w_dotp;
  1088.             lastoff = curwp->w_doto;
  1089.         }
  1090.         else if (matchlen == 0)
  1091.         {
  1092.             mlwrite(TEXT91);
  1093. /*                              "Empty string replaced, stopping." */
  1094.             return(FALSE);
  1095.         }
  1096.  
  1097.         numsub++;    /* increment # of substitutions */
  1098.     }
  1099.  
  1100.     /* And report the results.
  1101.      */
  1102.     mlwrite(TEXT92, numsub);
  1103. /*              "%d substitutions" */
  1104.     return(TRUE);
  1105. }
  1106.  
  1107. /*
  1108.  * delins -- Delete a specified length from the current point
  1109.  *    then either insert the string directly, or make use of
  1110.  *    replacement meta-array.
  1111.  */
  1112. int PASCAL NEAR delins(dlength, instr, use_rmc)
  1113. int    dlength;
  1114. char    *instr;
  1115. int    use_rmc;
  1116. {
  1117.     int    status;
  1118. #if    MAGIC
  1119.     RMC    *rmcptr;
  1120. #endif
  1121.  
  1122.     /* Zap what we gotta,
  1123.      * and insert its replacement.
  1124.      */
  1125.     if ((status = ldelete((long) dlength, FALSE)) != TRUE)
  1126.         mlwrite(TEXT93);
  1127. /*                      "%%ERROR while deleting" */
  1128.     else
  1129. #if    MAGIC
  1130.         if ((rmagical && use_rmc) &&
  1131.              (curwp->w_bufp->b_mode & MDMAGIC) != 0)
  1132.         {
  1133.             rmcptr = &rmcpat[0];
  1134.             while (rmcptr->mc_type != MCNIL && status == TRUE)
  1135.             {
  1136.                 if (rmcptr->mc_type == LITCHAR)
  1137.                     status = linstr(rmcptr->rstr);
  1138.                 else
  1139.                     status = linstr(patmatch);
  1140.                 rmcptr++;
  1141.             }
  1142.         }
  1143.         else
  1144. #endif
  1145.             status = linstr(instr);
  1146.  
  1147.     return(status);
  1148. }
  1149.  
  1150. /*
  1151.  * expandp -- Expand control key sequences for output.
  1152.  */
  1153. int PASCAL NEAR expandp(srcstr, deststr, maxlength)
  1154. char    *srcstr;    /* string to expand */
  1155. char    *deststr;    /* destination of expanded string */
  1156. int    maxlength;    /* maximum chars in destination */
  1157. {
  1158.     unsigned char c;    /* current char to translate */
  1159.  
  1160.     /* Scan through the string.
  1161.      */
  1162.     while ((c = *srcstr++) != 0)
  1163.     {
  1164.         if (c == '\r')        /* it's a newline */
  1165.         {
  1166.             *deststr++ = '<';
  1167.             *deststr++ = 'N';
  1168.             *deststr++ = 'L';
  1169.             *deststr++ = '>';
  1170.             maxlength -= 4;
  1171.         }
  1172.         else if (c < 0x20 || c == 0x7f)    /* control character */
  1173.         {
  1174.             *deststr++ = '^';
  1175.             *deststr++ = c ^ 0x40;
  1176.             maxlength -= 2;
  1177.         }
  1178.         else if (c == '%')
  1179.         {
  1180.             *deststr++ = '%';
  1181.             *deststr++ = '%';
  1182.             maxlength -= 2;
  1183.         }
  1184.         else            /* any other character */
  1185.         {
  1186.             *deststr++ = c;
  1187.             maxlength--;
  1188.         }
  1189.  
  1190.         /* check for maxlength */
  1191.         if (maxlength < 4)
  1192.         {
  1193.             *deststr++ = '$';
  1194.             *deststr = '\0';
  1195.             return(FALSE);
  1196.         }
  1197.     }
  1198.     *deststr = '\0';
  1199.     return(TRUE);
  1200. }
  1201.  
  1202. /*
  1203.  * boundry -- Return information depending on whether we may search no
  1204.  *    further.  Beginning of file and end of file are the obvious
  1205.  *    cases, but we may want to add further optional boundry restrictions
  1206.  *    in future, a' la VMS EDT.  At the moment, just return TRUE or
  1207.  *    FALSE depending on if a boundry is hit (ouch).
  1208.  */
  1209. int PASCAL NEAR boundry(curline, curoff, dir)
  1210. LINE    *curline;
  1211. int    curoff, dir;
  1212. {
  1213.     register int    border;
  1214.  
  1215.     if (dir == FORWARD)
  1216.     {
  1217.         border = (curoff == llength(curline)) &&
  1218.              (lforw(curline) == curbp->b_linep);
  1219.     }
  1220.     else
  1221.     {
  1222.         border = (curoff == 0) &&
  1223.              (lback(curline) == curbp->b_linep);
  1224.     }
  1225.     return(border);
  1226. }
  1227.  
  1228. /*
  1229.  * nextch -- retrieve the next/previous character in the buffer,
  1230.  *    and advance/retreat the point.
  1231.  *    The order in which this is done is significant, and depends
  1232.  *    upon the direction of the search.  Forward searches look at
  1233.  *    the current character and move, reverse searches move and
  1234.  *    look at the character.
  1235.  */
  1236. int PASCAL NEAR nextch(pcurline, pcuroff, dir)
  1237. LINE    **pcurline;
  1238. int    *pcuroff;
  1239. int    dir;
  1240. {
  1241.     register LINE    *curline;
  1242.     register int    curoff;
  1243.     register int    c;
  1244.  
  1245.     curline = *pcurline;
  1246.     curoff = *pcuroff;
  1247.  
  1248.     if (dir == FORWARD)
  1249.     {
  1250.         if (curoff == llength(curline))        /* if at EOL */
  1251.         {
  1252.             curline = lforw(curline);    /* skip to next line */
  1253.             curoff = 0;
  1254.             c = '\r';            /* and return a <NL> */
  1255.         }
  1256.         else
  1257.             c = lgetc(curline, curoff++);    /* get the char */
  1258.     }
  1259.     else            /* Reverse.*/
  1260.     {
  1261.         if (curoff == 0)
  1262.         {
  1263.             curline = lback(curline);
  1264.             curoff = llength(curline);
  1265.             c = '\r';
  1266.         }
  1267.         else
  1268.             c = lgetc(curline, --curoff);
  1269.  
  1270.     }
  1271.     *pcurline = curline;
  1272.     *pcuroff = curoff;
  1273.  
  1274.     return(c);
  1275. }
  1276.  
  1277. #if    MAGIC
  1278. /*
  1279.  * mcstr -- Set up the 'magic' array.  The closure symbol is taken as
  1280.  *    a literal character when (1) it is the first character in the
  1281.  *    pattern, and (2) when preceded by a symbol that does not allow
  1282.  *    closure, such as beginning or end of line symbol, or another
  1283.  *    closure symbol.
  1284.  *
  1285.  *    Coding comment (jmg):  yes, i know i have gotos that are, strictly
  1286.  *    speaking, unnecessary.  But right now we are so cramped for
  1287.  *    code space that i will grab what i can in order to remain
  1288.  *    within the 64K limit.  C compilers actually do very little
  1289.  *    in the way of optimizing - they expect you to do that.
  1290.  */
  1291. int PASCAL NEAR mcstr()
  1292. {
  1293.     MC    *mcptr, *rtpcm;
  1294.     char    *patptr;
  1295.     int    pchr;
  1296.     int    status = TRUE;
  1297.     int    does_closure = FALSE;
  1298.     int    mj = 0;
  1299.     int    group_level = 0;
  1300.  
  1301.     /* If we had metacharacters in the MC array previously,
  1302.      * free up any bitmaps that may have been allocated, and
  1303.      * reset magical.
  1304.      */
  1305.     if (magical)
  1306.         mcclear();
  1307.  
  1308.     mcptr = &mcpat[0];
  1309.     patptr = &pat[0];
  1310.  
  1311.     while ((pchr = *patptr) && status)
  1312.     {
  1313.         switch (pchr)
  1314.         {
  1315.             case MC_CCL:
  1316.                 status = cclmake(&patptr, mcptr);
  1317.                 magical = TRUE;
  1318.                 does_closure = TRUE;
  1319.                 break;
  1320.  
  1321.             case MC_BOL:
  1322.                 /*
  1323.                  * If the BOL character isn't the
  1324.                  * first in the pattern, we assume
  1325.                  * it's a literal instead.
  1326.                  */
  1327.                 if (mj != 0)
  1328.                     goto litcase;
  1329.  
  1330.                 mcptr->mc_type = BOL;
  1331.                 magical = TRUE;
  1332.                 break;
  1333.  
  1334.             case MC_EOL:
  1335.                 /*
  1336.                  * If the EOL character isn't the
  1337.                  * last in the pattern, we assume
  1338.                  * it's a literal instead.
  1339.                  */
  1340.                 if (*(patptr + 1) != '\0')
  1341.                     goto litcase;
  1342.  
  1343.                 mcptr->mc_type = EOL;
  1344.                 magical = TRUE;
  1345.                 break;
  1346.  
  1347.             case MC_ANY:
  1348.                 mcptr->mc_type = ANY;
  1349.                 magical = TRUE;
  1350.                 does_closure = TRUE;
  1351.                 break;
  1352.  
  1353.             case MC_CLOSURE:
  1354.             case MC_CLOSURE_1:
  1355.             case MC_ZEROONE:
  1356.                 /*
  1357.                  * Does the closure symbol mean closure here?
  1358.                  * If so, back up to the previous element
  1359.                  * and indicate it is enclosed.  A bit is
  1360.                  * set in gr_closure in case we enclose a
  1361.                  * group.
  1362.                  */
  1363.                 if (does_closure == FALSE)
  1364.                     goto litcase;
  1365.                 mj--;
  1366.                 mcptr--;
  1367.                 if (pchr == MC_CLOSURE)
  1368.                     mcptr->mc_type |= CLOSURE;
  1369.                 else if (pchr == MC_CLOSURE_1)
  1370.                     mcptr->mc_type |= CLOSURE_1;
  1371.                 else
  1372.                     mcptr->mc_type |= ZEROONE;
  1373.  
  1374.                 if (mcptr->mc_type & GRPEND)
  1375.                     gr_closure |= BIT(group_level + 1);
  1376.  
  1377.                 magical = TRUE;
  1378.                 does_closure = FALSE;
  1379.                 break;
  1380. #if 0
  1381.             case MC_GRPBEG:
  1382.                 /*
  1383.                  * Start of a group.  Indicate it,
  1384.                  * set magical, and do not advance
  1385.                  * mcptr or mj.
  1386.                  */
  1387.                 if (++group_level < MAXGROUPS)
  1388.                 {
  1389.                     mcptr->mc_type = GRPBEG;
  1390.                     mcptr->group_no = group_level;
  1391.                     magical = TRUE;
  1392.                     does_closure = FALSE;
  1393.                 }
  1394.                 else
  1395.                     status = FALSE;
  1396.  
  1397.                 break;
  1398.  
  1399.             case MC_GRPEND:
  1400.                 /*
  1401.                  * If we've no groups to close,
  1402.                  * assume a literal character.
  1403.                  * Otherwise, back up and indicate
  1404.                  * the end of a group.  The case
  1405.                  * GRPBEG has already set magical.
  1406.                  */
  1407.                 if (group_level == 0)
  1408.                     goto litcase;
  1409.  
  1410.                 mcptr->group_no = group_level--;
  1411.                 mcptr->mc_type = GRPEND;
  1412.             /* Will become TRUE when scan can handle it.*/
  1413.                 does_closure = FALSE;
  1414.                 break;
  1415. #endif
  1416.             case MC_ESC:
  1417.                 /*
  1418.                  * Note: no break between MC_ESC case
  1419.                  * and the default.
  1420.                  */
  1421.                 if (*(patptr + 1) != '\0')
  1422.                 {
  1423.                     pchr = *++patptr;
  1424.                     magical = TRUE;
  1425.                 }
  1426.             default:
  1427. litcase:            mcptr->mc_type = LITCHAR;
  1428.                 mcptr->u.lchar = pchr;
  1429.                 does_closure = TRUE;
  1430.                 break;
  1431.         }        /* End of switch.*/
  1432.         mcptr++;
  1433.         patptr++;
  1434.         mj++;
  1435.     }        /* End of while.*/
  1436.  
  1437.     /*
  1438.      * Close off the meta-string, then set up the reverse array,
  1439.      * if the status is good.
  1440.      * Please note the structure assignment - your compiler may
  1441.      * not like that.
  1442.      *
  1443.      * The beginning and end of groups are reversed by flipping
  1444.      * the bits, if they are set.
  1445.      *
  1446.      * If the status is not good, nil out the meta-pattern.
  1447.      * The only way the status would be bad is from the cclmake()
  1448.      * routine, and the bitmap for that member is guarenteed to be
  1449.      * freed.  So we stomp a MCNIL value there, and call mcclear()
  1450.      * to free any other bitmaps.
  1451.      */
  1452.     mcptr->mc_type = MCNIL;
  1453.  
  1454.     if (status)
  1455.     {
  1456.         rtpcm = &tapcm[0];
  1457.         while (--mj >= 0)
  1458.         {
  1459. #if    LATTICE
  1460.             movmem(--mcptr, rtpcm, sizeof (MC));
  1461. #else
  1462.             *rtpcm = *--mcptr;
  1463. #endif
  1464.             if (rtpcm->mc_type & (GRPBEG | GRPEND))
  1465.                 rtpcm->mc_type ^= (GRPBEG | GRPEND);
  1466.             rtpcm++;
  1467.         }
  1468.         rtpcm->mc_type = MCNIL;
  1469.     }
  1470.     else
  1471.     {
  1472.         (--mcptr)->mc_type = MCNIL;
  1473.         mcclear();
  1474.     }
  1475.  
  1476.     return(status);
  1477. }
  1478.  
  1479. /*
  1480.  * rmcstr -- Set up the replacement 'magic' array.  Note that if there
  1481.  *    are no meta-characters encountered in the replacement string,
  1482.  *    the array is never actually created - we will just use the
  1483.  *    character array rpat[] as the replacement string.
  1484.  */
  1485. int PASCAL NEAR rmcstr()
  1486. {
  1487.     RMC    *rmcptr;
  1488.     char    *patptr;
  1489.     int    status = TRUE;
  1490.     int    mj;
  1491.  
  1492.     patptr = &rpat[0];
  1493.     rmcptr = &rmcpat[0];
  1494.     mj = 0;
  1495.     rmagical = FALSE;
  1496.  
  1497.     while (*patptr && status == TRUE)
  1498.     {
  1499.         switch (*patptr)
  1500.         {
  1501.             case MC_DITTO:
  1502.  
  1503.                 /* If there were non-magical characters
  1504.                  * in the string before reaching this
  1505.                  * character, plunk it in the replacement
  1506.                  * array before processing the current
  1507.                  * meta-character.
  1508.                  */
  1509.                 if (mj != 0)
  1510.                 {
  1511.                     rmcptr->mc_type = LITCHAR;
  1512.                     if ((rmcptr->rstr = malloc(mj + 1)) == NULL)
  1513.                     {
  1514.                         mlwrite(TEXT94);
  1515. /*                                                      "%%Out of memory" */
  1516.                         status = FALSE;
  1517.                         break;
  1518.                     }
  1519.                     bytecopy(rmcptr->rstr, patptr - mj, mj);
  1520.                     rmcptr++;
  1521.                     mj = 0;
  1522.                 }
  1523.                 rmcptr->mc_type = DITTO;
  1524.                 rmcptr++;
  1525.                 rmagical = TRUE;
  1526.                 break;
  1527.  
  1528.             case MC_ESC:
  1529.                 rmcptr->mc_type = LITCHAR;
  1530.  
  1531.                 /* We malloc mj plus two here, instead
  1532.                  * of one, because we have to count the
  1533.                  * current character.
  1534.                  */
  1535.                 if ((rmcptr->rstr = malloc(mj + 2)) == NULL)
  1536.                 {
  1537.                     mlwrite(TEXT94);
  1538. /*                                              "%%Out of memory" */
  1539.                     status = FALSE;
  1540.                     break;
  1541.                 }
  1542.  
  1543.                 bytecopy(rmcptr->rstr, patptr - mj, mj + 1);
  1544.  
  1545.                 /* If MC_ESC is not the last character
  1546.                  * in the string, find out what it is
  1547.                  * escaping, and overwrite the last
  1548.                  * character with it.
  1549.                  */
  1550.                 if (*(patptr + 1) != '\0')
  1551.                     *((rmcptr->rstr) + mj) = *++patptr;
  1552.  
  1553.                 rmcptr++;
  1554.                 mj = 0;
  1555.                 rmagical = TRUE;
  1556.                 break;
  1557.  
  1558.             default:
  1559.                 mj++;
  1560.         }
  1561.         patptr++;
  1562.     }
  1563.  
  1564.     if (rmagical && mj > 0)
  1565.     {
  1566.         rmcptr->mc_type = LITCHAR;
  1567.         if ((rmcptr->rstr = malloc(mj + 1)) == NULL)
  1568.         {
  1569.             mlwrite(TEXT94);
  1570. /*                              "%%Out of memory" */
  1571.             status = FALSE;
  1572.         }
  1573.         bytecopy(rmcptr->rstr, patptr - mj, mj);
  1574.         rmcptr++;
  1575.     }
  1576.  
  1577.     rmcptr->mc_type = MCNIL;
  1578. }
  1579.  
  1580. /*
  1581.  * mcclear -- Free up any CCL bitmaps, and MCNIL the MC search arrays.
  1582.  */
  1583. PASCAL NEAR mcclear()
  1584. {
  1585.     register MC    *mcptr;
  1586.  
  1587.     mcptr = &mcpat[0];
  1588.  
  1589.     while (mcptr->mc_type != MCNIL)
  1590.     {
  1591.         if ((mcptr->mc_type & MASKCLO) == CCL ||
  1592.             (mcptr->mc_type & MASKCLO) == NCCL)
  1593.             free(mcptr->u.cclmap);
  1594.         mcptr++;
  1595.     }
  1596.     mcpat[0].mc_type = tapcm[0].mc_type = MCNIL;
  1597.     magical = FALSE;
  1598.     gr_closure = 0;
  1599. }
  1600.  
  1601. /*
  1602.  * rmcclear -- Free up any strings, and MCNIL the RMC array.
  1603.  */
  1604. PASCAL NEAR rmcclear()
  1605. {
  1606.     register RMC    *rmcptr;
  1607.  
  1608.     rmcptr = &rmcpat[0];
  1609.  
  1610.     while (rmcptr->mc_type != MCNIL)
  1611.     {
  1612.         if (rmcptr->mc_type == LITCHAR)
  1613.             free(rmcptr->rstr);
  1614.         rmcptr++;
  1615.     }
  1616.  
  1617.     rmcpat[0].mc_type = MCNIL;
  1618. }
  1619.  
  1620. /*
  1621.  * mceq -- meta-character equality with a character.  In Kernighan & Plauger's
  1622.  *    Software Tools, this is the function omatch(), but i felt there
  1623.  *    were too many functions with the 'match' name already.
  1624.  */
  1625. int PASCAL NEAR    mceq(bc, mt)
  1626. int    bc;
  1627. MC    *mt;
  1628. {
  1629.     register int result;
  1630.  
  1631.     switch (mt->mc_type & MASKCLO)
  1632.     {
  1633.         case LITCHAR:
  1634.             result = eq(bc, (int) mt->u.lchar);
  1635.             break;
  1636.  
  1637.         case ANY:
  1638.             result = (bc != '\r');
  1639.             break;
  1640.  
  1641.         case CCL:
  1642.             if (!(result = biteq(bc, mt->u.cclmap)))
  1643.             {
  1644.                 if ((curwp->w_bufp->b_mode & MDEXACT) == 0 &&
  1645.                     (isletter(bc)))
  1646.                 {
  1647.                     result = biteq(chcase(bc), mt->u.cclmap);
  1648.                 }
  1649.             }
  1650.             break;
  1651.  
  1652.         case NCCL:
  1653.             result = !biteq(bc, mt->u.cclmap);
  1654.  
  1655.             if ((curwp->w_bufp->b_mode & MDEXACT) == 0 &&
  1656.                 (isletter(bc)))
  1657.             {
  1658.                 result &= !biteq(chcase(bc), mt->u.cclmap);
  1659.             }
  1660.             break;
  1661.  
  1662.         default:
  1663.             mlwrite(TEXT95, mt->mc_type);
  1664. /*                              "%%mceq: what is %d?" */
  1665.             result = FALSE;
  1666.             break;
  1667.  
  1668.     }    /* End of switch.*/
  1669.  
  1670.     return(result);
  1671. }
  1672.  
  1673. /*
  1674.  * cclmake -- create the bitmap for the character class.
  1675.  *    ppatptr is left pointing to the end-of-character-class character,
  1676.  *    so that a loop may automatically increment with safety.
  1677.  */
  1678. int PASCAL NEAR    cclmake(ppatptr, mcptr)
  1679. char    **ppatptr;
  1680. MC    *mcptr;
  1681. {
  1682.     BITMAP        bmap;
  1683.     register char    *patptr;
  1684.     register int    pchr, ochr;
  1685.  
  1686.     if ((bmap = clearbits()) == NULL)
  1687.     {
  1688.         mlwrite(TEXT94);
  1689. /*                      "%%Out of memory" */
  1690.         return FALSE;
  1691.     }
  1692.  
  1693.     mcptr->u.cclmap = bmap;
  1694.     patptr = *ppatptr;
  1695.  
  1696.     /*
  1697.      * Test the initial character(s) in ccl for
  1698.      * special cases - negate ccl, or an end ccl
  1699.      * character as a first character.  Anything
  1700.      * else gets set in the bitmap.
  1701.      */
  1702.     if (*++patptr == MC_NCCL)
  1703.     {
  1704.         patptr++;
  1705.         mcptr->mc_type = NCCL;
  1706.     }
  1707.     else
  1708.         mcptr->mc_type = CCL;
  1709.  
  1710.     if ((ochr = *patptr) == MC_ECCL)
  1711.     {
  1712.         mlwrite(TEXT96);
  1713. /*                      "%%No characters in character class" */
  1714.         free(bmap);
  1715.         return(FALSE);
  1716.     }
  1717.     else
  1718.     {
  1719.         if (ochr == MC_ESC)
  1720.             ochr = *++patptr;
  1721.  
  1722.         setbit(ochr, bmap);
  1723.         patptr++;
  1724.     }
  1725.  
  1726.     while (ochr != '\0' && (pchr = *patptr) != MC_ECCL)
  1727.     {
  1728.         switch (pchr)
  1729.         {
  1730.             /* Range character loses its meaning
  1731.              * if it is the last character in
  1732.              * the class.
  1733.              */
  1734.             case MC_RCCL:
  1735.                 if (*(patptr + 1) == MC_ECCL)
  1736.                     setbit(pchr, bmap);
  1737.                 else
  1738.                 {
  1739.                     pchr = *++patptr;
  1740.                     while (++ochr <= pchr)
  1741.                         setbit(ochr, bmap);
  1742.                 }
  1743.                 break;
  1744.  
  1745.             /* Note: no break between case MC_ESC and the default.
  1746.              */
  1747.             case MC_ESC:
  1748.                 pchr = *++patptr;
  1749.             default:
  1750.                 setbit(pchr, bmap);
  1751.                 break;
  1752.         }
  1753.         patptr++;
  1754.         ochr = pchr;
  1755.     }
  1756.  
  1757.     *ppatptr = patptr;
  1758.  
  1759.     if (ochr == '\0')
  1760.     {
  1761.         mlwrite(TEXT97);
  1762. /*                      "%%Character class not ended" */
  1763.         free(bmap);
  1764.         return FALSE;
  1765.     }
  1766.     return TRUE;
  1767. }
  1768.  
  1769. /*
  1770.  * biteq -- is the character in the bitmap?
  1771.  */
  1772. int PASCAL NEAR    biteq(bc, cclmap)
  1773. int    bc;
  1774. BITMAP    cclmap;
  1775. {
  1776.     if (bc >= HICHAR)
  1777.         return FALSE;
  1778.  
  1779.     return( (*(cclmap + (bc >> 3)) & BIT(bc & 7))? TRUE: FALSE );
  1780. }
  1781.  
  1782. /*
  1783.  * clearbits -- Allocate and zero out a CCL bitmap.
  1784.  */
  1785. BITMAP PASCAL NEAR clearbits()
  1786. {
  1787.     BITMAP        cclstart, cclmap;
  1788.     register int    j;
  1789.  
  1790.     if ((cclmap = cclstart = (BITMAP) malloc(HIBYTE)) != NULL)
  1791.         for (j = 0; j < (HIBYTE); j++)
  1792.             *cclmap++ = 0;
  1793.  
  1794.     return(cclstart);
  1795. }
  1796.  
  1797. /*
  1798.  * setbit -- Set a bit (ON only) in the bitmap.
  1799.  */
  1800. PASCAL NEAR setbit(bc, cclmap)
  1801. int    bc;
  1802. BITMAP    cclmap;
  1803. {
  1804.     if (bc < HICHAR)
  1805.         *(cclmap + (bc >> 3)) |= BIT(bc & 7);
  1806. }
  1807. #endif
  1808.