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