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