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