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.7 (Berkeley) 6/7/85"; 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) < 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: vSCROLL = cnt; 306: cnt = 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: vSCROLL = cnt; 326: cnt = 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 : ' '; 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: 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 = Putchar; 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: Putchar = 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: sync(); 968: } 969: splitw = 0; 970: 971: /* 972: * Special case: did list/number options change? 973: */ 974: if (onumber != value(NUMBER)) 975: setnumb(value(NUMBER)); 976: if (olist != value(LIST)) 977: 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 && Peekkey == ':') { 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: 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: }