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