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