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