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