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