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