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