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