home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / jove-4.16-src.tgz / tar.out / bsd / jove / reapp.c < prev    next >
C/C++ Source or Header  |  1996-09-28  |  14KB  |  682 lines

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