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