1: /* Copyright (c) 1981 Regents of the University of California */ 2: static char *sccsid = "@(#)ex_voper.c 7.1 7/8/81"; 3: #include "ex.h" 4: #include "ex_tty.h" 5: #include "ex_vis.h" 6: 7: #define blank() isspace(wcursor[0]) 8: #define forbid(a) if (a) goto errlab; 9: 10: char vscandir[2] = { '/', 0 }; 11: 12: /* 13: * Decode an operator/operand type command. 14: * Eventually we switch to an operator subroutine in ex_vops.c. 15: * The work here is setting up a function variable to point 16: * to the routine we want, and manipulation of the variables 17: * wcursor and wdot, which mark the other end of the affected 18: * area. If wdot is zero, then the current line is the other end, 19: * and if wcursor is zero, then the first non-blank location of the 20: * other line is implied. 21: */ 22: operate(c, cnt) 23: register int c, cnt; 24: { 25: register int i; 26: int (*moveop)(), (*deleteop)(); 27: register int (*opf)(); 28: bool subop = 0; 29: char *oglobp, *ocurs; 30: register line *addr; 31: line *odot; 32: static char lastFKND, lastFCHR; 33: char d; 34: 35: moveop = vmove, deleteop = vdelete; 36: wcursor = cursor; 37: wdot = NOLINE; 38: notecnt = 0; 39: dir = 1; 40: switch (c) { 41: 42: /* 43: * d delete operator. 44: */ 45: case 'd': 46: moveop = vdelete; 47: deleteop = beep; 48: break; 49: 50: /* 51: * s substitute characters, like c\040, i.e. change space. 52: */ 53: case 's': 54: ungetkey(' '); 55: subop++; 56: /* fall into ... */ 57: 58: /* 59: * c Change operator. 60: */ 61: case 'c': 62: if (c == 'c' && workcmd[0] == 'C' || workcmd[0] == 'S') 63: subop++; 64: moveop = vchange; 65: deleteop = beep; 66: break; 67: 68: /* 69: * ! Filter through a UNIX command. 70: */ 71: case '!': 72: moveop = vfilter; 73: deleteop = beep; 74: break; 75: 76: /* 77: * y Yank operator. Place specified text so that it 78: * can be put back with p/P. Also yanks to named buffers. 79: */ 80: case 'y': 81: moveop = vyankit; 82: deleteop = beep; 83: break; 84: 85: /* 86: * = Reformat operator (for LISP). 87: */ 88: #ifdef LISPCODE 89: case '=': 90: forbid(!value(LISP)); 91: /* fall into ... */ 92: #endif 93: 94: /* 95: * > Right shift operator. 96: * < Left shift operator. 97: */ 98: case '<': 99: case '>': 100: moveop = vshftop; 101: deleteop = beep; 102: break; 103: 104: /* 105: * r Replace character under cursor with single following 106: * character. 107: */ 108: case 'r': 109: vmacchng(1); 110: vrep(cnt); 111: return; 112: 113: default: 114: goto nocount; 115: } 116: vmacchng(1); 117: /* 118: * Had an operator, so accept another count. 119: * Multiply counts together. 120: */ 121: if (isdigit(peekkey()) && peekkey() != '0') { 122: cnt *= vgetcnt(); 123: Xcnt = cnt; 124: forbid (cnt <= 0); 125: } 126: 127: /* 128: * Get next character, mapping it and saving as 129: * part of command for repeat. 130: */ 131: c = map(getesc(),arrows); 132: if (c == 0) 133: return; 134: if (!subop) 135: *lastcp++ = c; 136: nocount: 137: opf = moveop; 138: switch (c) { 139: 140: /* 141: * b Back up a word. 142: * B Back up a word, liberal definition. 143: */ 144: case 'b': 145: case 'B': 146: dir = -1; 147: /* fall into ... */ 148: 149: /* 150: * w Forward a word. 151: * W Forward a word, liberal definition. 152: */ 153: case 'W': 154: case 'w': 155: wdkind = c & ' '; 156: forbid(lfind(2, cnt, opf, 0) < 0); 157: vmoving = 0; 158: break; 159: 160: /* 161: * E to end of following blank/nonblank word 162: */ 163: case 'E': 164: wdkind = 0; 165: goto ein; 166: 167: /* 168: * e To end of following word. 169: */ 170: case 'e': 171: wdkind = 1; 172: ein: 173: forbid(lfind(3, cnt - 1, opf, 0) < 0); 174: vmoving = 0; 175: break; 176: 177: /* 178: * ( Back an s-expression. 179: */ 180: case '(': 181: dir = -1; 182: /* fall into... */ 183: 184: /* 185: * ) Forward an s-expression. 186: */ 187: case ')': 188: forbid(lfind(0, cnt, opf, (line *) 0) < 0); 189: markDOT(); 190: break; 191: 192: /* 193: * { Back an s-expression, but don't stop on atoms. 194: * In text mode, a paragraph. For C, a balanced set 195: * of {}'s. 196: */ 197: case '{': 198: dir = -1; 199: /* fall into... */ 200: 201: /* 202: * } Forward an s-expression, but don't stop on atoms. 203: * In text mode, back paragraph. For C, back a balanced 204: * set of {}'s. 205: */ 206: case '}': 207: forbid(lfind(1, cnt, opf, (line *) 0) < 0); 208: markDOT(); 209: break; 210: 211: /* 212: * % To matching () or {}. If not at ( or { scan for 213: * first such after cursor on this line. 214: */ 215: case '%': 216: vsave(); 217: i = lmatchp((line *) 0); 218: #ifdef TRACE 219: if (trace) 220: fprintf(trace, "after lmatchp in %, dot=%d, wdot=%d, dol=%d\n", lineno(dot), lineno(wdot), lineno(dol)); 221: #endif 222: getDOT(); 223: forbid(!i); 224: if (opf != vmove) 225: if (dir > 0) 226: wcursor++; 227: else 228: cursor++; 229: else 230: markDOT(); 231: vmoving = 0; 232: break; 233: 234: /* 235: * [ Back to beginning of defun, i.e. an ( in column 1. 236: * For text, back to a section macro. 237: * For C, back to a { in column 1 (~~ beg of function.) 238: */ 239: case '[': 240: dir = -1; 241: /* fall into ... */ 242: 243: /* 244: * ] Forward to next defun, i.e. a ( in column 1. 245: * For text, forward section. 246: * For C, forward to a } in column 1 (if delete or such) 247: * or if a move to a { in column 1. 248: */ 249: case ']': 250: if (!vglobp) 251: forbid(getkey() != c); 252: forbid (Xhadcnt); 253: vsave(); 254: i = lbrack(c, opf); 255: getDOT(); 256: forbid(!i); 257: markDOT(); 258: if (ospeed > B300) 259: hold |= HOLDWIG; 260: break; 261: 262: /* 263: * , Invert last find with f F t or T, like inverse 264: * of ;. 265: */ 266: case ',': 267: forbid (lastFKND == 0); 268: c = isupper(lastFKND) ? tolower(lastFKND) : toupper(lastFKND); 269: i = lastFCHR; 270: if (vglobp == 0) 271: vglobp = ""; 272: subop++; 273: goto nocount; 274: 275: /* 276: * 0 To beginning of real line. 277: */ 278: case '0': 279: wcursor = linebuf; 280: vmoving = 0; 281: break; 282: 283: /* 284: * ; Repeat last find with f F t or T. 285: */ 286: case ';': 287: forbid (lastFKND == 0); 288: c = lastFKND; 289: i = lastFCHR; 290: subop++; 291: goto nocount; 292: 293: /* 294: * F Find single character before cursor in current line. 295: * T Like F, but stops before character. 296: */ 297: case 'F': /* inverted find */ 298: case 'T': 299: dir = -1; 300: /* fall into ... */ 301: 302: /* 303: * f Find single character following cursor in current line. 304: * t Like f, but stope before character. 305: */ 306: case 'f': /* find */ 307: case 't': 308: if (!subop) { 309: i = getesc(); 310: if (i == 0) 311: return; 312: *lastcp++ = i; 313: } 314: if (vglobp == 0) 315: lastFKND = c, lastFCHR = i; 316: for (; cnt > 0; cnt--) 317: forbid (find(i) == 0); 318: vmoving = 0; 319: switch (c) { 320: 321: case 'T': 322: wcursor++; 323: break; 324: 325: case 't': 326: wcursor--; 327: case 'f': 328: fixup: 329: if (moveop != vmove) 330: wcursor++; 331: break; 332: } 333: break; 334: 335: /* 336: * | Find specified print column in current line. 337: */ 338: case '|': 339: if (Pline == numbline) 340: cnt += 8; 341: vmovcol = cnt; 342: vmoving = 1; 343: wcursor = vfindcol(cnt); 344: break; 345: 346: /* 347: * ^ To beginning of non-white space on line. 348: */ 349: case '^': 350: wcursor = vskipwh(linebuf); 351: vmoving = 0; 352: break; 353: 354: /* 355: * $ To end of line. 356: */ 357: case '$': 358: if (opf == vmove) { 359: vmoving = 1; 360: vmovcol = 20000; 361: } else 362: vmoving = 0; 363: if (cnt > 1) { 364: if (opf == vmove) { 365: wcursor = 0; 366: cnt--; 367: } else 368: wcursor = linebuf; 369: /* This is wrong at EOF */ 370: wdot = dot + cnt; 371: break; 372: } 373: if (linebuf[0]) { 374: wcursor = strend(linebuf) - 1; 375: goto fixup; 376: } 377: wcursor = linebuf; 378: break; 379: 380: /* 381: * h Back a character. 382: * ^H Back a character. 383: */ 384: case 'h': 385: case CTRL(h): 386: dir = -1; 387: /* fall into ... */ 388: 389: /* 390: * space Forward a character. 391: */ 392: case 'l': 393: case ' ': 394: forbid (margin() || opf == vmove && edge()); 395: while (cnt > 0 && !margin()) 396: wcursor += dir, cnt--; 397: if (margin() && opf == vmove || wcursor < linebuf) 398: wcursor -= dir; 399: vmoving = 0; 400: break; 401: 402: /* 403: * D Delete to end of line, short for d$. 404: */ 405: case 'D': 406: cnt = INF; 407: goto deleteit; 408: 409: /* 410: * X Delete character before cursor. 411: */ 412: case 'X': 413: dir = -1; 414: /* fall into ... */ 415: deleteit: 416: /* 417: * x Delete character at cursor, leaving cursor where it is. 418: */ 419: case 'x': 420: if (margin()) 421: goto errlab; 422: vmacchng(1); 423: while (cnt > 0 && !margin()) 424: wcursor += dir, cnt--; 425: opf = deleteop; 426: vmoving = 0; 427: break; 428: 429: default: 430: /* 431: * Stuttered operators are equivalent to the operator on 432: * a line, thus turn dd into d_. 433: */ 434: if (opf == vmove || c != workcmd[0]) { 435: errlab: 436: beep(); 437: vmacp = 0; 438: return; 439: } 440: /* fall into ... */ 441: 442: /* 443: * _ Target for a line or group of lines. 444: * Stuttering is more convenient; this is mostly 445: * for aesthetics. 446: */ 447: case '_': 448: wdot = dot + cnt - 1; 449: vmoving = 0; 450: wcursor = 0; 451: break; 452: 453: /* 454: * H To first, home line on screen. 455: * Count is for count'th line rather than first. 456: */ 457: case 'H': 458: wdot = (dot - vcline) + cnt - 1; 459: if (opf == vmove) 460: markit(wdot); 461: vmoving = 0; 462: wcursor = 0; 463: break; 464: 465: /* 466: * - Backwards lines, to first non-white character. 467: */ 468: case '-': 469: wdot = dot - cnt; 470: vmoving = 0; 471: wcursor = 0; 472: break; 473: 474: /* 475: * ^P To previous line same column. Ridiculous on the 476: * console of the VAX since it puts console in LSI mode. 477: */ 478: case 'k': 479: case CTRL(p): 480: wdot = dot - cnt; 481: if (vmoving == 0) 482: vmoving = 1, vmovcol = column(cursor); 483: wcursor = 0; 484: break; 485: 486: /* 487: * L To last line on screen, or count'th line from the 488: * bottom. 489: */ 490: case 'L': 491: wdot = dot + vcnt - vcline - cnt; 492: if (opf == vmove) 493: markit(wdot); 494: vmoving = 0; 495: wcursor = 0; 496: break; 497: 498: /* 499: * M To the middle of the screen. 500: */ 501: case 'M': 502: wdot = dot + ((vcnt + 1) / 2) - vcline - 1; 503: if (opf == vmove) 504: markit(wdot); 505: vmoving = 0; 506: wcursor = 0; 507: break; 508: 509: /* 510: * + Forward line, to first non-white. 511: * 512: * CR Convenient synonym for +. 513: */ 514: case '+': 515: case CR: 516: wdot = dot + cnt; 517: vmoving = 0; 518: wcursor = 0; 519: break; 520: 521: /* 522: * ^N To next line, same column if possible. 523: * 524: * LF Linefeed is a convenient synonym for ^N. 525: */ 526: case CTRL(n): 527: case 'j': 528: case NL: 529: wdot = dot + cnt; 530: if (vmoving == 0) 531: vmoving = 1, vmovcol = column(cursor); 532: wcursor = 0; 533: break; 534: 535: /* 536: * n Search to next match of current pattern. 537: */ 538: case 'n': 539: vglobp = vscandir; 540: c = *vglobp++; 541: goto nocount; 542: 543: /* 544: * N Like n but in reverse direction. 545: */ 546: case 'N': 547: vglobp = vscandir[0] == '/' ? "?" : "/"; 548: c = *vglobp++; 549: goto nocount; 550: 551: /* 552: * ' Return to line specified by following mark, 553: * first white position on line. 554: * 555: * ` Return to marked line at remembered column. 556: */ 557: case '\'': 558: case '`': 559: d = c; 560: c = getesc(); 561: if (c == 0) 562: return; 563: c = markreg(c); 564: forbid (c == 0); 565: wdot = getmark(c); 566: forbid (wdot == NOLINE); 567: forbid (Xhadcnt); 568: vmoving = 0; 569: wcursor = d == '`' ? ncols[c - 'a'] : 0; 570: if (opf == vmove && (wdot != dot || (d == '`' && wcursor != cursor))) 571: markDOT(); 572: if (wcursor) { 573: vsave(); 574: getline(*wdot); 575: if (wcursor > strend(linebuf)) 576: wcursor = 0; 577: getDOT(); 578: } 579: if (ospeed > B300) 580: hold |= HOLDWIG; 581: break; 582: 583: /* 584: * G Goto count'th line, or last line if no count 585: * given. 586: */ 587: case 'G': 588: if (!Xhadcnt) 589: cnt = lineDOL(); 590: wdot = zero + cnt; 591: forbid (wdot < one || wdot > dol); 592: if (opf == vmove) 593: markit(wdot); 594: vmoving = 0; 595: wcursor = 0; 596: break; 597: 598: /* 599: * / Scan forward for following re. 600: * ? Scan backward for following re. 601: */ 602: case '/': 603: case '?': 604: forbid (Xhadcnt); 605: vsave(); 606: ocurs = cursor; 607: odot = dot; 608: wcursor = 0; 609: if (readecho(c)) 610: return; 611: if (!vglobp) 612: vscandir[0] = genbuf[0]; 613: oglobp = globp; CP(vutmp, genbuf); globp = vutmp; 614: d = peekc; 615: fromsemi: 616: ungetchar(0); 617: fixech(); 618: CATCH 619: #ifndef CBREAK 620: /* 621: * Lose typeahead (ick). 622: */ 623: vcook(); 624: #endif 625: addr = address(cursor); 626: #ifndef CBREAK 627: vraw(); 628: #endif 629: ONERR 630: #ifndef CBREAK 631: vraw(); 632: #endif 633: slerr: 634: globp = oglobp; 635: dot = odot; 636: cursor = ocurs; 637: ungetchar(d); 638: splitw = 0; 639: vclean(); 640: vjumpto(dot, ocurs, 0); 641: return; 642: ENDCATCH 643: if (globp == 0) 644: globp = ""; 645: else if (peekc) 646: --globp; 647: if (*globp == ';') { 648: /* /foo/;/bar/ */ 649: globp++; 650: dot = addr; 651: cursor = loc1; 652: goto fromsemi; 653: } 654: dot = odot; 655: ungetchar(d); 656: c = 0; 657: if (*globp == 'z') 658: globp++, c = '\n'; 659: if (any(*globp, "^+-.")) 660: c = *globp++; 661: i = 0; 662: while (isdigit(*globp)) 663: i = i * 10 + *globp++ - '0'; 664: if (any(*globp, "^+-.")) 665: c = *globp++; 666: if (*globp) { 667: /* random junk after the pattern */ 668: beep(); 669: goto slerr; 670: } 671: globp = oglobp; 672: splitw = 0; 673: vmoving = 0; 674: wcursor = loc1; 675: if (i != 0) 676: vsetsiz(i); 677: if (opf == vmove) { 678: if (state == ONEOPEN || state == HARDOPEN) 679: outline = destline = WBOT; 680: if (addr != dot || loc1 != cursor) 681: markDOT(); 682: if (loc1 > linebuf && *loc1 == 0) 683: loc1--; 684: if (c) 685: vjumpto(addr, loc1, c); 686: else { 687: vmoving = 0; 688: if (loc1) { 689: vmoving++; 690: vmovcol = column(loc1); 691: } 692: getDOT(); 693: if (state == CRTOPEN && addr != dot) 694: vup1(); 695: vupdown(addr - dot, NOSTR); 696: } 697: return; 698: } 699: lastcp[-1] = 'n'; 700: getDOT(); 701: wdot = addr; 702: break; 703: } 704: /* 705: * Apply. 706: */ 707: if (vreg && wdot == 0) 708: wdot = dot; 709: (*opf)(c); 710: wdot = NOLINE; 711: } 712: 713: /* 714: * Find single character c, in direction dir from cursor. 715: */ 716: find(c) 717: char c; 718: { 719: 720: for(;;) { 721: if (edge()) 722: return (0); 723: wcursor += dir; 724: if (*wcursor == c) 725: return (1); 726: } 727: } 728: 729: /* 730: * Do a word motion with operator op, and cnt more words 731: * to go after this. 732: */ 733: word(op, cnt) 734: register int (*op)(); 735: int cnt; 736: { 737: register int which; 738: register char *iwc; 739: register line *iwdot = wdot; 740: 741: if (dir == 1) { 742: iwc = wcursor; 743: which = wordch(wcursor); 744: while (wordof(which, wcursor)) { 745: if (cnt == 1 && op != vmove && wcursor[1] == 0) { 746: wcursor++; 747: break; 748: } 749: if (!lnext()) 750: return (0); 751: if (wcursor == linebuf) 752: break; 753: } 754: /* Unless last segment of a change skip blanks */ 755: if (op != vchange || cnt > 1) 756: while (!margin() && blank()) 757: wcursor++; 758: else 759: if (wcursor == iwc && iwdot == wdot && *iwc) 760: wcursor++; 761: if (op == vmove && margin()) 762: wcursor--; 763: } else { 764: if (!lnext()) 765: return (0); 766: while (blank()) 767: if (!lnext()) 768: return (0); 769: if (!margin()) { 770: which = wordch(wcursor); 771: while (!margin() && wordof(which, wcursor)) 772: wcursor--; 773: } 774: if (wcursor < linebuf || !wordof(which, wcursor)) 775: wcursor++; 776: } 777: return (1); 778: } 779: 780: /* 781: * To end of word, with operator op and cnt more motions 782: * remaining after this. 783: */ 784: eend(op) 785: register int (*op)(); 786: { 787: register int which; 788: 789: if (!lnext()) 790: return; 791: while (blank()) 792: if (!lnext()) 793: return; 794: which = wordch(wcursor); 795: while (wordof(which, wcursor)) { 796: if (wcursor[1] == 0) { 797: wcursor++; 798: break; 799: } 800: if (!lnext()) 801: return; 802: } 803: if (op != vchange && op != vdelete && wcursor > linebuf) 804: wcursor--; 805: } 806: 807: /* 808: * Wordof tells whether the character at *wc is in a word of 809: * kind which (blank/nonblank words are 0, conservative words 1). 810: */ 811: wordof(which, wc) 812: char which; 813: register char *wc; 814: { 815: 816: if (isspace(*wc)) 817: return (0); 818: return (!wdkind || wordch(wc) == which); 819: } 820: 821: /* 822: * Wordch tells whether character at *wc is a word character 823: * i.e. an alfa, digit, or underscore. 824: */ 825: wordch(wc) 826: char *wc; 827: { 828: register int c; 829: 830: c = wc[0]; 831: return (isalpha(c) || isdigit(c) || c == '_'); 832: } 833: 834: /* 835: * Edge tells when we hit the last character in the current line. 836: */ 837: edge() 838: { 839: 840: if (linebuf[0] == 0) 841: return (1); 842: if (dir == 1) 843: return (wcursor[1] == 0); 844: else 845: return (wcursor == linebuf); 846: } 847: 848: /* 849: * Margin tells us when we have fallen off the end of the line. 850: */ 851: margin() 852: { 853: 854: return (wcursor < linebuf || wcursor[0] == 0); 855: }