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