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