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