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