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