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: * Deal with the screen, clearing, cursor positioning, putting characters 8: * into the screen image, and deleting characters. 9: * Really hard stuff here is utilizing insert character operations 10: * on intelligent terminals which differs widely from terminal to terminal. 11: */ 12: vclear() 13: { 14: 15: #ifdef ADEBUG 16: if (trace) 17: tfixnl(), fprintf(trace, "------\nvclear\n"); 18: #endif 19: tputs(CL, LINES, putch); 20: destcol = 0; 21: outcol = 0; 22: destline = 0; 23: outline = 0; 24: if (inopen) 25: vclrbyte(vtube0, WCOLS * (WECHO - ZERO + 1)); 26: } 27: 28: /* 29: * Clear memory. 30: */ 31: vclrbyte(cp, i) 32: register char *cp; 33: register int i; 34: { 35: 36: if (i > 0) 37: do 38: *cp++ = 0; 39: while (--i != 0); 40: } 41: 42: /* 43: * Clear a physical display line, high level. 44: */ 45: vclrlin(l, tp) 46: int l; 47: line *tp; 48: { 49: 50: vigoto(l, 0); 51: if ((hold & HOLDAT) == 0) 52: putchar(tp > dol ? ((UPPERCASE || HZ) ? '^' : '~') : '@'); 53: #ifdef OPENCODE 54: if (state == HARDOPEN) 55: sethard(); 56: #endif 57: vclreol(); 58: } 59: 60: /* 61: * Clear to the end of the current physical line 62: */ 63: vclreol() 64: { 65: register int i, j; 66: register char *tp; 67: 68: if (destcol == WCOLS) 69: return; 70: destline += destcol / WCOLS; 71: destcol %= WCOLS; 72: if (destline < 0 || destline > WECHO) 73: error("Internal error: vclreol"); 74: i = WCOLS - destcol; 75: tp = vtube[destline] + destcol; 76: if (CE) { 77: if (IN && *tp || !ateopr()) { 78: vcsync(); 79: vputp(CE, 1); 80: } 81: vclrbyte(tp, i); 82: return; 83: } 84: if (*tp == 0) 85: return; 86: while (i > 0 && (j = *tp & (QUOTE|TRIM))) { 87: if (j != ' ' && (j & QUOTE) == 0) { 88: destcol = WCOLS - i; 89: vputchar(' '); 90: } 91: --i, *tp++ = 0; 92: } 93: } 94: 95: /* 96: * Clear the echo line. 97: * If didphys then its been cleared physically (as 98: * a side effect of a clear to end of display, e.g.) 99: * so just do it logically. 100: * If work here is being held off, just remember, in 101: * heldech, if work needs to be done, don't do anything. 102: */ 103: vclrech(didphys) 104: bool didphys; 105: { 106: 107: if (Peekkey == ATTN) 108: return; 109: if (hold & HOLDECH) { 110: heldech = !didphys; 111: return; 112: } 113: if (!didphys && (CD || CE)) { 114: splitw++; 115: /* 116: * If display is retained below, then MUST use CD or CE 117: * since we don't really know whats out there. 118: * Vigoto might decide (incorrectly) to do nothing. 119: */ 120: if (DB) { 121: vgoto(WECHO, 0); 122: vputp(CD ? CD : CE, 1); 123: } else { 124: if (XT) { 125: /* 126: * This code basically handles the t1061 127: * where positioning at (0, 0) won't work 128: * because the terminal won't let you put 129: * the cursor on its magic cookie. 130: * 131: * Should probably be XS above, or even a 132: * new X? glitch, but right now t1061 is the 133: * only terminal with XT. 134: */ 135: vgoto(WECHO, 0); 136: vputp(DL, 1); 137: } else { 138: vigoto(WECHO, 0); 139: vclreol(); 140: } 141: } 142: splitw = 0; 143: didphys = 1; 144: } 145: if (didphys) 146: vclrbyte(vtube[WECHO], WCOLS); 147: heldech = 0; 148: } 149: 150: /* 151: * Fix the echo area for use, setting 152: * the state variable splitw so we wont rollup 153: * when we move the cursor there. 154: */ 155: fixech() 156: { 157: 158: splitw++; 159: #ifdef OPENCODE 160: if (state != VISUAL && state != CRTOPEN) { 161: vclean(); 162: vcnt = 0; 163: } 164: #endif 165: vgoto(WECHO, 0); flusho(); 166: } 167: 168: /* 169: * Put the cursor ``before'' cp. 170: */ 171: vcursbef(cp) 172: register char *cp; 173: { 174: 175: if (cp <= linebuf) 176: vgotoCL(value(NUMBER) << 3); 177: else 178: vgotoCL(column(cp - 1) - 1); 179: } 180: 181: /* 182: * Put the cursor ``at'' cp. 183: */ 184: vcursat(cp) 185: register char *cp; 186: { 187: 188: if (cp <= linebuf && linebuf[0] == 0) 189: vgotoCL(value(NUMBER) << 3); 190: else 191: vgotoCL(column(cp - 1)); 192: } 193: 194: /* 195: * Put the cursor ``after'' cp. 196: */ 197: vcursaft(cp) 198: register char *cp; 199: { 200: 201: vgotoCL(column(cp)); 202: } 203: 204: /* 205: * Fix the cursor to be positioned in the correct place 206: * to accept a command. 207: */ 208: vfixcurs() 209: { 210: 211: vsetcurs(cursor); 212: } 213: 214: /* 215: * Compute the column position implied by the cursor at ``nc'', 216: * and move the cursor there. 217: */ 218: vsetcurs(nc) 219: register char *nc; 220: { 221: register int col; 222: 223: col = column(nc); 224: if (linebuf[0]) 225: col--; 226: vgotoCL(col); 227: cursor = nc; 228: } 229: 230: /* 231: * Move the cursor invisibly, i.e. only remember to do it. 232: */ 233: vigoto(y, x) 234: int y, x; 235: { 236: 237: destline = y; 238: destcol = x; 239: } 240: 241: /* 242: * Move the cursor to the position implied by any previous 243: * vigoto (or low level hacking with destcol/destline as in readecho). 244: */ 245: vcsync() 246: { 247: 248: vgoto(destline, destcol); 249: } 250: 251: /* 252: * Goto column x of the current line. 253: */ 254: vgotoCL(x) 255: register int x; 256: { 257: 258: if (splitw) 259: vgoto(WECHO, x); 260: else 261: vgoto(LINE(vcline), x); 262: } 263: 264: /* 265: * Invisible goto column x of current line. 266: */ 267: vigotoCL(x) 268: register int x; 269: { 270: 271: if (splitw) 272: vigoto(WECHO, x); 273: else 274: vigoto(LINE(vcline), x); 275: } 276: 277: /* 278: * Move cursor to line y, column x, handling wraparound and scrolling. 279: */ 280: vgoto(y, x) 281: register int y, x; 282: { 283: register char *tp; 284: register int c; 285: 286: /* 287: * Fold the possibly too large value of x. 288: */ 289: if (x >= WCOLS) { 290: y += x / WCOLS; 291: x %= WCOLS; 292: } 293: if (y < 0) 294: error("Internal error: vgoto"); 295: if (outcol >= WCOLS) { 296: if (AM) { 297: outline += outcol / WCOLS; 298: outcol %= WCOLS; 299: } else 300: outcol = WCOLS - 1; 301: } 302: 303: #ifdef OPENCODE 304: /* 305: * In a hardcopy or glass crt open, print the stuff 306: * implied by a motion, or backspace. 307: */ 308: if (state == HARDOPEN || state == ONEOPEN) { 309: if (y != outline) 310: error("Line too long for open"); 311: if (x + 1 < outcol - x || (outcol > x && !BS)) 312: destcol = 0, fgoto(); 313: tp = vtube[WBOT] + outcol; 314: while (outcol != x) 315: if (outcol < x) { 316: if (*tp == 0) 317: *tp = ' '; 318: c = *tp++ & TRIM; 319: vputc(c && (!OS || EO) ? c : ' '), outcol++; 320: } else { 321: if (BC) 322: vputp(BC, 0); 323: else 324: vputc('\b'); 325: outcol--; 326: } 327: destcol = outcol = x; 328: destline = outline; 329: return; 330: } 331: #endif 332: 333: /* 334: * If the destination position implies a scroll, do it. 335: */ 336: destline = y; 337: if (destline > WBOT && (!splitw || destline > WECHO)) { 338: endim(); 339: vrollup(destline); 340: } 341: 342: /* 343: * If there really is a motion involved, do it. 344: * The check here is an optimization based on profiling. 345: */ 346: destcol = x; 347: if ((destline - outline) * WCOLS != destcol - outcol) { 348: if (!MI) 349: endim(); 350: fgoto(); 351: } 352: } 353: 354: /* 355: * This is the hardest code in the editor, and deals with insert modes 356: * on different kinds of intelligent terminals. The complexity is due 357: * to the cross product of three factors: 358: * 359: * 1. Lines may display as more than one segment on the screen. 360: * 2. There are 2 kinds of intelligent terminal insert modes. 361: * 3. Tabs squash when you insert characters in front of them, 362: * in a way in which current intelligent terminals don't handle. 363: * 364: * The two kinds of terminals are typified by the DM2500 or HP2645 for 365: * one and the CONCEPT-100 or the FOX for the other. 366: * 367: * The first (HP2645) kind has an insert mode where the characters 368: * fall off the end of the line and the screen is shifted rigidly 369: * no matter how the display came about. 370: * 371: * The second (CONCEPT-100) kind comes from terminals which are designed 372: * for forms editing and which distinguish between blanks and ``spaces'' 373: * on the screen, spaces being like blank, but never having had 374: * and data typed into that screen position (since, e.g. a clear operation 375: * like clear screen). On these terminals, when you insert a character, 376: * the characters from where you are to the end of the screen shift 377: * over till a ``space'' is found, and the null character there gets 378: * eaten up. 379: * 380: * 381: * The code here considers the line as consisting of several parts 382: * the first part is the ``doomed'' part, i.e. a part of the line 383: * which is being typed over. Next comes some text up to the first 384: * following tab. The tab is the next segment of the line, and finally 385: * text after the tab. 386: * 387: * We have to consider each of these segments and the effect of the 388: * insertion of a character on them. On terminals like HP2645's we 389: * must simulate a multi-line insert mode using the primitive one 390: * line insert mode. If we are inserting in front of a tab, we have 391: * to either delete characters from the tab or insert white space 392: * (when the tab reaches a new spot where it gets larger) before we 393: * insert the new character. 394: * 395: * On a terminal like a CONCEPT our strategy is to make all 396: * blanks be displayed, while trying to keep the screen having ``spaces'' 397: * for portions of tabs. In this way the terminal hardward does some 398: * of the hacking for compression of tabs, although this tends to 399: * disappear as you work on the line and spaces change into blanks. 400: * 401: * There are a number of boundary conditions (like typing just before 402: * the first following tab) where we can avoid a lot of work. Most 403: * of them have to be dealt with explicitly because performance is 404: * much, much worse if we don't. 405: * 406: * A final thing which is hacked here is two flavors of insert mode. 407: * Datamedia's do this by an insert mode which you enter and leave 408: * and by having normal motion character operate differently in this 409: * mode, notably by having a newline insert a line on the screen in 410: * this mode. This generally means it is unsafe to move around 411: * the screen ignoring the fact that we are in this mode. 412: * This is possible on some terminals, and wins big (e.g. HP), so 413: * we encode this as a ``can move in insert capability'' mi, 414: * and terminals which have it can do insert mode with much less 415: * work when tabs are present following the cursor on the current line. 416: */ 417: 418: /* 419: * Routine to expand a tab, calling the normal Outchar routine 420: * to put out each implied character. Note that we call outchar 421: * with a QUOTE. We use QUOTE internally to represent a position 422: * which is part of the expansion of a tab. 423: */ 424: vgotab() 425: { 426: register int i = destcol; 427: 428: do 429: (*Outchar)(QUOTE); 430: while (++i % value(TABSTOP)); 431: } 432: 433: /* 434: * Variables for insert mode. 435: */ 436: int linend; /* The column position of end of line */ 437: int tabstart; /* Column of start of first following tab */ 438: int tabend; /* Column of end of following tabs */ 439: int tabsize; /* Size of the following tabs */ 440: int tabslack; /* Number of ``spaces'' in following tabs */ 441: int inssiz; /* Number of characters to be inserted */ 442: int inscol; /* Column where insertion is taking place */ 443: int shft; /* Amount tab expansion shifted rest of line */ 444: int slakused; /* This much of tabslack will be used up */ 445: 446: /* 447: * This routine MUST be called before insert mode is run, 448: * and brings all segments of the current line to the top 449: * of the screen image buffer so it is easier for us to 450: * maniuplate them. 451: */ 452: vprepins() 453: { 454: register int i; 455: register char *cp = vtube0; 456: 457: for (i = 0; i < DEPTH(vcline); i++) { 458: vmaktop(LINE(vcline) + i, cp); 459: cp += WCOLS; 460: } 461: } 462: 463: vmaktop(p, cp) 464: register int p; 465: char *cp; 466: { 467: register int i; 468: char temp[TUBECOLS]; 469: 470: if (vtube[p] == cp) 471: return; 472: for (i = ZERO; i <= WECHO; i++) 473: if (vtube[i] == cp) { 474: copy(temp, vtube[i], WCOLS); 475: copy(vtube[i], vtube[p], WCOLS); 476: copy(vtube[p], temp, WCOLS); 477: vtube[i] = vtube[p]; 478: vtube[p] = cp; 479: return; 480: } 481: error("Line too long"); 482: } 483: 484: /* 485: * Insert character c at current cursor position. 486: * Multi-character inserts occur only as a result 487: * of expansion of tabs (i.e. inssize == 1 except 488: * for tabs) and code assumes this in several place 489: * to make life simpler. 490: */ 491: vinschar(c) 492: char c; 493: { 494: register int i; 495: register char *tp; 496: 497: if ((!IM || !EI) && ((hold & HOLDQIK) || !value(REDRAW) || value(SLOWOPEN))) { 498: /* 499: * Don't want to try to use terminal 500: * insert mode, or to try to fake it. 501: * Just put the character out; the screen 502: * will probably be wrong but we will fix it later. 503: */ 504: if (c == '\t') { 505: vgotab(); 506: return; 507: } 508: vputchar(c); 509: if (DEPTH(vcline) * WCOLS + !value(REDRAW) > 510: (destline - LINE(vcline)) * WCOLS + destcol) 511: return; 512: /* 513: * The next line is about to be clobbered 514: * make space for another segment of this line 515: * (on an intelligent terminal) or just remember 516: * that next line was clobbered (on a dumb one 517: * if we don't care to redraw the tail. 518: */ 519: if (AL) { 520: vnpins(0); 521: } else { 522: c = LINE(vcline) + DEPTH(vcline); 523: if (c < LINE(vcline + 1) || c > WBOT) 524: return; 525: i = destcol; 526: vinslin(c, 1, vcline); 527: DEPTH(vcline)++; 528: vigoto(c, i); 529: vprepins(); 530: } 531: return; 532: } 533: /* 534: * Compute the number of positions in the line image of the 535: * current line. This is done from the physical image 536: * since that is faster. Note that we have no memory 537: * from insertion to insertion so that routines which use 538: * us don't have to worry about moving the cursor around. 539: */ 540: if (*vtube0 == 0) 541: linend = 0; 542: else { 543: /* 544: * Search backwards for a non-null character 545: * from the end of the displayed line. 546: */ 547: i = WCOLS * DEPTH(vcline); 548: if (i == 0) 549: i = WCOLS; 550: tp = vtube0 + i; 551: while (*--tp == 0) 552: if (--i == 0) 553: break; 554: linend = i; 555: } 556: 557: /* 558: * We insert at a position based on the physical location 559: * of the output cursor. 560: */ 561: inscol = destcol + (destline - LINE(vcline)) * WCOLS; 562: if (c == '\t') { 563: /* 564: * Characters inserted from a tab must be 565: * remembered as being part of a tab, but we can't 566: * use QUOTE here since we really need to print blanks. 567: * QUOTE|' ' is the representation of this. 568: */ 569: inssiz = value(TABSTOP) - inscol % value(TABSTOP); 570: c = ' ' | QUOTE; 571: } else 572: inssiz = 1; 573: 574: /* 575: * If the text to be inserted is less than the number 576: * of doomed positions, then we don't need insert mode, 577: * rather we can just typeover. 578: */ 579: if (inssiz <= doomed) { 580: endim(); 581: if (inscol != linend) 582: doomed -= inssiz; 583: do 584: vputchar(c); 585: while (--inssiz); 586: return; 587: } 588: 589: /* 590: * Have to really do some insertion, thus 591: * stake out the bounds of the first following 592: * group of tabs, computing starting position, 593: * ending position, and the number of ``spaces'' therein 594: * so we can tell how much it will squish. 595: */ 596: tp = vtube0 + inscol; 597: for (i = inscol; i < linend; i++) 598: if (*tp++ & QUOTE) { 599: --tp; 600: break; 601: } 602: tabstart = tabend = i; 603: tabslack = 0; 604: while (tabend < linend) { 605: i = *tp++; 606: if ((i & QUOTE) == 0) 607: break; 608: if ((i & TRIM) == 0) 609: tabslack++; 610: tabsize++; 611: tabend++; 612: } 613: tabsize = tabend - tabstart; 614: 615: /* 616: * For HP's and DM's, e.g. tabslack has no meaning. 617: */ 618: if (!IN) 619: tabslack = 0; 620: #ifdef IDEBUG 621: if (trace) { 622: fprintf(trace, "inscol %d, inssiz %d, tabstart %d, ", 623: inscol, inssiz, tabstart); 624: fprintf(trace, "tabend %d, tabslack %d, linend %d\n", 625: tabend, tabslack, linend); 626: } 627: #endif 628: 629: /* 630: * The real work begins. 631: */ 632: slakused = 0; 633: shft = 0; 634: if (tabsize) { 635: /* 636: * There are tabs on this line. 637: * If they need to expand, then the rest of the line 638: * will have to be shifted over. In this case, 639: * we will need to make sure there are no ``spaces'' 640: * in the rest of the line (on e.g. CONCEPT-100) 641: * and then grab another segment on the screen if this 642: * line is now deeper. We then do the shift 643: * implied by the insertion. 644: */ 645: if (inssiz >= doomed + value(TABSTOP) - tabstart % value(TABSTOP)) { 646: if (IN) 647: vrigid(); 648: vneedpos(value(TABSTOP)); 649: vishft(); 650: } 651: } else if (inssiz > doomed) 652: /* 653: * No tabs, but line may still get deeper. 654: */ 655: vneedpos(inssiz - doomed); 656: /* 657: * Now put in the inserted characters. 658: */ 659: viin(c); 660: 661: /* 662: * Now put the cursor in its final resting place. 663: */ 664: destline = LINE(vcline); 665: destcol = inscol + inssiz; 666: vcsync(); 667: } 668: 669: /* 670: * Rigidify the rest of the line after the first 671: * group of following tabs, typing blanks over ``spaces''. 672: */ 673: vrigid() 674: { 675: register int col; 676: register char *tp = vtube0 + tabend; 677: 678: for (col = tabend; col < linend; col++) 679: if ((*tp++ & TRIM) == 0) { 680: endim(); 681: vgotoCL(col); 682: vputchar(' ' | QUOTE); 683: } 684: } 685: 686: /* 687: * We need cnt more positions on this line. 688: * Open up new space on the screen (this may in fact be a 689: * screen rollup). 690: * 691: * On a dumb terminal we may infact redisplay the rest of the 692: * screen here brute force to keep it pretty. 693: */ 694: vneedpos(cnt) 695: int cnt; 696: { 697: register int d = DEPTH(vcline); 698: register int rmdr = d * WCOLS - linend; 699: 700: if (cnt <= rmdr - IN) 701: return; 702: endim(); 703: vnpins(1); 704: } 705: 706: vnpins(dosync) 707: int dosync; 708: { 709: register int d = DEPTH(vcline); 710: register int e; 711: 712: e = LINE(vcline) + DEPTH(vcline); 713: if (e < LINE(vcline + 1)) { 714: vigoto(e, 0); 715: vclreol(); 716: return; 717: } 718: DEPTH(vcline)++; 719: if (e < WECHO) { 720: e = vglitchup(vcline, d); 721: vigoto(e, 0); vclreol(); 722: if (dosync) { 723: Outchar = vputchar; 724: vsync(e + 1); 725: Outchar = vinschar; 726: } 727: } else { 728: vup1(); 729: vigoto(WBOT, 0); 730: vclreol(); 731: } 732: vprepins(); 733: } 734: 735: /* 736: * Do the shift of the next tabstop implied by 737: * insertion so it expands. 738: */ 739: vishft() 740: { 741: int tshft = 0; 742: int j; 743: register int i; 744: register char *tp = vtube0; 745: register char *up; 746: short oldhold = hold; 747: 748: shft = value(TABSTOP); 749: hold |= HOLDPUPD; 750: if (!IM && !EI) { 751: /* 752: * Dumb terminals are easy, we just have 753: * to retype the text. 754: */ 755: vigotoCL(tabend + shft); 756: up = tp + tabend; 757: for (i = tabend; i < linend; i++) 758: vputchar(*up++); 759: } else if (IN) { 760: /* 761: * CONCEPT-like terminals do most of the work for us, 762: * we don't have to muck with simulation of multi-line 763: * insert mode. Some of the shifting may come for free 764: * also if the tabs don't have enough slack to take up 765: * all the inserted characters. 766: */ 767: i = shft; 768: slakused = inssiz - doomed; 769: if (slakused > tabslack) { 770: i -= slakused - tabslack; 771: slakused -= tabslack; 772: } 773: if (i > 0 && tabend != linend) { 774: tshft = i; 775: vgotoCL(tabend); 776: goim(); 777: do 778: vputchar(' ' | QUOTE); 779: while (--i); 780: } 781: } else { 782: /* 783: * HP and Datamedia type terminals have to have multi-line 784: * insert faked. Hack each segment after where we are 785: * (going backwards to where we are.) We then can 786: * hack the segment where the end of the first following 787: * tab group is. 788: */ 789: for (j = DEPTH(vcline) - 1; j > (tabend + shft) / WCOLS; j--) { 790: vgotoCL(j * WCOLS); 791: goim(); 792: up = tp + j * WCOLS - shft; 793: i = shft; 794: do { 795: if (*up) 796: vputchar(*up++); 797: else 798: break; 799: } while (--i); 800: } 801: vigotoCL(tabstart); 802: i = shft - (inssiz - doomed); 803: if (i > 0) { 804: tabslack = inssiz - doomed; 805: vcsync(); 806: goim(); 807: do 808: vputchar(' '); 809: while (--i); 810: } 811: } 812: /* 813: * Now do the data moving in the internal screen 814: * image which is common to all three cases. 815: */ 816: tp += linend; 817: up = tp + shft; 818: i = linend - tabend; 819: if (i > 0) 820: do 821: *--up = *--tp; 822: while (--i); 823: if (IN && tshft) { 824: i = tshft; 825: do 826: *--up = ' ' | QUOTE; 827: while (--i); 828: } 829: hold = oldhold; 830: } 831: 832: /* 833: * Now do the insert of the characters (finally). 834: */ 835: viin(c) 836: char c; 837: { 838: register char *tp, *up; 839: register int i, j; 840: register bool noim = 0; 841: int remdoom; 842: short oldhold = hold; 843: 844: hold |= HOLDPUPD; 845: if (tabsize && (IM && EI) && inssiz - doomed > tabslack) 846: /* 847: * There is a tab out there which will be affected 848: * by the insertion since there aren't enough doomed 849: * characters to take up all the insertion and we do 850: * have insert mode capability. 851: */ 852: if (inscol + doomed == tabstart) { 853: /* 854: * The end of the doomed characters sits right at the 855: * start of the tabs, then we don't need to use insert 856: * mode; unless the tab has already been expanded 857: * in which case we MUST use insert mode. 858: */ 859: slakused = 0; 860: noim = !shft; 861: } else { 862: /* 863: * The last really special case to handle is case 864: * where the tab is just sitting there and doesn't 865: * have enough slack to let the insertion take 866: * place without shifting the rest of the line 867: * over. In this case we have to go out and 868: * delete some characters of the tab before we start 869: * or the answer will be wrong, as the rest of the 870: * line will have been shifted. This code means 871: * that terminals with only insert chracter (no 872: * delete character) won't work correctly. 873: */ 874: i = inssiz - doomed - tabslack - slakused; 875: i %= value(TABSTOP); 876: if (i > 0) { 877: vgotoCL(tabstart); 878: godm(); 879: for (i = inssiz - doomed - tabslack; i > 0; i--) 880: vputp(DC, DEPTH(vcline)); 881: enddm(); 882: } 883: } 884: 885: /* 886: * Now put out the characters of the actual insertion. 887: */ 888: vigotoCL(inscol); 889: remdoom = doomed; 890: for (i = inssiz; i > 0; i--) { 891: if (remdoom > 0) { 892: remdoom--; 893: endim(); 894: } else if (noim) 895: endim(); 896: else if (IM && EI) { 897: vcsync(); 898: goim(); 899: } 900: vputchar(c); 901: } 902: 903: if (!IM || !EI) { 904: /* 905: * We are a dumb terminal; brute force update 906: * the rest of the line; this is very much an n^^2 process, 907: * and totally unreasonable at low speed. 908: * 909: * You asked for it, you get it. 910: */ 911: tp = vtube0 + inscol + doomed; 912: for (i = inscol + doomed; i < tabstart; i++) 913: vputchar(*tp++); 914: hold = oldhold; 915: vigotoCL(tabstart + inssiz - doomed); 916: for (i = tabsize - (inssiz - doomed) + shft; i > 0; i--) 917: vputchar(' ' | QUOTE); 918: } else { 919: if (!IN) { 920: /* 921: * On terminals without multi-line 922: * insert in the hardware, we must go fix the segments 923: * between the inserted text and the following 924: * tabs, if they are on different lines. 925: * 926: * Aaargh. 927: */ 928: tp = vtube0; 929: for (j = (inscol + inssiz - 1) / WCOLS + 1; 930: j <= (tabstart + inssiz - doomed - 1) / WCOLS; j++) { 931: vgotoCL(j * WCOLS); 932: i = inssiz - doomed; 933: up = tp + j * WCOLS - i; 934: goim(); 935: do 936: vputchar(*up++); 937: while (--i && *up); 938: } 939: } else { 940: /* 941: * On terminals with multi line inserts, 942: * life is simpler, just reflect eating of 943: * the slack. 944: */ 945: tp = vtube0 + tabend; 946: for (i = tabsize - (inssiz - doomed); i >= 0; i--) { 947: if ((*--tp & (QUOTE|TRIM)) == QUOTE) { 948: --tabslack; 949: if (tabslack >= slakused) 950: continue; 951: } 952: *tp = ' ' | QUOTE; 953: } 954: } 955: /* 956: * Blank out the shifted positions to be tab positions. 957: */ 958: if (shft) { 959: tp = vtube0 + tabend + shft; 960: for (i = tabsize - (inssiz - doomed) + shft; i > 0; i--) 961: if ((*--tp & QUOTE) == 0) 962: *tp = ' ' | QUOTE; 963: } 964: } 965: 966: /* 967: * Finally, complete the screen image update 968: * to reflect the insertion. 969: */ 970: hold = oldhold; 971: tp = vtube0 + tabstart; up = tp + inssiz - doomed; 972: for (i = tabstart; i > inscol + doomed; i--) 973: *--up = *--tp; 974: for (i = inssiz; i > 0; i--) 975: *--up = c; 976: doomed = 0; 977: } 978: 979: /* 980: * Go into ``delete mode''. If the 981: * sequence which goes into delete mode 982: * is the same as that which goes into insert 983: * mode, then we are in delete mode already. 984: */ 985: godm() 986: { 987: 988: if (insmode) { 989: if (eq(DM, IM)) 990: return; 991: endim(); 992: } 993: vputp(DM, 0); 994: } 995: 996: /* 997: * If we are coming out of delete mode, but 998: * delete and insert mode end with the same sequence, 999: * it wins to pretend we are now in insert mode, 1000: * since we will likely want to be there again soon 1001: * if we just moved over to delete space from part of 1002: * a tab (above). 1003: */ 1004: enddm() 1005: { 1006: 1007: if (eq(DM, IM)) { 1008: insmode = 1; 1009: return; 1010: } 1011: vputp(ED, 0); 1012: } 1013: 1014: /* 1015: * In and out of insert mode. 1016: * Note that the code here demands that there be 1017: * a string for insert mode (the null string) even 1018: * if the terminal does all insertions a single character 1019: * at a time, since it branches based on whether IM is null. 1020: */ 1021: goim() 1022: { 1023: 1024: if (!insmode) 1025: vputp(IM, 0); 1026: insmode = 1; 1027: } 1028: 1029: endim() 1030: { 1031: 1032: if (insmode) { 1033: vputp(EI, 0); 1034: insmode = 0; 1035: } 1036: } 1037: 1038: /* 1039: * Put the character c on the screen at the current cursor position. 1040: * This routine handles wraparound and scrolling and understands not 1041: * to roll when splitw is set, i.e. we are working in the echo area. 1042: * There is a bunch of hacking here dealing with the difference between 1043: * QUOTE, QUOTE|' ', and ' ' for CONCEPT-100 like terminals, and also 1044: * code to deal with terminals which overstrike, including CRT's where 1045: * you can erase overstrikes with some work. CRT's which do underlining 1046: * implicitly which has to be erased (like CONCEPTS) are also handled. 1047: */ 1048: vputchar(c) 1049: register int c; 1050: { 1051: register char *tp; 1052: register int d; 1053: 1054: c &= (QUOTE|TRIM); 1055: #ifdef TRACE 1056: if (trace) 1057: tracec(c); 1058: #endif 1059: /* Fix problem of >79 chars on echo line. */ 1060: if (destcol >= WCOLS-1 && splitw && destline == WECHO) 1061: pofix(); 1062: if (destcol >= WCOLS) { 1063: destline += destcol / WCOLS; 1064: destcol %= WCOLS; 1065: } 1066: if (destline > WBOT && (!splitw || destline > WECHO)) 1067: vrollup(destline); 1068: tp = vtube[destline] + destcol; 1069: switch (c) { 1070: 1071: case '\t': 1072: vgotab(); 1073: return; 1074: 1075: case ' ': 1076: /* 1077: * We can get away without printing a space in a number 1078: * of cases, but not always. We get away with doing nothing 1079: * if we are not in insert mode, and not on a CONCEPT-100 1080: * like terminal, and either not in hardcopy open or in hardcopy 1081: * open on a terminal with no overstriking, provided, 1082: * in all cases, that nothing has ever been displayed 1083: * at this position. Ugh. 1084: */ 1085: if (!insmode && !IN && 1086: #ifdef OPENCODE 1087: (state != HARDOPEN || OS) && 1088: #endif 1089: (*tp&TRIM) == 0) { 1090: *tp = ' '; 1091: destcol++; 1092: return; 1093: } 1094: goto def; 1095: 1096: case QUOTE: 1097: if (insmode) { 1098: /* 1099: * When in insert mode, tabs have to expand 1100: * to real, printed blanks. 1101: */ 1102: c = ' ' | QUOTE; 1103: goto def; 1104: } 1105: if (*tp == 0) { 1106: /* 1107: * A ``space''. 1108: */ 1109: if ((hold & HOLDPUPD) == 0) 1110: *tp = QUOTE; 1111: destcol++; 1112: return; 1113: } 1114: /* 1115: * A ``space'' ontop of a part of a tab. 1116: */ 1117: if (*tp & QUOTE) { 1118: destcol++; 1119: return; 1120: } 1121: c = ' ' | QUOTE; 1122: /* fall into ... */ 1123: 1124: def: 1125: default: 1126: d = *tp & TRIM; 1127: /* 1128: * Now get away with doing nothing if the characters 1129: * are the same, provided we are not in insert mode 1130: * and if we are in hardopen, that the terminal has overstrike. 1131: */ 1132: if (d == (c & TRIM) && !insmode 1133: #ifdef OPENCODE 1134: && (state != HARDOPEN || OS) 1135: #endif 1136: ) { 1137: if ((hold & HOLDPUPD) == 0) 1138: *tp = c; 1139: destcol++; 1140: return; 1141: } 1142: /* 1143: * Backwards looking optimization. 1144: * The low level cursor motion routines will use 1145: * a cursor motion right sequence to step 1 character 1146: * right. On, e.g., a DM3025A this is 2 characters 1147: * and printing is noticeably slower at 300 baud. 1148: * Since the low level routines are not allowed to use 1149: * spaces for positioning, we discover the common 1150: * case of a single space here and force a space 1151: * to be printed. 1152: */ 1153: if (destcol == outcol + 1 && tp[-1] == ' ' && outline == destline) { 1154: vputc(' '); 1155: outcol++; 1156: } 1157: 1158: /* 1159: * This is an inline expansion a call to vcsync() dictated 1160: * by high frequency in a profile. 1161: */ 1162: if (outcol != destcol || outline != destline) 1163: vgoto(destline, destcol); 1164: 1165: /* 1166: * Deal with terminals which have overstrike. 1167: * We handle erasing general overstrikes, erasing 1168: * underlines on terminals (such as CONCEPTS) which 1169: * do underlining correctly automatically (e.g. on nroff 1170: * output), and remembering, in hardcopy mode, 1171: * that we have overstruct something. 1172: */ 1173: if (!insmode && d && d != ' ' && d != (c & TRIM)) { 1174: if (EO && (OS || UL && (c == '_' || d == '_'))) { 1175: vputc(' '); 1176: outcol++, destcol++; 1177: back1(); 1178: } else 1179: rubble = 1; 1180: } 1181: 1182: /* 1183: * Unless we are just bashing characters around for 1184: * inner working of insert mode, update the display. 1185: */ 1186: if ((hold & HOLDPUPD) == 0) 1187: *tp = c; 1188: 1189: /* 1190: * In insert mode, put out the IC sequence, padded 1191: * based on the depth of the current line. 1192: * A terminal which had no real insert mode, rather 1193: * opening a character position at a time could do this. 1194: * Actually should use depth to end of current line 1195: * but this rarely matters. 1196: */ 1197: if (insmode) 1198: vputp(IC, DEPTH(vcline)); 1199: vputc(c & TRIM); 1200: 1201: /* 1202: * In insert mode, IP is a post insert pad. 1203: */ 1204: if (insmode) 1205: vputp(IP, DEPTH(vcline)); 1206: destcol++, outcol++; 1207: 1208: /* 1209: * CONCEPT braindamage in early models: after a wraparound 1210: * the next newline is eaten. It's hungry so we just 1211: * feed it now rather than worrying about it. 1212: */ 1213: if (XN && outcol % WCOLS == 0) 1214: vputc('\n'); 1215: } 1216: } 1217: 1218: /* 1219: * Delete display positions stcol through endcol. 1220: * Amount of use of special terminal features here is limited. 1221: */ 1222: physdc(stcol, endcol) 1223: int stcol, endcol; 1224: { 1225: register char *tp, *up; 1226: char *tpe; 1227: register int i; 1228: register int nc = endcol - stcol; 1229: 1230: #ifdef IDEBUG 1231: if (trace) 1232: tfixnl(), fprintf(trace, "physdc(%d, %d)\n", stcol, endcol); 1233: #endif 1234: if (!DC || nc <= 0) 1235: return; 1236: if (IN) { 1237: /* 1238: * CONCEPT-100 like terminal. 1239: * If there are any ``spaces'' in the material to be 1240: * deleted, then this is too hard, just retype. 1241: */ 1242: vprepins(); 1243: up = vtube0 + stcol; 1244: i = nc; 1245: do 1246: if ((*up++ & (QUOTE|TRIM)) == QUOTE) 1247: return; 1248: while (--i); 1249: i = 2 * nc; 1250: do 1251: if (*up == 0 || (*up++ & QUOTE) == QUOTE) 1252: return; 1253: while (--i); 1254: vgotoCL(stcol); 1255: } else { 1256: /* 1257: * HP like delete mode. 1258: * Compute how much text we are moving over by deleting. 1259: * If it appears to be faster to just retype 1260: * the line, do nothing and that will be done later. 1261: * We are assuming 2 output characters per deleted 1262: * characters and that clear to end of line is available. 1263: */ 1264: i = stcol / WCOLS; 1265: if (i != endcol / WCOLS) 1266: return; 1267: i += LINE(vcline); 1268: stcol %= WCOLS; 1269: endcol %= WCOLS; 1270: up = vtube[i]; tp = up + endcol; tpe = up + WCOLS; 1271: while (tp < tpe && *tp) 1272: tp++; 1273: if (tp - (up + stcol) < 2 * nc) 1274: return; 1275: vgoto(i, stcol); 1276: } 1277: 1278: /* 1279: * Go into delete mode and do the actual delete. 1280: * Padding is on DC itself. 1281: */ 1282: godm(); 1283: for (i = nc; i > 0; i--) 1284: vputp(DC, DEPTH(vcline)); 1285: vputp(ED, 0); 1286: 1287: /* 1288: * Straighten up. 1289: * With CONCEPT like terminals, characters are pulled left 1290: * from first following null. HP like terminals shift rest of 1291: * this (single physical) line rigidly. 1292: */ 1293: if (IN) { 1294: up = vtube0 + stcol; 1295: tp = vtube0 + endcol; 1296: while (i = *tp++) { 1297: if ((i & (QUOTE|TRIM)) == QUOTE) 1298: break; 1299: *up++ = i; 1300: } 1301: do 1302: *up++ = i; 1303: while (--nc); 1304: } else { 1305: copy(up + stcol, up + endcol, WCOLS - endcol); 1306: vclrbyte(tpe - nc, nc); 1307: } 1308: } 1309: 1310: #ifdef TRACE 1311: tfixnl() 1312: { 1313: 1314: if (trubble || techoin) 1315: fprintf(trace, "\n"); 1316: trubble = 0, techoin = 0; 1317: } 1318: 1319: tvliny() 1320: { 1321: register int i; 1322: 1323: if (!trace) 1324: return; 1325: tfixnl(); 1326: fprintf(trace, "vcnt = %d, vcline = %d, vliny = ", vcnt, vcline); 1327: for (i = 0; i <= vcnt; i++) { 1328: fprintf(trace, "%d", LINE(i)); 1329: if (FLAGS(i) & VDIRT) 1330: fprintf(trace, "*"); 1331: if (DEPTH(i) != 1) 1332: fprintf(trace, "<%d>", DEPTH(i)); 1333: if (i < vcnt) 1334: fprintf(trace, " "); 1335: } 1336: fprintf(trace, "\n"); 1337: } 1338: 1339: tracec(c) 1340: char c; 1341: { 1342: 1343: if (!techoin) 1344: trubble = 1; 1345: if (c == ESCAPE) 1346: fprintf(trace, "$"); 1347: else if (c < ' ' || c == DELETE) 1348: fprintf(trace, "^%c", ctlof(c)); 1349: else 1350: fprintf(trace, "%c", c); 1351: } 1352: #endif 1353: 1354: /* 1355: * Put a character with possible tracing. 1356: */ 1357: vputch(c) 1358: int c; 1359: { 1360: 1361: #ifdef TRACE 1362: if (trace) 1363: tracec(c); 1364: #endif 1365: vputc(c); 1366: }