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