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