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