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