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