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