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