home *** CD-ROM | disk | FTP | other *** search
/ Carousel Volume 2 #1 / carousel.iso / mactosh / code / microema.sit / src / search.c < prev    next >
Encoding:
C/C++ Source or Header  |  1989-06-09  |  34.9 KB  |  1,575 lines  |  [TEXT/Earl]

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