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