1/* $NetBSD: vi.c,v 1.43 2012/01/16 14:57:45 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#include <stdlib.h> 37#include <unistd.h> 38#include <limits.h> 39#include <sys/wait.h> 40 41#if !defined(lint) && !defined(SCCSID) 42#if 0 43static char sccsid[] = "@(#)vi.c 8.1 (Berkeley) 6/4/93"; 44#else 45__RCSID("$NetBSD: vi.c,v 1.43 2012/01/16 14:57:45 christos Exp $"); 46#endif 47#endif /* not lint && not SCCSID */ 48 49/* 50 * vi.c: Vi mode commands. 51 */ 52#include "el.h" 53 54private el_action_t cv_action(EditLine *, Int); 55private el_action_t cv_paste(EditLine *, Int); 56 57/* cv_action(): 58 * Handle vi actions. 59 */ 60private el_action_t 61cv_action(EditLine *el, Int c) 62{ 63 64 if (el->el_chared.c_vcmd.action != NOP) { 65 /* 'cc', 'dd' and (possibly) friends */ 66 if (c != (Int)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 */ 92private el_action_t 93cv_paste(EditLine *el, Int 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: \"%.*s\"\n", (int)len, k->buf); 102#endif 103 104 cv_undo(el); 105 106 if (!c && el->el_line.cursor < el->el_line.lastchar) 107 el->el_line.cursor++; 108 109 c_insert(el, (int)len); 110 if (el->el_line.cursor + len > el->el_line.lastchar) 111 return CC_ERROR; 112 (void) memcpy(el->el_line.cursor, k->buf, len * 113 sizeof(*el->el_line.cursor)); 114 115 return CC_REFRESH; 116} 117 118 119/* vi_paste_next(): 120 * Vi paste previous deletion to the right of the cursor 121 * [p] 122 */ 123protected el_action_t 124/*ARGSUSED*/ 125vi_paste_next(EditLine *el, Int 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 */ 136protected el_action_t 137/*ARGSUSED*/ 138vi_paste_prev(EditLine *el, Int c __attribute__((__unused__))) 139{ 140 141 return cv_paste(el, 1); 142} 143 144 145/* vi_prev_big_word(): 146 * Vi move to the previous space delimited word 147 * [B] 148 */ 149protected el_action_t 150/*ARGSUSED*/ 151vi_prev_big_word(EditLine *el, Int c __attribute__((__unused__))) 152{ 153 154 if (el->el_line.cursor == el->el_line.buffer) 155 return CC_ERROR; 156 157 el->el_line.cursor = cv_prev_word(el->el_line.cursor, 158 el->el_line.buffer, 159 el->el_state.argument, 160 cv__isWord); 161 162 if (el->el_chared.c_vcmd.action != NOP) { 163 cv_delfini(el); 164 return CC_REFRESH; 165 } 166 return CC_CURSOR; 167} 168 169 170/* vi_prev_word(): 171 * Vi move to the previous word 172 * [b] 173 */ 174protected el_action_t 175/*ARGSUSED*/ 176vi_prev_word(EditLine *el, Int c __attribute__((__unused__))) 177{ 178 179 if (el->el_line.cursor == el->el_line.buffer) 180 return CC_ERROR; 181 182 el->el_line.cursor = cv_prev_word(el->el_line.cursor, 183 el->el_line.buffer, 184 el->el_state.argument, 185 cv__isword); 186 187 if (el->el_chared.c_vcmd.action != NOP) { 188 cv_delfini(el); 189 return CC_REFRESH; 190 } 191 return CC_CURSOR; 192} 193 194 195/* vi_next_big_word(): 196 * Vi move to the next space delimited word 197 * [W] 198 */ 199protected el_action_t 200/*ARGSUSED*/ 201vi_next_big_word(EditLine *el, Int c __attribute__((__unused__))) 202{ 203 204 if (el->el_line.cursor >= el->el_line.lastchar - 1) 205 return CC_ERROR; 206 207 el->el_line.cursor = cv_next_word(el, el->el_line.cursor, 208 el->el_line.lastchar, el->el_state.argument, cv__isWord); 209 210 if (el->el_map.type == MAP_VI) 211 if (el->el_chared.c_vcmd.action != NOP) { 212 cv_delfini(el); 213 return CC_REFRESH; 214 } 215 return CC_CURSOR; 216} 217 218 219/* vi_next_word(): 220 * Vi move to the next word 221 * [w] 222 */ 223protected el_action_t 224/*ARGSUSED*/ 225vi_next_word(EditLine *el, Int c __attribute__((__unused__))) 226{ 227 228 if (el->el_line.cursor >= el->el_line.lastchar - 1) 229 return CC_ERROR; 230 231 el->el_line.cursor = cv_next_word(el, el->el_line.cursor, 232 el->el_line.lastchar, el->el_state.argument, cv__isword); 233 234 if (el->el_map.type == MAP_VI) 235 if (el->el_chared.c_vcmd.action != NOP) { 236 cv_delfini(el); 237 return CC_REFRESH; 238 } 239 return CC_CURSOR; 240} 241 242 243/* vi_change_case(): 244 * Vi change case of character under the cursor and advance one character 245 * [~] 246 */ 247protected el_action_t 248vi_change_case(EditLine *el, Int c) 249{ 250 int i; 251 252 if (el->el_line.cursor >= el->el_line.lastchar) 253 return CC_ERROR; 254 cv_undo(el); 255 for (i = 0; i < el->el_state.argument; i++) { 256 257 c = *el->el_line.cursor; 258 if (Isupper(c)) 259 *el->el_line.cursor = Tolower(c); 260 else if (Islower(c)) 261 *el->el_line.cursor = Toupper(c); 262 263 if (++el->el_line.cursor >= el->el_line.lastchar) { 264 el->el_line.cursor--; 265 re_fastaddc(el); 266 break; 267 } 268 re_fastaddc(el); 269 } 270 return CC_NORM; 271} 272 273 274/* vi_change_meta(): 275 * Vi change prefix command 276 * [c] 277 */ 278protected el_action_t 279/*ARGSUSED*/ 280vi_change_meta(EditLine *el, Int c __attribute__((__unused__))) 281{ 282 283 /* 284 * Delete with insert == change: first we delete and then we leave in 285 * insert mode. 286 */ 287 return cv_action(el, DELETE | INSERT); 288} 289 290 291/* vi_insert_at_bol(): 292 * Vi enter insert mode at the beginning of line 293 * [I] 294 */ 295protected el_action_t 296/*ARGSUSED*/ 297vi_insert_at_bol(EditLine *el, Int c __attribute__((__unused__))) 298{ 299 300 el->el_line.cursor = el->el_line.buffer; 301 cv_undo(el); 302 el->el_map.current = el->el_map.key; 303 return CC_CURSOR; 304} 305 306 307/* vi_replace_char(): 308 * Vi replace character under the cursor with the next character typed 309 * [r] 310 */ 311protected el_action_t 312/*ARGSUSED*/ 313vi_replace_char(EditLine *el, Int c __attribute__((__unused__))) 314{ 315 316 if (el->el_line.cursor >= el->el_line.lastchar) 317 return CC_ERROR; 318 319 el->el_map.current = el->el_map.key; 320 el->el_state.inputmode = MODE_REPLACE_1; 321 cv_undo(el); 322 return CC_ARGHACK; 323} 324 325 326/* vi_replace_mode(): 327 * Vi enter replace mode 328 * [R] 329 */ 330protected el_action_t 331/*ARGSUSED*/ 332vi_replace_mode(EditLine *el, Int c __attribute__((__unused__))) 333{ 334 335 el->el_map.current = el->el_map.key; 336 el->el_state.inputmode = MODE_REPLACE; 337 cv_undo(el); 338 return CC_NORM; 339} 340 341 342/* vi_substitute_char(): 343 * Vi replace character under the cursor and enter insert mode 344 * [s] 345 */ 346protected el_action_t 347/*ARGSUSED*/ 348vi_substitute_char(EditLine *el, Int c __attribute__((__unused__))) 349{ 350 351 c_delafter(el, el->el_state.argument); 352 el->el_map.current = el->el_map.key; 353 return CC_REFRESH; 354} 355 356 357/* vi_substitute_line(): 358 * Vi substitute entire line 359 * [S] 360 */ 361protected el_action_t 362/*ARGSUSED*/ 363vi_substitute_line(EditLine *el, Int c __attribute__((__unused__))) 364{ 365 366 cv_undo(el); 367 cv_yank(el, el->el_line.buffer, 368 (int)(el->el_line.lastchar - el->el_line.buffer)); 369 (void) em_kill_line(el, 0); 370 el->el_map.current = el->el_map.key; 371 return CC_REFRESH; 372} 373 374 375/* vi_change_to_eol(): 376 * Vi change to end of line 377 * [C] 378 */ 379protected el_action_t 380/*ARGSUSED*/ 381vi_change_to_eol(EditLine *el, Int c __attribute__((__unused__))) 382{ 383 384 cv_undo(el); 385 cv_yank(el, el->el_line.cursor, 386 (int)(el->el_line.lastchar - el->el_line.cursor)); 387 (void) ed_kill_line(el, 0); 388 el->el_map.current = el->el_map.key; 389 return CC_REFRESH; 390} 391 392 393/* vi_insert(): 394 * Vi enter insert mode 395 * [i] 396 */ 397protected el_action_t 398/*ARGSUSED*/ 399vi_insert(EditLine *el, Int c __attribute__((__unused__))) 400{ 401 402 el->el_map.current = el->el_map.key; 403 cv_undo(el); 404 return CC_NORM; 405} 406 407 408/* vi_add(): 409 * Vi enter insert mode after the cursor 410 * [a] 411 */ 412protected el_action_t 413/*ARGSUSED*/ 414vi_add(EditLine *el, Int c __attribute__((__unused__))) 415{ 416 int ret; 417 418 el->el_map.current = el->el_map.key; 419 if (el->el_line.cursor < el->el_line.lastchar) { 420 el->el_line.cursor++; 421 if (el->el_line.cursor > el->el_line.lastchar) 422 el->el_line.cursor = el->el_line.lastchar; 423 ret = CC_CURSOR; 424 } else 425 ret = CC_NORM; 426 427 cv_undo(el); 428 429 return (el_action_t)ret; 430} 431 432 433/* vi_add_at_eol(): 434 * Vi enter insert mode at end of line 435 * [A] 436 */ 437protected el_action_t 438/*ARGSUSED*/ 439vi_add_at_eol(EditLine *el, Int c __attribute__((__unused__))) 440{ 441 442 el->el_map.current = el->el_map.key; 443 el->el_line.cursor = el->el_line.lastchar; 444 cv_undo(el); 445 return CC_CURSOR; 446} 447 448 449/* vi_delete_meta(): 450 * Vi delete prefix command 451 * [d] 452 */ 453protected el_action_t 454/*ARGSUSED*/ 455vi_delete_meta(EditLine *el, Int c __attribute__((__unused__))) 456{ 457 458 return cv_action(el, DELETE); 459} 460 461 462/* vi_end_big_word(): 463 * Vi move to the end of the current space delimited word 464 * [E] 465 */ 466protected el_action_t 467/*ARGSUSED*/ 468vi_end_big_word(EditLine *el, Int c __attribute__((__unused__))) 469{ 470 471 if (el->el_line.cursor == el->el_line.lastchar) 472 return CC_ERROR; 473 474 el->el_line.cursor = cv__endword(el->el_line.cursor, 475 el->el_line.lastchar, el->el_state.argument, cv__isWord); 476 477 if (el->el_chared.c_vcmd.action != NOP) { 478 el->el_line.cursor++; 479 cv_delfini(el); 480 return CC_REFRESH; 481 } 482 return CC_CURSOR; 483} 484 485 486/* vi_end_word(): 487 * Vi move to the end of the current word 488 * [e] 489 */ 490protected el_action_t 491/*ARGSUSED*/ 492vi_end_word(EditLine *el, Int c __attribute__((__unused__))) 493{ 494 495 if (el->el_line.cursor == el->el_line.lastchar) 496 return CC_ERROR; 497 498 el->el_line.cursor = cv__endword(el->el_line.cursor, 499 el->el_line.lastchar, el->el_state.argument, cv__isword); 500 501 if (el->el_chared.c_vcmd.action != NOP) { 502 el->el_line.cursor++; 503 cv_delfini(el); 504 return CC_REFRESH; 505 } 506 return CC_CURSOR; 507} 508 509 510/* vi_undo(): 511 * Vi undo last change 512 * [u] 513 */ 514protected el_action_t 515/*ARGSUSED*/ 516vi_undo(EditLine *el, Int c __attribute__((__unused__))) 517{ 518 c_undo_t un = el->el_chared.c_undo; 519 520 if (un.len == -1) 521 return CC_ERROR; 522 523 /* switch line buffer and undo buffer */ 524 el->el_chared.c_undo.buf = el->el_line.buffer; 525 el->el_chared.c_undo.len = el->el_line.lastchar - el->el_line.buffer; 526 el->el_chared.c_undo.cursor = 527 (int)(el->el_line.cursor - el->el_line.buffer); 528 el->el_line.limit = un.buf + (el->el_line.limit - el->el_line.buffer); 529 el->el_line.buffer = un.buf; 530 el->el_line.cursor = un.buf + un.cursor; 531 el->el_line.lastchar = un.buf + un.len; 532 533 return CC_REFRESH; 534} 535 536 537/* vi_command_mode(): 538 * Vi enter command mode (use alternative key bindings) 539 * [<ESC>] 540 */ 541protected el_action_t 542/*ARGSUSED*/ 543vi_command_mode(EditLine *el, Int c __attribute__((__unused__))) 544{ 545 546 /* [Esc] cancels pending action */ 547 el->el_chared.c_vcmd.action = NOP; 548 el->el_chared.c_vcmd.pos = 0; 549 550 el->el_state.doingarg = 0; 551 552 el->el_state.inputmode = MODE_INSERT; 553 el->el_map.current = el->el_map.alt; 554#ifdef VI_MOVE 555 if (el->el_line.cursor > el->el_line.buffer) 556 el->el_line.cursor--; 557#endif 558 return CC_CURSOR; 559} 560 561 562/* vi_zero(): 563 * Vi move to the beginning of line 564 * [0] 565 */ 566protected el_action_t 567vi_zero(EditLine *el, Int c) 568{ 569 570 if (el->el_state.doingarg) 571 return ed_argument_digit(el, c); 572 573 el->el_line.cursor = el->el_line.buffer; 574 if (el->el_chared.c_vcmd.action != NOP) { 575 cv_delfini(el); 576 return CC_REFRESH; 577 } 578 return CC_CURSOR; 579} 580 581 582/* vi_delete_prev_char(): 583 * Vi move to previous character (backspace) 584 * [^H] in insert mode only 585 */ 586protected el_action_t 587/*ARGSUSED*/ 588vi_delete_prev_char(EditLine *el, Int c __attribute__((__unused__))) 589{ 590 591 if (el->el_line.cursor <= el->el_line.buffer) 592 return CC_ERROR; 593 594 c_delbefore1(el); 595 el->el_line.cursor--; 596 return CC_REFRESH; 597} 598 599 600/* vi_list_or_eof(): 601 * Vi list choices for completion or indicate end of file if empty line 602 * [^D] 603 */ 604protected el_action_t 605/*ARGSUSED*/ 606vi_list_or_eof(EditLine *el, Int c) 607{ 608 609 if (el->el_line.cursor == el->el_line.lastchar) { 610 if (el->el_line.cursor == el->el_line.buffer) { 611 terminal_writec(el, c); /* then do a EOF */ 612 return CC_EOF; 613 } else { 614 /* 615 * Here we could list completions, but it is an 616 * error right now 617 */ 618 terminal_beep(el); 619 return CC_ERROR; 620 } 621 } else { 622#ifdef notyet 623 re_goto_bottom(el); 624 *el->el_line.lastchar = '\0'; /* just in case */ 625 return CC_LIST_CHOICES; 626#else 627 /* 628 * Just complain for now. 629 */ 630 terminal_beep(el); 631 return CC_ERROR; 632#endif 633 } 634} 635 636 637/* vi_kill_line_prev(): 638 * Vi cut from beginning of line to cursor 639 * [^U] 640 */ 641protected el_action_t 642/*ARGSUSED*/ 643vi_kill_line_prev(EditLine *el, Int c __attribute__((__unused__))) 644{ 645 Char *kp, *cp; 646 647 cp = el->el_line.buffer; 648 kp = el->el_chared.c_kill.buf; 649 while (cp < el->el_line.cursor) 650 *kp++ = *cp++; /* copy it */ 651 el->el_chared.c_kill.last = kp; 652 c_delbefore(el, (int)(el->el_line.cursor - el->el_line.buffer)); 653 el->el_line.cursor = el->el_line.buffer; /* zap! */ 654 return CC_REFRESH; 655} 656 657 658/* vi_search_prev(): 659 * Vi search history previous 660 * [?] 661 */ 662protected el_action_t 663/*ARGSUSED*/ 664vi_search_prev(EditLine *el, Int c __attribute__((__unused__))) 665{ 666 667 return cv_search(el, ED_SEARCH_PREV_HISTORY); 668} 669 670 671/* vi_search_next(): 672 * Vi search history next 673 * [/] 674 */ 675protected el_action_t 676/*ARGSUSED*/ 677vi_search_next(EditLine *el, Int c __attribute__((__unused__))) 678{ 679 680 return cv_search(el, ED_SEARCH_NEXT_HISTORY); 681} 682 683 684/* vi_repeat_search_next(): 685 * Vi repeat current search in the same search direction 686 * [n] 687 */ 688protected el_action_t 689/*ARGSUSED*/ 690vi_repeat_search_next(EditLine *el, Int c __attribute__((__unused__))) 691{ 692 693 if (el->el_search.patlen == 0) 694 return CC_ERROR; 695 else 696 return cv_repeat_srch(el, el->el_search.patdir); 697} 698 699 700/* vi_repeat_search_prev(): 701 * Vi repeat current search in the opposite search direction 702 * [N] 703 */ 704/*ARGSUSED*/ 705protected el_action_t 706vi_repeat_search_prev(EditLine *el, Int c __attribute__((__unused__))) 707{ 708 709 if (el->el_search.patlen == 0) 710 return CC_ERROR; 711 else 712 return (cv_repeat_srch(el, 713 el->el_search.patdir == ED_SEARCH_PREV_HISTORY ? 714 ED_SEARCH_NEXT_HISTORY : ED_SEARCH_PREV_HISTORY)); 715} 716 717 718/* vi_next_char(): 719 * Vi move to the character specified next 720 * [f] 721 */ 722protected el_action_t 723/*ARGSUSED*/ 724vi_next_char(EditLine *el, Int c __attribute__((__unused__))) 725{ 726 return cv_csearch(el, CHAR_FWD, -1, el->el_state.argument, 0); 727} 728 729 730/* vi_prev_char(): 731 * Vi move to the character specified previous 732 * [F] 733 */ 734protected el_action_t 735/*ARGSUSED*/ 736vi_prev_char(EditLine *el, Int c __attribute__((__unused__))) 737{ 738 return cv_csearch(el, CHAR_BACK, -1, el->el_state.argument, 0); 739} 740 741 742/* vi_to_next_char(): 743 * Vi move up to the character specified next 744 * [t] 745 */ 746protected el_action_t 747/*ARGSUSED*/ 748vi_to_next_char(EditLine *el, Int c __attribute__((__unused__))) 749{ 750 return cv_csearch(el, CHAR_FWD, -1, el->el_state.argument, 1); 751} 752 753 754/* vi_to_prev_char(): 755 * Vi move up to the character specified previous 756 * [T] 757 */ 758protected el_action_t 759/*ARGSUSED*/ 760vi_to_prev_char(EditLine *el, Int c __attribute__((__unused__))) 761{ 762 return cv_csearch(el, CHAR_BACK, -1, el->el_state.argument, 1); 763} 764 765 766/* vi_repeat_next_char(): 767 * Vi repeat current character search in the same search direction 768 * [;] 769 */ 770protected el_action_t 771/*ARGSUSED*/ 772vi_repeat_next_char(EditLine *el, Int c __attribute__((__unused__))) 773{ 774 775 return cv_csearch(el, el->el_search.chadir, el->el_search.chacha, 776 el->el_state.argument, el->el_search.chatflg); 777} 778 779 780/* vi_repeat_prev_char(): 781 * Vi repeat current character search in the opposite search direction 782 * [,] 783 */ 784protected el_action_t 785/*ARGSUSED*/ 786vi_repeat_prev_char(EditLine *el, Int c __attribute__((__unused__))) 787{ 788 el_action_t r; 789 int dir = el->el_search.chadir; 790 791 r = cv_csearch(el, -dir, el->el_search.chacha, 792 el->el_state.argument, el->el_search.chatflg); 793 el->el_search.chadir = dir; 794 return r; 795} 796 797 798/* vi_match(): 799 * Vi go to matching () {} or [] 800 * [%] 801 */ 802protected el_action_t 803/*ARGSUSED*/ 804vi_match(EditLine *el, Int c __attribute__((__unused__))) 805{ 806 const Char match_chars[] = STR("()[]{}"); 807 Char *cp; 808 size_t delta, i, count; 809 Char o_ch, c_ch; 810 811 *el->el_line.lastchar = '\0'; /* just in case */ 812 813 i = Strcspn(el->el_line.cursor, match_chars); 814 o_ch = el->el_line.cursor[i]; 815 if (o_ch == 0) 816 return CC_ERROR; 817 delta = (size_t)(Strchr(match_chars, o_ch) - match_chars); 818 c_ch = match_chars[delta ^ 1]; 819 count = 1; 820 delta = 1 - (delta & 1) * 2; 821 822 for (cp = &el->el_line.cursor[i]; count; ) { 823 cp += delta; 824 if (cp < el->el_line.buffer || cp >= el->el_line.lastchar) 825 return CC_ERROR; 826 if (*cp == o_ch) 827 count++; 828 else if (*cp == c_ch) 829 count--; 830 } 831 832 el->el_line.cursor = cp; 833 834 if (el->el_chared.c_vcmd.action != NOP) { 835 /* NB posix says char under cursor should NOT be deleted 836 for -ve delta - this is different to netbsd vi. */ 837 if (delta > 0) 838 el->el_line.cursor++; 839 cv_delfini(el); 840 return CC_REFRESH; 841 } 842 return CC_CURSOR; 843} 844 845/* vi_undo_line(): 846 * Vi undo all changes to line 847 * [U] 848 */ 849protected el_action_t 850/*ARGSUSED*/ 851vi_undo_line(EditLine *el, Int c __attribute__((__unused__))) 852{ 853 854 cv_undo(el); 855 return hist_get(el); 856} 857 858/* vi_to_column(): 859 * Vi go to specified column 860 * [|] 861 * NB netbsd vi goes to screen column 'n', posix says nth character 862 */ 863protected el_action_t 864/*ARGSUSED*/ 865vi_to_column(EditLine *el, Int c __attribute__((__unused__))) 866{ 867 868 el->el_line.cursor = el->el_line.buffer; 869 el->el_state.argument--; 870 return ed_next_char(el, 0); 871} 872 873/* vi_yank_end(): 874 * Vi yank to end of line 875 * [Y] 876 */ 877protected el_action_t 878/*ARGSUSED*/ 879vi_yank_end(EditLine *el, Int c __attribute__((__unused__))) 880{ 881 882 cv_yank(el, el->el_line.cursor, 883 (int)(el->el_line.lastchar - el->el_line.cursor)); 884 return CC_REFRESH; 885} 886 887/* vi_yank(): 888 * Vi yank 889 * [y] 890 */ 891protected el_action_t 892/*ARGSUSED*/ 893vi_yank(EditLine *el, Int c __attribute__((__unused__))) 894{ 895 896 return cv_action(el, YANK); 897} 898 899/* vi_comment_out(): 900 * Vi comment out current command 901 * [#] 902 */ 903protected el_action_t 904/*ARGSUSED*/ 905vi_comment_out(EditLine *el, Int c __attribute__((__unused__))) 906{ 907 908 el->el_line.cursor = el->el_line.buffer; 909 c_insert(el, 1); 910 *el->el_line.cursor = '#'; 911 re_refresh(el); 912 return ed_newline(el, 0); 913} 914 915/* vi_alias(): 916 * Vi include shell alias 917 * [@] 918 * NB: posix implies that we should enter insert mode, however 919 * this is against historical precedent... 920 */ 921#ifdef __weak_reference 922__weakref_visible char *my_get_alias_text(const char *) 923 __weak_reference(get_alias_text); 924#endif 925protected el_action_t 926/*ARGSUSED*/ 927vi_alias(EditLine *el __attribute__((__unused__)), Int c __attribute__((__unused__))) 928{ 929#ifdef __weak_reference 930 char alias_name[3]; 931 char *alias_text; 932 933 if (my_get_alias_text == 0) { 934 return CC_ERROR; 935 } 936 937 alias_name[0] = '_'; 938 alias_name[2] = 0; 939 if (el_getc(el, &alias_name[1]) != 1) 940 return CC_ERROR; 941 942 alias_text = my_get_alias_text(alias_name); 943 if (alias_text != NULL) 944 FUN(el,push)(el, ct_decode_string(alias_text, &el->el_scratch)); 945 return CC_NORM; 946#else 947 return CC_ERROR; 948#endif 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, Int 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, Int 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); 1050 if (st > 0) { 1051 len = (size_t)(el->el_line.lastchar - 1052 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, Int 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 = 0; 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 == 0 || (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, Int 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