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