1: /* Copyright (c) 1981 Regents of the University of California */ 2: static char *sccsid = "@(#)ex_vmain.c 7.1 7/8/81"; 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 != NTTYDISC); 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: }