1 /* $NetBSD: vi.c,v 1.19 2003/08/07 16:44:35 agc Exp $ */ 2 3 /*- 4 * Copyright (c) 1992, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Christos Zoulas of Cornell University. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 #include "config.h" 36 #include <stdlib.h> 37 #include <unistd.h> 38 #include <sys/wait.h> 39 40 #if !defined(lint) && !defined(SCCSID) 41 #if 0 42 static char sccsid[] = "@(#)vi.c 8.1 (Berkeley) 6/4/93"; 43 #else 44 __RCSID("$NetBSD: vi.c,v 1.19 2003/08/07 16:44:35 agc Exp $"); 45 #endif 46 #endif /* not lint && not SCCSID */ 47 48 /* 49 * vi.c: Vi mode commands. 50 */ 51 #include "el.h" 52 53 private el_action_t cv_action(EditLine *, int); 54 private el_action_t cv_paste(EditLine *, int); 55 56 /* cv_action(): 57 * Handle vi actions. 58 */ 59 private el_action_t 60 cv_action(EditLine *el, int c) 61 { 62 63 if (el->el_chared.c_vcmd.action != NOP) { 64 /* 'cc', 'dd' and (possibly) friends */ 65 if (c != el->el_chared.c_vcmd.action) 66 return CC_ERROR; 67 68 if (!(c & YANK)) 69 cv_undo(el); 70 cv_yank(el, el->el_line.buffer, 71 el->el_line.lastchar - el->el_line.buffer); 72 el->el_chared.c_vcmd.action = NOP; 73 el->el_chared.c_vcmd.pos = 0; 74 el->el_line.lastchar = el->el_line.buffer; 75 el->el_line.cursor = el->el_line.buffer; 76 if (c & INSERT) 77 el->el_map.current = el->el_map.key; 78 79 return (CC_REFRESH); 80 } 81 el->el_chared.c_vcmd.pos = el->el_line.cursor; 82 el->el_chared.c_vcmd.action = c; 83 return (CC_ARGHACK); 84 } 85 86 /* cv_paste(): 87 * Paste previous deletion before or after the cursor 88 */ 89 private el_action_t 90 cv_paste(EditLine *el, int c) 91 { 92 char *ptr; 93 c_kill_t *k = &el->el_chared.c_kill; 94 int len = k->last - k->buf; 95 96 if (k->buf == NULL || len == 0) 97 return (CC_ERROR); 98 #ifdef DEBUG_PASTE 99 (void) fprintf(el->el_errfile, "Paste: \"%.*s\"\n", len, k->buf); 100 #endif 101 102 cv_undo(el); 103 104 if (!c && el->el_line.cursor < el->el_line.lastchar) 105 el->el_line.cursor++; 106 ptr = el->el_line.cursor; 107 108 c_insert(el, len); 109 if (el->el_line.cursor + len > el->el_line.lastchar) 110 return (CC_ERROR); 111 (void) memcpy(ptr, k->buf, len +0u); 112 return (CC_REFRESH); 113 } 114 115 116 /* vi_paste_next(): 117 * Vi paste previous deletion to the right of the cursor 118 * [p] 119 */ 120 protected el_action_t 121 /*ARGSUSED*/ 122 vi_paste_next(EditLine *el, int c __attribute__((__unused__))) 123 { 124 125 return (cv_paste(el, 0)); 126 } 127 128 129 /* vi_paste_prev(): 130 * Vi paste previous deletion to the left of the cursor 131 * [P] 132 */ 133 protected el_action_t 134 /*ARGSUSED*/ 135 vi_paste_prev(EditLine *el, int c __attribute__((__unused__))) 136 { 137 138 return (cv_paste(el, 1)); 139 } 140 141 142 /* vi_prev_big_word(): 143 * Vi move to the previous space delimited word 144 * [B] 145 */ 146 protected el_action_t 147 /*ARGSUSED*/ 148 vi_prev_big_word(EditLine *el, int c) 149 { 150 151 if (el->el_line.cursor == el->el_line.buffer) 152 return (CC_ERROR); 153 154 el->el_line.cursor = cv_prev_word(el->el_line.cursor, 155 el->el_line.buffer, 156 el->el_state.argument, 157 cv__isWord); 158 159 if (el->el_chared.c_vcmd.action != NOP) { 160 cv_delfini(el); 161 return (CC_REFRESH); 162 } 163 return (CC_CURSOR); 164 } 165 166 167 /* vi_prev_word(): 168 * Vi move to the previous word 169 * [b] 170 */ 171 protected el_action_t 172 /*ARGSUSED*/ 173 vi_prev_word(EditLine *el, int c __attribute__((__unused__))) 174 { 175 176 if (el->el_line.cursor == el->el_line.buffer) 177 return (CC_ERROR); 178 179 el->el_line.cursor = cv_prev_word(el->el_line.cursor, 180 el->el_line.buffer, 181 el->el_state.argument, 182 cv__isword); 183 184 if (el->el_chared.c_vcmd.action != NOP) { 185 cv_delfini(el); 186 return (CC_REFRESH); 187 } 188 return (CC_CURSOR); 189 } 190 191 192 /* vi_next_big_word(): 193 * Vi move to the next space delimited word 194 * [W] 195 */ 196 protected el_action_t 197 /*ARGSUSED*/ 198 vi_next_big_word(EditLine *el, int c) 199 { 200 201 if (el->el_line.cursor >= el->el_line.lastchar - 1) 202 return (CC_ERROR); 203 204 el->el_line.cursor = cv_next_word(el, el->el_line.cursor, 205 el->el_line.lastchar, el->el_state.argument, cv__isWord); 206 207 if (el->el_map.type == MAP_VI) 208 if (el->el_chared.c_vcmd.action != NOP) { 209 cv_delfini(el); 210 return (CC_REFRESH); 211 } 212 return (CC_CURSOR); 213 } 214 215 216 /* vi_next_word(): 217 * Vi move to the next word 218 * [w] 219 */ 220 protected el_action_t 221 /*ARGSUSED*/ 222 vi_next_word(EditLine *el, int c __attribute__((__unused__))) 223 { 224 225 if (el->el_line.cursor >= el->el_line.lastchar - 1) 226 return (CC_ERROR); 227 228 el->el_line.cursor = cv_next_word(el, el->el_line.cursor, 229 el->el_line.lastchar, el->el_state.argument, cv__isword); 230 231 if (el->el_map.type == MAP_VI) 232 if (el->el_chared.c_vcmd.action != NOP) { 233 cv_delfini(el); 234 return (CC_REFRESH); 235 } 236 return (CC_CURSOR); 237 } 238 239 240 /* vi_change_case(): 241 * Vi change case of character under the cursor and advance one character 242 * [~] 243 */ 244 protected el_action_t 245 vi_change_case(EditLine *el, int c) 246 { 247 int i; 248 249 if (el->el_line.cursor >= el->el_line.lastchar) 250 return (CC_ERROR); 251 cv_undo(el); 252 for (i = 0; i < el->el_state.argument; i++) { 253 254 c = *(unsigned char *)el->el_line.cursor; 255 if (isupper(c)) 256 *el->el_line.cursor = tolower(c); 257 else if (islower(c)) 258 *el->el_line.cursor = toupper(c); 259 260 if (++el->el_line.cursor >= el->el_line.lastchar) { 261 el->el_line.cursor--; 262 re_fastaddc(el); 263 break; 264 } 265 re_fastaddc(el); 266 } 267 return CC_NORM; 268 } 269 270 271 /* vi_change_meta(): 272 * Vi change prefix command 273 * [c] 274 */ 275 protected el_action_t 276 /*ARGSUSED*/ 277 vi_change_meta(EditLine *el, int c __attribute__((__unused__))) 278 { 279 280 /* 281 * Delete with insert == change: first we delete and then we leave in 282 * insert mode. 283 */ 284 return (cv_action(el, DELETE | INSERT)); 285 } 286 287 288 /* vi_insert_at_bol(): 289 * Vi enter insert mode at the beginning of line 290 * [I] 291 */ 292 protected el_action_t 293 /*ARGSUSED*/ 294 vi_insert_at_bol(EditLine *el, int c __attribute__((__unused__))) 295 { 296 297 el->el_line.cursor = el->el_line.buffer; 298 cv_undo(el); 299 el->el_map.current = el->el_map.key; 300 return (CC_CURSOR); 301 } 302 303 304 /* vi_replace_char(): 305 * Vi replace character under the cursor with the next character typed 306 * [r] 307 */ 308 protected el_action_t 309 /*ARGSUSED*/ 310 vi_replace_char(EditLine *el, int c __attribute__((__unused__))) 311 { 312 313 if (el->el_line.cursor >= el->el_line.lastchar) 314 return CC_ERROR; 315 316 el->el_map.current = el->el_map.key; 317 el->el_state.inputmode = MODE_REPLACE_1; 318 cv_undo(el); 319 return (CC_ARGHACK); 320 } 321 322 323 /* vi_replace_mode(): 324 * Vi enter replace mode 325 * [R] 326 */ 327 protected el_action_t 328 /*ARGSUSED*/ 329 vi_replace_mode(EditLine *el, int c __attribute__((__unused__))) 330 { 331 332 el->el_map.current = el->el_map.key; 333 el->el_state.inputmode = MODE_REPLACE; 334 cv_undo(el); 335 return (CC_NORM); 336 } 337 338 339 /* vi_substitute_char(): 340 * Vi replace character under the cursor and enter insert mode 341 * [s] 342 */ 343 protected el_action_t 344 /*ARGSUSED*/ 345 vi_substitute_char(EditLine *el, int c __attribute__((__unused__))) 346 { 347 348 c_delafter(el, el->el_state.argument); 349 el->el_map.current = el->el_map.key; 350 return (CC_REFRESH); 351 } 352 353 354 /* vi_substitute_line(): 355 * Vi substitute entire line 356 * [S] 357 */ 358 protected el_action_t 359 /*ARGSUSED*/ 360 vi_substitute_line(EditLine *el, int c __attribute__((__unused__))) 361 { 362 363 cv_undo(el); 364 cv_yank(el, el->el_line.buffer, 365 el->el_line.lastchar - el->el_line.buffer); 366 (void) em_kill_line(el, 0); 367 el->el_map.current = el->el_map.key; 368 return (CC_REFRESH); 369 } 370 371 372 /* vi_change_to_eol(): 373 * Vi change to end of line 374 * [C] 375 */ 376 protected el_action_t 377 /*ARGSUSED*/ 378 vi_change_to_eol(EditLine *el, int c __attribute__((__unused__))) 379 { 380 381 cv_undo(el); 382 cv_yank(el, el->el_line.cursor, 383 el->el_line.lastchar - el->el_line.cursor); 384 (void) ed_kill_line(el, 0); 385 el->el_map.current = el->el_map.key; 386 return (CC_REFRESH); 387 } 388 389 390 /* vi_insert(): 391 * Vi enter insert mode 392 * [i] 393 */ 394 protected el_action_t 395 /*ARGSUSED*/ 396 vi_insert(EditLine *el, int c __attribute__((__unused__))) 397 { 398 399 el->el_map.current = el->el_map.key; 400 cv_undo(el); 401 return (CC_NORM); 402 } 403 404 405 /* vi_add(): 406 * Vi enter insert mode after the cursor 407 * [a] 408 */ 409 protected el_action_t 410 /*ARGSUSED*/ 411 vi_add(EditLine *el, int c __attribute__((__unused__))) 412 { 413 int ret; 414 415 el->el_map.current = el->el_map.key; 416 if (el->el_line.cursor < el->el_line.lastchar) { 417 el->el_line.cursor++; 418 if (el->el_line.cursor > el->el_line.lastchar) 419 el->el_line.cursor = el->el_line.lastchar; 420 ret = CC_CURSOR; 421 } else 422 ret = CC_NORM; 423 424 cv_undo(el); 425 426 return (ret); 427 } 428 429 430 /* vi_add_at_eol(): 431 * Vi enter insert mode at end of line 432 * [A] 433 */ 434 protected el_action_t 435 /*ARGSUSED*/ 436 vi_add_at_eol(EditLine *el, int c __attribute__((__unused__))) 437 { 438 439 el->el_map.current = el->el_map.key; 440 el->el_line.cursor = el->el_line.lastchar; 441 cv_undo(el); 442 return (CC_CURSOR); 443 } 444 445 446 /* vi_delete_meta(): 447 * Vi delete prefix command 448 * [d] 449 */ 450 protected el_action_t 451 /*ARGSUSED*/ 452 vi_delete_meta(EditLine *el, int c __attribute__((__unused__))) 453 { 454 455 return (cv_action(el, DELETE)); 456 } 457 458 459 /* vi_end_big_word(): 460 * Vi move to the end of the current space delimited word 461 * [E] 462 */ 463 protected el_action_t 464 /*ARGSUSED*/ 465 vi_end_big_word(EditLine *el, int c) 466 { 467 468 if (el->el_line.cursor == el->el_line.lastchar) 469 return (CC_ERROR); 470 471 el->el_line.cursor = cv__endword(el->el_line.cursor, 472 el->el_line.lastchar, el->el_state.argument, cv__isWord); 473 474 if (el->el_chared.c_vcmd.action != NOP) { 475 el->el_line.cursor++; 476 cv_delfini(el); 477 return (CC_REFRESH); 478 } 479 return (CC_CURSOR); 480 } 481 482 483 /* vi_end_word(): 484 * Vi move to the end of the current word 485 * [e] 486 */ 487 protected el_action_t 488 /*ARGSUSED*/ 489 vi_end_word(EditLine *el, int c __attribute__((__unused__))) 490 { 491 492 if (el->el_line.cursor == el->el_line.lastchar) 493 return (CC_ERROR); 494 495 el->el_line.cursor = cv__endword(el->el_line.cursor, 496 el->el_line.lastchar, el->el_state.argument, cv__isword); 497 498 if (el->el_chared.c_vcmd.action != NOP) { 499 el->el_line.cursor++; 500 cv_delfini(el); 501 return (CC_REFRESH); 502 } 503 return (CC_CURSOR); 504 } 505 506 507 /* vi_undo(): 508 * Vi undo last change 509 * [u] 510 */ 511 protected el_action_t 512 /*ARGSUSED*/ 513 vi_undo(EditLine *el, int c __attribute__((__unused__))) 514 { 515 c_undo_t un = el->el_chared.c_undo; 516 517 if (un.len == -1) 518 return CC_ERROR; 519 520 /* switch line buffer and undo buffer */ 521 el->el_chared.c_undo.buf = el->el_line.buffer; 522 el->el_chared.c_undo.len = el->el_line.lastchar - el->el_line.buffer; 523 el->el_chared.c_undo.cursor = el->el_line.cursor - el->el_line.buffer; 524 el->el_line.limit = un.buf + (el->el_line.limit - el->el_line.buffer); 525 el->el_line.buffer = un.buf; 526 el->el_line.cursor = un.buf + un.cursor; 527 el->el_line.lastchar = un.buf + un.len; 528 529 return (CC_REFRESH); 530 } 531 532 533 /* vi_command_mode(): 534 * Vi enter command mode (use alternative key bindings) 535 * [<ESC>] 536 */ 537 protected el_action_t 538 /*ARGSUSED*/ 539 vi_command_mode(EditLine *el, int c __attribute__((__unused__))) 540 { 541 542 /* [Esc] cancels pending action */ 543 el->el_chared.c_vcmd.action = NOP; 544 el->el_chared.c_vcmd.pos = 0; 545 546 el->el_state.doingarg = 0; 547 548 el->el_state.inputmode = MODE_INSERT; 549 el->el_map.current = el->el_map.alt; 550 #ifdef VI_MOVE 551 if (el->el_line.cursor > el->el_line.buffer) 552 el->el_line.cursor--; 553 #endif 554 return (CC_CURSOR); 555 } 556 557 558 /* vi_zero(): 559 * Vi move to the beginning of line 560 * [0] 561 */ 562 protected el_action_t 563 vi_zero(EditLine *el, int c) 564 { 565 566 if (el->el_state.doingarg) 567 return ed_argument_digit(el, c); 568 569 el->el_line.cursor = el->el_line.buffer; 570 if (el->el_chared.c_vcmd.action != NOP) { 571 cv_delfini(el); 572 return (CC_REFRESH); 573 } 574 return (CC_CURSOR); 575 } 576 577 578 /* vi_delete_prev_char(): 579 * Vi move to previous character (backspace) 580 * [^H] in insert mode only 581 */ 582 protected el_action_t 583 /*ARGSUSED*/ 584 vi_delete_prev_char(EditLine *el, int c __attribute__((__unused__))) 585 { 586 char *cp; 587 588 cp = el->el_line.cursor; 589 if (cp <= el->el_line.buffer) 590 return (CC_ERROR); 591 592 /* do the delete here so we dont mess up the undo and paste buffers */ 593 el->el_line.cursor = --cp; 594 for (; cp < el->el_line.lastchar; cp++) 595 cp[0] = cp[1]; 596 el->el_line.lastchar = cp - 1; 597 598 return (CC_REFRESH); 599 } 600 601 602 /* vi_list_or_eof(): 603 * Vi list choices for completion or indicate end of file if empty line 604 * [^D] 605 */ 606 protected el_action_t 607 /*ARGSUSED*/ 608 vi_list_or_eof(EditLine *el, int c __attribute__((__unused__))) 609 { 610 611 if (el->el_line.cursor == el->el_line.lastchar) { 612 if (el->el_line.cursor == el->el_line.buffer) { 613 term_overwrite(el, STReof, 4); /* then do a EOF */ 614 term__flush(); 615 return (CC_EOF); 616 } else { 617 /* 618 * Here we could list completions, but it is an 619 * error right now 620 */ 621 term_beep(el); 622 return (CC_ERROR); 623 } 624 } else { 625 #ifdef notyet 626 re_goto_bottom(el); 627 *el->el_line.lastchar = '\0'; /* just in case */ 628 return (CC_LIST_CHOICES); 629 #else 630 /* 631 * Just complain for now. 632 */ 633 term_beep(el); 634 return (CC_ERROR); 635 #endif 636 } 637 } 638 639 640 /* vi_kill_line_prev(): 641 * Vi cut from beginning of line to cursor 642 * [^U] 643 */ 644 protected el_action_t 645 /*ARGSUSED*/ 646 vi_kill_line_prev(EditLine *el, int c __attribute__((__unused__))) 647 { 648 char *kp, *cp; 649 650 cp = el->el_line.buffer; 651 kp = el->el_chared.c_kill.buf; 652 while (cp < el->el_line.cursor) 653 *kp++ = *cp++; /* copy it */ 654 el->el_chared.c_kill.last = kp; 655 c_delbefore(el, el->el_line.cursor - el->el_line.buffer); 656 el->el_line.cursor = el->el_line.buffer; /* zap! */ 657 return (CC_REFRESH); 658 } 659 660 661 /* vi_search_prev(): 662 * Vi search history previous 663 * [?] 664 */ 665 protected el_action_t 666 /*ARGSUSED*/ 667 vi_search_prev(EditLine *el, int c __attribute__((__unused__))) 668 { 669 670 return (cv_search(el, ED_SEARCH_PREV_HISTORY)); 671 } 672 673 674 /* vi_search_next(): 675 * Vi search history next 676 * [/] 677 */ 678 protected el_action_t 679 /*ARGSUSED*/ 680 vi_search_next(EditLine *el, int c __attribute__((__unused__))) 681 { 682 683 return (cv_search(el, ED_SEARCH_NEXT_HISTORY)); 684 } 685 686 687 /* vi_repeat_search_next(): 688 * Vi repeat current search in the same search direction 689 * [n] 690 */ 691 protected el_action_t 692 /*ARGSUSED*/ 693 vi_repeat_search_next(EditLine *el, int c __attribute__((__unused__))) 694 { 695 696 if (el->el_search.patlen == 0) 697 return (CC_ERROR); 698 else 699 return (cv_repeat_srch(el, el->el_search.patdir)); 700 } 701 702 703 /* vi_repeat_search_prev(): 704 * Vi repeat current search in the opposite search direction 705 * [N] 706 */ 707 /*ARGSUSED*/ 708 protected el_action_t 709 vi_repeat_search_prev(EditLine *el, int c __attribute__((__unused__))) 710 { 711 712 if (el->el_search.patlen == 0) 713 return (CC_ERROR); 714 else 715 return (cv_repeat_srch(el, 716 el->el_search.patdir == ED_SEARCH_PREV_HISTORY ? 717 ED_SEARCH_NEXT_HISTORY : ED_SEARCH_PREV_HISTORY)); 718 } 719 720 721 /* vi_next_char(): 722 * Vi move to the character specified next 723 * [f] 724 */ 725 protected el_action_t 726 /*ARGSUSED*/ 727 vi_next_char(EditLine *el, int c __attribute__((__unused__))) 728 { 729 return cv_csearch(el, CHAR_FWD, -1, el->el_state.argument, 0); 730 } 731 732 733 /* vi_prev_char(): 734 * Vi move to the character specified previous 735 * [F] 736 */ 737 protected el_action_t 738 /*ARGSUSED*/ 739 vi_prev_char(EditLine *el, int c __attribute__((__unused__))) 740 { 741 return cv_csearch(el, CHAR_BACK, -1, el->el_state.argument, 0); 742 } 743 744 745 /* vi_to_next_char(): 746 * Vi move up to the character specified next 747 * [t] 748 */ 749 protected el_action_t 750 /*ARGSUSED*/ 751 vi_to_next_char(EditLine *el, int c __attribute__((__unused__))) 752 { 753 return cv_csearch(el, CHAR_FWD, -1, el->el_state.argument, 1); 754 } 755 756 757 /* vi_to_prev_char(): 758 * Vi move up to the character specified previous 759 * [T] 760 */ 761 protected el_action_t 762 /*ARGSUSED*/ 763 vi_to_prev_char(EditLine *el, int c __attribute__((__unused__))) 764 { 765 return cv_csearch(el, CHAR_BACK, -1, el->el_state.argument, 1); 766 } 767 768 769 /* vi_repeat_next_char(): 770 * Vi repeat current character search in the same search direction 771 * [;] 772 */ 773 protected el_action_t 774 /*ARGSUSED*/ 775 vi_repeat_next_char(EditLine *el, int c __attribute__((__unused__))) 776 { 777 778 return cv_csearch(el, el->el_search.chadir, el->el_search.chacha, 779 el->el_state.argument, el->el_search.chatflg); 780 } 781 782 783 /* vi_repeat_prev_char(): 784 * Vi repeat current character search in the opposite search direction 785 * [,] 786 */ 787 protected el_action_t 788 /*ARGSUSED*/ 789 vi_repeat_prev_char(EditLine *el, int c __attribute__((__unused__))) 790 { 791 el_action_t r; 792 int dir = el->el_search.chadir; 793 794 r = cv_csearch(el, -dir, el->el_search.chacha, 795 el->el_state.argument, el->el_search.chatflg); 796 el->el_search.chadir = dir; 797 return r; 798 } 799 800 801 /* vi_match(): 802 * Vi go to matching () {} or [] 803 * [%] 804 */ 805 protected el_action_t 806 /*ARGSUSED*/ 807 vi_match(EditLine *el, int c) 808 { 809 const char match_chars[] = "()[]{}"; 810 char *cp; 811 int delta, i, count; 812 char o_ch, c_ch; 813 814 *el->el_line.lastchar = '\0'; /* just in case */ 815 816 i = strcspn(el->el_line.cursor, match_chars); 817 o_ch = el->el_line.cursor[i]; 818 if (o_ch == 0) 819 return CC_ERROR; 820 delta = strchr(match_chars, o_ch) - match_chars; 821 c_ch = match_chars[delta ^ 1]; 822 count = 1; 823 delta = 1 - (delta & 1) * 2; 824 825 for (cp = &el->el_line.cursor[i]; count; ) { 826 cp += delta; 827 if (cp < el->el_line.buffer || cp >= el->el_line.lastchar) 828 return CC_ERROR; 829 if (*cp == o_ch) 830 count++; 831 else if (*cp == c_ch) 832 count--; 833 } 834 835 el->el_line.cursor = cp; 836 837 if (el->el_chared.c_vcmd.action != NOP) { 838 /* NB posix says char under cursor should NOT be deleted 839 for -ve delta - this is different to netbsd vi. */ 840 if (delta > 0) 841 el->el_line.cursor++; 842 cv_delfini(el); 843 return (CC_REFRESH); 844 } 845 return (CC_CURSOR); 846 } 847 848 /* vi_undo_line(): 849 * Vi undo all changes to line 850 * [U] 851 */ 852 protected el_action_t 853 /*ARGSUSED*/ 854 vi_undo_line(EditLine *el, int c) 855 { 856 857 cv_undo(el); 858 return hist_get(el); 859 } 860 861 /* vi_to_column(): 862 * Vi go to specified column 863 * [|] 864 * NB netbsd vi goes to screen column 'n', posix says nth character 865 */ 866 protected el_action_t 867 /*ARGSUSED*/ 868 vi_to_column(EditLine *el, int c) 869 { 870 871 el->el_line.cursor = el->el_line.buffer; 872 el->el_state.argument--; 873 return ed_next_char(el, 0); 874 } 875 876 /* vi_yank_end(): 877 * Vi yank to end of line 878 * [Y] 879 */ 880 protected el_action_t 881 /*ARGSUSED*/ 882 vi_yank_end(EditLine *el, int c) 883 { 884 885 cv_yank(el, el->el_line.cursor, 886 el->el_line.lastchar - el->el_line.cursor); 887 return CC_REFRESH; 888 } 889 890 /* vi_yank(): 891 * Vi yank 892 * [y] 893 */ 894 protected el_action_t 895 /*ARGSUSED*/ 896 vi_yank(EditLine *el, int c) 897 { 898 899 return cv_action(el, YANK); 900 } 901 902 /* vi_comment_out(): 903 * Vi comment out current command 904 * [c] 905 */ 906 protected el_action_t 907 /*ARGSUSED*/ 908 vi_comment_out(EditLine *el, int c) 909 { 910 911 el->el_line.cursor = el->el_line.buffer; 912 c_insert(el, 1); 913 *el->el_line.cursor = '#'; 914 re_refresh(el); 915 return ed_newline(el, 0); 916 } 917 918 /* vi_alias(): 919 * Vi include shell alias 920 * [@] 921 * NB: posix impiles that we should enter insert mode, however 922 * this is against historical precedent... 923 */ 924 protected el_action_t 925 /*ARGSUSED*/ 926 vi_alias(EditLine *el, int c) 927 { 928 #ifdef __weak_extern 929 char alias_name[3]; 930 char *alias_text; 931 extern char *get_alias_text(const char *); 932 __weak_extern(get_alias_text); 933 934 if (get_alias_text == 0) { 935 return CC_ERROR; 936 } 937 938 alias_name[0] = '_'; 939 alias_name[2] = 0; 940 if (el_getc(el, &alias_name[1]) != 1) 941 return CC_ERROR; 942 943 alias_text = get_alias_text(alias_name); 944 if (alias_text != NULL) 945 el_push(el, alias_text); 946 return CC_NORM; 947 #else 948 return CC_ERROR; 949 #endif 950 } 951 952 /* vi_to_history_line(): 953 * Vi go to specified history file line. 954 * [G] 955 */ 956 protected el_action_t 957 /*ARGSUSED*/ 958 vi_to_history_line(EditLine *el, int c) 959 { 960 int sv_event_no = el->el_history.eventno; 961 el_action_t rval; 962 963 964 if (el->el_history.eventno == 0) { 965 (void) strncpy(el->el_history.buf, el->el_line.buffer, 966 EL_BUFSIZ); 967 el->el_history.last = el->el_history.buf + 968 (el->el_line.lastchar - el->el_line.buffer); 969 } 970 971 /* Lack of a 'count' means oldest, not 1 */ 972 if (!el->el_state.doingarg) { 973 el->el_history.eventno = 0x7fffffff; 974 hist_get(el); 975 } else { 976 /* This is brain dead, all the rest of this code counts 977 * upwards going into the past. Here we need count in the 978 * other direction (to match the output of fc -l). 979 * I could change the world, but this seems to suffice. 980 */ 981 el->el_history.eventno = 1; 982 if (hist_get(el) == CC_ERROR) 983 return CC_ERROR; 984 el->el_history.eventno = 1 + el->el_history.ev.num 985 - el->el_state.argument; 986 if (el->el_history.eventno < 0) { 987 el->el_history.eventno = sv_event_no; 988 return CC_ERROR; 989 } 990 } 991 rval = hist_get(el); 992 if (rval == CC_ERROR) 993 el->el_history.eventno = sv_event_no; 994 return rval; 995 } 996 997 /* vi_histedit(): 998 * Vi edit history line with vi 999 * [v] 1000 */ 1001 protected el_action_t 1002 /*ARGSUSED*/ 1003 vi_histedit(EditLine *el, int c) 1004 { 1005 int fd; 1006 pid_t pid; 1007 int st; 1008 char tempfile[] = "/tmp/histedit.XXXXXXXXXX"; 1009 char *cp; 1010 1011 if (el->el_state.doingarg) { 1012 if (vi_to_history_line(el, 0) == CC_ERROR) 1013 return CC_ERROR; 1014 } 1015 1016 fd = mkstemp(tempfile); 1017 if (fd < 0) 1018 return CC_ERROR; 1019 cp = el->el_line.buffer; 1020 write(fd, cp, el->el_line.lastchar - cp +0u); 1021 write(fd, "\n", 1); 1022 pid = fork(); 1023 switch (pid) { 1024 case -1: 1025 close(fd); 1026 unlink(tempfile); 1027 return CC_ERROR; 1028 case 0: 1029 close(fd); 1030 execlp("vi", "vi", tempfile, 0); 1031 exit(0); 1032 /*NOTREACHED*/ 1033 default: 1034 while (waitpid(pid, &st, 0) != pid) 1035 continue; 1036 lseek(fd, 0ll, SEEK_SET); 1037 st = read(fd, cp, el->el_line.limit - cp +0u); 1038 if (st > 0 && cp[st - 1] == '\n') 1039 st--; 1040 el->el_line.cursor = cp; 1041 el->el_line.lastchar = cp + st; 1042 break; 1043 } 1044 1045 close(fd); 1046 unlink(tempfile); 1047 /* return CC_REFRESH; */ 1048 return ed_newline(el, 0); 1049 } 1050 1051 /* vi_history_word(): 1052 * Vi append word from previous input line 1053 * [_] 1054 * Who knows where this one came from! 1055 * '_' in vi means 'entire current line', so 'cc' is a synonym for 'c_' 1056 */ 1057 protected el_action_t 1058 /*ARGSUSED*/ 1059 vi_history_word(EditLine *el, int c) 1060 { 1061 const char *wp = HIST_FIRST(el); 1062 const char *wep, *wsp; 1063 int len; 1064 char *cp; 1065 const char *lim; 1066 1067 if (wp == NULL) 1068 return CC_ERROR; 1069 1070 wep = wsp = 0; 1071 do { 1072 while (isspace((unsigned char)*wp)) 1073 wp++; 1074 if (*wp == 0) 1075 break; 1076 wsp = wp; 1077 while (*wp && !isspace((unsigned char)*wp)) 1078 wp++; 1079 wep = wp; 1080 } while ((!el->el_state.doingarg || --el->el_state.argument > 0) && *wp != 0); 1081 1082 if (wsp == 0 || (el->el_state.doingarg && el->el_state.argument != 0)) 1083 return CC_ERROR; 1084 1085 cv_undo(el); 1086 len = wep - wsp; 1087 if (el->el_line.cursor < el->el_line.lastchar) 1088 el->el_line.cursor++; 1089 c_insert(el, len + 1); 1090 cp = el->el_line.cursor; 1091 lim = el->el_line.limit; 1092 if (cp < lim) 1093 *cp++ = ' '; 1094 while (wsp < wep && cp < lim) 1095 *cp++ = *wsp++; 1096 el->el_line.cursor = cp; 1097 1098 el->el_map.current = el->el_map.key; 1099 return CC_REFRESH; 1100 } 1101 1102 /* vi_redo(): 1103 * Vi redo last non-motion command 1104 * [.] 1105 */ 1106 protected el_action_t 1107 /*ARGSUSED*/ 1108 vi_redo(EditLine *el, int c) 1109 { 1110 c_redo_t *r = &el->el_chared.c_redo; 1111 1112 if (!el->el_state.doingarg && r->count) { 1113 el->el_state.doingarg = 1; 1114 el->el_state.argument = r->count; 1115 } 1116 1117 el->el_chared.c_vcmd.pos = el->el_line.cursor; 1118 el->el_chared.c_vcmd.action = r->action; 1119 if (r->pos != r->buf) { 1120 if (r->pos + 1 > r->lim) 1121 /* sanity */ 1122 r->pos = r->lim - 1; 1123 r->pos[0] = 0; 1124 el_push(el, r->buf); 1125 } 1126 1127 el->el_state.thiscmd = r->cmd; 1128 el->el_state.thisch = r->ch; 1129 return (*el->el_map.func[r->cmd])(el, r->ch); 1130 } 1131