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