1: /* Copyright (c) 1979 Regents of the University of California */ 2: #include "ex.h" 3: #include "ex_tty.h" 4: #include "ex_vis.h" 5: 6: /* 7: * This file defines the operation sequences which interface the 8: * logical changes to the file buffer with the internal and external 9: * display representations. 10: */ 11: 12: /* 13: * Undo. 14: * 15: * Undo is accomplished in two ways. We often for small changes in the 16: * current line know how (in terms of a change operator) how the change 17: * occurred. Thus on an intelligent terminal we can undo the operation 18: * by another such operation, using insert and delete character 19: * stuff. The pointers vU[AD][12] index the buffer vutmp when this 20: * is possible and provide the necessary information. 21: * 22: * The other case is that the change involved multiple lines or that 23: * we have moved away from the line or forgotten how the change was 24: * accomplished. In this case we do a redisplay and hope that the 25: * low level optimization routines (which don't look for winning 26: * via insert/delete character) will not lose too badly. 27: */ 28: char *vUA1, *vUA2; 29: char *vUD1, *vUD2; 30: 31: vUndo() 32: { 33: 34: /* 35: * Avoid UU which clobbers ability to do u. 36: */ 37: if (vundkind == VCAPU || vUNDdot != dot) { 38: beep(); 39: return; 40: } 41: CP(vutmp, linebuf); 42: vUD1 = linebuf; vUD2 = strend(linebuf); 43: putmk1(dot, vUNDsav); 44: getDOT(); 45: vUA1 = linebuf; vUA2 = strend(linebuf); 46: vundkind = VCAPU; 47: #ifdef OPENCODE 48: if (state == ONEOPEN || state == HARDOPEN) { 49: vjumpto(dot, vUNDcurs, 0); 50: return; 51: } 52: #endif 53: vdirty(vcline, 1); 54: vsyncCL(); 55: cursor = linebuf; 56: vfixcurs(); 57: } 58: 59: vundo() 60: { 61: register int cnt; 62: register line *addr; 63: register char *cp; 64: char temp[LBSIZE]; 65: bool savenote; 66: int (*OO)(); 67: short oldhold = hold; 68: 69: switch (vundkind) { 70: 71: case VMANYINS: 72: wcursor = 0; 73: addr1 = undap1; 74: addr2 = undap2 - 1; 75: vsave(); 76: YANKreg('1'); 77: notecnt = 0; 78: /* fall into ... */ 79: 80: case VMANY: 81: case VMCHNG: 82: vsave(); 83: addr = dot - vcline; 84: notecnt = 1; 85: if (undkind == UNDPUT && undap1 == undap2) { 86: beep(); 87: return; 88: } 89: /* 90: * Undo() call below basically replaces undap1 to undap2-1 91: * with dol through unddol-1. Hack screen image to 92: * reflect this replacement. 93: */ 94: if (undkind == UNDMOVE) 95: vdirty(0, LINES); 96: else 97: vreplace(undap1 - addr, undap2 - undap1, 98: undkind == UNDPUT ? 0 : unddol - dol); 99: savenote = notecnt; 100: undo(1); 101: if (vundkind != VMCHNG || addr != dot) 102: killU(); 103: vundkind = VMANY; 104: cnt = dot - addr; 105: if (cnt < 0 || cnt > vcnt 106: #ifdef OPENCODE 107: || state != VISUAL 108: #endif 109: ) { 110: vjumpto(dot, NOSTR, '.'); 111: return; 112: } 113: if (!savenote) 114: notecnt = 0; 115: vcline = cnt; 116: vrepaint(vmcurs); 117: vmcurs = 0; 118: return; 119: 120: case VCHNG: 121: case VCAPU: 122: vundkind = VCHNG; 123: strcpy(temp, vutmp); 124: strcpy(vutmp, linebuf); 125: doomed = column(vUA2 - 1) - column(vUA1 - 1); 126: strcLIN(temp); 127: cp = vUA1; vUA1 = vUD1; vUD1 = cp; 128: cp = vUA2; vUA2 = vUD2; vUD2 = cp; 129: cursor = vUD1; 130: #ifdef OPENCODE 131: if (state == HARDOPEN) { 132: doomed = 0; 133: vsave(); 134: vopen(dot, WBOT); 135: vnline(cursor); 136: return; 137: } 138: #endif 139: /* 140: * Pseudo insert command. 141: */ 142: vcursat(cursor); 143: OO = Outchar; Outchar = vinschar; hold |= HOLDQIK; 144: vprepins(); 145: temp[vUA2 - linebuf] = 0; 146: for (cp = &temp[vUA1 - linebuf]; *cp;) 147: putchar(*cp++); 148: Outchar = OO; hold = oldhold; 149: endim(); 150: physdc(cindent(), cindent() + doomed); 151: doomed = 0; 152: vdirty(vcline, 1); 153: vsyncCL(); 154: if (cursor > linebuf && cursor >= strend(linebuf)) 155: cursor--; 156: vfixcurs(); 157: return; 158: 159: case VNONE: 160: beep(); 161: return; 162: } 163: } 164: 165: /* 166: * Initialize undo information before an append. 167: */ 168: vnoapp() 169: { 170: 171: vUD1 = vUD2 = cursor; 172: } 173: 174: /* 175: * All the rest of the motion sequences have one or more 176: * cases to deal with. In the case wdot == 0, operation 177: * is totally within current line, from cursor to wcursor. 178: * If wdot is given, but wcursor is 0, then operation affects 179: * the inclusive line range. The hardest case is when both wdot 180: * and wcursor are given, then operation affects from line dot at 181: * cursor to line wdot at wcursor. 182: */ 183: 184: /* 185: * Move is simple, except for moving onto new lines in hardcopy open mode. 186: */ 187: vmove() 188: { 189: register int cnt; 190: 191: if (wdot) { 192: if (wdot < one || wdot > dol) { 193: beep(); 194: return; 195: } 196: cnt = wdot - dot; 197: wdot = NOLINE; 198: if (cnt) 199: killU(); 200: vupdown(cnt, wcursor); 201: return; 202: } 203: 204: /* 205: * When we move onto a new line, save information for U undo. 206: */ 207: if (vUNDdot != dot) { 208: vUNDsav = *dot; 209: vUNDcurs = wcursor; 210: vUNDdot = dot; 211: } 212: 213: #ifdef OPENCODE 214: /* 215: * In hardcopy open, type characters to left of cursor 216: * on new line, or back cursor up if its to left of where we are. 217: * In any case if the current line is ``rubbled'' i.e. has trashy 218: * looking overstrikes on it or \'s from deletes, we reprint 219: * so it is more comprehensible (and also because we can't work 220: * if we let it get more out of sync since column() won't work right. 221: */ 222: if (state == HARDOPEN) { 223: register char *cp; 224: if (rubble) { 225: register int c; 226: int oldhold = hold; 227: 228: sethard(); 229: cp = wcursor; 230: c = *cp; 231: *cp = 0; 232: hold |= HOLDDOL; 233: vreopen(WTOP, lineDOT(), vcline); 234: hold = oldhold; 235: *cp = c; 236: } else if (wcursor > cursor) { 237: vfixcurs(); 238: for (cp = cursor; *cp && cp < wcursor;) { 239: register int c = *cp++ & TRIM; 240: 241: putchar(c ? c : ' '); 242: } 243: } 244: } 245: #endif 246: vsetcurs(wcursor); 247: } 248: 249: /* 250: * Delete operator. 251: * 252: * Hard case of deleting a range where both wcursor and wdot 253: * are specified is treated as a special case of change and handled 254: * by vchange (although vchange may pass it back if it degenerates 255: * to a full line range delete.) 256: */ 257: vdelete(c) 258: char c; 259: { 260: register char *cp; 261: register int i; 262: 263: if (wdot) { 264: if (wcursor) { 265: vchange('d'); 266: return; 267: } 268: if ((i = xdw()) < 0) 269: return; 270: #ifdef OPENCODE 271: if (state != VISUAL) { 272: vgoto(LINE(0), 0); 273: vputchar('@'); 274: } 275: #endif 276: wdot = dot; 277: vremote(i, delete, 0); 278: notenam = "delete"; 279: DEL[0] = 0; 280: killU(); 281: vreplace(vcline, i, 0); 282: if (wdot > dol) 283: vcline--; 284: vrepaint(NOSTR); 285: return; 286: } 287: if (wcursor < linebuf) 288: wcursor = linebuf; 289: if (cursor == wcursor) { 290: beep(); 291: return; 292: } 293: i = vdcMID(); 294: cp = cursor; 295: setDEL(); 296: CP(cp, wcursor); 297: if (cp > linebuf && (cp[0] == 0 || c == '#')) 298: cp--; 299: #ifdef OPENCODE 300: if (state == HARDOPEN) { 301: bleep(i, cp); 302: cursor = cp; 303: return; 304: } 305: #endif 306: physdc(column(cursor - 1), i); 307: DEPTH(vcline) = 0; 308: vreopen(LINE(vcline), lineDOT(), vcline); 309: vsyncCL(); 310: vsetcurs(cp); 311: } 312: 313: /* 314: * Change operator. 315: * 316: * In a single line we mark the end of the changed area with '$'. 317: * On multiple whole lines, we clear the lines first. 318: * Across lines with both wcursor and wdot given, we delete 319: * and sync then append (but one operation for undo). 320: */ 321: vchange(c) 322: char c; 323: { 324: register char *cp; 325: register int i, ind, cnt; 326: line *addr; 327: 328: if (wdot) { 329: /* 330: * Change/delete of lines or across line boundaries. 331: */ 332: if ((cnt = xdw()) < 0) 333: return; 334: getDOT(); 335: if (wcursor && cnt == 1) { 336: /* 337: * Not really. 338: */ 339: wdot = 0; 340: if (c == 'd') { 341: vdelete(c); 342: return; 343: } 344: goto smallchange; 345: } 346: if (cursor && wcursor) { 347: /* 348: * Across line boundaries, but not 349: * necessarily whole lines. 350: * Construct what will be left. 351: */ 352: *cursor = 0; 353: strcpy(genbuf, linebuf); 354: getline(*wdot); 355: if (strlen(genbuf) + strlen(wcursor) > LBSIZE - 2) { 356: getDOT(); 357: beep(); 358: return; 359: } 360: strcat(genbuf, wcursor); 361: if (c == 'd' && *vpastwh(genbuf) == 0) { 362: /* 363: * Although this is a delete 364: * spanning line boundaries, what 365: * would be left is all white space, 366: * so take it all away. 367: */ 368: wcursor = 0; 369: getDOT(); 370: op = 0; 371: notpart(lastreg); 372: notpart('1'); 373: vdelete(c); 374: return; 375: } 376: ind = -1; 377: } else if (c == 'd' && wcursor == 0) { 378: vdelete(c); 379: return; 380: } else 381: #ifdef LISPCODE 382: /* 383: * We are just substituting text for whole lines, 384: * so determine the first autoindent. 385: */ 386: if (value(LISP) && value(AUTOINDENT)) 387: ind = lindent(dot); 388: else 389: #endif 390: ind = whitecnt(linebuf); 391: i = vcline >= 0 ? LINE(vcline) : WTOP; 392: 393: /* 394: * Delete the lines from the buffer, 395: * and remember how the partial stuff came about in 396: * case we are told to put. 397: */ 398: addr = dot; 399: vremote(cnt, delete, 0); 400: setpk(); 401: notenam = "delete"; 402: if (c != 'd') 403: notenam = "change"; 404: /* 405: * If DEL[0] were nonzero, put would put it back 406: * rather than the deleted lines. 407: */ 408: DEL[0] = 0; 409: if (cnt > 1) 410: killU(); 411: 412: /* 413: * Now hack the screen image coordination. 414: */ 415: vreplace(vcline, cnt, 0); 416: wdot = NOLINE; 417: noteit(0); 418: vcline--; 419: if (addr <= dol) 420: dot--; 421: 422: /* 423: * If this is a across line delete/change, 424: * cursor stays where it is; just splice together the pieces 425: * of the new line. Otherwise generate a autoindent 426: * after a S command. 427: */ 428: if (ind >= 0) { 429: *genindent(ind) = 0; 430: vdoappend(genbuf); 431: } else { 432: vmcurs = cursor; 433: strcLIN(genbuf); 434: vdoappend(linebuf); 435: } 436: 437: #ifdef OPENCODE 438: /* 439: * Indicate a change on hardcopies by 440: * erasing the current line. 441: */ 442: if (c != 'd' && state != VISUAL && state != HARDOPEN) { 443: int oldhold = hold; 444: 445: hold |= HOLDAT, vclrlin(i, dot), hold = oldhold; 446: } 447: #endif 448: 449: /* 450: * Open the line (logically) on the screen, and 451: * update the screen tail. Unless we are really a delete 452: * go off and gather up inserted characters. 453: */ 454: vcline++; 455: if (vcline < 0) 456: vcline = 0; 457: vopen(dot, i); 458: vsyncCL(); 459: noteit(1); 460: if (c != 'd') { 461: if (ind >= 0) { 462: cursor = linebuf; 463: linebuf[0] = 0; 464: vfixcurs(); 465: } else { 466: ind = 0; 467: vcursat(cursor); 468: } 469: vappend('x', 1, ind); 470: return; 471: } 472: if (*cursor == 0 && cursor > linebuf) 473: cursor--; 474: vrepaint(cursor); 475: return; 476: } 477: 478: smallchange: 479: /* 480: * The rest of this is just low level hacking on changes 481: * of small numbers of characters. 482: */ 483: if (wcursor < linebuf) 484: wcursor = linebuf; 485: if (cursor == wcursor) { 486: beep(); 487: return; 488: } 489: i = vdcMID(); 490: cp = cursor; 491: #ifdef OPENCODE 492: if (state != HARDOPEN) 493: #endif 494: vfixcurs(); 495: 496: /* 497: * Put out the \\'s indicating changed text in hardcopy, 498: * or mark the end of the change with $ if not hardcopy. 499: */ 500: #ifdef OPENCODE 501: if (state == HARDOPEN) 502: bleep(i, cp); 503: else { 504: #endif 505: vcursbef(wcursor); 506: putchar('$'); 507: i = cindent(); 508: #ifdef OPENCODE 509: } 510: #endif 511: 512: /* 513: * Remember the deleted text for possible put, 514: * and then prepare and execute the input portion of the change. 515: */ 516: cursor = cp; 517: setDEL(); 518: CP(cursor, wcursor); 519: #ifdef OPENCODE 520: if (state != HARDOPEN) { 521: #endif 522: vcursaft(cursor - 1); 523: doomed = i - cindent(); 524: #ifdef OPENCODE 525: } else { 526: /* 527: sethard(); 528: wcursor = cursor; 529: cursor = linebuf; 530: vgoto(outline, value(NUMBER) << 3); 531: vmove(); 532: */ 533: doomed = 0; 534: } 535: #endif 536: prepapp(); 537: vappend('c', 1, 0); 538: } 539: 540: /* 541: * Open new lines. 542: * 543: * Tricky thing here is slowopen. This causes display updating 544: * to be held off so that 300 baud dumb terminals don't lose badly. 545: * This also suppressed counts, which otherwise say how many blank 546: * space to open up. Counts are also suppressed on intelligent terminals. 547: * Actually counts are obsoleted, since if your terminal is slow 548: * you are better off with slowopen. 549: */ 550: voOpen(c, cnt) 551: char c; 552: register int cnt; 553: { 554: register int ind = 0, i; 555: short oldhold = hold; 556: 557: if (value(SLOWOPEN) || value(REDRAW) && AL && DL) 558: cnt = 1; 559: vsave(); 560: setLAST(); 561: if (value(AUTOINDENT)) 562: ind = whitecnt(linebuf); 563: if (c == 'O') { 564: vcline--; 565: dot--; 566: if (dot > zero) 567: getDOT(); 568: } 569: if (value(AUTOINDENT)) { 570: #ifdef LISPCODE 571: if (value(LISP)) 572: ind = lindent(dot + 1); 573: #endif 574: } 575: killU(); 576: prepapp(); 577: vundkind = VMANY; 578: #ifdef OPENCODE 579: if (state != VISUAL) 580: c = WBOT + 1; 581: else { 582: #endif 583: c = vcline < 0 ? WTOP - cnt : LINE(vcline) + DEPTH(vcline); 584: if (c < ZERO) 585: c = ZERO; 586: i = LINE(vcline + 1) - c; 587: if (i < cnt && c <= WBOT && (!AL || !DL)) 588: vinslin(c, cnt - i, vcline); 589: #ifdef OPENCODE 590: } 591: #endif 592: *genindent(ind) = 0; 593: vdoappend(genbuf); 594: vcline++; 595: oldhold = hold; 596: hold |= HOLDROL; 597: vopen(dot, c); 598: hold = oldhold; 599: if (value(SLOWOPEN)) 600: /* 601: * Oh, so lazy! 602: */ 603: vscrap(); 604: else 605: vsync1(LINE(vcline)); 606: cursor = linebuf; 607: linebuf[0] = 0; 608: vappend('o', 1, ind); 609: } 610: 611: /* 612: * > < and = shift operators. 613: * 614: * Note that =, which aligns lisp, is just a ragged sort of shift, 615: * since it never distributes text between lines. 616: */ 617: char vshnam[2] = { 'x', 0 }; 618: 619: vshftop() 620: { 621: register line *addr; 622: register int cnt; 623: 624: if ((cnt = xdw()) < 0) 625: return; 626: addr = dot; 627: vremote(cnt, vshift, 0); 628: vshnam[0] = op; 629: notenam = vshnam; 630: dot = addr; 631: vreplace(vcline, cnt, cnt); 632: #ifdef OPENCODE 633: if (state == HARDOPEN) 634: vcnt = 0; 635: #endif 636: vrepaint(NOSTR); 637: } 638: 639: /* 640: * !. 641: * 642: * Filter portions of the buffer through unix commands. 643: */ 644: vfilter() 645: { 646: register line *addr; 647: register int cnt; 648: char *oglobp, d; 649: 650: if ((cnt = xdw()) < 0) 651: return; 652: if (vglobp) 653: vglobp = uxb; 654: if (readecho('!')) 655: return; 656: oglobp = globp; globp = genbuf + 1; 657: d = peekc; ungetchar(0); 658: CATCH 659: fixech(); 660: unix0(0); 661: ONERR 662: splitw = 0; 663: ungetchar(d); 664: vrepaint(cursor); 665: globp = oglobp; 666: return; 667: ENDCATCH 668: ungetchar(d); globp = oglobp; 669: addr = dot; 670: CATCH 671: vgoto(WECHO, 0); flusho(); 672: vremote(cnt, filter, 2); 673: ONERR 674: vdirty(0, LINES); 675: ENDCATCH 676: if (dot == zero && dol > zero) 677: dot = one; 678: splitw = 0; 679: notenam = ""; 680: vreplace(vcline, cnt, undap2 - undap1); 681: dot = addr; 682: if (dot > dol) { 683: dot--; 684: vcline--; 685: } 686: vrepaint(NOSTR); 687: } 688: 689: /* 690: * Xdw exchanges dot and wdot if appropriate and also checks 691: * that wdot is reasonable. Its name comes from 692: * xchange dotand wdot 693: */ 694: xdw() 695: { 696: register char *cp; 697: register int cnt; 698: /* 699: register int notp = 0; 700: */ 701: 702: if (wdot == NOLINE || wdot < one || wdot > dol) { 703: beep(); 704: return (-1); 705: } 706: vsave(); 707: setLAST(); 708: if (dot > wdot) { 709: register line *addr; 710: 711: vcline -= dot - wdot; 712: addr = dot; dot = wdot; wdot = addr; 713: cp = cursor; cursor = wcursor; wcursor = cp; 714: } 715: /* 716: * If a region is specified but wcursor is at the begining 717: * of the last line, then we move it to be the end of the 718: * previous line (actually off the end). 719: */ 720: if (cursor && wcursor == linebuf && wdot > dot) { 721: wdot--; 722: getDOT(); 723: if (vpastwh(linebuf) >= cursor) 724: wcursor = 0; 725: else { 726: getline(*wdot); 727: wcursor = strend(linebuf); 728: getDOT(); 729: } 730: /* 731: * Should prepare in caller for possible dot == wdot. 732: */ 733: } 734: cnt = wdot - dot + 1; 735: if (vreg) { 736: vremote(cnt, YANKreg, vreg); 737: /* 738: if (notp) 739: notpart(vreg); 740: */ 741: } 742: 743: /* 744: * Kill buffer code. If delete operator is c or d, then save 745: * the region in numbered buffers. 746: * 747: * BUG: This may be somewhat inefficient due 748: * to the way named buffer are implemented, 749: * necessitating some optimization. 750: */ 751: vreg = 0; 752: if (any(op, "cd")) { 753: vremote(cnt, YANKreg, '1'); 754: /* 755: if (notp) 756: notpart('1'); 757: */ 758: } 759: return (cnt); 760: } 761: 762: /* 763: * Routine for vremote to call to implement shifts. 764: */ 765: vshift() 766: { 767: 768: shift(op, 1); 769: } 770: 771: /* 772: * Replace a single character with the next input character. 773: * A funny kind of insert. 774: */ 775: vrep(cnt) 776: register int cnt; 777: { 778: register int i, c; 779: 780: if (cnt > strlen(cursor)) { 781: beep(); 782: return; 783: } 784: i = column(cursor + cnt - 1); 785: vcursat(cursor); 786: doomed = i - cindent(); 787: if (!vglobp) { 788: c = getesc(); 789: if (c == 0) { 790: vfixcurs(); 791: return; 792: } 793: ungetkey(c); 794: } 795: CP(vutmp, linebuf); 796: vundkind = VCHNG; 797: wcursor = cursor + cnt; 798: vUD1 = cursor; vUD2 = wcursor; 799: CP(cursor, wcursor); 800: prepapp(); 801: vappend('r', cnt, 0); 802: *lastcp++ = INS[0]; 803: setLAST(); 804: } 805: 806: /* 807: * Yank. 808: * 809: * Yanking to string registers occurs for free (essentially) 810: * in the routine xdw(). 811: */ 812: vyankit() 813: { 814: register int cnt; 815: 816: if (wdot) { 817: if ((cnt = xdw()) < 0) 818: return; 819: vremote(cnt, yank, 0); 820: setpk(); 821: notenam = "yank"; 822: vundkind = VNONE; 823: DEL[0] = 0; 824: wdot = NOLINE; 825: if (notecnt <= vcnt - vcline && notecnt < value(REPORT)) 826: notecnt = 0; 827: vrepaint(cursor); 828: return; 829: } 830: takeout(DEL); 831: } 832: 833: /* 834: * Set pkill variables so a put can 835: * know how to put back partial text. 836: * This is necessary because undo needs the complete 837: * line images to be saved, while a put wants to trim 838: * the first and last lines. The compromise 839: * is for put to be more clever. 840: */ 841: setpk() 842: { 843: 844: if (wcursor) { 845: pkill[0] = cursor; 846: pkill[1] = wcursor; 847: } 848: }