1 /* Copyright (c) 1980 Regents of the University of California */ 2 static char *sccsid = "@(#)ex_voper.c 5.1 08/28/80"; 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 vrep(cnt); 110 vmacchng(1); 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 getDOT(); 219 forbid(!i); 220 if (opf != vmove) 221 if (dir > 0) 222 wcursor++; 223 else 224 cursor++; 225 else 226 markDOT(); 227 vmoving = 0; 228 break; 229 230 /* 231 * [ Back to beginning of defun, i.e. an ( in column 1. 232 * For text, back to a section macro. 233 * For C, back to a { in column 1 (~~ beg of function.) 234 */ 235 case '[': 236 dir = -1; 237 /* fall into ... */ 238 239 /* 240 * ] Forward to next defun, i.e. a ( in column 1. 241 * For text, forward section. 242 * For C, forward to a } in column 1 (if delete or such) 243 * or if a move to a { in column 1. 244 */ 245 case ']': 246 if (!vglobp) 247 forbid(getkey() != c); 248 forbid (Xhadcnt); 249 vsave(); 250 i = lbrack(c, opf); 251 getDOT(); 252 forbid(!i); 253 markDOT(); 254 if (ospeed > B300) 255 hold |= HOLDWIG; 256 break; 257 258 /* 259 * , Invert last find with f F t or T, like inverse 260 * of ;. 261 */ 262 case ',': 263 forbid (lastFKND == 0); 264 c = isupper(lastFKND) ? tolower(lastFKND) : toupper(lastFKND); 265 i = lastFCHR; 266 if (vglobp == 0) 267 vglobp = ""; 268 subop++; 269 goto nocount; 270 271 /* 272 * 0 To beginning of real line. 273 */ 274 case '0': 275 wcursor = linebuf; 276 vmoving = 0; 277 break; 278 279 /* 280 * ; Repeat last find with f F t or T. 281 */ 282 case ';': 283 forbid (lastFKND == 0); 284 c = lastFKND; 285 i = lastFCHR; 286 subop++; 287 goto nocount; 288 289 /* 290 * F Find single character before cursor in current line. 291 * T Like F, but stops before character. 292 */ 293 case 'F': /* inverted find */ 294 case 'T': 295 dir = -1; 296 /* fall into ... */ 297 298 /* 299 * f Find single character following cursor in current line. 300 * t Like f, but stope before character. 301 */ 302 case 'f': /* find */ 303 case 't': 304 if (!subop) { 305 i = getesc(); 306 if (i == 0) 307 return; 308 *lastcp++ = i; 309 } 310 if (vglobp == 0) 311 lastFKND = c, lastFCHR = i; 312 for (; cnt > 0; cnt--) 313 forbid (find(i) == 0); 314 vmoving = 0; 315 switch (c) { 316 317 case 'T': 318 wcursor++; 319 break; 320 321 case 't': 322 wcursor--; 323 case 'f': 324 fixup: 325 if (moveop != vmove) 326 wcursor++; 327 break; 328 } 329 break; 330 331 /* 332 * | Find specified print column in current line. 333 */ 334 case '|': 335 if (Pline == numbline) 336 cnt += 8; 337 vmovcol = cnt; 338 vmoving = 1; 339 wcursor = vfindcol(cnt); 340 break; 341 342 /* 343 * ^ To beginning of non-white space on line. 344 */ 345 case '^': 346 wcursor = vskipwh(linebuf); 347 vmoving = 0; 348 break; 349 350 /* 351 * $ To end of line. 352 */ 353 case '$': 354 if (opf == vmove) { 355 vmoving = 1; 356 vmovcol = 20000; 357 } else 358 vmoving = 0; 359 if (cnt > 1) { 360 if (opf == vmove) { 361 wcursor = 0; 362 cnt--; 363 } else 364 wcursor = linebuf; 365 /* This is wrong at EOF */ 366 wdot = dot + cnt; 367 break; 368 } 369 if (linebuf[0]) { 370 wcursor = strend(linebuf) - 1; 371 goto fixup; 372 } 373 wcursor = linebuf; 374 break; 375 376 /* 377 * h Back a character. 378 * ^H Back a character. 379 */ 380 case 'h': 381 case CTRL(h): 382 dir = -1; 383 /* fall into ... */ 384 385 /* 386 * space Forward a character. 387 */ 388 case 'l': 389 case ' ': 390 forbid (margin() || opf == vmove && edge()); 391 while (cnt > 0 && !margin()) 392 wcursor += dir, cnt--; 393 if (margin() && opf == vmove || wcursor < linebuf) 394 wcursor -= dir; 395 vmoving = 0; 396 break; 397 398 /* 399 * D Delete to end of line, short for d$. 400 */ 401 case 'D': 402 cnt = INF; 403 goto deleteit; 404 405 /* 406 * X Delete character before cursor. 407 */ 408 case 'X': 409 dir = -1; 410 /* fall into ... */ 411 deleteit: 412 /* 413 * x Delete character at cursor, leaving cursor where it is. 414 */ 415 case 'x': 416 if (margin()) 417 goto errlab; 418 vmacchng(1); 419 while (cnt > 0 && !margin()) 420 wcursor += dir, cnt--; 421 opf = deleteop; 422 vmoving = 0; 423 break; 424 425 default: 426 /* 427 * Stuttered operators are equivalent to the operator on 428 * a line, thus turn dd into d_. 429 */ 430 if (opf == vmove || c != workcmd[0]) { 431 errlab: 432 beep(); 433 vmacp = 0; 434 return; 435 } 436 /* fall into ... */ 437 438 /* 439 * _ Target for a line or group of lines. 440 * Stuttering is more convenient; this is mostly 441 * for aesthetics. 442 */ 443 case '_': 444 wdot = dot + cnt - 1; 445 vmoving = 0; 446 wcursor = 0; 447 break; 448 449 /* 450 * H To first, home line on screen. 451 * Count is for count'th line rather than first. 452 */ 453 case 'H': 454 wdot = (dot - vcline) + cnt - 1; 455 if (opf == vmove) 456 markit(wdot); 457 vmoving = 0; 458 wcursor = 0; 459 break; 460 461 /* 462 * - Backwards lines, to first non-white character. 463 */ 464 case '-': 465 wdot = dot - cnt; 466 vmoving = 0; 467 wcursor = 0; 468 break; 469 470 /* 471 * ^P To previous line same column. Ridiculous on the 472 * console of the VAX since it puts console in LSI mode. 473 */ 474 case 'k': 475 case CTRL(p): 476 wdot = dot - cnt; 477 if (vmoving == 0) 478 vmoving = 1, vmovcol = column(cursor); 479 wcursor = 0; 480 break; 481 482 /* 483 * L To last line on screen, or count'th line from the 484 * bottom. 485 */ 486 case 'L': 487 wdot = dot + vcnt - vcline - cnt; 488 if (opf == vmove) 489 markit(wdot); 490 vmoving = 0; 491 wcursor = 0; 492 break; 493 494 /* 495 * M To the middle of the screen. 496 */ 497 case 'M': 498 wdot = dot + ((vcnt + 1) / 2) - vcline - 1; 499 if (opf == vmove) 500 markit(wdot); 501 vmoving = 0; 502 wcursor = 0; 503 break; 504 505 /* 506 * + Forward line, to first non-white. 507 * 508 * CR Convenient synonym for +. 509 */ 510 case '+': 511 case CR: 512 wdot = dot + cnt; 513 vmoving = 0; 514 wcursor = 0; 515 break; 516 517 /* 518 * ^N To next line, same column if possible. 519 * 520 * LF Linefeed is a convenient synonym for ^N. 521 */ 522 case CTRL(n): 523 case 'j': 524 case NL: 525 wdot = dot + cnt; 526 if (vmoving == 0) 527 vmoving = 1, vmovcol = column(cursor); 528 wcursor = 0; 529 break; 530 531 /* 532 * n Search to next match of current pattern. 533 */ 534 case 'n': 535 vglobp = vscandir; 536 c = *vglobp++; 537 goto nocount; 538 539 /* 540 * N Like n but in reverse direction. 541 */ 542 case 'N': 543 vglobp = vscandir[0] == '/' ? "?" : "/"; 544 c = *vglobp++; 545 goto nocount; 546 547 /* 548 * ' Return to line specified by following mark, 549 * first white position on line. 550 * 551 * ` Return to marked line at remembered column. 552 */ 553 case '\'': 554 case '`': 555 d = c; 556 c = getesc(); 557 if (c == 0) 558 return; 559 c = markreg(c); 560 forbid (c == 0); 561 wdot = getmark(c); 562 forbid (wdot == NOLINE); 563 forbid (Xhadcnt); 564 vmoving = 0; 565 wcursor = d == '`' ? ncols[c - 'a'] : 0; 566 if (opf == vmove && (wdot != dot || (d == '`' && wcursor != cursor))) 567 markDOT(); 568 if (wcursor) { 569 vsave(); 570 getline(*wdot); 571 if (wcursor > strend(linebuf)) 572 wcursor = 0; 573 getDOT(); 574 } 575 if (ospeed > B300) 576 hold |= HOLDWIG; 577 break; 578 579 /* 580 * G Goto count'th line, or last line if no count 581 * given. 582 */ 583 case 'G': 584 if (!Xhadcnt) 585 cnt = lineDOL(); 586 wdot = zero + cnt; 587 forbid (wdot < one || wdot > dol); 588 if (opf == vmove) 589 markit(wdot); 590 vmoving = 0; 591 wcursor = 0; 592 break; 593 594 /* 595 * / Scan forward for following re. 596 * ? Scan backward for following re. 597 */ 598 case '/': 599 case '?': 600 forbid (Xhadcnt); 601 vsave(); 602 ocurs = cursor; 603 odot = dot; 604 wcursor = 0; 605 if (readecho(c)) 606 return; 607 if (!vglobp) 608 vscandir[0] = genbuf[0]; 609 oglobp = globp; CP(vutmp, genbuf); globp = vutmp; 610 d = peekc; 611 fromsemi: 612 ungetchar(0); 613 fixech(); 614 CATCH 615 #ifndef CBREAK 616 /* 617 * Lose typeahead (ick). 618 */ 619 vcook(); 620 #endif 621 addr = address(cursor); 622 #ifndef CBREAK 623 vraw(); 624 #endif 625 ONERR 626 #ifndef CBREAK 627 vraw(); 628 #endif 629 slerr: 630 globp = oglobp; 631 dot = odot; 632 cursor = ocurs; 633 ungetchar(d); 634 splitw = 0; 635 vclean(); 636 vjumpto(dot, ocurs, 0); 637 return; 638 ENDCATCH 639 if (globp == 0) 640 globp = ""; 641 else if (peekc) 642 --globp; 643 if (*globp == ';') { 644 /* /foo/;/bar/ */ 645 globp++; 646 dot = addr; 647 cursor = loc1; 648 goto fromsemi; 649 } 650 dot = odot; 651 ungetchar(d); 652 c = 0; 653 if (*globp == 'z') 654 globp++, c = '\n'; 655 if (any(*globp, "^+-.")) 656 c = *globp++; 657 i = 0; 658 while (isdigit(*globp)) 659 i = i * 10 + *globp++ - '0'; 660 if (any(*globp, "^+-.")) 661 c = *globp++; 662 if (*globp) { 663 /* random junk after the pattern */ 664 beep(); 665 goto slerr; 666 } 667 globp = oglobp; 668 splitw = 0; 669 vmoving = 0; 670 wcursor = loc1; 671 if (i != 0) 672 vsetsiz(i); 673 if (opf == vmove) { 674 if (state == ONEOPEN || state == HARDOPEN) 675 outline = destline = WBOT; 676 if (addr != dot || loc1 != cursor) 677 markDOT(); 678 if (loc1 > linebuf && *loc1 == 0) 679 loc1--; 680 if (c) 681 vjumpto(addr, loc1, c); 682 else { 683 vmoving = 0; 684 if (loc1) { 685 vmoving++; 686 vmovcol = column(loc1); 687 } 688 getDOT(); 689 if (state == CRTOPEN && addr != dot) 690 vup1(); 691 vupdown(addr - dot, NOSTR); 692 } 693 return; 694 } 695 lastcp[-1] = 'n'; 696 getDOT(); 697 wdot = addr; 698 break; 699 } 700 /* 701 * Apply. 702 */ 703 if (vreg && wdot == 0) 704 wdot = dot; 705 (*opf)(c); 706 wdot = NOLINE; 707 } 708 709 /* 710 * Find single character c, in direction dir from cursor. 711 */ 712 find(c) 713 char c; 714 { 715 716 for(;;) { 717 if (edge()) 718 return (0); 719 wcursor += dir; 720 if (*wcursor == c) 721 return (1); 722 } 723 } 724 725 /* 726 * Do a word motion with operator op, and cnt more words 727 * to go after this. 728 */ 729 word(op, cnt) 730 register int (*op)(); 731 int cnt; 732 { 733 register int which; 734 register char *iwc; 735 register line *iwdot = wdot; 736 737 if (dir == 1) { 738 iwc = wcursor; 739 which = wordch(wcursor); 740 while (wordof(which, wcursor)) { 741 if (cnt == 1 && op != vmove && wcursor[1] == 0) { 742 wcursor++; 743 break; 744 } 745 if (!lnext()) 746 return (0); 747 if (wcursor == linebuf) 748 break; 749 } 750 /* Unless last segment of a change skip blanks */ 751 if (op != vchange || cnt > 1) 752 while (!margin() && blank()) 753 wcursor++; 754 else 755 if (wcursor == iwc && iwdot == wdot && *iwc) 756 wcursor++; 757 if (op == vmove && margin()) 758 wcursor--; 759 } else { 760 if (!lnext()) 761 return (0); 762 while (blank()) 763 if (!lnext()) 764 return (0); 765 if (!margin()) { 766 which = wordch(wcursor); 767 while (!margin() && wordof(which, wcursor)) 768 wcursor--; 769 } 770 if (wcursor < linebuf || !wordof(which, wcursor)) 771 wcursor++; 772 } 773 return (1); 774 } 775 776 /* 777 * To end of word, with operator op and cnt more motions 778 * remaining after this. 779 */ 780 eend(op) 781 register int (*op)(); 782 { 783 register int which; 784 785 if (!lnext()) 786 return; 787 while (blank()) 788 if (!lnext()) 789 return; 790 which = wordch(wcursor); 791 while (wordof(which, wcursor)) { 792 if (wcursor[1] == 0) { 793 wcursor++; 794 break; 795 } 796 if (!lnext()) 797 return; 798 } 799 if (op != vchange && op != vdelete && wcursor > linebuf) 800 wcursor--; 801 } 802 803 /* 804 * Wordof tells whether the character at *wc is in a word of 805 * kind which (blank/nonblank words are 0, conservative words 1). 806 */ 807 wordof(which, wc) 808 char which; 809 register char *wc; 810 { 811 812 if (isspace(*wc)) 813 return (0); 814 return (!wdkind || wordch(wc) == which); 815 } 816 817 /* 818 * Wordch tells whether character at *wc is a word character 819 * i.e. an alfa, digit, or underscore. 820 */ 821 wordch(wc) 822 char *wc; 823 { 824 register int c; 825 826 c = wc[0]; 827 return (isalpha(c) || isdigit(c) || c == '_'); 828 } 829 830 /* 831 * Edge tells when we hit the last character in the current line. 832 */ 833 edge() 834 { 835 836 if (linebuf[0] == 0) 837 return (1); 838 if (dir == 1) 839 return (wcursor[1] == 0); 840 else 841 return (wcursor == linebuf); 842 } 843 844 /* 845 * Margin tells us when we have fallen off the end of the line. 846 */ 847 margin() 848 { 849 850 return (wcursor < linebuf || wcursor[0] == 0); 851 } 852