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