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