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