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