home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / jove414s.zip / re1.c < prev    next >
C/C++ Source or Header  |  1991-07-06  |  14KB  |  578 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. #include "jove.h"
  9. #include "fp.h"
  10. #include "re.h"
  11. #include "ctype.h"
  12. #include "chars.h"
  13. #include "disp.h"
  14. #ifdef MAC
  15. #include "mac.h"
  16. #else
  17. #include <sys/stat.h>
  18. #endif
  19.  
  20. private Bufpos *doisearch proto((int, int, int));
  21.  
  22. private void
  23. IncSearch proto((int)),
  24.  replace proto((int, int));
  25. private int
  26. isearch proto((int, Bufpos *)),
  27.  lookup proto((char *, char *, char *, char *)),
  28.  substitute proto((struct RE_block *, int, Line *, int, Line *, int));
  29.  
  30. private int substitute(re_blk, query, l1, char1, l2, char2)
  31. struct RE_block *re_blk;
  32. Line *l1, *l2;
  33. int query, char1, char2;
  34. {
  35.     int _fastcall jgetchar(void);
  36.     Line *lp;
  37.     int numdone = 0, UNDO_nd = 0, offset = char1, stop = NO;
  38.     daddr UNDO_da = 0;
  39.     Line *UNDO_lp = NULL;
  40.  
  41.     lsave();
  42.     REdirection = FORWARD;
  43.  
  44.     for (lp = l1; lp != l2->l_next; lp = lp->l_next) {
  45.     int crater = -1;    /* end of last substitution on this line */
  46.     int LineDone = NO;    /* already replaced last empty string on line? */
  47.  
  48.     while (!LineDone
  49.            && re_lindex(lp, offset, re_blk, NO, crater)
  50.            && (lp != l2 || REeom <= char2)) {
  51.         DotTo(lp, REeom);
  52.         offset = curchar;
  53.         if (query) {
  54.         int c;
  55.  
  56.         message("Replace (Type '?' for help)? ");
  57.           reswitch:
  58.         redisplay();
  59.         c = jgetchar();
  60.         if (c == AbortChar)
  61.             return numdone;
  62.  
  63.         switch (CharUpcase(c)) {
  64.         case '.':
  65.             stop = YES;
  66.             /*FALLTHROUGH*/
  67.         case ' ':
  68.         case 'Y':
  69.             break;
  70.  
  71.         case BS:
  72.         case RUBOUT:
  73.         case 'N':
  74.             if (REbom == REeom) {
  75.             offset += 1;
  76.             if (linebuf[REeom] == '\0')
  77.                 LineDone = YES;
  78.             }
  79.             continue;
  80.  
  81.         case CTL('W'):
  82.             re_dosub(re_blk, linebuf, YES);
  83.             if (lp == l2)
  84.             char2 += REdelta;
  85.             modify();
  86.             numdone += 1;
  87.             curchar = REbom;
  88.             makedirty(curline);
  89.             UNDO_da = curline->l_dline;
  90.             UNDO_lp = curline;
  91.             /*FALLTHROUGH*/
  92.         case CTL('R'):
  93.         case 'R':
  94.             RErecur();
  95.             UNDO_lp = NULL;    /* can't reliably undo this */
  96.             offset = curchar;
  97.             lp = curline;
  98.             continue;
  99.  
  100.         case CTL('U'):
  101.         case 'U':
  102.             if (UNDO_lp == NULL) {
  103.             rbell();
  104.             goto reswitch;
  105.             }
  106.             if (UNDO_lp == NULL)
  107.             getline(UNDO_da, linebuf);    /* someone ought to */
  108.             lp = UNDO_lp;
  109.             lp->l_dline = UNDO_da;
  110.             makedirty(lp);
  111.             offset = 0;
  112.             numdone = UNDO_nd;
  113.             UNDO_lp = NULL;
  114.             continue;
  115.  
  116.         case 'P':
  117.         case '!':
  118.             query = 0;
  119.             break;
  120.  
  121.         case CR:
  122.         case LF:
  123.         case 'Q':
  124.             return numdone;
  125.  
  126.         case CTL('L'):
  127.             RedrawDisplay();
  128.             goto reswitch;
  129.  
  130.         default:
  131.             rbell();
  132.             message("Space or Y, Period, Rubout or N, C-R or R, C-W, C-U or U, P or !, Return.");
  133.             goto reswitch;
  134.         }
  135.         }
  136.         if (UNDO_lp != curline) {
  137.         UNDO_da = curline->l_dline;
  138.         UNDO_lp = curline;
  139.         UNDO_nd = numdone;
  140.         }
  141.         if (REbom == REeom && linebuf[REeom] == '\0')
  142.         LineDone = YES;
  143.         re_dosub(re_blk, linebuf, NO);
  144.         if (lp == l2)
  145.         char2 += REdelta;
  146.         numdone += 1;
  147.         modify();
  148.         crater = offset = curchar = REeom;
  149.         makedirty(curline);
  150.         if (query) {
  151.         message(mesgbuf);    /* no blinking */
  152.         redisplay();    /* show the change */
  153.         }
  154.         if (stop)
  155.         return numdone;
  156.     }
  157.     offset = 0;
  158.     }
  159.     return numdone;
  160. }
  161.  
  162. /* prompt for search and replacement strings and do the substitution */
  163. private void replace(query, inreg)
  164. int query, inreg;
  165. {
  166.     Mark *m;
  167.     char *rep_ptr;
  168.     Line *l1 = curline, *l2 = curbuf->b_last;
  169.     int char1 = curchar, char2 = length(curbuf->b_last), numdone;
  170.     struct RE_block re_blk;
  171.  
  172.     if (inreg) {
  173.     m = CurMark();
  174.     l2 = m->m_line;
  175.     char2 = m->m_char;
  176.     (void) fixorder(&l1, &char1, &l2, &char2);
  177.     }
  178.     /* get search string */
  179.     strcpy(rep_search, ask(rep_search[0] ? rep_search : (char *) 0, ProcFmt));
  180.     REcompile(rep_search, UseRE, &re_blk);
  181.     /* Now the replacement string.  Do_ask() so the user can play with
  182.        the default (previous) replacement string by typing C-R in ask(),
  183.        OR, he can just hit Return to replace with nothing. */
  184.     rep_ptr = do_ask("\r\n", (int (*) proto((int))) 0, rep_str,
  185.              ": %f %s with ", rep_search);
  186.     if (rep_ptr == 0)
  187.     rep_ptr = NullStr;
  188.     strcpy(rep_str, rep_ptr);
  189.  
  190.     if (((numdone = substitute(&re_blk, query, l1, char1, l2, char2)) != 0) &&
  191.     (inreg == NO)) {
  192.     do_set_mark(l1, char1);
  193.     add_mess(" ");        /* just making things pretty */
  194.     } else
  195.     message("");
  196.     add_mess("(%d substitution%n)", numdone, numdone);
  197. }
  198.  
  199. void RegReplace()
  200. {
  201.     replace(0, YES);
  202. }
  203.  
  204. void QRepSearch()
  205. {
  206.     replace(1, NO);
  207. }
  208.  
  209. void RepSearch()
  210. {
  211.     replace(0, NO);
  212. }
  213.  
  214. /* Lookup a tag in tag file FILE.  FILE is assumed to be sorted
  215.    alphabetically.  The FASTTAGS code, which is implemented with
  216.    a binary search, depends on this assumption.  If it's not true
  217.    it is possible to comment out the fast tag code (which is clearly
  218.    labeled), delete the marked test in the sequential loop, and
  219.    everything else will just work. */
  220.  
  221. private int lookup(searchbuf, filebuf, tag, file)
  222. char *searchbuf, *filebuf, *tag, *file;
  223. {
  224.     /* register */ size_t taglen = strlen(tag);
  225.     char line[JBUFSIZ], pattern[128];
  226.     /* register */ File *fp;
  227.     struct stat stbuf;
  228.     int success = NO;
  229.  
  230.     fp = open_file(file, iobuff, F_READ, NO, YES);
  231.     if (fp == NIL)
  232.     return NO;
  233.     swritef(pattern, "^%s[^\t]*\t*\\([^\t]*\\)\t*\\([?/]\\)\\(.*\\)\\2$", tag);
  234.  
  235.     /* ********BEGIN FAST TAG CODE******** */
  236.  
  237.     if (stat(file, &stbuf) >= 0) {
  238.     /* Invariant: if there is a line matching the tag, it
  239.      * begins somewhere after position lower, and begins
  240.      * at or before upper.  There is one possible
  241.      * exception: if lower is 0, the line with the tag
  242.      * might be the very first line.
  243.      *
  244.      * When this loop is done, we seek to lower, advance
  245.      * past the next newline (unless lower is 0), and fall
  246.      * into the sequential search.
  247.      */
  248.     /* register */ off_t lower = 0;
  249.     /* register */ off_t upper = stbuf.st_size;
  250.  
  251.     for (;;) {
  252.         off_t mid;
  253.         int chars_eq;
  254.  
  255.         if (upper - lower < JBUFSIZ)
  256.         break;        /* small range: search sequentially */
  257.         mid = (lower + upper) / 2;
  258.         f_seek(fp, mid);    /* mid will not be 0 */
  259.         f_toNL(fp);
  260.         if (f_gets(fp, line, sizeof line) == EOF)
  261.         break;        /* unexpected: bail out */
  262.         chars_eq = numcomp(line, tag);
  263.         if (chars_eq == taglen && iswhite(line[chars_eq])) {
  264.         /* we hit the exact line: get out */
  265.         lower = mid;
  266.         break;
  267.         }
  268.         if (line[chars_eq] < tag[chars_eq])
  269.         lower = mid;    /* line is BEFORE tag */
  270.         else
  271.         upper = mid;    /* line is AFTER tag */
  272.     }
  273.     /* sequentially search from lower */
  274.     f_seek(fp, lower);
  275.     if (lower > 0)
  276.         f_toNL(fp);
  277.     }
  278.     /* END FAST TAG CODE */
  279.  
  280.     while (f_gets(fp, line, sizeof line) != EOF) {
  281.     int cmp = line[0] - *tag;
  282.  
  283.     if (cmp == 0) {
  284.         cmp = strncmp(line, tag, taglen);
  285.         if (cmp == 0) {
  286.         /* we've found the match */
  287.         if (!LookingAt(pattern, line, 0)) {
  288.             complain("I thought I saw it!");
  289.         } else {
  290.             putmatch(1, filebuf, (size_t) FILESIZE);
  291.             putmatch(3, searchbuf, (size_t) 100);
  292.             success = YES;
  293.         }
  294.         break;
  295.         }
  296.     }
  297.     if (cmp > 0)
  298.         break;        /* failure: gone too far.  PRESUMES ALPHABETIC ORDER */
  299.     }
  300.     close_file(fp);
  301.  
  302.     if (success == NO)
  303.     s_mess("Can't find tag \"%s\".", tag);
  304.     return success;
  305. }
  306.  
  307. #if !(defined(MSDOS) || defined(MAC))
  308. char TagFile[FILESIZE] = "./tags";
  309. #else                /* MSDOS */
  310. char TagFile[FILESIZE] = "tags";
  311. #endif                /* MSDOS */
  312.  
  313. void find_tag(tag, localp)
  314. char *tag;
  315. int localp;
  316. {
  317.     char filebuf[FILESIZE], sstr[100], tfbuf[FILESIZE];
  318.     /* register */ Bufpos *bp;
  319.     /* register */ Buffer *b;
  320.     char *tagfname;
  321.  
  322.     if (!localp) {
  323.     char prompt[128];
  324.  
  325.     swritef(prompt, "With tag file (%s default): ", TagFile);
  326.     tagfname = ask_file(prompt, TagFile, tfbuf);
  327.     } else
  328.     tagfname = TagFile;
  329.     if (lookup(sstr, filebuf, tag, tagfname) == 0)
  330.     return;
  331.     set_mark();
  332.     b = do_find(curwind, filebuf, 0);
  333.     if (curbuf != b)
  334.     SetABuf(curbuf);
  335.     SetBuf(b);
  336.     if ((bp = dosearch(sstr, BACKWARD, 0)) == 0 &&
  337.     ((bp = dosearch(sstr, FORWARD, 0)) == 0))
  338.     message("Well, I found the file, but the tag is missing.");
  339.     else
  340.     SetDot(bp);
  341. }
  342.  
  343. void FindTag()
  344. {
  345.     int localp = !is_an_arg();
  346.     char tag[128];
  347.  
  348.     strcpy(tag, ask((char *) 0, ProcFmt));
  349.     find_tag(tag, localp);
  350. }
  351.  
  352. /* Find Tag at Dot. */
  353.  
  354. void FDotTag()
  355. {
  356.     int c1 = curchar, c2 = c1;
  357.     char tagname[50];
  358.  
  359.     if (!ismword(linebuf[curchar]))
  360.     complain("Not a tag!");
  361.     while (c1 > 0 && ismword(linebuf[c1 - 1]))
  362.     c1 -= 1;
  363.     while (ismword(linebuf[c2]))
  364.     c2 += 1;
  365.  
  366.     null_ncpy(tagname, linebuf + c1, (size_t) (c2 - c1));
  367.     find_tag(tagname, !is_an_arg());
  368. }
  369.  
  370. /* I-search returns a code saying what to do:
  371.    STOP:    We found the match, so unwind the stack and leave
  372.         where it is.
  373.    DELETE:    Rubout the last command.
  374.    BACKUP:    Back up to where the isearch was last NOT failing.
  375.  
  376.    When a character is typed it is appended to the search string, and
  377.    then, isearch is called recursively.  When C-S or C-R is typed, isearch
  378.    is again called recursively. */
  379.  
  380. #define STOP    1
  381. #define DELETE    2
  382. #define BACKUP    3
  383. #define TOSTART    4
  384.  
  385. static char ISbuf[128], *incp = 0;
  386. int SExitChar = CR;
  387.  
  388. #define cmp_char(a, b)    ((a) == (b) || (CaseIgnore && (CharUpcase(a) == CharUpcase(b))))
  389.  
  390. static Bufpos *
  391.  doisearch(dir, c, failing)
  392. /* register */ int c, dir, failing;
  393. {
  394.     static Bufpos buf;
  395.     Bufpos *bp;
  396.  
  397.     if (c == CTL('S') || c == CTL('R'))
  398.     goto dosrch;
  399.  
  400.     if (failing)
  401.     return 0;
  402.     DOTsave(&buf);
  403.     if (dir == FORWARD) {
  404.     if (cmp_char(linebuf[curchar], c)) {
  405.         buf.p_char = curchar + 1;
  406.         return &buf;
  407.     }
  408.     } else {
  409.     if (look_at(ISbuf))
  410.         return &buf;
  411.     }
  412.   dosrch:okay_wrap = YES;
  413.     if ((bp = dosearch(ISbuf, dir, 0)) == 0)
  414.     rbell();        /* ring the first time there's no match */
  415.     okay_wrap = NO;
  416.     return bp;
  417. }
  418.  
  419. void IncFSearch()
  420. {
  421.     IncSearch(FORWARD);
  422. }
  423.  
  424. void IncRSearch()
  425. {
  426.     IncSearch(BACKWARD);
  427. }
  428.  
  429. private void IncSearch(dir)
  430. int dir;
  431. {
  432.     Bufpos save_env;
  433.  
  434.     DOTsave(&save_env);
  435.     ISbuf[0] = 0;
  436.     incp = ISbuf;
  437.     if (isearch(dir, &save_env) == TOSTART)
  438.     SetDot(&save_env);
  439.     else {
  440.     if (LineDist(curline, save_env.p_line) >= MarkThresh)
  441.         do_set_mark(save_env.p_line, save_env.p_char);
  442.     }
  443.     setsearch(ISbuf);
  444. }
  445.  
  446. /* Nicely recursive. */
  447.  
  448. private int isearch(dir, bp)
  449. int dir;
  450. Bufpos *bp;
  451. {
  452.     Bufpos pushbp;
  453.     int c, ndir, failing;
  454.     char *orig_incp;
  455.     int      _fastcall get_ch(void);
  456.  
  457.     if (bp != 0) {        /* Move to the new position. */
  458.     pushbp.p_line = bp->p_line;
  459.     pushbp.p_char = bp->p_char;
  460.     SetDot(bp);
  461.     failing = 0;
  462.     } else {
  463.     DOTsave(&pushbp);
  464.     failing = 1;
  465.     }
  466.     orig_incp = incp;
  467.     ndir = dir;            /* Same direction as when we got here, unless
  468.                    we change it with C-S or C-R. */
  469.     for (;;) {
  470.     SetDot(&pushbp);
  471.     message(NullStr);
  472.     if (failing)
  473.         add_mess("Failing ");
  474.     if (dir == BACKWARD)
  475.         add_mess("reverse-");
  476.     add_mess("I-search: %s", ISbuf);
  477.     DrawMesg(NO);
  478.     add_mess(NullStr);    /* tell me this is disgusting ... */
  479.     c = get_ch();
  480.     if (c == SExitChar)
  481.         return STOP;
  482.     if (c == AbortChar) {
  483.         /* If we're failing, we backup until we're no longer
  484.            failing or we've reached the beginning; else, we
  485.            just about the search and go back to the start. */
  486.         if (failing)
  487.         return BACKUP;
  488.         return TOSTART;
  489.     }
  490.     switch (c) {
  491.     case RUBOUT:
  492.     case BS:
  493.         return DELETE;
  494.  
  495.     case CTL('\\'):
  496.         c = CTL('S');
  497.         /*FALLTHROUGH*/
  498.     case CTL('S'):
  499.     case CTL('R'):
  500.         /* If this is the first time through and we have a
  501.            search string left over from last time, use that
  502.            one now. */
  503.         if (incp == ISbuf) {
  504.         strcpy(ISbuf, getsearch());
  505.         incp = &ISbuf[strlen(ISbuf)];
  506.         }
  507.         ndir = (c == CTL('S')) ? FORWARD : BACKWARD;
  508.         /* If we're failing and we're not changing our
  509.            direction, don't recur since there's no way
  510.            the search can work. */
  511.         if (failing && ndir == dir) {
  512.         rbell();
  513.         continue;
  514.         }
  515.         break;
  516.  
  517.     case '\\':
  518.         if (incp > &ISbuf[(sizeof ISbuf) - 1]) {
  519.         rbell();
  520.         continue;
  521.         }
  522.         *incp++ = '\\';
  523.         add_mess("\\");
  524.         /*FALLTHROUGH*/
  525.     case CTL('Q'):
  526.     case CTL('^'):
  527.         add_mess("");
  528.         c = get_ch() | 0400;
  529.         /*FALLTHROUGH*/
  530.     default:
  531.         if (c & 0400)
  532.         c &= CHARMASK;
  533.         else {
  534. #ifdef IBMPC
  535.         if (c == RUBOUT || c == 0xff ||
  536.             (c < ' ' && c != '\t')
  537. #else
  538.         if (c > RUBOUT || (c < ' ' && c != '\t')
  539. #endif
  540.             || PrefChar(c)) {
  541.             Ungetc(c);
  542.             return STOP;
  543.         }
  544.         }
  545.         if (incp > &ISbuf[(sizeof ISbuf) - 1]) {
  546.         rbell();
  547.         continue;
  548.         }
  549.         *incp++ = c;
  550.         *incp = 0;
  551.         break;
  552.     }
  553.     add_mess("%s", orig_incp);
  554.     add_mess(" ...");    /* so we know what's going on */
  555.     DrawMesg(NO);        /* do it now */
  556.     switch (isearch(ndir, doisearch(ndir, c, failing))) {
  557.     case TOSTART:
  558.         return TOSTART;
  559.  
  560.     case STOP:
  561.         return STOP;
  562.  
  563.     case BACKUP:
  564.         /* If we're not failing, we just continue to to the
  565.            for loop; otherwise we keep returning to the
  566.            previous levels until we find one that isn't
  567.            failing OR we reach the beginning. */
  568.         if (failing)
  569.         return BACKUP;
  570.         /*FALLTHROUGH*/
  571.     case DELETE:
  572.         incp = orig_incp;
  573.         *incp = 0;
  574.         continue;
  575.     }
  576.     }
  577. }
  578.