home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume14 / jove4.9 / part10 / re.c < prev   
C/C++ Source or Header  |  1988-04-25  |  18KB  |  959 lines

  1. /***************************************************************************
  2.  * This program is Copyright (C) 1986, 1987, 1988 by Jonathan Payne.  JOVE *
  3.  * is provided to you without charge, and with no warranty.  You may give  *
  4.  * away copies of JOVE, including sources, provided that this notice is    *
  5.  * included in all the files.                                              *
  6.  ***************************************************************************/
  7.  
  8. /* search package */
  9.  
  10. #include "jove.h"
  11. #include "ctype.h"
  12. #ifdef MAC
  13. #    undef private
  14. #    define private
  15. #endif
  16.  
  17. #ifdef    LINT_ARGS
  18. private char * insert(char *, char *, int);
  19.  
  20. private void
  21.     REreset(void),
  22.     search(int, int, int);
  23. private int
  24.     backref(int, char *),
  25.     do_comp(int),
  26.     member(char *, int, int),
  27.     REgetc(void),
  28.     REmatch(char *, char *);
  29. #else
  30. private char * insert();
  31.  
  32. private void
  33.     REreset(),
  34.     search();
  35. private int
  36.     backref(),
  37.     do_comp(),
  38.     member(),
  39.     REgetc(),
  40.     REmatch();
  41. #endif    /* LINT_ARGS */
  42.  
  43. #ifdef MAC
  44. #    undef private
  45. #    define private static
  46. #endif
  47.  
  48. #define NALTS    16    /* number of alternate search strings */
  49.  
  50. char    searchstr[128],
  51.     compbuf[256],        /* global default compbuf */
  52.     rep_search[128],    /* replace search string */
  53.     rep_str[128],        /* contains replacement string */
  54.     *cur_compb,        /* usually points at compbuf */
  55.     REbuf[LBSIZE],        /* points at line we're scanning */
  56.     *alternates[NALTS];
  57.  
  58. int    REdirection;
  59.  
  60. int    CaseIgnore = 0,
  61.     WrapScan = 0,
  62.     UseRE = 0;
  63.  
  64. #define cind_cmp(a, b)    (CaseEquiv[a] == CaseEquiv[b])
  65.  
  66. private int    REpeekc;
  67. private char    *REptr;
  68.  
  69. private int
  70. REgetc()
  71. {
  72.     int    c;
  73.  
  74.     if ((c = REpeekc) != -1)
  75.         REpeekc = -1;
  76.     else if (*REptr)
  77.         c = *REptr++;
  78.     else
  79.         c = 0;
  80.  
  81.     return c;
  82. }
  83.  
  84. #define STAR     01    /* Match any number of last RE. */
  85. #define AT_BOL    2    /* ^ */
  86. #define AT_EOL    4    /* $ */
  87. #define AT_BOW    6    /* \< */
  88. #define AT_EOW    8    /* \> */
  89. #define OPENP    10    /* \( */
  90. #define CLOSEP    12    /* \) */
  91. #define CURLYB    14    /* \{ */
  92.  
  93. #define NOSTR    14    /* Codes <= NOSTR can't be *'d. */
  94.  
  95. #define ANYC    NOSTR+2        /* . */
  96. #define NORMC    ANYC+2        /* normal character */
  97. #define CINDC    NORMC+2        /* case independent character */
  98. #define ONE_OF    CINDC+2        /* [xxx] */
  99. #define NONE_OF    ONE_OF+2    /* [^xxx] */
  100. #define BACKREF    NONE_OF+2    /* \# */
  101. #define EOP    BACKREF+2    /* end of pattern */
  102.  
  103. #define NPAR    10    /* [0-9] - 0th is the entire matched string, i.e. & */
  104. private int    nparens;
  105. private char    *comp_p,
  106.         *start_p,
  107.         **alt_p,
  108.         **alt_endp;
  109.  
  110. void
  111. REcompile(pattern, re, into_buf, alt_bufp)
  112. char    *pattern,
  113.     *into_buf,
  114.     **alt_bufp;
  115. {
  116.     REptr = pattern;
  117.     REpeekc = -1;
  118.     comp_p = cur_compb = start_p = into_buf;
  119.     alt_p = alt_bufp;
  120.     alt_endp = alt_p + NALTS;
  121.     *alt_p++ = comp_p;
  122.     nparens = 0;
  123.     (void) do_comp(re ? OKAY_RE : NORM);
  124.     *alt_p = 0;
  125. }
  126.  
  127. /* compile the pattern into an internal code */
  128.  
  129. private int
  130. do_comp(kind)
  131. {
  132.     char    *last_p,
  133.         *chr_cnt = 0;
  134.     int    parens[NPAR],
  135.         *parenp,
  136.         c,
  137.         ret_code;
  138.  
  139.     parenp = parens;
  140.     last_p = 0;
  141.     ret_code = 1;
  142.  
  143.     if (kind == OKAY_RE) {
  144.         *comp_p++ = OPENP;
  145.         *comp_p++ = nparens;
  146.         *parenp++ = nparens++;
  147.         start_p = comp_p;
  148.     }
  149.  
  150.     while (c = REgetc()) {
  151.         if (comp_p > &cur_compb[(sizeof compbuf) - 6])
  152. toolong:        complain("Search string too long/complex.");
  153.         if (c != '*')
  154.             last_p = comp_p;
  155.  
  156.         if (kind == NORM && index(".[*", c) != 0)
  157.             goto defchar;
  158.         switch (c) {
  159.         case '\\':
  160.             switch (c = REgetc()) {
  161.             case 0:
  162.                 complain("Premature end of pattern.");
  163.  
  164.             case '{':
  165.                 {
  166.                     char    *wcntp;        /* word count */
  167.  
  168.                     *comp_p++ = CURLYB;
  169.                     wcntp = comp_p;
  170.                     *comp_p++ = 0;
  171.                     for (;;) {
  172.                         int    comp_val;
  173.                         char    *comp_len;
  174.  
  175.                         comp_len = comp_p++;
  176.                         comp_val = do_comp(IN_CB);
  177.                         *comp_len = comp_p - comp_len;
  178.                         (*wcntp) += 1;
  179.                         if (comp_val == 0)
  180.                             break;
  181.                     }
  182.                     break;
  183.                 }
  184.  
  185.             case '}':
  186.                 if (kind != IN_CB)
  187.                     complain("Unexpected \}.");
  188.                 ret_code = 0;
  189.                 goto outahere;
  190.  
  191.             case '(':
  192.                 if (nparens >= NPAR)
  193.                     complain("Too many ('s; max is %d.", NPAR);
  194.                 *comp_p++ = OPENP;
  195.                 *comp_p++ = nparens;
  196.                 *parenp++ = nparens++;
  197.                 break;
  198.  
  199.             case ')':
  200.                 if (parenp == parens)
  201.                     complain("Too many )'s.");
  202.                 *comp_p++ = CLOSEP;
  203.                 *comp_p++ = *--parenp;
  204.                 break;
  205.  
  206.             case '|':
  207.                 if (alt_p >= alt_endp)
  208.                     complain("Too many alternates; max %d.", NALTS);
  209.                 *comp_p++ = CLOSEP;
  210.                 *comp_p++ = *--parenp;
  211.                 *comp_p++ = EOP;
  212.                 *alt_p++ = comp_p;
  213.                 nparens = 0;
  214.                 *comp_p++ = OPENP;
  215.                 *comp_p++ = nparens;
  216.                 *parenp++ = nparens++;
  217.                 start_p = comp_p;
  218.                 break;
  219.  
  220.             case '1':
  221.             case '2':
  222.             case '3':
  223.             case '4':
  224.             case '5':
  225.             case '6':
  226.             case '7':
  227.             case '8':
  228.             case '9':
  229.                 *comp_p++ = BACKREF;
  230.                 *comp_p++ = c - '0';
  231.                 break;
  232.  
  233.             case '<':
  234.                 *comp_p++ = AT_BOW;
  235.                 break;
  236.  
  237.             case '>':
  238.                 *comp_p++ = AT_EOW;
  239.                 break;
  240.  
  241.             default:
  242.                 goto defchar;
  243.             }
  244.             break;
  245.  
  246.         case ',':
  247.             if (kind != IN_CB)
  248.                 goto defchar;
  249.             goto outahere;
  250.  
  251.         case '.':
  252.             *comp_p++ = ANYC;
  253.             break;
  254.  
  255.         case '^':
  256.             if (comp_p == start_p) {
  257.                 *comp_p++ = AT_BOL;
  258.                 break;
  259.             }
  260.             goto defchar;
  261.  
  262.         case '$':
  263.             if ((REpeekc = REgetc()) != 0 && REpeekc != '\\')
  264.                 goto defchar;
  265.             *comp_p++ = AT_EOL;
  266.             break;
  267.  
  268.         case '[':
  269.             {
  270.                 int    chrcnt;
  271.  
  272.                 *comp_p++ = ONE_OF;
  273.             if (comp_p + 16 >= &cur_compb[(sizeof compbuf)])
  274.                 goto toolong;
  275.                 bzero(comp_p, 16);
  276.                 if ((REpeekc = REgetc()) == '^') {
  277.                     *last_p = NONE_OF;
  278.                     /* Get it for real this time. */
  279.                     (void) REgetc();
  280.                 }
  281.                 chrcnt = 1;
  282.                 while ((c = REgetc()) != ']' && c != 0) {
  283.                     if (c == '\\')
  284.                         c = REgetc();
  285.                 else if ((REpeekc = REgetc()) == '-') {
  286.                     int    c2;
  287.  
  288.                     (void) REgetc();     /* reread '-' */
  289.                     c2 = REgetc();
  290.                     while (c < c2) {
  291.                         comp_p[c/8] |= (1 << (c%8));
  292.                         c += 1;
  293.                     }
  294.                 }
  295.                 comp_p[c/8] |= (1 << (c%8));
  296.                     chrcnt += 1;
  297.                 }
  298.                 if (c == 0)
  299.                     complain("Missing ].");
  300.                 if (chrcnt == 1)
  301.                     complain("Empty [].");
  302.                 comp_p += 16;
  303.                 break;
  304.             }
  305.  
  306.         case '*':
  307.             if (last_p == 0 || *last_p <= NOSTR)
  308.                 goto defchar;
  309.  
  310.             /* The * operator applies only to the previous
  311.                character.  If we were building a chr_cnt at
  312.                the time we got the *, we have to remove the
  313.                last character from the chr_cnt (by decrementing
  314.                *chr_cnt) and replacing it with a new STAR entry.
  315.  
  316.                If we are decrementing the count to 0, we just
  317.                delete the chr_cnt entry altogether, replacing
  318.                it with the STAR entry. */
  319.  
  320.             if (chr_cnt) {
  321.                 char    lastc = chr_cnt[*chr_cnt];
  322.  
  323.              /* The * operator applies only to the previous
  324.                 character.  If we were building a chr_cnt at
  325.                 the time we got the *, we have to remove the
  326.                 last character from the chr_cnt (by decrementing
  327.                 *chr_cnt) and replacing it with a new STAR entry.
  328.  
  329.                 If we are decrementing the count to 0, we just
  330.                 delete the chr_cnt entry altogether, replacing
  331.                 it with the STAR entry. */
  332.  
  333.                  if (*chr_cnt == 1) {
  334.                      comp_p = chr_cnt;
  335.                      comp_p[-1] |= STAR;
  336.                      *comp_p++ = lastc;
  337.                  } else {
  338.                      comp_p = chr_cnt + *chr_cnt;
  339.                      (*chr_cnt) -= 1;
  340.                      *comp_p++ = chr_cnt[-1] | STAR;
  341.                      *comp_p++ = lastc;
  342.                  }
  343.             } else
  344.                 *last_p |= STAR;
  345.             break;
  346.         default:
  347. defchar:        if (chr_cnt)
  348.                 (*chr_cnt) += 1;
  349.             else {
  350.                 *comp_p++ = (CaseIgnore) ? CINDC : NORMC;
  351.                 chr_cnt = comp_p++;
  352.                 *chr_cnt = 1;   /* last_p[1] = 1; */
  353.             }
  354.             *comp_p++ = c;
  355.             continue;
  356.         }
  357.         chr_cnt = FALSE;
  358.     }
  359. outahere:
  360.     /* End of pattern, let's do some error checking. */
  361.     if (kind == OKAY_RE) {
  362.         *comp_p++ = CLOSEP;
  363.         *comp_p++ = *--parenp;
  364.     }
  365.     if (parenp != parens)
  366.         complain("Unmatched ()'s.");
  367.     if (kind == IN_CB && c == 0)    /* End of pattern with \}. */
  368.         complain("Missing \}.");
  369.     *comp_p++ = EOP;
  370.  
  371.     return ret_code;
  372. }
  373.  
  374. private char    *pstrtlst[NPAR],    /* index into REbuf */
  375.         *pendlst[NPAR],
  376.         *REbolp,
  377.         *locs,
  378.         *loc1,
  379.         *loc2;
  380.  
  381. int    REbom,
  382.     REeom,        /* beginning and end of match */
  383.     REalt_num;    /* if alternatives, which one matched? */
  384.  
  385. private int
  386. backref(n, linep)
  387. register char    *linep;
  388. {
  389.     register char    *backsp,
  390.             *backep;
  391.  
  392.     backsp = pstrtlst[n];
  393.     backep = pendlst[n];
  394.     while (*backsp++ == *linep++)
  395.         if (backsp >= backep)
  396.             return 1;
  397.     return 0;
  398. }
  399.  
  400. private int
  401. member(comp_p, c, af)
  402. register char    *comp_p;
  403. register int    c,
  404.         af;
  405. {
  406.     if (c == 0)
  407.         return 0;    /* try to match EOL always fails */
  408.     if (comp_p[c/8] & (1 << (c%8)))
  409.         return af;
  410.     return !af;
  411. }
  412.  
  413. private int
  414. REmatch(linep, comp_p)
  415. register char    *linep,
  416.         *comp_p;
  417. {
  418.     char    *first_p = linep;
  419.     register int    n;
  420.  
  421.     for (;;) switch (*comp_p++) {
  422.     case NORMC:
  423.         n = *comp_p++;
  424.         while (--n >= 0)
  425.             if (*linep++ != *comp_p++)
  426.                 return 0;
  427.         continue;
  428.  
  429.     case CINDC:    /* case independent comparison */
  430.         n = *comp_p++;
  431.         while (--n >= 0)
  432.             if (!cind_cmp(*linep++, *comp_p++))
  433.                 return 0;
  434.         continue;
  435.  
  436.     case EOP:
  437.         loc2 = linep;
  438.         REeom = (loc2 - REbolp);
  439.         return 1;    /* Success! */
  440.  
  441.     case AT_BOL:
  442.         if (linep == REbolp)
  443.             continue;
  444.         return 0;
  445.  
  446.     case AT_EOL:
  447.         if (*linep == 0)
  448.             continue;
  449.         return 0;
  450.  
  451.     case ANYC:
  452.         if (*linep++ != 0)
  453.             continue;
  454.         return 0;
  455.  
  456.     case AT_BOW:
  457.         if (ismword(*linep) && (linep == REbolp || !ismword(linep[-1])))
  458.             continue;
  459.         return 0;
  460.  
  461.     case AT_EOW:
  462.         if ((*linep == 0 || !ismword(*linep)) &&
  463.             (linep != REbolp && ismword(linep[-1])))
  464.             continue;
  465.         return 0;
  466.  
  467.     case ONE_OF:
  468.     case NONE_OF:
  469.         if (member(comp_p, *linep++, comp_p[-1] == ONE_OF)) {
  470.             comp_p += 16;
  471.             continue;
  472.         }
  473.         return 0;
  474.  
  475.     case OPENP:
  476.         pstrtlst[*comp_p++] = linep;
  477.         continue;
  478.  
  479.     case CLOSEP:
  480.         pendlst[*comp_p++] = linep;
  481.         continue;
  482.  
  483.     case BACKREF:
  484.         if (pstrtlst[n = *comp_p++] == 0) {
  485.             s_mess("\\%d was not specified.", n + 1);
  486.             return 0;
  487.         }
  488.         if (backref(n, linep)) {
  489.             linep += pendlst[n] - pstrtlst[n];
  490.             continue;
  491.         }
  492.         return 0;
  493.  
  494.     case CURLYB:
  495.         {
  496.             int    wcnt,
  497.                 any;
  498.  
  499.             wcnt = *comp_p++;
  500.             any = 0;
  501.  
  502.             while (--wcnt >= 0) {
  503.                 if (any == 0)
  504.                     any = REmatch(linep, comp_p + 1);
  505.                 comp_p += *comp_p;
  506.             }
  507.             if (any == 0)
  508.                 return 0;
  509.             linep = loc2;
  510.             continue;
  511.         }
  512.  
  513.     case ANYC | STAR:
  514.         first_p = linep;
  515.         while (*linep++)
  516.             ;
  517.         goto star;
  518.  
  519.     case NORMC | STAR:
  520.         first_p = linep;
  521.         while (*comp_p == *linep++)
  522.             ;
  523.         comp_p += 1;
  524.         goto star;
  525.  
  526.     case CINDC | STAR:
  527.         first_p = linep;
  528.         while (cind_cmp(*comp_p, *linep++))
  529.             ;
  530.         comp_p += 1;
  531.         goto star;
  532.  
  533.     case ONE_OF | STAR:
  534.     case NONE_OF | STAR:
  535.         first_p = linep;
  536.         while (member(comp_p, *linep++, comp_p[-1] == (ONE_OF | STAR)))
  537.             ;
  538.         comp_p += 16;
  539.         goto star;
  540.  
  541.     case BACKREF | STAR:
  542.         first_p = linep;
  543.         n = *comp_p++;
  544.         while (backref(n, linep))
  545.             linep += pendlst[n] - pstrtlst[n];
  546.         while (linep >= first_p) {
  547.             if (REmatch(linep, comp_p))
  548.                 return 1;
  549.             linep -= pendlst[n] - pstrtlst[n];
  550.         }
  551.         continue;
  552.  
  553. star:        do {
  554.             linep -= 1;
  555.             if (linep < locs)
  556.                 break;
  557.             if (REmatch(linep, comp_p))
  558.                 return 1;
  559.         } while (linep > first_p);
  560.         return 0;
  561.  
  562.     default:
  563.         complain("RE error match (%d).", comp_p[-1]);
  564.     }
  565.     /* NOTREACHED. */
  566. }
  567.  
  568. private void
  569. REreset()
  570. {
  571.     register int    i;
  572.  
  573.     for (i = 0; i < NPAR; i++)
  574.         pstrtlst[i] = pendlst[i] = 0;
  575. }
  576.  
  577. /* Index LINE at OFFSET, the compiled EXPR, with alternates ALTS.  If
  578.    lbuf_okay is nonzero it's okay to use linebuf if LINE is the current
  579.    line.  This should save lots of time in things like paren matching in
  580.    LISP mode.  Saves all that copying from linebuf to REbuf.  substitute()
  581.    is the guy who calls re_lindex with lbuf_okay as 0, since the substitution
  582.    gets placed in linebuf ... doesn't work too well when the source and
  583.    destination strings are the same.  I hate all these arguments!
  584.  
  585.    This code is cumbersome, repetetive for reasons of efficiency.  Fast
  586.    search is a must as far as I am concerned. */
  587.  
  588. int
  589. re_lindex(line, offset, expr, alts, lbuf_okay)
  590. Line    *line;
  591. char    *expr,
  592.     **alts;
  593. {
  594.     int    isquick;
  595.     register int    firstc,
  596.             c;
  597.     register char    *resp;
  598.  
  599.     REreset();
  600.     if (lbuf_okay) {
  601.         REbolp = lbptr(line);
  602.         if (offset == -1)
  603.             offset = strlen(REbolp);    /* arg! */
  604.     } else {
  605.         REbolp = ltobuf(line, REbuf);
  606.         if (offset == -1) {    /* Reverse search, find end of line. */
  607.             extern int    Jr_Len;
  608.  
  609.             offset = Jr_Len;    /* Just Read Len. */
  610.         }
  611.     }
  612.     resp = REbolp;
  613.     isquick = ((expr[0] == NORMC || expr[0] == CINDC) &&
  614.            (alternates[1] == 0));
  615.     if (isquick) {
  616.         firstc = expr[2];
  617.         if (expr[0] == CINDC)
  618.             firstc = CaseEquiv[firstc];
  619.     }
  620.     locs = REbolp + offset;
  621.  
  622.     if (REdirection == FORWARD) {
  623.         do {
  624.         char    **altp = alts;
  625.  
  626.         if (isquick) {
  627.             if (expr[0] == NORMC)
  628.                 while ((c = *locs++) != 0 && c != firstc)
  629.                     ;
  630.             else
  631.                 while (((c = *locs++) != 0) &&
  632.                     (CaseEquiv[c] != firstc))
  633.                     ;
  634.             if (*--locs == 0)
  635.                 break;
  636.         }
  637.         REalt_num = 1;
  638.         while (*altp) {
  639.             if (REmatch(locs, *altp++)) {
  640.                 loc1 = locs;
  641.                 REbom = loc1 - REbolp;
  642.                 return 1;
  643.             }
  644.             REalt_num += 1;
  645.         }
  646.         } while (*locs++);
  647.     } else {
  648.         do {
  649.         char    **altp = alts;
  650.  
  651.         if (isquick) {
  652.             if (expr[0] == NORMC) {
  653.                 while (locs >= REbolp && *locs-- != firstc)
  654.                     ;
  655.                 if (*++locs != firstc)
  656.                     break;
  657.             } else {
  658.                 while (locs >= REbolp && CaseEquiv[*locs--] != firstc)
  659.                     ;
  660.                 if (CaseEquiv[*++locs] != firstc)
  661.                     break;
  662.             }
  663.         }
  664.         REalt_num = 1;
  665.         while (*altp) {
  666.             if (REmatch(locs, *altp++)) {
  667.                 loc1 = locs;
  668.                 REbom = loc1 - REbolp;
  669.                 return 1;
  670.             }
  671.             REalt_num += 1;
  672.         }
  673.         } while (--locs >= resp);
  674.     }
  675.  
  676.     return 0;
  677. }
  678.  
  679. int    okay_wrap = 0;    /* Do a wrap search ... not when we're
  680.                parsing errors ... */
  681.  
  682. Bufpos *
  683. dosearch(pattern, dir, re)
  684. char    *pattern;
  685. {
  686.     Bufpos    *pos;
  687.  
  688.     if (bobp() && eobp())    /* Can't match!  There's no buffer. */
  689.         return 0;
  690.  
  691.     REcompile(pattern, re, compbuf, alternates);
  692.  
  693.     pos = docompiled(dir, compbuf, alternates);
  694.     return pos;
  695. }
  696.  
  697. Bufpos *
  698. docompiled(dir, expr, alts)
  699. char    *expr,
  700.     **alts;
  701. {
  702.     static Bufpos    ret;
  703.     register Line    *lp;
  704.     register int    offset;
  705.     int    we_wrapped = NO;
  706.  
  707.     lsave();
  708.     /* Search now lsave()'s so it doesn't make any assumptions on
  709.        whether the the contents of curline/curchar are in linebuf.
  710.        Nowhere does search write all over linebuf.  However, we have to
  711.        be careful about what calls we make here, because many of them
  712.        assume (and rightly so) that curline is in linebuf. */
  713.  
  714.     REdirection = dir;
  715.     lp = curline;
  716.     offset = curchar;
  717.     if (dir == BACKWARD) {
  718.         if (bobp()) {
  719.             if (okay_wrap && WrapScan)
  720.                 goto doit;
  721.             return 0;
  722.         }
  723.         /* here we simulate BackChar() */
  724.         if (bolp()) {
  725.             lp = lp->l_prev;
  726.             offset = strlen(lbptr(lp));
  727.         } else
  728.             offset -= 1;
  729.     } else if ((dir == FORWARD) &&
  730.            (lbptr(lp)[offset] == '\0') &&
  731.            !lastp(lp)) {
  732.         lp = lp->l_next;
  733.         offset = 0;
  734.     }
  735.  
  736.     do {
  737.         if (re_lindex(lp, offset, expr, alts, YES))
  738.             break;
  739. doit:        lp = (dir == FORWARD) ? lp->l_next : lp->l_prev;
  740.         if (lp == 0) {
  741.             if (okay_wrap && WrapScan) {
  742.                 lp = (dir == FORWARD) ?
  743.                      curbuf->b_first : curbuf->b_last;
  744.                 we_wrapped = YES;
  745.             } else
  746.                  break;
  747.         }
  748.         if (dir == FORWARD)
  749.             offset = 0;
  750.         else
  751.             offset = -1;    /* signals re_lindex ... */
  752.     } while (lp != curline);
  753.  
  754.     if (lp == curline && we_wrapped)
  755.         lp = 0;
  756.     if (lp == 0)
  757.         return 0;
  758.     ret.p_line = lp;
  759.     ret.p_char = (dir == FORWARD) ? REeom : REbom;
  760.     return &ret;
  761. }
  762.  
  763. private char *
  764. insert(off, endp, which)
  765. char    *off,
  766.     *endp;
  767. {
  768.     register char    *pp;
  769.     register int    n;
  770.  
  771.     n = pendlst[which] - pstrtlst[which];
  772.     pp = pstrtlst[which];
  773.     while (--n >= 0) {
  774.         *off++ = *pp++;
  775.         if (off >= endp)
  776.             len_error(ERROR);
  777.     }
  778.     return off;
  779. }
  780.  
  781. /* Perform the substitution.  If DELP is nonzero the matched string is
  782.    deleted, i.e., the substitution string is not inserted. */
  783.  
  784. void
  785. re_dosub(tobuf, delp)
  786. char    *tobuf;
  787. {
  788.     register char    *tp,
  789.             *rp,
  790.             *repp;
  791.     int    c;
  792.     char    *endp;
  793.  
  794.     tp = tobuf;
  795.     endp = tp + LBSIZE;
  796.     rp = REbuf;
  797.     repp = rep_str;
  798.  
  799.     while (rp < loc1)
  800.         *tp++ = *rp++;
  801.  
  802.     if (!delp) while (c = *repp++) {
  803.         if (c == '\\') {
  804.             c = *repp++;
  805.             if (c == '\0') {
  806.                 *tp++ = '\\';
  807.                   goto endchk;
  808.             } else if (c >= '1' && c <= nparens + '1') {
  809.                 tp = insert(tp, endp, c - '0');
  810.                 continue;
  811.             }
  812.         } else if (c == '&') {
  813.             tp = insert(tp, endp, 0);
  814.             continue;
  815.         }
  816.         *tp++ = c;
  817. endchk:        if (tp >= endp)
  818.             len_error(ERROR);
  819.     }
  820.     rp = loc2;
  821.     loc2 = REbuf + max(1, tp - tobuf);
  822.     REeom = loc2 - REbuf;
  823.     /* At least one character past the match, to prevent an infinite
  824.        number of replacements in the same position, e.g.,
  825.        replace "^" with "". */
  826.     while (*tp++ = *rp++)
  827.         if (tp >= endp)
  828.             len_error(ERROR);
  829. }
  830.  
  831. void
  832. putmatch(which, buf, size)
  833. char    *buf;
  834. {
  835.     *(insert(buf, buf + size, which)) = 0;
  836. }
  837.  
  838. void
  839. setsearch(str)
  840. char    *str;
  841. {
  842.     strcpy(searchstr, str);
  843. }
  844.  
  845. char *
  846. getsearch()
  847. {
  848.     return searchstr;
  849. }
  850.  
  851. void
  852. RErecur()
  853. {
  854.     char    sbuf[sizeof searchstr],
  855.         cbuf[sizeof compbuf],
  856.         repbuf[sizeof rep_str],
  857.         *altbuf[NALTS];
  858.     int    npars;
  859.     Mark    *m = MakeMark(curline, REbom, M_FLOATER);
  860.  
  861.     message("Type C-X C-C to continue with query replace.");
  862.  
  863.     npars = nparens;
  864.     byte_copy(compbuf, cbuf, sizeof compbuf);
  865.     byte_copy(searchstr, sbuf, sizeof searchstr);
  866.     byte_copy(rep_str, repbuf, sizeof rep_str);
  867.     byte_copy((char *) alternates, (char *) altbuf, sizeof alternates);
  868.     Recur();
  869.     nparens = npars;
  870.     byte_copy(cbuf, compbuf, sizeof compbuf);
  871.     byte_copy(sbuf, searchstr, sizeof searchstr);
  872.     byte_copy(repbuf, rep_str, sizeof rep_str);
  873.     byte_copy((char *) altbuf, (char *) alternates, sizeof alternates);
  874.     if (!is_an_arg())
  875.         ToMark(m);
  876.     DelMark(m);
  877. }
  878.  
  879. void
  880. ForSearch()
  881. {
  882.     search(FORWARD, UseRE, YES);
  883. }
  884.  
  885. void
  886. RevSearch()
  887. {
  888.     search(BACKWARD, UseRE, YES);
  889. }
  890.  
  891. void
  892. FSrchND()
  893. {
  894.     search(FORWARD, UseRE, NO);
  895. }
  896.  
  897. void
  898. RSrchND()
  899. {
  900.     search(BACKWARD, UseRE, NO);
  901. }
  902.  
  903. private void
  904. search(dir, re, setdefault)
  905. {
  906.     Bufpos    *newdot;
  907.     char    *s;
  908.  
  909.     s = ask(searchstr, ProcFmt);
  910.     if (setdefault)
  911.         setsearch(s);
  912.     okay_wrap = YES;
  913.     newdot = dosearch(s, dir, re);
  914.     okay_wrap = NO;
  915.     if (newdot == 0) {
  916.         if (WrapScan)
  917.             complain("No \"%s\" in buffer.", s);
  918.         else
  919.             complain("No \"%s\" found to %s.", s,
  920.                  (dir == FORWARD) ? "bottom" : "top");
  921.     }
  922.     PushPntp(newdot->p_line);
  923.     SetDot(newdot);
  924. }
  925.  
  926. /* Do we match PATTERN at OFFSET in BUF? */
  927.  
  928. int
  929. LookingAt(pattern, buf, offset)
  930. char    *pattern,
  931.     *buf;
  932. {
  933.     register char    **alt = alternates;
  934.  
  935.     REcompile(pattern, 1, compbuf, alternates);
  936.     REreset();
  937.     locs = buf + offset;
  938.     REbolp = buf;
  939.  
  940.     while (*alt)
  941.         if (REmatch(locs, *alt++))
  942.             return 1;
  943.     return 0;
  944. }
  945.  
  946. int
  947. look_at(expr)
  948. char    *expr;
  949. {
  950.     REcompile(expr, 0, compbuf, alternates);
  951.     REreset();
  952.     locs = linebuf + curchar;
  953.     REbolp = linebuf;
  954.     if (REmatch(locs, alternates[0]))
  955.         return 1;
  956.     return 0;
  957. }
  958.  
  959.