|
|
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 "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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.