]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - lib/libedit/vi.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.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.25 2006/03/06 21:11: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                             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         int len = 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", 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, 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 +0u);
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                     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                     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 = el->el_line.cursor - el->el_line.buffer;
520         el->el_line.limit = un.buf + (el->el_line.limit - el->el_line.buffer);
521         el->el_line.buffer = un.buf;
522         el->el_line.cursor = un.buf + un.cursor;
523         el->el_line.lastchar = un.buf + un.len;
524
525         return (CC_REFRESH);
526 }
527
528
529 /* vi_command_mode():
530  *      Vi enter command mode (use alternative key bindings)
531  *      [<ESC>]
532  */
533 protected el_action_t
534 /*ARGSUSED*/
535 vi_command_mode(EditLine *el, int c __unused)
536 {
537
538         /* [Esc] cancels pending action */
539         el->el_chared.c_vcmd.action = NOP;
540         el->el_chared.c_vcmd.pos = 0;
541
542         el->el_state.doingarg = 0;
543
544         el->el_state.inputmode = MODE_INSERT;
545         el->el_map.current = el->el_map.alt;
546 #ifdef VI_MOVE
547         if (el->el_line.cursor > el->el_line.buffer)
548                 el->el_line.cursor--;
549 #endif
550         return (CC_CURSOR);
551 }
552
553
554 /* vi_zero():
555  *      Vi move to the beginning of line
556  *      [0]
557  */
558 protected el_action_t
559 vi_zero(EditLine *el, int c)
560 {
561
562         if (el->el_state.doingarg)
563                 return ed_argument_digit(el, c);
564
565         el->el_line.cursor = el->el_line.buffer;
566         if (el->el_chared.c_vcmd.action != NOP) {
567                 cv_delfini(el);
568                 return (CC_REFRESH);
569         }
570         return (CC_CURSOR);
571 }
572
573
574 /* vi_delete_prev_char():
575  *      Vi move to previous character (backspace)
576  *      [^H] in insert mode only
577  */
578 protected el_action_t
579 /*ARGSUSED*/
580 vi_delete_prev_char(EditLine *el, int c __unused)
581 {
582
583         if (el->el_line.cursor <= el->el_line.buffer)
584                 return (CC_ERROR);
585
586         c_delbefore1(el);
587         el->el_line.cursor--;
588         return (CC_REFRESH);
589 }
590
591
592 /* vi_list_or_eof():
593  *      Vi list choices for completion or indicate end of file if empty line
594  *      [^D]
595  */
596 protected el_action_t
597 /*ARGSUSED*/
598 vi_list_or_eof(EditLine *el, int c)
599 {
600
601         if (el->el_line.cursor == el->el_line.lastchar) {
602                 if (el->el_line.cursor == el->el_line.buffer) {
603                         term_writec(el, c);     /* then do a EOF */
604                         return (CC_EOF);
605                 } else {
606                         /*
607                          * Here we could list completions, but it is an
608                          * error right now
609                          */
610                         term_beep(el);
611                         return (CC_ERROR);
612                 }
613         } else {
614 #ifdef notyet
615                 re_goto_bottom(el);
616                 *el->el_line.lastchar = '\0';   /* just in case */
617                 return (CC_LIST_CHOICES);
618 #else
619                 /*
620                  * Just complain for now.
621                  */
622                 term_beep(el);
623                 return (CC_ERROR);
624 #endif
625         }
626 }
627
628
629 /* vi_kill_line_prev():
630  *      Vi cut from beginning of line to cursor
631  *      [^U]
632  */
633 protected el_action_t
634 /*ARGSUSED*/
635 vi_kill_line_prev(EditLine *el, int c __unused)
636 {
637         char *kp, *cp;
638
639         cp = el->el_line.buffer;
640         kp = el->el_chared.c_kill.buf;
641         while (cp < el->el_line.cursor)
642                 *kp++ = *cp++;  /* copy it */
643         el->el_chared.c_kill.last = kp;
644         c_delbefore(el, el->el_line.cursor - el->el_line.buffer);
645         el->el_line.cursor = el->el_line.buffer;        /* zap! */
646         return (CC_REFRESH);
647 }
648
649
650 /* vi_search_prev():
651  *      Vi search history previous
652  *      [?]
653  */
654 protected el_action_t
655 /*ARGSUSED*/
656 vi_search_prev(EditLine *el, int c __unused)
657 {
658
659         return (cv_search(el, ED_SEARCH_PREV_HISTORY));
660 }
661
662
663 /* vi_search_next():
664  *      Vi search history next
665  *      [/]
666  */
667 protected el_action_t
668 /*ARGSUSED*/
669 vi_search_next(EditLine *el, int c __unused)
670 {
671
672         return (cv_search(el, ED_SEARCH_NEXT_HISTORY));
673 }
674
675
676 /* vi_repeat_search_next():
677  *      Vi repeat current search in the same search direction
678  *      [n]
679  */
680 protected el_action_t
681 /*ARGSUSED*/
682 vi_repeat_search_next(EditLine *el, int c __unused)
683 {
684
685         if (el->el_search.patlen == 0)
686                 return (CC_ERROR);
687         else
688                 return (cv_repeat_srch(el, el->el_search.patdir));
689 }
690
691
692 /* vi_repeat_search_prev():
693  *      Vi repeat current search in the opposite search direction
694  *      [N]
695  */
696 /*ARGSUSED*/
697 protected el_action_t
698 vi_repeat_search_prev(EditLine *el, int c __unused)
699 {
700
701         if (el->el_search.patlen == 0)
702                 return (CC_ERROR);
703         else
704                 return (cv_repeat_srch(el,
705                     el->el_search.patdir == ED_SEARCH_PREV_HISTORY ?
706                     ED_SEARCH_NEXT_HISTORY : ED_SEARCH_PREV_HISTORY));
707 }
708
709
710 /* vi_next_char():
711  *      Vi move to the character specified next
712  *      [f]
713  */
714 protected el_action_t
715 /*ARGSUSED*/
716 vi_next_char(EditLine *el, int c __unused)
717 {
718         return cv_csearch(el, CHAR_FWD, -1, el->el_state.argument, 0);
719 }
720
721
722 /* vi_prev_char():
723  *      Vi move to the character specified previous
724  *      [F]
725  */
726 protected el_action_t
727 /*ARGSUSED*/
728 vi_prev_char(EditLine *el, int c __unused)
729 {
730         return cv_csearch(el, CHAR_BACK, -1, el->el_state.argument, 0);
731 }
732
733
734 /* vi_to_next_char():
735  *      Vi move up to the character specified next
736  *      [t]
737  */
738 protected el_action_t
739 /*ARGSUSED*/
740 vi_to_next_char(EditLine *el, int c __unused)
741 {
742         return cv_csearch(el, CHAR_FWD, -1, el->el_state.argument, 1);
743 }
744
745
746 /* vi_to_prev_char():
747  *      Vi move up to the character specified previous
748  *      [T]
749  */
750 protected el_action_t
751 /*ARGSUSED*/
752 vi_to_prev_char(EditLine *el, int c __unused)
753 {
754         return cv_csearch(el, CHAR_BACK, -1, el->el_state.argument, 1);
755 }
756
757
758 /* vi_repeat_next_char():
759  *      Vi repeat current character search in the same search direction
760  *      [;]
761  */
762 protected el_action_t
763 /*ARGSUSED*/
764 vi_repeat_next_char(EditLine *el, int c __unused)
765 {
766
767         return cv_csearch(el, el->el_search.chadir, el->el_search.chacha,
768                 el->el_state.argument, el->el_search.chatflg);
769 }
770
771
772 /* vi_repeat_prev_char():
773  *      Vi repeat current character search in the opposite search direction
774  *      [,]
775  */
776 protected el_action_t
777 /*ARGSUSED*/
778 vi_repeat_prev_char(EditLine *el, int c __unused)
779 {
780         el_action_t r;
781         int dir = el->el_search.chadir;
782
783         r = cv_csearch(el, -dir, el->el_search.chacha,
784                 el->el_state.argument, el->el_search.chatflg);
785         el->el_search.chadir = dir;
786         return r;
787 }
788
789
790 /* vi_match():
791  *      Vi go to matching () {} or []
792  *      [%]
793  */
794 protected el_action_t
795 /*ARGSUSED*/
796 vi_match(EditLine *el, int c)
797 {
798         const char match_chars[] = "()[]{}";
799         char *cp;
800         int delta, i, count;
801         char o_ch, c_ch;
802
803         *el->el_line.lastchar = '\0';           /* just in case */
804
805         i = strcspn(el->el_line.cursor, match_chars);
806         o_ch = el->el_line.cursor[i];
807         if (o_ch == 0)
808                 return CC_ERROR;
809         delta = strchr(match_chars, o_ch) - match_chars;
810         c_ch = match_chars[delta ^ 1];
811         count = 1;
812         delta = 1 - (delta & 1) * 2;
813
814         for (cp = &el->el_line.cursor[i]; count; ) {
815                 cp += delta;
816                 if (cp < el->el_line.buffer || cp >= el->el_line.lastchar)
817                         return CC_ERROR;
818                 if (*cp == o_ch)
819                         count++;
820                 else if (*cp == c_ch)
821                         count--;
822         }
823
824         el->el_line.cursor = cp;
825
826         if (el->el_chared.c_vcmd.action != NOP) {
827                 /* NB posix says char under cursor should NOT be deleted
828                    for -ve delta - this is different to netbsd vi. */
829                 if (delta > 0)
830                         el->el_line.cursor++;
831                 cv_delfini(el);
832                 return (CC_REFRESH);
833         }
834         return (CC_CURSOR);
835 }
836
837 /* vi_undo_line():
838  *      Vi undo all changes to line
839  *      [U]
840  */
841 protected el_action_t
842 /*ARGSUSED*/
843 vi_undo_line(EditLine *el, int c)
844 {
845
846         cv_undo(el);
847         return hist_get(el);
848 }
849
850 /* vi_to_column():
851  *      Vi go to specified column
852  *      [|]
853  * NB netbsd vi goes to screen column 'n', posix says nth character
854  */
855 protected el_action_t
856 /*ARGSUSED*/
857 vi_to_column(EditLine *el, int c)
858 {
859
860         el->el_line.cursor = el->el_line.buffer;
861         el->el_state.argument--;
862         return ed_next_char(el, 0);
863 }
864
865 /* vi_yank_end():
866  *      Vi yank to end of line
867  *      [Y]
868  */
869 protected el_action_t
870 /*ARGSUSED*/
871 vi_yank_end(EditLine *el, int c)
872 {
873
874         cv_yank(el, el->el_line.cursor,
875                 el->el_line.lastchar - el->el_line.cursor);
876         return CC_REFRESH;
877 }
878
879 /* vi_yank():
880  *      Vi yank
881  *      [y]
882  */
883 protected el_action_t
884 /*ARGSUSED*/
885 vi_yank(EditLine *el, int c)
886 {
887
888         return cv_action(el, YANK);
889 }
890
891 /* vi_comment_out():
892  *      Vi comment out current command
893  *      [#]
894  */
895 protected el_action_t
896 /*ARGSUSED*/
897 vi_comment_out(EditLine *el, int c)
898 {
899
900         el->el_line.cursor = el->el_line.buffer;
901         c_insert(el, 1);
902         *el->el_line.cursor = '#';
903         re_refresh(el);
904         return ed_newline(el, 0);
905 }
906
907 /* vi_alias():
908  *      Vi include shell alias
909  *      [@]
910  * NB: posix implies that we should enter insert mode, however
911  * this is against historical precedent...
912  */
913 protected el_action_t
914 /*ARGSUSED*/
915 vi_alias(EditLine *el, int c)
916 {
917 #ifdef __weak_extern
918         char alias_name[3];
919         char *alias_text;
920         extern char *get_alias_text(const char *);
921         __weak_extern(get_alias_text);
922
923         if (get_alias_text == 0) {
924                 return CC_ERROR;
925         }
926
927         alias_name[0] = '_';
928         alias_name[2] = 0;
929         if (el_getc(el, &alias_name[1]) != 1)
930                 return CC_ERROR;
931
932         alias_text = get_alias_text(alias_name);
933         if (alias_text != NULL)
934                 el_push(el, alias_text);
935         return CC_NORM;
936 #else
937         return CC_ERROR;
938 #endif
939 }
940
941 /* vi_to_history_line():
942  *      Vi go to specified history file line.
943  *      [G]
944  */
945 protected el_action_t
946 /*ARGSUSED*/
947 vi_to_history_line(EditLine *el, int c)
948 {
949         int sv_event_no = el->el_history.eventno;
950         el_action_t rval;
951
952
953         if (el->el_history.eventno == 0) {
954                  (void) strncpy(el->el_history.buf, el->el_line.buffer,
955                      EL_BUFSIZ);
956                  el->el_history.last = el->el_history.buf +
957                          (el->el_line.lastchar - el->el_line.buffer);
958         }
959
960         /* Lack of a 'count' means oldest, not 1 */
961         if (!el->el_state.doingarg) {
962                 el->el_history.eventno = 0x7fffffff;
963                 hist_get(el);
964         } else {
965                 /* This is brain dead, all the rest of this code counts
966                  * upwards going into the past.  Here we need count in the
967                  * other direction (to match the output of fc -l).
968                  * I could change the world, but this seems to suffice.
969                  */
970                 el->el_history.eventno = 1;
971                 if (hist_get(el) == CC_ERROR)
972                         return CC_ERROR;
973                 el->el_history.eventno = 1 + el->el_history.ev.num 
974                                         - el->el_state.argument;
975                 if (el->el_history.eventno < 0) {
976                         el->el_history.eventno = sv_event_no;
977                         return CC_ERROR;
978                 }
979         }
980         rval = hist_get(el);
981         if (rval == CC_ERROR)
982                 el->el_history.eventno = sv_event_no;
983         return rval;
984 }
985
986 /* vi_histedit():
987  *      Vi edit history line with vi
988  *      [v]
989  */
990 protected el_action_t
991 /*ARGSUSED*/
992 vi_histedit(EditLine *el, int c)
993 {
994         int fd;
995         pid_t pid;
996         int st;
997         char tempfile[] = "/tmp/histedit.XXXXXXXXXX";
998         char *cp;
999
1000         if (el->el_state.doingarg) {
1001                 if (vi_to_history_line(el, 0) == CC_ERROR)
1002                         return CC_ERROR;
1003         }
1004
1005         fd = mkstemp(tempfile);
1006         if (fd < 0)
1007                 return CC_ERROR;
1008         cp = el->el_line.buffer;
1009         write(fd, cp, el->el_line.lastchar - cp +0u);
1010         write(fd, "\n", 1);
1011         pid = fork();
1012         switch (pid) {
1013         case -1:
1014                 close(fd);
1015                 unlink(tempfile);
1016                 return CC_ERROR;
1017         case 0:
1018                 close(fd);
1019                 execlp("vi", "vi", tempfile, NULL);
1020                 exit(0);
1021                 /*NOTREACHED*/
1022         default:
1023                 while (waitpid(pid, &st, 0) != pid)
1024                         continue;
1025                 lseek(fd, 0ll, SEEK_SET);
1026                 st = read(fd, cp, el->el_line.limit - cp +0u);
1027                 if (st > 0 && cp[st - 1] == '\n')
1028                         st--;
1029                 el->el_line.cursor = cp;
1030                 el->el_line.lastchar = cp + st;
1031                 break;
1032         }
1033
1034         close(fd);
1035         unlink(tempfile);
1036         /* return CC_REFRESH; */
1037         return ed_newline(el, 0);
1038 }
1039
1040 /* vi_history_word():
1041  *      Vi append word from previous input line
1042  *      [_]
1043  * Who knows where this one came from!
1044  * '_' in vi means 'entire current line', so 'cc' is a synonym for 'c_'
1045  */
1046 protected el_action_t
1047 /*ARGSUSED*/
1048 vi_history_word(EditLine *el, int c)
1049 {
1050         const char *wp = HIST_FIRST(el);
1051         const char *wep, *wsp;
1052         int len;
1053         char *cp;
1054         const char *lim;
1055
1056         if (wp == NULL)
1057                 return CC_ERROR;
1058
1059         wep = wsp = 0;
1060         do {
1061                 while (isspace((unsigned char)*wp))
1062                         wp++;
1063                 if (*wp == 0)
1064                         break;
1065                 wsp = wp;
1066                 while (*wp && !isspace((unsigned char)*wp))
1067                         wp++;
1068                 wep = wp;
1069         } while ((!el->el_state.doingarg || --el->el_state.argument > 0) && *wp != 0);
1070
1071         if (wsp == 0 || (el->el_state.doingarg && el->el_state.argument != 0))
1072                 return CC_ERROR;
1073
1074         cv_undo(el);
1075         len = wep - wsp;
1076         if (el->el_line.cursor < el->el_line.lastchar)
1077                 el->el_line.cursor++;
1078         c_insert(el, len + 1);
1079         cp = el->el_line.cursor;
1080         lim = el->el_line.limit;
1081         if (cp < lim)
1082                 *cp++ = ' ';
1083         while (wsp < wep && cp < lim)
1084                 *cp++ = *wsp++;
1085         el->el_line.cursor = cp;
1086
1087         el->el_map.current = el->el_map.key;
1088         return CC_REFRESH;
1089 }
1090
1091 /* vi_redo():
1092  *      Vi redo last non-motion command
1093  *      [.]
1094  */
1095 protected el_action_t
1096 /*ARGSUSED*/
1097 vi_redo(EditLine *el, int c)
1098 {
1099         c_redo_t *r = &el->el_chared.c_redo;
1100
1101         if (!el->el_state.doingarg && r->count) {
1102                 el->el_state.doingarg = 1;
1103                 el->el_state.argument = r->count;
1104         }
1105
1106         el->el_chared.c_vcmd.pos = el->el_line.cursor;
1107         el->el_chared.c_vcmd.action = r->action;
1108         if (r->pos != r->buf) {
1109                 if (r->pos + 1 > r->lim)
1110                         /* sanity */
1111                         r->pos = r->lim - 1;
1112                 r->pos[0] = 0;
1113                 el_push(el, r->buf);
1114         }
1115
1116         el->el_state.thiscmd = r->cmd;
1117         el->el_state.thisch = r->ch;
1118         return  (*el->el_map.func[r->cmd])(el, r->ch);
1119 }