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 38static 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 60static el_action_t cv_action(EditLine *, wint_t); 61static el_action_t cv_paste(EditLine *, wint_t); 62 63/* cv_action(): 64 * Handle vi actions. 65 */ 66static el_action_t 67cv_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 */ 98static el_action_t 99cv_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 */ 130libedit_private el_action_t 131/*ARGSUSED*/ 132vi_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 */ 143libedit_private el_action_t 144/*ARGSUSED*/ 145vi_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 */ 156libedit_private el_action_t 157/*ARGSUSED*/ 158vi_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 */ 181libedit_private el_action_t 182/*ARGSUSED*/ 183vi_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 */ 206libedit_private el_action_t 207/*ARGSUSED*/ 208vi_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 */ 230libedit_private el_action_t 231/*ARGSUSED*/ 232vi_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 */ 254libedit_private el_action_t 255vi_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 */ 285libedit_private el_action_t 286/*ARGSUSED*/ 287vi_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 */ 302libedit_private el_action_t 303/*ARGSUSED*/ 304vi_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 */ 318libedit_private el_action_t 319/*ARGSUSED*/ 320vi_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 */ 337libedit_private el_action_t 338/*ARGSUSED*/ 339vi_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 */ 353libedit_private el_action_t 354/*ARGSUSED*/ 355vi_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 */ 368libedit_private el_action_t 369/*ARGSUSED*/ 370vi_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 */ 386libedit_private el_action_t 387/*ARGSUSED*/ 388vi_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 */ 404libedit_private el_action_t 405/*ARGSUSED*/ 406vi_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 */ 419libedit_private el_action_t 420/*ARGSUSED*/ 421vi_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 */ 444libedit_private el_action_t 445/*ARGSUSED*/ 446vi_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 */ 460libedit_private el_action_t 461/*ARGSUSED*/ 462vi_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 */ 473libedit_private el_action_t 474/*ARGSUSED*/ 475vi_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 */ 497libedit_private el_action_t 498/*ARGSUSED*/ 499vi_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 */ 521libedit_private el_action_t 522/*ARGSUSED*/ 523vi_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 */ 548libedit_private el_action_t 549/*ARGSUSED*/ 550vi_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 */ 573libedit_private el_action_t 574vi_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 */ 593libedit_private el_action_t 594/*ARGSUSED*/ 595vi_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 */ 611libedit_private el_action_t 612/*ARGSUSED*/ 613vi_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 */ 648libedit_private el_action_t 649/*ARGSUSED*/ 650vi_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 */ 669libedit_private el_action_t 670/*ARGSUSED*/ 671vi_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 */ 682libedit_private el_action_t 683/*ARGSUSED*/ 684vi_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 */ 695libedit_private el_action_t 696/*ARGSUSED*/ 697vi_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*/ 712libedit_private el_action_t 713vi_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 */ 729libedit_private el_action_t 730/*ARGSUSED*/ 731vi_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 */ 741libedit_private el_action_t 742/*ARGSUSED*/ 743vi_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 */ 753libedit_private el_action_t 754/*ARGSUSED*/ 755vi_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 */ 765libedit_private el_action_t 766/*ARGSUSED*/ 767vi_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 */ 777libedit_private el_action_t 778/*ARGSUSED*/ 779vi_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 */ 791libedit_private el_action_t 792/*ARGSUSED*/ 793vi_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 */ 809libedit_private el_action_t 810/*ARGSUSED*/ 811vi_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 */ 856libedit_private el_action_t 857/*ARGSUSED*/ 858vi_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 */ 870libedit_private el_action_t 871/*ARGSUSED*/ 872vi_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 */ 884libedit_private el_action_t 885/*ARGSUSED*/ 886vi_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 */ 898libedit_private el_action_t 899/*ARGSUSED*/ 900vi_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 */ 910libedit_private el_action_t 911/*ARGSUSED*/ 912vi_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 */ 928libedit_private el_action_t 929/*ARGSUSED*/ 930vi_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 */ 954libedit_private el_action_t 955/*ARGSUSED*/ 956vi_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 */ 999libedit_private el_action_t 1000/*ARGSUSED*/ 1001vi_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); 1072error: 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 */ 1086libedit_private el_action_t 1087/*ARGSUSED*/ 1088vi_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 */ 1136libedit_private el_action_t 1137/*ARGSUSED*/ 1138vi_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