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
43static 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
54private el_action_t	cv_action(EditLine *, Int);
55private el_action_t	cv_paste(EditLine *, Int);
56
57/* cv_action():
58 *	Handle vi actions.
59 */
60private el_action_t
61cv_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 */
92private el_action_t
93cv_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 */
123protected el_action_t
124/*ARGSUSED*/
125vi_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 */
136protected el_action_t
137/*ARGSUSED*/
138vi_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 */
149protected el_action_t
150/*ARGSUSED*/
151vi_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 */
174protected el_action_t
175/*ARGSUSED*/
176vi_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 */
199protected el_action_t
200/*ARGSUSED*/
201vi_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 */
223protected el_action_t
224/*ARGSUSED*/
225vi_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 */
247protected el_action_t
248vi_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 */
278protected el_action_t
279/*ARGSUSED*/
280vi_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 */
295protected el_action_t
296/*ARGSUSED*/
297vi_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 */
311protected el_action_t
312/*ARGSUSED*/
313vi_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 */
330protected el_action_t
331/*ARGSUSED*/
332vi_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 */
346protected el_action_t
347/*ARGSUSED*/
348vi_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 */
361protected el_action_t
362/*ARGSUSED*/
363vi_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 */
379protected el_action_t
380/*ARGSUSED*/
381vi_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 */
397protected el_action_t
398/*ARGSUSED*/
399vi_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 */
412protected el_action_t
413/*ARGSUSED*/
414vi_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 */
437protected el_action_t
438/*ARGSUSED*/
439vi_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 */
453protected el_action_t
454/*ARGSUSED*/
455vi_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 */
466protected el_action_t
467/*ARGSUSED*/
468vi_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 */
490protected el_action_t
491/*ARGSUSED*/
492vi_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 */
514protected el_action_t
515/*ARGSUSED*/
516vi_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 */
541protected el_action_t
542/*ARGSUSED*/
543vi_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 */
566protected el_action_t
567vi_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 */
586protected el_action_t
587/*ARGSUSED*/
588vi_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 */
604protected el_action_t
605/*ARGSUSED*/
606vi_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 */
641protected el_action_t
642/*ARGSUSED*/
643vi_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 */
662protected el_action_t
663/*ARGSUSED*/
664vi_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 */
675protected el_action_t
676/*ARGSUSED*/
677vi_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 */
688protected el_action_t
689/*ARGSUSED*/
690vi_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*/
705protected el_action_t
706vi_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 */
722protected el_action_t
723/*ARGSUSED*/
724vi_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 */
734protected el_action_t
735/*ARGSUSED*/
736vi_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 */
746protected el_action_t
747/*ARGSUSED*/
748vi_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 */
758protected el_action_t
759/*ARGSUSED*/
760vi_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 */
770protected el_action_t
771/*ARGSUSED*/
772vi_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 */
784protected el_action_t
785/*ARGSUSED*/
786vi_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 */
802protected el_action_t
803/*ARGSUSED*/
804vi_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 */
849protected el_action_t
850/*ARGSUSED*/
851vi_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 */
863protected el_action_t
864/*ARGSUSED*/
865vi_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 */
877protected el_action_t
878/*ARGSUSED*/
879vi_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 */
891protected el_action_t
892/*ARGSUSED*/
893vi_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 */
903protected el_action_t
904/*ARGSUSED*/
905vi_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_reference
922__weakref_visible char *my_get_alias_text(const char *)
923    __weak_reference(get_alias_text);
924#endif
925protected el_action_t
926/*ARGSUSED*/
927vi_alias(EditLine *el __attribute__((__unused__)), Int c __attribute__((__unused__)))
928{
929#ifdef __weak_reference
930	char alias_name[3];
931	char *alias_text;
932
933	if (my_get_alias_text == 0) {
934		return CC_ERROR;
935	}
936
937	alias_name[0] = '_';
938	alias_name[2] = 0;
939	if (el_getc(el, &alias_name[1]) != 1)
940		return CC_ERROR;
941
942	alias_text = my_get_alias_text(alias_name);
943	if (alias_text != NULL)
944		FUN(el,push)(el, ct_decode_string(alias_text, &el->el_scratch));
945	return CC_NORM;
946#else
947	return CC_ERROR;
948#endif
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, Int 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, Int 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);
1050		if (st > 0) {
1051			len = (size_t)(el->el_line.lastchar -
1052			    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, Int 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 = 0;
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 == 0 || (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, Int 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