home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / vol_100 / 198_02 / search.c < prev    next >
C/C++ Source or Header  |  1990-01-23  |  42KB  |  1,780 lines

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