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