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