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 #ifndef PRESUNEUC 39 #include <wctype.h> 40 /* Undef putchar/getchar if they're defined. */ 41 #ifdef putchar 42 # undef putchar 43 #endif 44 #ifdef getchar 45 # undef getchar 46 #endif 47 #endif /* PRESUNEUC */ 48 49 /* 50 * This is the main routine for visual. 51 * We here decode the count and possible named buffer specification 52 * preceding a command and interpret a few of the commands. 53 * Commands which involve a target (i.e. an operator) are decoded 54 * in the routine operate in ex_voperate.c. 55 */ 56 57 #define forbid(a) { if (a) goto fonfon; } 58 59 extern int windowchg; 60 extern int sigok; 61 void redraw(), windowinit(); 62 63 #ifdef XPG4 64 extern int P_cursor_offset; 65 #endif 66 67 void 68 vmain(void) 69 { 70 int c, cnt, i; 71 wchar_t esave[TUBECOLS]; 72 extern wchar_t atube[]; 73 unsigned char *oglobp; 74 short d; 75 line *addr; 76 int ind, nlput; 77 int shouldpo = 0; 78 int tag_reset_wrap = 0; 79 int onumber, olist, (*OPline)(), (*OPutchar)(); 80 81 82 vch_mac = VC_NOTINMAC; 83 ixlatctl(0); 84 85 /* 86 * If we started as a vi command (on the command line) 87 * then go process initial commands (recover, next or tag). 88 */ 89 if (initev) { 90 oglobp = globp; 91 globp = initev; 92 hadcnt = cnt = 0; 93 i = tchng; 94 addr = dot; 95 goto doinit; 96 } 97 98 vshowmode(""); /* As a precaution */ 99 /* 100 * NB: 101 * 102 * The current line is always in the line buffer linebuf, 103 * and the cursor at the position cursor. You should do 104 * a vsave() before moving off the line to make sure the disk 105 * copy is updated if it has changed, and a getDOT() to get 106 * the line back if you mung linebuf. The motion 107 * routines in ex_vwind.c handle most of this. 108 */ 109 for (;;) { 110 /* 111 * Decode a visual command. 112 * First sync the temp file if there has been a reasonable 113 * amount of change. Clear state for decoding of next 114 * command. 115 */ 116 TSYNC(); 117 vglobp = 0; 118 vreg = 0; 119 hold = 0; 120 seenprompt = 1; 121 wcursor = 0; 122 Xhadcnt = hadcnt = 0; 123 Xcnt = cnt = 1; 124 splitw = 0; 125 if (i = holdupd && !windowchg) { 126 if (state == VISUAL) { 127 sigok = 1; 128 (void)peekkey(); 129 sigok = 0; 130 } 131 132 holdupd = 0; 133 /* 134 if (LINE(0) < ZERO) { 135 vclear(); 136 vcnt = 0; 137 i = 3; 138 } 139 */ 140 if (state != VISUAL) { 141 vcnt = 0; 142 vsave(); 143 vrepaint(cursor); 144 } else if (i == 3) 145 vredraw(WTOP); 146 else 147 vsync(WTOP); 148 vfixcurs(); 149 } else if(windowchg) 150 redraw(); 151 152 /* 153 * Gobble up counts and named buffer specifications. 154 */ 155 for (;;) { 156 looptop: 157 #ifdef MDEBUG 158 if (trace) 159 fprintf(trace, "pc=%c",peekkey()); 160 #endif 161 sigok = 1; 162 c = peekkey(); 163 sigok = 0; 164 if (isdigit(peekkey()) && peekkey() != '0') { 165 hadcnt = 1; 166 cnt = vgetcnt(); 167 forbid (cnt <= 0); 168 } 169 if (peekkey() != '"') 170 break; 171 (void)getkey(), c = getkey(); 172 /* 173 * Buffer names be letters or digits. 174 * But not '0' as that is the source of 175 * an 'empty' named buffer spec in the routine 176 * kshift (see ex_temp.c). 177 */ 178 if(!isascii(c) && MB_CUR_MAX > 1) { 179 /* get rest of character */ 180 wchar_t wchar; 181 char multic[MULTI_BYTE_MAX]; 182 ungetkey(c); 183 (void)_mbftowc(multic, &wchar, getkey, &Peekkey); 184 } 185 forbid (c == '0' || !isalpha(c) && !isascii(c) && !isdigit(c)); 186 vreg = c; 187 } 188 reread: 189 /* 190 * Come to reread from below after some macro expansions. 191 * The call to map allows use of function key pads 192 * by performing a terminal dependent mapping of inputs. 193 */ 194 #ifdef MDEBUG 195 if (trace) 196 fprintf(trace,"pcb=%c,",peekkey()); 197 #endif 198 op = getkey(); 199 maphopcnt = 0; 200 do { 201 /* 202 * Keep mapping the char as long as it changes. 203 * This allows for double mappings, e.g., q to #, 204 * #1 to something else. 205 */ 206 c = op; 207 op = map(c, arrows, 0); 208 #ifdef MDEBUG 209 if (trace) 210 fprintf(trace,"pca=%c,",c); 211 #endif 212 /* 213 * Maybe the mapped to char is a count. If so, we have 214 * to go back to the "for" to interpret it. Likewise 215 * for a buffer name. 216 */ 217 if ((isdigit(c) && c!='0') || c == '"') { 218 ungetkey(c); 219 goto looptop; 220 } 221 if (!value(vi_REMAP)) { 222 c = op; 223 break; 224 } 225 if (++maphopcnt > 256) 226 error(gettext("Infinite macro loop")); 227 } while (c != op); 228 229 /* 230 * Begin to build an image of this command for possible 231 * later repeat in the buffer workcmd. It will be copied 232 * to lastcmd by the routine setLAST 233 * if/when completely specified. 234 */ 235 lastcp = workcmd; 236 if (!vglobp) 237 *lastcp++ = c; 238 239 /* 240 * First level command decode. 241 */ 242 switch (c) { 243 244 /* 245 * ^L Clear screen e.g. after transmission error. 246 */ 247 248 /* 249 * ^R Retype screen, getting rid of @ lines. 250 * If in open, equivalent to ^L. 251 * On terminals where the right arrow key sends 252 * ^L we make ^R act like ^L, since there is no 253 * way to get ^L. These terminals (adm31, tvi) 254 * are intelligent so ^R is useless. Soroc 255 * will probably foul this up, but nobody has 256 * one of them. 257 */ 258 case CTRL('l'): 259 case CTRL('r'): 260 if (c == CTRL('l') || (key_right && *key_right==CTRL('l'))) { 261 vclear(); 262 vdirty(0, vcnt); 263 } 264 if (state != VISUAL) { 265 /* 266 * Get a clean line, throw away the 267 * memory of what is displayed now, 268 * and move back onto the current line. 269 */ 270 vclean(); 271 vcnt = 0; 272 vmoveto(dot, cursor, 0); 273 continue; 274 } 275 vredraw(WTOP); 276 /* 277 * Weird glitch -- when we enter visual 278 * in a very small window we may end up with 279 * no lines on the screen because the line 280 * at the top is too long. This forces the screen 281 * to be expanded to make room for it (after 282 * we have printed @'s ick showing we goofed). 283 */ 284 if (vcnt == 0) 285 vrepaint(cursor); 286 vfixcurs(); 287 continue; 288 289 /* 290 * $ Escape just cancels the current command 291 * with a little feedback. 292 */ 293 case ESCAPE: 294 (void) beep(); 295 continue; 296 297 /* 298 * @ Macros. Bring in the macro and put it 299 * in vmacbuf, point vglobp there and punt. 300 */ 301 case '@': 302 c = getesc(); 303 if (c == 0) 304 continue; 305 if (c == '@') 306 c = lastmac; 307 if (isupper(c)) 308 c = tolower(c); 309 forbid(!islower(c)); 310 lastmac = c; 311 vsave(); 312 CATCH 313 unsigned char tmpbuf[BUFSIZE]; 314 315 regbuf(c, tmpbuf, sizeof (vmacbuf)); 316 macpush(tmpbuf, 1); 317 ONERR 318 lastmac = 0; 319 splitw = 0; 320 getDOT(); 321 vrepaint(cursor); 322 continue; 323 ENDCATCH 324 vmacp = vmacbuf; 325 goto reread; 326 327 /* 328 * . Repeat the last (modifying) open/visual command. 329 */ 330 case '.': 331 /* 332 * Check that there was a last command, and 333 * take its count and named buffer unless they 334 * were given anew. Special case if last command 335 * referenced a numeric named buffer -- increment 336 * the number and go to a named buffer again. 337 * This allows a sequence like "1pu.u.u... 338 * to successively look for stuff in the kill chain 339 * much as one does in EMACS with C-Y and M-Y. 340 */ 341 forbid (lastcmd[0] == 0); 342 if (hadcnt) 343 lastcnt = cnt; 344 if (vreg) 345 lastreg = vreg; 346 else if (isdigit(lastreg) && lastreg < '9') 347 lastreg++; 348 vreg = lastreg; 349 cnt = lastcnt; 350 hadcnt = lasthad; 351 vglobp = lastcmd; 352 goto reread; 353 354 /* 355 * ^U Scroll up. A count sticks around for 356 * future scrolls as the scroll amount. 357 * Attempt to hold the indentation from the 358 * top of the screen (in logical lines). 359 * 360 * BUG: A ^U near the bottom of the screen 361 * on a dumb terminal (which can't roll back) 362 * causes the screen to be cleared and then 363 * redrawn almost as it was. In this case 364 * one should simply move the cursor. 365 */ 366 case CTRL('u'): 367 if (hadcnt) 368 vSCROLL = cnt; 369 cnt = vSCROLL; 370 if (state == VISUAL) 371 ind = vcline, cnt += ind; 372 else 373 ind = 0; 374 vmoving = 0; 375 vup(cnt, ind, 1); 376 vnline((unsigned char *)NOSTR); 377 continue; 378 379 /* 380 * ^D Scroll down. Like scroll up. 381 */ 382 case CTRL('d'): 383 #ifdef TRACE 384 if (trace) 385 fprintf(trace, "before vdown in ^D, dot=%d, wdot=%d, dol=%d\n", lineno(dot), lineno(wdot), lineno(dol)); 386 #endif 387 if (hadcnt) 388 vSCROLL = cnt; 389 cnt = vSCROLL; 390 if (state == VISUAL) 391 ind = vcnt - vcline - 1, cnt += ind; 392 else 393 ind = 0; 394 vmoving = 0; 395 vdown(cnt, ind, 1); 396 #ifdef TRACE 397 if (trace) 398 fprintf(trace, "before vnline in ^D, dot=%d, wdot=%d, dol=%d\n", lineno(dot), lineno(wdot), lineno(dol)); 399 #endif 400 vnline((unsigned char *)NOSTR); 401 #ifdef TRACE 402 if (trace) 403 fprintf(trace, "after vnline in ^D, dot=%d, wdot=%d, dol=%d\n", lineno(dot), lineno(wdot), lineno(dol)); 404 #endif 405 continue; 406 407 /* 408 * ^E Glitch the screen down (one) line. 409 * Cursor left on same line in file. 410 */ 411 case CTRL('e'): 412 if (state != VISUAL) 413 continue; 414 if (!hadcnt) 415 cnt = 1; 416 /* Bottom line of file already on screen */ 417 forbid(lineDOL()-lineDOT() <= vcnt-1-vcline); 418 ind = vcnt - vcline - 1 + cnt; 419 vdown(ind, ind, 1); 420 vnline(cursor); 421 continue; 422 423 /* 424 * ^Y Like ^E but up 425 */ 426 case CTRL('y'): 427 if (state != VISUAL) 428 continue; 429 if (!hadcnt) 430 cnt = 1; 431 forbid(lineDOT()-1<=vcline); /* line 1 already there */ 432 ind = vcline + cnt; 433 vup(ind, ind, 1); 434 vnline(cursor); 435 continue; 436 437 438 /* 439 * m Mark position in mark register given 440 * by following letter. Return is 441 * accomplished via ' or `; former 442 * to beginning of line where mark 443 * was set, latter to column where marked. 444 */ 445 case 'm': 446 /* 447 * Getesc is generally used when a character 448 * is read as a latter part of a command 449 * to allow one to hit rubout/escape to cancel 450 * what you have typed so far. These characters 451 * are mapped to 0 by the subroutine. 452 */ 453 c = getesc(); 454 if (c == 0) 455 continue; 456 457 /* 458 * Markreg checks that argument is a letter 459 * and also maps ' and ` to the end of the range 460 * to allow '' or `` to reference the previous 461 * context mark. 462 */ 463 c = markreg(c); 464 forbid (c == 0); 465 vsave(); 466 names[c - 'a'] = (*dot &~ 01); 467 ncols[c - 'a'] = cursor; 468 anymarks = 1; 469 continue; 470 471 /* 472 * ^F Window forwards, with 2 lines of continuity. 473 * Count repeats. 474 */ 475 case CTRL('f'): 476 vsave(); 477 if (vcnt > 2) { 478 addr = dot + (vcnt - vcline) - 2 + (cnt-1)*basWLINES; 479 forbid(addr > dol); 480 dot = addr; 481 vcnt = vcline = 0; 482 } 483 vzop(0, 0, '+'); 484 continue; 485 486 /* 487 * ^B Window backwards, with 2 lines of continuity. 488 * Inverse of ^F. 489 */ 490 case CTRL('b'): 491 vsave(); 492 if (one + vcline != dot && vcnt > 2) { 493 addr = dot - vcline + 2 - (cnt-1)*basWLINES; 494 forbid (addr <= zero); 495 dot = addr; 496 vcnt = vcline = 0; 497 } 498 vzop(0, 0, '^'); 499 continue; 500 501 /* 502 * z Screen adjustment, taking a following character: 503 * zcarriage_return current line to top 504 * z<NL> like zcarriage_return 505 * z- current line to bottom 506 * also z+, z^ like ^F and ^B. 507 * A preceding count is line to use rather 508 * than current line. A count between z and 509 * specifier character changes the screen size 510 * for the redraw. 511 * 512 */ 513 case 'z': 514 if (state == VISUAL) { 515 i = vgetcnt(); 516 if (i > 0) 517 vsetsiz(i); 518 c = getesc(); 519 if (c == 0) 520 continue; 521 } 522 vsave(); 523 vzop(hadcnt, cnt, c); 524 continue; 525 526 /* 527 * Y Yank lines, abbreviation for y_ or yy. 528 * Yanked lines can be put later if no 529 * changes intervene, or can be put in named 530 * buffers and put anytime in this session. 531 */ 532 case 'Y': 533 ungetkey('_'); 534 c = 'y'; 535 break; 536 537 /* 538 * J Join lines, 2 by default. Count is number 539 * of lines to join (no join operator sorry.) 540 */ 541 case 'J': 542 forbid (dot == dol); 543 if (cnt == 1) 544 cnt = 2; 545 if (cnt > (i = dol - dot + 1)) 546 cnt = i; 547 vsave(); 548 vmacchng(1); 549 setLAST(); 550 cursor = strend(linebuf); 551 vremote(cnt, join, 0); 552 notenam = (unsigned char *)"join"; 553 vmoving = 0; 554 killU(); 555 vreplace(vcline, cnt, 1); 556 if (!*cursor && cursor > linebuf) 557 cursor--; 558 if (notecnt == 2) 559 notecnt = 0; 560 vrepaint(cursor); 561 continue; 562 563 /* 564 * S Substitute text for whole lines, abbrev for c_. 565 * Count is number of lines to change. 566 */ 567 case 'S': 568 ungetkey('_'); 569 c = 'c'; 570 break; 571 572 /* 573 * O Create a new line above current and accept new 574 * input text, to an escape, there. 575 * A count specifies, for dumb terminals when 576 * slowopen is not set, the number of physical 577 * line space to open on the screen. 578 * 579 * o Like O, but opens lines below. 580 */ 581 case 'O': 582 case 'o': 583 vmacchng(1); 584 voOpen(c, cnt); 585 continue; 586 587 /* 588 * C Change text to end of line, short for c$. 589 */ 590 case 'C': 591 if (*cursor) { 592 ungetkey('$'), c = 'c'; 593 break; 594 } 595 goto appnd; 596 597 /* 598 * ~ Switch case of letter under cursor 599 */ 600 case '~': 601 { 602 unsigned char mbuf[2049]; 603 unsigned char *ccursor = cursor; 604 #ifdef PRESUNEUC 605 int tmp, length; 606 wchar_t wchar; 607 #else 608 int tmp, len, n; 609 wchar_t wc; 610 #endif /* PRESUNEUC */ 611 unsigned char tmp1; 612 setLAST(); 613 for (tmp = 0; tmp + 3 < 2048; ) { 614 /* 615 * Use multiple 'r' commands to replace 616 * alpha with alternate case. 617 */ 618 619 if(cnt-- <= 0) 620 break; 621 #ifdef PRESUNEUC 622 length = mbtowc(&wchar, (char *)ccursor, MULTI_BYTE_MAX); 623 #else 624 len = mbtowc(&wc, (char *)ccursor, MULTI_BYTE_MAX); 625 #endif /* PRESUNEUC */ 626 #ifdef PRESUNEUC 627 if(length > 1) { 628 #else 629 n = iswalpha(wc); 630 if(len > 1 && !iswalpha(wc)) { 631 #endif /* PRESUNEUC */ 632 mbuf[tmp+0] = ' '; 633 tmp++; 634 #ifdef PRESUNEUC 635 ccursor += length; 636 #else 637 ccursor += len; 638 #endif /* PRESUNEUC */ 639 continue; 640 } 641 mbuf[tmp] = 'r'; 642 #ifdef PRESUNEUC 643 mbuf[tmp+1] = *ccursor++; 644 #else 645 ccursor += ((len > 0) ? len : 1); 646 #endif /* PRESUNEUC */ 647 /* 648 * If pointing to an alpha character, 649 * change the case. 650 */ 651 652 tmp1 = mbuf[tmp+1]; 653 #ifdef PRESUNEUC 654 if (isupper((unsigned char)tmp1)) 655 mbuf[tmp+1] = tolower((unsigned char)tmp1); 656 else 657 mbuf[tmp+1] = toupper((unsigned char)tmp1); 658 #else 659 if (iswupper(wc)) 660 len = wctomb((char *)(mbuf + tmp + 1), 661 (wc = towlower(wc))); 662 else 663 len = wctomb((char *)(mbuf + tmp + 1), 664 (wc = towupper(wc))); 665 tmp += len - 1; 666 #endif /* PRESUNEUC */ 667 if(*ccursor) 668 /* 669 * If at end of line do not advance 670 * to the next character, else use a 671 * space to advance 1 column. 672 */ 673 mbuf[tmp+2] = ' '; 674 else { 675 mbuf[tmp+2] = '\0'; 676 tmp +=3; 677 break; 678 } 679 tmp += 3; 680 } 681 682 mbuf[tmp] = 0; 683 macpush(mbuf, 1); 684 } 685 continue; 686 687 688 /* 689 * A Append at end of line, short for $a. 690 */ 691 case 'A': 692 operate('$', 1); 693 appnd: 694 c = 'a'; 695 /* fall into ... */ 696 697 /* 698 * a Appends text after cursor. Text can continue 699 * through arbitrary number of lines. 700 */ 701 case 'a': 702 if (*cursor) { 703 wchar_t wchar; 704 int length = mbtowc(&wchar, (char *)cursor, MULTI_BYTE_MAX); 705 if (state == HARDOPEN) { 706 if(length < 0) { 707 putoctal = 1; 708 putchar(*cursor); 709 putoctal = 0; 710 } else 711 putchar(wchar); 712 } 713 if(length < 0) 714 cursor++; 715 else 716 cursor += length; 717 } 718 goto insrt; 719 720 /* 721 * I Insert at beginning of whitespace of line, 722 * short for ^i. 723 */ 724 case 'I': 725 operate('^', 1); 726 c = 'i'; 727 /* fall into ... */ 728 729 /* 730 * R Replace characters, one for one, by input 731 * (logically), like repeated r commands. 732 * 733 * BUG: This is like the typeover mode of many other 734 * editors, and is only rarely useful. Its 735 * implementation is a hack in a low level 736 * routine and it doesn't work very well, e.g. 737 * you can't move around within a R, etc. 738 */ 739 case 'R': 740 /* fall into... */ 741 742 /* 743 * i Insert text to an escape in the buffer. 744 * Text is arbitrary. This command reminds of 745 * the i command in bare teco. 746 */ 747 case 'i': 748 insrt: 749 /* 750 * Common code for all the insertion commands. 751 * Save for redo, position cursor, prepare for append 752 * at command and in visual undo. Note that nothing 753 * is doomed, unless R when all is, and save the 754 * current line in a the undo temporary buffer. 755 */ 756 vmacchng(1); 757 setLAST(); 758 vcursat(cursor); 759 prepapp(); 760 vnoapp(); 761 doomed = c == 'R' ? 10000 : 0; 762 if(FIXUNDO) 763 vundkind = VCHNG; 764 vmoving = 0; 765 CP(vutmp, linebuf); 766 767 /* 768 * If this is a repeated command, then suppress 769 * fake insert mode on dumb terminals which looks 770 * ridiculous and wastes lots of time even at 9600B. 771 */ 772 if (vglobp) 773 hold = HOLDQIK; 774 vappend(c, cnt, 0); 775 continue; 776 777 /* 778 * An attention, normally a DEL, just beeps. 779 * If you are a vi command within ex, then 780 * two ATTN's will drop you back to command mode. 781 */ 782 case ATTN: 783 (void) beep(); 784 if (initev || peekkey() != ATTN) 785 continue; 786 /* fall into... */ 787 788 /* 789 * ^\ A quit always gets command mode. 790 */ 791 case QUIT: 792 /* 793 * Have to be careful if we were called 794 * g/xxx/vi 795 * since a return will just start up again. 796 * So we simulate an interrupt. 797 */ 798 if (inglobal) 799 onintr(0); 800 /* fall into... */ 801 802 #ifdef notdef 803 /* 804 * q Quit back to command mode, unless called as 805 * vi on command line in which case dont do it 806 */ 807 case 'q': /* quit */ 808 if (initev) { 809 vsave(); 810 CATCH 811 error(gettext("Q gets ex command mode, :q leaves vi")); 812 ENDCATCH 813 splitw = 0; 814 getDOT(); 815 vrepaint(cursor); 816 continue; 817 } 818 #endif 819 /* fall into... */ 820 821 /* 822 * Q Is like q, but always gets to command mode 823 * even if command line invocation was as vi. 824 */ 825 case 'Q': 826 vsave(); 827 /* 828 * If we are in the middle of a macro, throw away 829 * the rest and fix up undo. 830 * This code copied from getbr(). 831 */ 832 if (vmacp) { 833 vmacp = 0; 834 if (inopen == -1) /* don't mess up undo for esc esc */ 835 vundkind = VMANY; 836 inopen = 1; /* restore old setting now that macro done */ 837 } 838 ixlatctl(1); 839 return; 840 841 842 /* 843 * ZZ Like :x 844 */ 845 case 'Z': 846 forbid(getkey() != 'Z'); 847 oglobp = globp; 848 globp = (unsigned char *)"x"; 849 vclrech(0); 850 goto gogo; 851 852 /* 853 * P Put back text before cursor or before current 854 * line. If text was whole lines goes back 855 * as whole lines. If part of a single line 856 * or parts of whole lines splits up current 857 * line to form many new lines. 858 * May specify a named buffer, or the delete 859 * saving buffers 1-9. 860 * 861 * p Like P but after rather than before. 862 */ 863 case 'P': 864 case 'p': 865 vmoving = 0; 866 #ifdef XPG4 867 P_cursor_offset = 0; 868 #endif 869 #ifdef notdef 870 forbid (!vreg && value(vi_UNDOMACRO) && inopen < 0); 871 #endif 872 /* 873 * If previous delete was partial line, use an 874 * append or insert to put it back so as to 875 * use insert mode on intelligent terminals. 876 */ 877 if (!vreg && DEL[0]) { 878 setLAST(); 879 forbid ((unsigned char)DEL[128] == 0200); 880 vglobp = DEL; 881 ungetkey(c == 'p' ? 'a' : 'i'); 882 goto reread; 883 } 884 885 /* 886 * If a register wasn't specified, then make 887 * sure there is something to put back. 888 */ 889 forbid (!vreg && unddol == dol); 890 /* 891 * If we just did a macro the whole buffer is in 892 * the undo save area. We don't want to put THAT. 893 */ 894 forbid (vundkind == VMANY && undkind==UNDALL); 895 vsave(); 896 vmacchng(1); 897 setLAST(); 898 i = 0; 899 if (vreg && partreg(vreg) || !vreg && pkill[0]) { 900 /* 901 * Restoring multiple lines which were partial 902 * lines; will leave cursor in middle 903 * of line after shoving restored text in to 904 * split the current line. 905 */ 906 i++; 907 if (c == 'p' && *cursor) 908 cursor = nextchr(cursor); 909 } else { 910 /* 911 * In whole line case, have to back up dot 912 * for P; also want to clear cursor so 913 * cursor will eventually be positioned 914 * at the beginning of the first put line. 915 */ 916 cursor = 0; 917 if (c == 'P') { 918 dot--, vcline--; 919 c = 'p'; 920 } 921 } 922 killU(); 923 924 /* 925 * The call to putreg can potentially 926 * bomb since there may be nothing in a named buffer. 927 * We thus put a catch in here. If we didn't and 928 * there was an error we would end up in command mode. 929 */ 930 addr = dol; /* old dol */ 931 CATCH 932 vremote(1, 933 vreg ? (int (*)())putreg : put, vreg); 934 ONERR 935 if (vreg == -1) { 936 splitw = 0; 937 if (op == 'P') 938 dot++, vcline++; 939 goto pfixup; 940 } 941 ENDCATCH 942 splitw = 0; 943 nlput = dol - addr + 1; 944 if (!i) { 945 /* 946 * Increment undap1, undap2 to make up 947 * for their incorrect initialization in the 948 * routine vremote before calling put/putreg. 949 */ 950 if (FIXUNDO) 951 undap1++, undap2++; 952 vcline++; 953 nlput--; 954 955 /* 956 * After a put want current line first line, 957 * and dot was made the last line put in code 958 * run so far. This is why we increment vcline 959 * above and decrease dot here. 960 */ 961 dot -= nlput - 1; 962 } 963 #ifdef TRACE 964 if (trace) 965 fprintf(trace, "vreplace(%d, %d, %d), undap1=%d, undap2=%d, dot=%d\n", vcline, i, nlput, lineno(undap1), lineno(undap2), lineno(dot)); 966 #endif 967 vreplace(vcline, i, nlput); 968 #ifdef XPG4 969 if (op == 'P' && i > 0) { 970 dot += nlput - 1; 971 vcline += nlput - 1; 972 cursor += P_cursor_offset; 973 } 974 #endif 975 if (state != VISUAL) { 976 /* 977 * Special case in open mode. 978 * Force action on the screen when a single 979 * line is put even if it is identical to 980 * the current line, e.g. on YP; otherwise 981 * you can't tell anything happened. 982 */ 983 vjumpto(dot, cursor, '.'); 984 continue; 985 } 986 pfixup: 987 vrepaint(cursor); 988 vfixcurs(); 989 continue; 990 991 /* 992 * ^^ Return to previous file. 993 * Like a :e #, and thus can be used after a 994 * "No Write" diagnostic. 995 */ 996 case CTRL('^'): 997 forbid (hadcnt); 998 vsave(); 999 ckaw(); 1000 oglobp = globp; 1001 if (value(vi_AUTOWRITE) && !value(vi_READONLY)) 1002 globp = (unsigned char *)"e! #"; 1003 else 1004 globp = (unsigned char *)"e #"; 1005 goto gogo; 1006 1007 #ifdef TAG_STACK 1008 /* 1009 * ^T Pop the tag stack if enabled or else reset it 1010 * if not. 1011 */ 1012 case CTRL('t'): 1013 forbid (hadcnt); 1014 vsave(); 1015 oglobp = globp; 1016 globp = (unsigned char *) "pop"; 1017 goto gogo; 1018 #endif 1019 /* 1020 * ^] Takes word after cursor as tag, and then does 1021 * tag command. Read ``go right to''. 1022 * This is not a search, so the wrapscan setting 1023 * must be ignored. If set, then it is unset 1024 * here and restored later. 1025 */ 1026 case CTRL(']'): 1027 grabtag(); 1028 oglobp = globp; 1029 if (value(vi_WRAPSCAN) == 0) { 1030 tag_reset_wrap = 1; 1031 value(vi_WRAPSCAN) = 1; 1032 } 1033 globp = (unsigned char *)"tag"; 1034 goto gogo; 1035 1036 /* 1037 * & Like :& 1038 */ 1039 case '&': 1040 oglobp = globp; 1041 globp = (unsigned char *)"&"; 1042 goto gogo; 1043 1044 /* 1045 * ^G Bring up a status line at the bottom of 1046 * the screen, like a :file command. 1047 * 1048 * BUG: Was ^S but doesn't work in cbreak mode 1049 */ 1050 case CTRL('g'): 1051 oglobp = globp; 1052 globp = (unsigned char *)"file"; 1053 gogo: 1054 addr = dot; 1055 vsave(); 1056 goto doinit; 1057 1058 #ifdef SIGTSTP 1059 /* 1060 * ^Z: suspend editor session and temporarily return 1061 * to shell. Only works with Berkeley/IIASA process 1062 * control in kernel. 1063 */ 1064 case CTRL('z'): 1065 forbid(dosusp == 0); 1066 vsave(); 1067 oglobp = globp; 1068 globp = (unsigned char *)"stop"; 1069 goto gogo; 1070 #endif 1071 1072 /* 1073 * : Read a command from the echo area and 1074 * execute it in command mode. 1075 */ 1076 case ':': 1077 forbid (hadcnt); 1078 vsave(); 1079 i = tchng; 1080 addr = dot; 1081 if (readecho(c)) { 1082 esave[0] = 0; 1083 goto fixup; 1084 } 1085 getDOT(); 1086 /* 1087 * Use the visual undo buffer to store the global 1088 * string for command mode, since it is idle right now. 1089 */ 1090 oglobp = globp; strcpy(vutmp, genbuf+1); globp = vutmp; 1091 doinit: 1092 esave[0] = 0; 1093 fixech(); 1094 1095 /* 1096 * Have to finagle around not to lose last 1097 * character after this command (when run from ex 1098 * command mode). This is clumsy. 1099 */ 1100 d = peekc; ungetchar(0); 1101 if (shouldpo) { 1102 /* 1103 * So after a "Hit return..." ":", we do 1104 * another "Hit return..." the next time 1105 */ 1106 pofix(); 1107 shouldpo = 0; 1108 } 1109 CATCH 1110 /* 1111 * Save old values of options so we can 1112 * notice when they change; switch into 1113 * cooked mode so we are interruptible. 1114 */ 1115 onumber = value(vi_NUMBER); 1116 olist = value(vi_LIST); 1117 OPline = Pline; 1118 OPutchar = Putchar; 1119 #ifndef CBREAK 1120 vcook(); 1121 #endif 1122 commands(1, 1); 1123 if (dot == zero && dol > zero) 1124 dot = one; 1125 #ifndef CBREAK 1126 vraw(); 1127 #endif 1128 ONERR 1129 #ifndef CBREAK 1130 vraw(); 1131 #endif 1132 copy(esave, vtube[WECHO], TUBECOLS * sizeof(wchar_t)); 1133 ENDCATCH 1134 fixol(); 1135 Pline = OPline; 1136 Putchar = OPutchar; 1137 ungetchar(d); 1138 globp = oglobp; 1139 1140 /* 1141 * If we ended up with no lines in the buffer, make 1142 * a line. 1143 */ 1144 if (dot == zero) { 1145 fixzero(); 1146 } 1147 splitw = 0; 1148 1149 /* 1150 * Special case: did list/number options change? 1151 */ 1152 if (onumber != value(vi_NUMBER)) 1153 setnumb(value(vi_NUMBER)); 1154 if (olist != value(vi_LIST)) 1155 setlist(value(vi_LIST)); 1156 1157 fixup: 1158 /* 1159 * If a change occurred, other than 1160 * a write which clears changes, then 1161 * we should allow an undo even if . 1162 * didn't move. 1163 * 1164 * BUG: You can make this wrong by 1165 * tricking around with multiple commands 1166 * on one line of : escape, and including 1167 * a write command there, but it's not 1168 * worth worrying about. 1169 */ 1170 if (FIXUNDO && tchng && tchng != i) 1171 vundkind = VMANY, cursor = 0; 1172 1173 /* 1174 * If we are about to do another :, hold off 1175 * updating of screen. 1176 */ 1177 if (vcnt < 0 && Peekkey == ':') { 1178 getDOT(); 1179 shouldpo = 1; 1180 continue; 1181 } 1182 shouldpo = 0; 1183 1184 /* 1185 * In the case where the file being edited is 1186 * new; e.g. if the initial state hasn't been 1187 * saved yet, then do so now. 1188 */ 1189 if (unddol == truedol) { 1190 vundkind = VNONE; 1191 Vlines = lineDOL(); 1192 if (!inglobal) 1193 savevis(); 1194 addr = zero; 1195 vcnt = 0; 1196 if (esave[0] == 0) 1197 copy(esave, vtube[WECHO], TUBECOLS * sizeof(wchar_t)); 1198 } 1199 1200 /* 1201 * If the current line moved reset the cursor position. 1202 */ 1203 if (dot != addr) { 1204 vmoving = 0; 1205 cursor = 0; 1206 } 1207 1208 /* 1209 * If current line is not on screen or if we are 1210 * in open mode and . moved, then redraw. 1211 */ 1212 i = vcline + (dot - addr); 1213 if(windowchg) 1214 windowinit(); 1215 if (i < 0 || i >= vcnt && i >= -vcnt || state != VISUAL && dot != addr) { 1216 if (state == CRTOPEN) 1217 vup1(); 1218 if (vcnt > 0) 1219 vcnt = 0; 1220 vjumpto(dot, (unsigned char *) 0, '.'); 1221 } else { 1222 /* 1223 * Current line IS on screen. 1224 * If we did a [Hit return...] then 1225 * restore vcnt and clear screen if in visual 1226 */ 1227 vcline = i; 1228 if (vcnt < 0) { 1229 vcnt = -vcnt; 1230 if (state == VISUAL) 1231 vclear(); 1232 else if (state == CRTOPEN) { 1233 vcnt = 0; 1234 } 1235 } 1236 1237 /* 1238 * Limit max value of vcnt based on $ 1239 */ 1240 i = vcline + lineDOL() - lineDOT() + 1; 1241 if (i < vcnt) 1242 vcnt = i; 1243 1244 /* 1245 * Dirty and repaint. 1246 */ 1247 vdirty(0, lines); 1248 vrepaint(cursor); 1249 } 1250 1251 /* 1252 * If in visual, put back the echo area 1253 * if it was clobbered. 1254 */ 1255 if (state == VISUAL) { 1256 int sdc = destcol, sdl = destline; 1257 1258 splitw++; 1259 vigoto(WECHO, 0); 1260 for (i = 0; i < TUBECOLS - 1; i++) { 1261 if (esave[i] == 0) 1262 break; 1263 if(esave[i] != FILLER) 1264 (void) vputchar(esave[i]); 1265 } 1266 splitw = 0; 1267 vgoto(sdl, sdc); 1268 } 1269 if (tag_reset_wrap == 1) { 1270 tag_reset_wrap = 0; 1271 value(vi_WRAPSCAN) = 0; 1272 } 1273 continue; 1274 1275 /* 1276 * u undo the last changing command. 1277 */ 1278 case 'u': 1279 vundo(1); 1280 continue; 1281 1282 /* 1283 * U restore current line to initial state. 1284 */ 1285 case 'U': 1286 vUndo(); 1287 continue; 1288 1289 fonfon: 1290 (void) beep(); 1291 vmacp = 0; 1292 inopen = 1; /* might have been -1 */ 1293 continue; 1294 } 1295 1296 /* 1297 * Rest of commands are decoded by the operate 1298 * routine. 1299 */ 1300 operate(c, cnt); 1301 } 1302 } 1303 1304 /* 1305 * Grab the word after the cursor so we can look for it as a tag. 1306 */ 1307 void 1308 grabtag(void) 1309 { 1310 unsigned char *cp, *dp; 1311 1312 cp = vpastwh(cursor); 1313 if (*cp) { 1314 dp = lasttag; 1315 do { 1316 if (dp < &lasttag[sizeof lasttag - 2]) 1317 *dp++ = *cp; 1318 cp++; 1319 /* only allow ascii alphabetics */ 1320 } while ((isascii(*cp) && isalpha(*cp)) || isdigit(*cp) || *cp == '_'); 1321 *dp++ = 0; 1322 } 1323 } 1324 1325 /* 1326 * Before appending lines, set up addr1 and 1327 * the command mode undo information. 1328 */ 1329 void 1330 prepapp(void) 1331 { 1332 1333 addr1 = dot; 1334 deletenone(); 1335 addr1++; 1336 appendnone(); 1337 } 1338 1339 /* 1340 * Execute function f with the address bounds addr1 1341 * and addr2 surrounding cnt lines starting at dot. 1342 */ 1343 void 1344 vremote(cnt, f, arg) 1345 int cnt, (*f)(), arg; 1346 { 1347 int oing = inglobal; 1348 1349 addr1 = dot; 1350 addr2 = dot + cnt - 1; 1351 inglobal = 0; 1352 if (FIXUNDO) 1353 undap1 = undap2 = dot; 1354 (*f)(arg); 1355 inglobal = oing; 1356 if (FIXUNDO) 1357 vundkind = VMANY; 1358 vmcurs = 0; 1359 } 1360 1361 /* 1362 * Save the current contents of linebuf, if it has changed. 1363 */ 1364 void 1365 vsave(void) 1366 { 1367 unsigned char temp[LBSIZE]; 1368 1369 strncpy(temp, linebuf, sizeof (temp)); 1370 if (FIXUNDO && vundkind == VCHNG || vundkind == VCAPU) { 1371 /* 1372 * If the undo state is saved in the temporary buffer 1373 * vutmp, then we sync this into the temp file so that 1374 * we will be able to undo even after we have moved off 1375 * the line. It would be possible to associate a line 1376 * with vutmp but we assume that vutmp is only associated 1377 * with line dot (e.g. in case ':') above, so beware. 1378 */ 1379 prepapp(); 1380 strcLIN(vutmp); 1381 putmark(dot); 1382 vremote(1, yank, 0); 1383 vundkind = VMCHNG; 1384 notecnt = 0; 1385 undkind = UNDCHANGE; 1386 } 1387 /* 1388 * Get the line out of the temp file and do nothing if it hasn't 1389 * changed. This may seem like a loss, but the line will 1390 * almost always be in a read buffer so this may well avoid disk i/o. 1391 */ 1392 getDOT(); 1393 if (strncmp(linebuf, temp, sizeof (temp)) == 0) 1394 return; 1395 strcLIN(temp); 1396 putmark(dot); 1397 } 1398 1399 #undef forbid 1400 #define forbid(a) if (a) { (void) beep(); return; } 1401 1402 /* 1403 * Do a z operation. 1404 * Code here is rather long, and very uninteresting. 1405 */ 1406 void 1407 vzop(bool hadcnt, int cnt, int c) 1408 { 1409 line *addr; 1410 1411 if (state != VISUAL) { 1412 /* 1413 * Z from open; always like a z=. 1414 * This code is a mess and should be cleaned up. 1415 */ 1416 vmoveitup(1, 1); 1417 vgoto(outline, 0); 1418 ostop(normf); 1419 setoutt(); 1420 addr2 = dot; 1421 vclear(); 1422 destline = WECHO; 1423 zop2(Xhadcnt ? Xcnt : value(vi_WINDOW) - 1, '='); 1424 if (state == CRTOPEN) 1425 putnl(); 1426 putNFL(); 1427 termreset(); 1428 Outchar = vputchar; 1429 (void)ostart(); 1430 vcnt = 0; 1431 outline = destline = 0; 1432 vjumpto(dot, cursor, 0); 1433 return; 1434 } 1435 if (hadcnt) { 1436 addr = zero + cnt; 1437 if (addr < one) 1438 addr = one; 1439 if (addr > dol) 1440 addr = dol; 1441 markit(addr); 1442 } else 1443 switch (c) { 1444 1445 case '+': 1446 addr = dot + vcnt - vcline; 1447 break; 1448 1449 case '^': 1450 addr = dot - vcline - 1; 1451 forbid (addr < one); 1452 c = '-'; 1453 break; 1454 1455 default: 1456 addr = dot; 1457 break; 1458 } 1459 switch (c) { 1460 1461 case '.': 1462 case '-': 1463 break; 1464 1465 case '^': 1466 forbid (addr <= one); 1467 break; 1468 1469 case '+': 1470 forbid (addr >= dol); 1471 /* fall into ... */ 1472 1473 case CR: 1474 case NL: 1475 c = CR; 1476 break; 1477 1478 default: 1479 (void) beep(); 1480 return; 1481 } 1482 vmoving = 0; 1483 vjumpto(addr, (unsigned char *)NOSTR, c); 1484 } 1485