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