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