1 /* Copyright (c) 1980 Regents of the University of California */ 2 static char *sccsid = "@(#)ex_vput.c 5.1 08/20/80"; 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 char c; 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 Outchar = vputchar; 719 vsync(e + 1); 720 Outchar = vinschar; 721 } 722 } else { 723 vup1(); 724 vigoto(WBOT, 0); 725 vclreol(); 726 } 727 vprepins(); 728 } 729 730 /* 731 * Do the shift of the next tabstop implied by 732 * insertion so it expands. 733 */ 734 vishft() 735 { 736 int tshft = 0; 737 int j; 738 register int i; 739 register char *tp = vtube0; 740 register char *up; 741 short oldhold = hold; 742 743 shft = value(TABSTOP); 744 hold |= HOLDPUPD; 745 if (!IM && !EI) { 746 /* 747 * Dumb terminals are easy, we just have 748 * to retype the text. 749 */ 750 vigotoCL(tabend + shft); 751 up = tp + tabend; 752 for (i = tabend; i < linend; i++) 753 vputchar(*up++); 754 } else if (IN) { 755 /* 756 * CONCEPT-like terminals do most of the work for us, 757 * we don't have to muck with simulation of multi-line 758 * insert mode. Some of the shifting may come for free 759 * also if the tabs don't have enough slack to take up 760 * all the inserted characters. 761 */ 762 i = shft; 763 slakused = inssiz - doomed; 764 if (slakused > tabslack) { 765 i -= slakused - tabslack; 766 slakused -= tabslack; 767 } 768 if (i > 0 && tabend != linend) { 769 tshft = i; 770 vgotoCL(tabend); 771 goim(); 772 do 773 vputchar(' ' | QUOTE); 774 while (--i); 775 } 776 } else { 777 /* 778 * HP and Datamedia type terminals have to have multi-line 779 * insert faked. Hack each segment after where we are 780 * (going backwards to where we are.) We then can 781 * hack the segment where the end of the first following 782 * tab group is. 783 */ 784 for (j = DEPTH(vcline) - 1; j > (tabend + shft) / WCOLS; j--) { 785 vgotoCL(j * WCOLS); 786 goim(); 787 up = tp + j * WCOLS - shft; 788 i = shft; 789 do { 790 if (*up) 791 vputchar(*up++); 792 else 793 break; 794 } while (--i); 795 } 796 vigotoCL(tabstart); 797 i = shft - (inssiz - doomed); 798 if (i > 0) { 799 tabslack = inssiz - doomed; 800 vcsync(); 801 goim(); 802 do 803 vputchar(' '); 804 while (--i); 805 } 806 } 807 /* 808 * Now do the data moving in the internal screen 809 * image which is common to all three cases. 810 */ 811 tp += linend; 812 up = tp + shft; 813 i = linend - tabend; 814 if (i > 0) 815 do 816 *--up = *--tp; 817 while (--i); 818 if (IN && tshft) { 819 i = tshft; 820 do 821 *--up = ' ' | QUOTE; 822 while (--i); 823 } 824 hold = oldhold; 825 } 826 827 /* 828 * Now do the insert of the characters (finally). 829 */ 830 viin(c) 831 char c; 832 { 833 register char *tp, *up; 834 register int i, j; 835 register bool noim = 0; 836 int remdoom; 837 short oldhold = hold; 838 839 hold |= HOLDPUPD; 840 if (tabsize && (IM && EI) && inssiz - doomed > tabslack) 841 /* 842 * There is a tab out there which will be affected 843 * by the insertion since there aren't enough doomed 844 * characters to take up all the insertion and we do 845 * have insert mode capability. 846 */ 847 if (inscol + doomed == tabstart) { 848 /* 849 * The end of the doomed characters sits right at the 850 * start of the tabs, then we don't need to use insert 851 * mode; unless the tab has already been expanded 852 * in which case we MUST use insert mode. 853 */ 854 slakused = 0; 855 noim = !shft; 856 } else { 857 /* 858 * The last really special case to handle is case 859 * where the tab is just sitting there and doesn't 860 * have enough slack to let the insertion take 861 * place without shifting the rest of the line 862 * over. In this case we have to go out and 863 * delete some characters of the tab before we start 864 * or the answer will be wrong, as the rest of the 865 * line will have been shifted. This code means 866 * that terminals with only insert chracter (no 867 * delete character) won't work correctly. 868 */ 869 i = inssiz - doomed - tabslack - slakused; 870 i %= value(TABSTOP); 871 if (i > 0) { 872 vgotoCL(tabstart); 873 godm(); 874 for (i = inssiz - doomed - tabslack; i > 0; i--) 875 vputp(DC, DEPTH(vcline)); 876 enddm(); 877 } 878 } 879 880 /* 881 * Now put out the characters of the actual insertion. 882 */ 883 vigotoCL(inscol); 884 remdoom = doomed; 885 for (i = inssiz; i > 0; i--) { 886 if (remdoom > 0) { 887 remdoom--; 888 endim(); 889 } else if (noim) 890 endim(); 891 else if (IM && EI) { 892 vcsync(); 893 goim(); 894 } 895 vputchar(c); 896 } 897 898 if (!IM || !EI) { 899 /* 900 * We are a dumb terminal; brute force update 901 * the rest of the line; this is very much an n^^2 process, 902 * and totally unreasonable at low speed. 903 * 904 * You asked for it, you get it. 905 */ 906 tp = vtube0 + inscol + doomed; 907 for (i = inscol + doomed; i < tabstart; i++) 908 vputchar(*tp++); 909 hold = oldhold; 910 vigotoCL(tabstart + inssiz - doomed); 911 for (i = tabsize - (inssiz - doomed) + shft; i > 0; i--) 912 vputchar(' ' | QUOTE); 913 } else { 914 if (!IN) { 915 /* 916 * On terminals without multi-line 917 * insert in the hardware, we must go fix the segments 918 * between the inserted text and the following 919 * tabs, if they are on different lines. 920 * 921 * Aaargh. 922 */ 923 tp = vtube0; 924 for (j = (inscol + inssiz - 1) / WCOLS + 1; 925 j <= (tabstart + inssiz - doomed - 1) / WCOLS; j++) { 926 vgotoCL(j * WCOLS); 927 i = inssiz - doomed; 928 up = tp + j * WCOLS - i; 929 goim(); 930 do 931 vputchar(*up++); 932 while (--i && *up); 933 } 934 } else { 935 /* 936 * On terminals with multi line inserts, 937 * life is simpler, just reflect eating of 938 * the slack. 939 */ 940 tp = vtube0 + tabend; 941 for (i = tabsize - (inssiz - doomed); i >= 0; i--) { 942 if ((*--tp & (QUOTE|TRIM)) == QUOTE) { 943 --tabslack; 944 if (tabslack >= slakused) 945 continue; 946 } 947 *tp = ' ' | QUOTE; 948 } 949 } 950 /* 951 * Blank out the shifted positions to be tab positions. 952 */ 953 if (shft) { 954 tp = vtube0 + tabend + shft; 955 for (i = tabsize - (inssiz - doomed) + shft; i > 0; i--) 956 if ((*--tp & QUOTE) == 0) 957 *tp = ' ' | QUOTE; 958 } 959 } 960 961 /* 962 * Finally, complete the screen image update 963 * to reflect the insertion. 964 */ 965 hold = oldhold; 966 tp = vtube0 + tabstart; up = tp + inssiz - doomed; 967 for (i = tabstart; i > inscol + doomed; i--) 968 *--up = *--tp; 969 for (i = inssiz; i > 0; i--) 970 *--up = c; 971 doomed = 0; 972 } 973 974 /* 975 * Go into ``delete mode''. If the 976 * sequence which goes into delete mode 977 * is the same as that which goes into insert 978 * mode, then we are in delete mode already. 979 */ 980 godm() 981 { 982 983 if (insmode) { 984 if (eq(DM, IM)) 985 return; 986 endim(); 987 } 988 vputp(DM, 0); 989 } 990 991 /* 992 * If we are coming out of delete mode, but 993 * delete and insert mode end with the same sequence, 994 * it wins to pretend we are now in insert mode, 995 * since we will likely want to be there again soon 996 * if we just moved over to delete space from part of 997 * a tab (above). 998 */ 999 enddm() 1000 { 1001 1002 if (eq(DM, IM)) { 1003 insmode = 1; 1004 return; 1005 } 1006 vputp(ED, 0); 1007 } 1008 1009 /* 1010 * In and out of insert mode. 1011 * Note that the code here demands that there be 1012 * a string for insert mode (the null string) even 1013 * if the terminal does all insertions a single character 1014 * at a time, since it branches based on whether IM is null. 1015 */ 1016 goim() 1017 { 1018 1019 if (!insmode) 1020 vputp(IM, 0); 1021 insmode = 1; 1022 } 1023 1024 endim() 1025 { 1026 1027 if (insmode) { 1028 vputp(EI, 0); 1029 insmode = 0; 1030 } 1031 } 1032 1033 /* 1034 * Put the character c on the screen at the current cursor position. 1035 * This routine handles wraparound and scrolling and understands not 1036 * to roll when splitw is set, i.e. we are working in the echo area. 1037 * There is a bunch of hacking here dealing with the difference between 1038 * QUOTE, QUOTE|' ', and ' ' for CONCEPT-100 like terminals, and also 1039 * code to deal with terminals which overstrike, including CRT's where 1040 * you can erase overstrikes with some work. CRT's which do underlining 1041 * implicitly which has to be erased (like CONCEPTS) are also handled. 1042 */ 1043 vputchar(c) 1044 register int c; 1045 { 1046 register char *tp; 1047 register int d; 1048 1049 c &= (QUOTE|TRIM); 1050 #ifdef TRACE 1051 if (trace) 1052 tracec(c); 1053 #endif 1054 /* Fix problem of >79 chars on echo line. */ 1055 if (destcol >= WCOLS-1 && splitw && destline == WECHO) 1056 pofix(); 1057 if (destcol >= WCOLS) { 1058 destline += destcol / WCOLS; 1059 destcol %= WCOLS; 1060 } 1061 if (destline > WBOT && (!splitw || destline > WECHO)) 1062 vrollup(destline); 1063 tp = vtube[destline] + destcol; 1064 switch (c) { 1065 1066 case '\t': 1067 vgotab(); 1068 return; 1069 1070 case ' ': 1071 /* 1072 * We can get away without printing a space in a number 1073 * of cases, but not always. We get away with doing nothing 1074 * if we are not in insert mode, and not on a CONCEPT-100 1075 * like terminal, and either not in hardcopy open or in hardcopy 1076 * open on a terminal with no overstriking, provided, 1077 * in all cases, that nothing has ever been displayed 1078 * at this position. Ugh. 1079 */ 1080 if (!insmode && !IN && (state != HARDOPEN || OS) && (*tp&TRIM) == 0) { 1081 *tp = ' '; 1082 destcol++; 1083 return; 1084 } 1085 goto def; 1086 1087 case QUOTE: 1088 if (insmode) { 1089 /* 1090 * When in insert mode, tabs have to expand 1091 * to real, printed blanks. 1092 */ 1093 c = ' ' | QUOTE; 1094 goto def; 1095 } 1096 if (*tp == 0) { 1097 /* 1098 * A ``space''. 1099 */ 1100 if ((hold & HOLDPUPD) == 0) 1101 *tp = QUOTE; 1102 destcol++; 1103 return; 1104 } 1105 /* 1106 * A ``space'' ontop of a part of a tab. 1107 */ 1108 if (*tp & QUOTE) { 1109 destcol++; 1110 return; 1111 } 1112 c = ' ' | QUOTE; 1113 /* fall into ... */ 1114 1115 def: 1116 default: 1117 d = *tp & TRIM; 1118 /* 1119 * Now get away with doing nothing if the characters 1120 * are the same, provided we are not in insert mode 1121 * and if we are in hardopen, that the terminal has overstrike. 1122 */ 1123 if (d == (c & TRIM) && !insmode && (state != HARDOPEN || OS)) { 1124 if ((hold & HOLDPUPD) == 0) 1125 *tp = c; 1126 destcol++; 1127 return; 1128 } 1129 /* 1130 * Backwards looking optimization. 1131 * The low level cursor motion routines will use 1132 * a cursor motion right sequence to step 1 character 1133 * right. On, e.g., a DM3025A this is 2 characters 1134 * and printing is noticeably slower at 300 baud. 1135 * Since the low level routines are not allowed to use 1136 * spaces for positioning, we discover the common 1137 * case of a single space here and force a space 1138 * to be printed. 1139 */ 1140 if (destcol == outcol + 1 && tp[-1] == ' ' && outline == destline) { 1141 vputc(' '); 1142 outcol++; 1143 } 1144 1145 /* 1146 * This is an inline expansion a call to vcsync() dictated 1147 * by high frequency in a profile. 1148 */ 1149 if (outcol != destcol || outline != destline) 1150 vgoto(destline, destcol); 1151 1152 /* 1153 * Deal with terminals which have overstrike. 1154 * We handle erasing general overstrikes, erasing 1155 * underlines on terminals (such as CONCEPTS) which 1156 * do underlining correctly automatically (e.g. on nroff 1157 * output), and remembering, in hardcopy mode, 1158 * that we have overstruct something. 1159 */ 1160 if (!insmode && d && d != ' ' && d != (c & TRIM)) { 1161 if (EO && (OS || UL && (c == '_' || d == '_'))) { 1162 vputc(' '); 1163 outcol++, destcol++; 1164 back1(); 1165 } else 1166 rubble = 1; 1167 } 1168 1169 /* 1170 * Unless we are just bashing characters around for 1171 * inner working of insert mode, update the display. 1172 */ 1173 if ((hold & HOLDPUPD) == 0) 1174 *tp = c; 1175 1176 /* 1177 * In insert mode, put out the IC sequence, padded 1178 * based on the depth of the current line. 1179 * A terminal which had no real insert mode, rather 1180 * opening a character position at a time could do this. 1181 * Actually should use depth to end of current line 1182 * but this rarely matters. 1183 */ 1184 if (insmode) 1185 vputp(IC, DEPTH(vcline)); 1186 vputc(c & TRIM); 1187 1188 /* 1189 * In insert mode, IP is a post insert pad. 1190 */ 1191 if (insmode) 1192 vputp(IP, DEPTH(vcline)); 1193 destcol++, outcol++; 1194 1195 /* 1196 * CONCEPT braindamage in early models: after a wraparound 1197 * the next newline is eaten. It's hungry so we just 1198 * feed it now rather than worrying about it. 1199 */ 1200 if (XN && outcol % WCOLS == 0) 1201 vputc('\n'); 1202 } 1203 } 1204 1205 /* 1206 * Delete display positions stcol through endcol. 1207 * Amount of use of special terminal features here is limited. 1208 */ 1209 physdc(stcol, endcol) 1210 int stcol, endcol; 1211 { 1212 register char *tp, *up; 1213 char *tpe; 1214 register int i; 1215 register int nc = endcol - stcol; 1216 1217 #ifdef IDEBUG 1218 if (trace) 1219 tfixnl(), fprintf(trace, "physdc(%d, %d)\n", stcol, endcol); 1220 #endif 1221 if (!DC || nc <= 0) 1222 return; 1223 if (IN) { 1224 /* 1225 * CONCEPT-100 like terminal. 1226 * If there are any ``spaces'' in the material to be 1227 * deleted, then this is too hard, just retype. 1228 */ 1229 vprepins(); 1230 up = vtube0 + stcol; 1231 i = nc; 1232 do 1233 if ((*up++ & (QUOTE|TRIM)) == QUOTE) 1234 return; 1235 while (--i); 1236 i = 2 * nc; 1237 do 1238 if (*up == 0 || (*up++ & QUOTE) == QUOTE) 1239 return; 1240 while (--i); 1241 vgotoCL(stcol); 1242 } else { 1243 /* 1244 * HP like delete mode. 1245 * Compute how much text we are moving over by deleting. 1246 * If it appears to be faster to just retype 1247 * the line, do nothing and that will be done later. 1248 * We are assuming 2 output characters per deleted 1249 * characters and that clear to end of line is available. 1250 */ 1251 i = stcol / WCOLS; 1252 if (i != endcol / WCOLS) 1253 return; 1254 i += LINE(vcline); 1255 stcol %= WCOLS; 1256 endcol %= WCOLS; 1257 up = vtube[i]; tp = up + endcol; tpe = up + WCOLS; 1258 while (tp < tpe && *tp) 1259 tp++; 1260 if (tp - (up + stcol) < 2 * nc) 1261 return; 1262 vgoto(i, stcol); 1263 } 1264 1265 /* 1266 * Go into delete mode and do the actual delete. 1267 * Padding is on DC itself. 1268 */ 1269 godm(); 1270 for (i = nc; i > 0; i--) 1271 vputp(DC, DEPTH(vcline)); 1272 vputp(ED, 0); 1273 1274 /* 1275 * Straighten up. 1276 * With CONCEPT like terminals, characters are pulled left 1277 * from first following null. HP like terminals shift rest of 1278 * this (single physical) line rigidly. 1279 */ 1280 if (IN) { 1281 up = vtube0 + stcol; 1282 tp = vtube0 + endcol; 1283 while (i = *tp++) { 1284 if ((i & (QUOTE|TRIM)) == QUOTE) 1285 break; 1286 *up++ = i; 1287 } 1288 do 1289 *up++ = i; 1290 while (--nc); 1291 } else { 1292 copy(up + stcol, up + endcol, WCOLS - endcol); 1293 vclrbyte(tpe - nc, nc); 1294 } 1295 } 1296 1297 #ifdef TRACE 1298 tfixnl() 1299 { 1300 1301 if (trubble || techoin) 1302 fprintf(trace, "\n"); 1303 trubble = 0, techoin = 0; 1304 } 1305 1306 tvliny() 1307 { 1308 register int i; 1309 1310 if (!trace) 1311 return; 1312 tfixnl(); 1313 fprintf(trace, "vcnt = %d, vcline = %d, vliny = ", vcnt, vcline); 1314 for (i = 0; i <= vcnt; i++) { 1315 fprintf(trace, "%d", LINE(i)); 1316 if (FLAGS(i) & VDIRT) 1317 fprintf(trace, "*"); 1318 if (DEPTH(i) != 1) 1319 fprintf(trace, "<%d>", DEPTH(i)); 1320 if (i < vcnt) 1321 fprintf(trace, " "); 1322 } 1323 fprintf(trace, "\n"); 1324 } 1325 1326 tracec(c) 1327 char c; 1328 { 1329 1330 if (!techoin) 1331 trubble = 1; 1332 if (c == ESCAPE) 1333 fprintf(trace, "$"); 1334 else if (c < ' ' || c == DELETE) 1335 fprintf(trace, "^%c", ctlof(c)); 1336 else 1337 fprintf(trace, "%c", c); 1338 } 1339 #endif 1340 1341 /* 1342 * Put a character with possible tracing. 1343 */ 1344 vputch(c) 1345 int c; 1346 { 1347 1348 #ifdef TRACE 1349 if (trace) 1350 tracec(c); 1351 #endif 1352 vputc(c); 1353 } 1354