1: /************************************************************************* 2: * This program is copyright (C) 1985, 1986 by Jonathan Payne. It is * 3: * provided to you without charge for use only on a licensed Unix * 4: * system. You may copy JOVE provided that this notice is included with * 5: * the copy. You may not sell copies of this program or versions * 6: * modified for use on microcomputer systems, unless the copies are * 7: * included with a Unix system distribution and the source is provided. * 8: *************************************************************************/ 9: 10: #include "jove.h" 11: #include "io.h" 12: #include "re.h" 13: 14: static 15: substitute(query, l1, char1, l2, char2) 16: Line *l1, 17: *l2; 18: { 19: Line *lp; 20: int numdone = 0, 21: offset = curchar, 22: stop = 0; 23: disk_line UNDO_da = 0; 24: Line *UNDO_lp = 0; 25: 26: lsave(); 27: REdirection = FORWARD; 28: 29: lp = l1; 30: for (lp = l1; (lp != l2->l_next) && !stop; lp = lp->l_next) { 31: offset = (lp == l1) ? char1 : 0; 32: while (!stop && re_lindex(lp, offset, compbuf, alternates, 0)) { 33: if (lp == l2 && REeom > char2) /* nope, leave this alone */ 34: break; 35: DotTo(lp, REeom); 36: offset = curchar; 37: if (query) { 38: message("Replace (Type '?' for help)? "); 39: reswitch: redisplay(); 40: switch (Upper(getchar())) { 41: case '.': 42: stop++; 43: /* Fall into ... */ 44: 45: case ' ': 46: case 'Y': 47: break; 48: 49: case BS: 50: case RUBOUT: 51: case 'N': 52: if (linebuf[offset++] == '\0') 53: goto nxtline; 54: continue; 55: 56: case CTL(W): 57: re_dosub(linebuf, YES); 58: numdone++; 59: offset = curchar = REbom; 60: makedirty(curline); 61: /* Fall into ... */ 62: 63: case CTL(R): 64: case 'R': 65: RErecur(); 66: offset = curchar; 67: lp = curline; 68: continue; 69: 70: case CTL(U): 71: case 'U': 72: if (UNDO_lp == 0) 73: continue; 74: lp = UNDO_lp; 75: lp->l_dline = UNDO_da | DIRTY; 76: offset = 0; 77: numdone--; 78: continue; 79: 80: case 'P': 81: case '!': 82: query = 0; 83: break; 84: 85: case CR: 86: case LF: 87: case 'Q': 88: goto done; 89: 90: case CTL(L): 91: RedrawDisplay(); 92: goto reswitch; 93: 94: default: 95: rbell(); 96: message("Space or Y, Period, Rubout or N, C-R or R, C-W, C-U or U, P or !, Return."); 97: goto reswitch; 98: } 99: } 100: re_dosub(linebuf, NO); 101: numdone++; 102: modify(); 103: offset = curchar = REeom; 104: makedirty(curline); 105: if (query) { 106: message(mesgbuf); /* No blinking. */ 107: redisplay(); /* Show the change. */ 108: } 109: UNDO_da = curline->l_dline; 110: UNDO_lp = curline; 111: if (linebuf[offset] == 0) 112: nxtline: break; 113: } 114: } 115: SetMark(); 116: done: s_mess("%d substitution%n.", numdone, numdone); 117: } 118: 119: /* Prompt for search and replacement strings and do the substitution. The 120: point is restored when we're done. */ 121: 122: static 123: replace(query, inreg) 124: { 125: Mark *save = MakeMark(curline, curchar, FLOATER), 126: *m; 127: char *rep_ptr; 128: Line *l1 = curline, 129: *l2 = curbuf->b_last; 130: int char1 = curchar, 131: char2 = length(curbuf->b_last); 132: 133: if (inreg) { 134: m = CurMark(); 135: l2 = m->m_line; 136: char2 = m->m_char; 137: (void) fixorder(&l1, &char1, &l2, &char2); 138: } 139: 140: /* Get search string. */ 141: strcpy(rep_search, ask(rep_search[0] ? rep_search : (char *) 0, ProcFmt)); 142: REcompile(rep_search, UseRE, compbuf, alternates); 143: /* Now the replacement string. Do_ask() so the user can play with 144: the default (previous) replacement string by typing C-R in ask(), 145: OR, he can just hit Return to replace with nothing. */ 146: rep_ptr = do_ask("\r\n", (int (*)()) 0, rep_str, ": %f %s with ", rep_search); 147: if (rep_ptr == 0) 148: rep_ptr = NullStr; 149: strcpy(rep_str, rep_ptr); 150: 151: substitute(query, l1, char1, l2, char2); 152: ToMark(save); 153: DelMark(save); 154: } 155: 156: RegReplace() 157: { 158: replace(0, YES); 159: } 160: 161: QRepSearch() 162: { 163: replace(1, NO); 164: } 165: 166: RepSearch() 167: { 168: replace(0, NO); 169: } 170: 171: /* C tags package. */ 172: 173: static 174: lookup(searchbuf, filebuf, tag, file) 175: char *searchbuf, 176: *filebuf, 177: *tag, 178: *file; 179: { 180: register int taglen = strlen(tag); 181: char line[128], 182: pattern[100]; 183: File *fp; 184: 185: fp = open_file(file, iobuff, F_READ, !COMPLAIN, QUIET); 186: if (fp == NIL) 187: return 0; 188: sprintf(pattern, "^%s[^\t]*\t\\([^\t]*\\)\t[?/]\\(.*\\)[?/]$", tag); 189: while (f_gets(fp, line, sizeof line) != EOF) { 190: if (line[0] != *tag || strncmp(tag, line, taglen) != 0) 191: continue; 192: if (!LookingAt(pattern, line, 0)) { 193: complain("I thought I saw it!"); 194: break; 195: } else { 196: putmatch(2, searchbuf, 100); 197: putmatch(1, filebuf, 100); 198: close_file(fp); 199: return 1; 200: } 201: } 202: f_close(fp); 203: s_mess("Can't find tag \"%s\".", tag); 204: return 0; 205: } 206: 207: char TagFile[128] = "./tags"; 208: 209: find_tag(tag, localp) 210: char *tag; 211: { 212: char filebuf[FILESIZE], 213: sstr[100]; 214: register Bufpos *bp; 215: register Buffer *b; 216: char *tagfname; 217: 218: if (!localp) 219: tagfname = ask(TagFile, "With tag file (%s default): ", TagFile); 220: else 221: tagfname = TagFile; 222: if (lookup(sstr, filebuf, tag, tagfname) == 0) 223: return; 224: SetMark(); 225: b = do_find(curwind, filebuf, 0); 226: if (curbuf != b) 227: SetABuf(curbuf); 228: SetBuf(b); 229: if ((bp = dosearch(sstr, BACKWARD, 0)) == 0 && 230: (WrapScan || ((bp = dosearch(sstr, FORWARD, 0)) == 0))) 231: message("Well, I found the file, but the tag is missing."); 232: else 233: SetDot(bp); 234: } 235: 236: FindTag() 237: { 238: int localp = !exp_p; 239: char tag[128]; 240: 241: strcpy(tag, ask((char *) 0, ProcFmt)); 242: find_tag(tag, localp); 243: } 244: 245: /* Find Tag at Dot. */ 246: 247: FDotTag() 248: { 249: int c1 = curchar, 250: c2 = c1; 251: char tagname[50]; 252: 253: if (!ismword(linebuf[curchar])) 254: complain("Not a tag!"); 255: while (c1 > 0 && ismword(linebuf[c1 - 1])) 256: c1--; 257: while (ismword(linebuf[c2])) 258: c2++; 259: 260: null_ncpy(tagname, linebuf + c1, c2 - c1); 261: find_tag(tagname, !exp_p); 262: } 263: 264: /* I-search returns a code saying what to do: 265: STOP: We found the match, so unwind the stack and leave 266: where it is. 267: DELETE: Rubout the last command. 268: BACKUP: Back up to where the isearch was last NOT failing. 269: 270: When a character is typed it is appended to the search string, and 271: then, isearch is called recursively. When C-S or C-R is typed, isearch 272: is again called recursively. */ 273: 274: #define STOP 1 275: #define DELETE 2 276: #define BACKUP 3 277: #define TOSTART 4 278: 279: static char ISbuf[128], 280: *incp = 0; 281: int SExitChar = CR; 282: 283: #define cmp_char(a, b) ((a) == (b) || (CaseIgnore && (Upper(a) == Upper(b)))) 284: 285: static Bufpos * 286: doisearch(dir, c, failing) 287: register int c, 288: dir, 289: failing; 290: { 291: static Bufpos buf; 292: Bufpos *bp; 293: 294: if (c == CTL(S) || c == CTL(R)) 295: goto dosrch; 296: 297: if (failing) 298: return 0; 299: DOTsave(&buf); 300: if (dir == FORWARD) { 301: if (cmp_char(linebuf[curchar], c)) { 302: buf.p_char = curchar + 1; 303: return &buf; 304: } 305: } else { 306: if (look_at(ISbuf)) 307: return &buf; 308: } 309: dosrch: if ((bp = dosearch(ISbuf, dir, 0)) == 0) 310: rbell(); /* ring the first time there's no match */ 311: return bp; 312: } 313: 314: IncFSearch() 315: { 316: IncSearch(FORWARD); 317: } 318: 319: IncRSearch() 320: { 321: IncSearch(BACKWARD); 322: } 323: 324: static 325: IncSearch(dir) 326: { 327: Bufpos save_env; 328: 329: DOTsave(&save_env); 330: ISbuf[0] = 0; 331: incp = ISbuf; 332: if (isearch(dir, &save_env) == TOSTART) 333: SetDot(&save_env); 334: else { 335: if (LineDist(curline, save_env.p_line) >= MarkThresh) 336: DoSetMark(save_env.p_line, save_env.p_char); 337: } 338: setsearch(ISbuf); 339: message(ISbuf); 340: } 341: 342: /* Nicely recursive. */ 343: 344: static 345: isearch(dir, bp) 346: Bufpos *bp; 347: { 348: Bufpos pushbp; 349: int c, 350: ndir, 351: failing; 352: char *orig_incp; 353: 354: if (bp != 0) { /* Move to the new position. */ 355: pushbp.p_line = bp->p_line; 356: pushbp.p_char = bp->p_char; 357: SetDot(bp); 358: failing = 0; 359: } else { 360: DOTsave(&pushbp); 361: failing = 1; 362: } 363: orig_incp = incp; 364: ndir = dir; /* Same direction as when we got here, unless 365: we change it with C-S or C-R. */ 366: for (;;) { 367: SetDot(&pushbp); 368: message(NullStr); 369: if (failing) 370: add_mess("Failing "); 371: if (dir == BACKWARD) 372: add_mess("reverse-"); 373: add_mess("I-search: %s", ISbuf); 374: DrawMesg(NO); 375: add_mess(NullStr); /* tell me this is disgusting ... */ 376: c = getch(); 377: if (c == SExitChar) 378: return STOP; 379: switch (c) { 380: case RUBOUT: 381: case BS: 382: return DELETE; 383: 384: case CTL(G): 385: /* If we're failing, we backup until we're no longer 386: failing or we've reached the beginning; else, we 387: just about the search and go back to the start. */ 388: if (failing) 389: return BACKUP; 390: return TOSTART; 391: 392: case CTL(\\): 393: c = CTL(S); 394: case CTL(S): 395: case CTL(R): 396: /* If this is the first time through and we have a 397: search string left over from last time, use that 398: one now. */ 399: if (incp == ISbuf) { 400: strcpy(ISbuf, getsearch()); 401: incp = &ISbuf[strlen(ISbuf)]; 402: } 403: ndir = (c == CTL(S)) ? FORWARD : BACKWARD; 404: /* If we're failing and we're not changing our 405: direction, don't recur since there's no way 406: the search can work. */ 407: if (failing && ndir == dir) { 408: rbell(); 409: continue; 410: } 411: break; 412: 413: case '\\': 414: if (incp > &ISbuf[(sizeof ISbuf) - 1]) { 415: rbell(); 416: continue; 417: } 418: *incp++ = '\\'; 419: add_mess("\\"); 420: /* Fall into ... */ 421: 422: case CTL(Q): 423: case CTL(^): 424: add_mess(""); 425: c = getch() | 0400; 426: /* Fall into ... */ 427: 428: default: 429: if (c & 0400) 430: c &= 0177; 431: else { 432: if (c > RUBOUT || (c < ' ' && c != '\t')) { 433: Ungetc(c); 434: return STOP; 435: } 436: } 437: if (incp > &ISbuf[(sizeof ISbuf) - 1]) { 438: rbell(); 439: continue; 440: } 441: *incp++ = c; 442: *incp = 0; 443: break; 444: } 445: add_mess("%s", orig_incp); 446: add_mess(" ..."); /* so we know what's going on */ 447: DrawMesg(NO); /* do it now */ 448: switch (isearch(ndir, doisearch(ndir, c, failing))) { 449: case TOSTART: 450: return TOSTART; 451: 452: case STOP: 453: return STOP; 454: 455: case BACKUP: 456: /* If we're not failing, we just continue to to the 457: for loop; otherwise we keep returning to the 458: previous levels until we find one that isn't 459: failing OR we reach the beginning. */ 460: if (failing) 461: return BACKUP; 462: /* Fall into ... */ 463: 464: case DELETE: 465: incp = orig_incp; 466: *incp = 0; 467: continue; 468: } 469: } 470: }