|
|
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.