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