xref: /freebsd-12.1/lib/libedit/vi.c (revision 0700a396)
1 /*	$NetBSD: vi.c,v 1.45 2014/06/18 18:12:28 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.45 2014/06/18 18:12:28 christos Exp $");
46 #endif
47 #endif /* not lint && not SCCSID */
48 #include <sys/cdefs.h>
49 __FBSDID("$FreeBSD$");
50 
51 /*
52  * vi.c: Vi mode commands.
53  */
54 #include "el.h"
55 
56 private el_action_t	cv_action(EditLine *, Int);
57 private el_action_t	cv_paste(EditLine *, Int);
58 
59 /* cv_action():
60  *	Handle vi actions.
61  */
62 private el_action_t
63 cv_action(EditLine *el, Int c)
64 {
65 
66 	if (el->el_chared.c_vcmd.action != NOP) {
67 		/* 'cc', 'dd' and (possibly) friends */
68 		if (c != (Int)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, Int 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, Int 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, Int 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, Int 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, Int 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, Int 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, Int 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, Int 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, Int 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, Int 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, Int 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, Int 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, Int 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, Int 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, Int 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, Int 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, Int 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, Int 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, Int 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, Int 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, Int 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, Int 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, Int 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, Int 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, Int 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, Int 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, Int 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, Int 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, Int 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, Int 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, Int 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, Int 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, Int 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, Int 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, Int 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, Int 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, Int 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, Int 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, Int 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, Int 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, Int 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, Int 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, Int 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, Int 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, Int 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, Int 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);
1044 		if (st > 0) {
1045 			len = (size_t)(el->el_line.lastchar -
1046 			    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, Int 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, Int 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