]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libedit/vi.c
Remove spurious newline
[FreeBSD/FreeBSD.git] / lib / libedit / vi.c
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
38 static 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$");
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
61 private el_action_t     cv_action(EditLine *, wint_t);
62 private el_action_t     cv_paste(EditLine *, wint_t);
63
64 /* cv_action():
65  *      Handle vi actions.
66  */
67 private el_action_t
68 cv_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  */
99 private el_action_t
100 cv_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  */
131 protected el_action_t
132 /*ARGSUSED*/
133 vi_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  */
144 protected el_action_t
145 /*ARGSUSED*/
146 vi_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  */
157 protected el_action_t
158 /*ARGSUSED*/
159 vi_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  */
182 protected el_action_t
183 /*ARGSUSED*/
184 vi_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  */
207 protected el_action_t
208 /*ARGSUSED*/
209 vi_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  */
231 protected el_action_t
232 /*ARGSUSED*/
233 vi_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  */
255 protected el_action_t
256 vi_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  */
286 protected el_action_t
287 /*ARGSUSED*/
288 vi_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  */
303 protected el_action_t
304 /*ARGSUSED*/
305 vi_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  */
319 protected el_action_t
320 /*ARGSUSED*/
321 vi_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  */
338 protected el_action_t
339 /*ARGSUSED*/
340 vi_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  */
354 protected el_action_t
355 /*ARGSUSED*/
356 vi_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  */
369 protected el_action_t
370 /*ARGSUSED*/
371 vi_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  */
387 protected el_action_t
388 /*ARGSUSED*/
389 vi_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  */
405 protected el_action_t
406 /*ARGSUSED*/
407 vi_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  */
420 protected el_action_t
421 /*ARGSUSED*/
422 vi_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  */
445 protected el_action_t
446 /*ARGSUSED*/
447 vi_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  */
461 protected el_action_t
462 /*ARGSUSED*/
463 vi_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  */
474 protected el_action_t
475 /*ARGSUSED*/
476 vi_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  */
498 protected el_action_t
499 /*ARGSUSED*/
500 vi_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  */
522 protected el_action_t
523 /*ARGSUSED*/
524 vi_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  */
549 protected el_action_t
550 /*ARGSUSED*/
551 vi_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  */
574 protected el_action_t
575 vi_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  */
594 protected el_action_t
595 /*ARGSUSED*/
596 vi_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  */
612 protected el_action_t
613 /*ARGSUSED*/
614 vi_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  */
649 protected el_action_t
650 /*ARGSUSED*/
651 vi_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  */
670 protected el_action_t
671 /*ARGSUSED*/
672 vi_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  */
683 protected el_action_t
684 /*ARGSUSED*/
685 vi_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  */
696 protected el_action_t
697 /*ARGSUSED*/
698 vi_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*/
713 protected el_action_t
714 vi_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  */
730 protected el_action_t
731 /*ARGSUSED*/
732 vi_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  */
742 protected el_action_t
743 /*ARGSUSED*/
744 vi_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  */
754 protected el_action_t
755 /*ARGSUSED*/
756 vi_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  */
766 protected el_action_t
767 /*ARGSUSED*/
768 vi_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  */
778 protected el_action_t
779 /*ARGSUSED*/
780 vi_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  */
792 protected el_action_t
793 /*ARGSUSED*/
794 vi_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  */
810 protected el_action_t
811 /*ARGSUSED*/
812 vi_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  */
857 protected el_action_t
858 /*ARGSUSED*/
859 vi_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  */
871 protected el_action_t
872 /*ARGSUSED*/
873 vi_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  */
885 protected el_action_t
886 /*ARGSUSED*/
887 vi_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  */
899 protected el_action_t
900 /*ARGSUSED*/
901 vi_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  */
911 protected el_action_t
912 /*ARGSUSED*/
913 vi_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  */
929 protected el_action_t
930 /*ARGSUSED*/
931 vi_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  */
955 protected el_action_t
956 /*ARGSUSED*/
957 vi_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  */
1000 protected el_action_t
1001 /*ARGSUSED*/
1002 vi_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);
1070 error:
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  */
1084 protected el_action_t
1085 /*ARGSUSED*/
1086 vi_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  */
1134 protected el_action_t
1135 /*ARGSUSED*/
1136 vi_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 }