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