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