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