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