1 /* $NetBSD: vi.c,v 1.55 2016/03/02 19:24:20 christos Exp $ */
4 * Copyright (c) 1992, 1993
5 * The Regents of the University of California. All rights reserved.
7 * This code is derived from software contributed to Berkeley by
8 * Christos Zoulas of Cornell University.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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.
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
36 #if !defined(lint) && !defined(SCCSID)
38 static char sccsid[] = "@(#)vi.c 8.1 (Berkeley) 6/4/93";
40 __RCSID("$NetBSD: vi.c,v 1.55 2016/03/02 19:24:20 christos Exp $");
42 #endif /* not lint && not SCCSID */
43 #include <sys/cdefs.h>
44 __FBSDID("$FreeBSD$");
47 * vi.c: Vi mode commands.
61 private el_action_t cv_action(EditLine *, wint_t);
62 private el_action_t cv_paste(EditLine *, wint_t);
68 cv_action(EditLine *el, wint_t c)
71 if (el->el_chared.c_vcmd.action != NOP) {
72 /* 'cc', 'dd' and (possibly) friends */
73 if (c != (wint_t)el->el_chared.c_vcmd.action)
78 cv_yank(el, el->el_line.buffer,
79 (int)(el->el_line.lastchar - el->el_line.buffer));
80 el->el_chared.c_vcmd.action = NOP;
81 el->el_chared.c_vcmd.pos = 0;
83 el->el_line.lastchar = el->el_line.buffer;
84 el->el_line.cursor = el->el_line.buffer;
87 el->el_map.current = el->el_map.key;
91 el->el_chared.c_vcmd.pos = el->el_line.cursor;
92 el->el_chared.c_vcmd.action = c;
97 * Paste previous deletion before or after the cursor
100 cv_paste(EditLine *el, wint_t c)
102 c_kill_t *k = &el->el_chared.c_kill;
103 size_t len = (size_t)(k->last - k->buf);
105 if (k->buf == NULL || len == 0)
108 (void) fprintf(el->el_errfile, "Paste: \"" FSTARSTR "\"\n", (int)len,
114 if (!c && el->el_line.cursor < el->el_line.lastchar)
115 el->el_line.cursor++;
117 c_insert(el, (int)len);
118 if (el->el_line.cursor + len > el->el_line.lastchar)
120 (void) memcpy(el->el_line.cursor, k->buf, len *
121 sizeof(*el->el_line.cursor));
128 * Vi paste previous deletion to the right of the cursor
131 protected el_action_t
133 vi_paste_next(EditLine *el, wint_t c __attribute__((__unused__)))
136 return cv_paste(el, 0);
141 * Vi paste previous deletion to the left of the cursor
144 protected el_action_t
146 vi_paste_prev(EditLine *el, wint_t c __attribute__((__unused__)))
149 return cv_paste(el, 1);
153 /* vi_prev_big_word():
154 * Vi move to the previous space delimited word
157 protected el_action_t
159 vi_prev_big_word(EditLine *el, wint_t c __attribute__((__unused__)))
162 if (el->el_line.cursor == el->el_line.buffer)
165 el->el_line.cursor = cv_prev_word(el->el_line.cursor,
167 el->el_state.argument,
170 if (el->el_chared.c_vcmd.action != NOP) {
179 * Vi move to the previous word
182 protected el_action_t
184 vi_prev_word(EditLine *el, wint_t c __attribute__((__unused__)))
187 if (el->el_line.cursor == el->el_line.buffer)
190 el->el_line.cursor = cv_prev_word(el->el_line.cursor,
192 el->el_state.argument,
195 if (el->el_chared.c_vcmd.action != NOP) {
203 /* vi_next_big_word():
204 * Vi move to the next space delimited word
207 protected el_action_t
209 vi_next_big_word(EditLine *el, wint_t c __attribute__((__unused__)))
212 if (el->el_line.cursor >= el->el_line.lastchar - 1)
215 el->el_line.cursor = cv_next_word(el, el->el_line.cursor,
216 el->el_line.lastchar, el->el_state.argument, cv__isWord);
218 if (el->el_map.type == MAP_VI)
219 if (el->el_chared.c_vcmd.action != NOP) {
228 * Vi move to the next word
231 protected el_action_t
233 vi_next_word(EditLine *el, wint_t c __attribute__((__unused__)))
236 if (el->el_line.cursor >= el->el_line.lastchar - 1)
239 el->el_line.cursor = cv_next_word(el, el->el_line.cursor,
240 el->el_line.lastchar, el->el_state.argument, cv__isword);
242 if (el->el_map.type == MAP_VI)
243 if (el->el_chared.c_vcmd.action != NOP) {
252 * Vi change case of character under the cursor and advance one character
255 protected el_action_t
256 vi_change_case(EditLine *el, wint_t c)
260 if (el->el_line.cursor >= el->el_line.lastchar)
263 for (i = 0; i < el->el_state.argument; i++) {
265 c = *el->el_line.cursor;
267 *el->el_line.cursor = Tolower(c);
269 *el->el_line.cursor = Toupper(c);
271 if (++el->el_line.cursor >= el->el_line.lastchar) {
272 el->el_line.cursor--;
283 * Vi change prefix command
286 protected el_action_t
288 vi_change_meta(EditLine *el, wint_t c __attribute__((__unused__)))
292 * Delete with insert == change: first we delete and then we leave in
295 return cv_action(el, DELETE | INSERT);
299 /* vi_insert_at_bol():
300 * Vi enter insert mode at the beginning of line
303 protected el_action_t
305 vi_insert_at_bol(EditLine *el, wint_t c __attribute__((__unused__)))
308 el->el_line.cursor = el->el_line.buffer;
310 el->el_map.current = el->el_map.key;
315 /* vi_replace_char():
316 * Vi replace character under the cursor with the next character typed
319 protected el_action_t
321 vi_replace_char(EditLine *el, wint_t c __attribute__((__unused__)))
324 if (el->el_line.cursor >= el->el_line.lastchar)
327 el->el_map.current = el->el_map.key;
328 el->el_state.inputmode = MODE_REPLACE_1;
334 /* vi_replace_mode():
335 * Vi enter replace mode
338 protected el_action_t
340 vi_replace_mode(EditLine *el, wint_t c __attribute__((__unused__)))
343 el->el_map.current = el->el_map.key;
344 el->el_state.inputmode = MODE_REPLACE;
350 /* vi_substitute_char():
351 * Vi replace character under the cursor and enter insert mode
354 protected el_action_t
356 vi_substitute_char(EditLine *el, wint_t c __attribute__((__unused__)))
359 c_delafter(el, el->el_state.argument);
360 el->el_map.current = el->el_map.key;
365 /* vi_substitute_line():
366 * Vi substitute entire line
369 protected el_action_t
371 vi_substitute_line(EditLine *el, wint_t c __attribute__((__unused__)))
375 cv_yank(el, el->el_line.buffer,
376 (int)(el->el_line.lastchar - el->el_line.buffer));
377 (void) em_kill_line(el, 0);
378 el->el_map.current = el->el_map.key;
383 /* vi_change_to_eol():
384 * Vi change to end of line
387 protected el_action_t
389 vi_change_to_eol(EditLine *el, wint_t c __attribute__((__unused__)))
393 cv_yank(el, el->el_line.cursor,
394 (int)(el->el_line.lastchar - el->el_line.cursor));
395 (void) ed_kill_line(el, 0);
396 el->el_map.current = el->el_map.key;
402 * Vi enter insert mode
405 protected el_action_t
407 vi_insert(EditLine *el, wint_t c __attribute__((__unused__)))
410 el->el_map.current = el->el_map.key;
417 * Vi enter insert mode after the cursor
420 protected el_action_t
422 vi_add(EditLine *el, wint_t c __attribute__((__unused__)))
426 el->el_map.current = el->el_map.key;
427 if (el->el_line.cursor < el->el_line.lastchar) {
428 el->el_line.cursor++;
429 if (el->el_line.cursor > el->el_line.lastchar)
430 el->el_line.cursor = el->el_line.lastchar;
437 return (el_action_t)ret;
442 * Vi enter insert mode at end of line
445 protected el_action_t
447 vi_add_at_eol(EditLine *el, wint_t c __attribute__((__unused__)))
450 el->el_map.current = el->el_map.key;
451 el->el_line.cursor = el->el_line.lastchar;
458 * Vi delete prefix command
461 protected el_action_t
463 vi_delete_meta(EditLine *el, wint_t c __attribute__((__unused__)))
466 return cv_action(el, DELETE);
470 /* vi_end_big_word():
471 * Vi move to the end of the current space delimited word
474 protected el_action_t
476 vi_end_big_word(EditLine *el, wint_t c __attribute__((__unused__)))
479 if (el->el_line.cursor == el->el_line.lastchar)
482 el->el_line.cursor = cv__endword(el->el_line.cursor,
483 el->el_line.lastchar, el->el_state.argument, cv__isWord);
485 if (el->el_chared.c_vcmd.action != NOP) {
486 el->el_line.cursor++;
495 * Vi move to the end of the current word
498 protected el_action_t
500 vi_end_word(EditLine *el, wint_t c __attribute__((__unused__)))
503 if (el->el_line.cursor == el->el_line.lastchar)
506 el->el_line.cursor = cv__endword(el->el_line.cursor,
507 el->el_line.lastchar, el->el_state.argument, cv__isword);
509 if (el->el_chared.c_vcmd.action != NOP) {
510 el->el_line.cursor++;
519 * Vi undo last change
522 protected el_action_t
524 vi_undo(EditLine *el, wint_t c __attribute__((__unused__)))
526 c_undo_t un = el->el_chared.c_undo;
531 /* switch line buffer and undo buffer */
532 el->el_chared.c_undo.buf = el->el_line.buffer;
533 el->el_chared.c_undo.len = el->el_line.lastchar - el->el_line.buffer;
534 el->el_chared.c_undo.cursor =
535 (int)(el->el_line.cursor - el->el_line.buffer);
536 el->el_line.limit = un.buf + (el->el_line.limit - el->el_line.buffer);
537 el->el_line.buffer = un.buf;
538 el->el_line.cursor = un.buf + un.cursor;
539 el->el_line.lastchar = un.buf + un.len;
545 /* vi_command_mode():
546 * Vi enter command mode (use alternative key bindings)
549 protected el_action_t
551 vi_command_mode(EditLine *el, wint_t c __attribute__((__unused__)))
554 /* [Esc] cancels pending action */
555 el->el_chared.c_vcmd.action = NOP;
556 el->el_chared.c_vcmd.pos = 0;
558 el->el_state.doingarg = 0;
560 el->el_state.inputmode = MODE_INSERT;
561 el->el_map.current = el->el_map.alt;
563 if (el->el_line.cursor > el->el_line.buffer)
564 el->el_line.cursor--;
571 * Vi move to the beginning of line
574 protected el_action_t
575 vi_zero(EditLine *el, wint_t c)
578 if (el->el_state.doingarg)
579 return ed_argument_digit(el, c);
581 el->el_line.cursor = el->el_line.buffer;
582 if (el->el_chared.c_vcmd.action != NOP) {
590 /* vi_delete_prev_char():
591 * Vi move to previous character (backspace)
592 * [^H] in insert mode only
594 protected el_action_t
596 vi_delete_prev_char(EditLine *el, wint_t c __attribute__((__unused__)))
599 if (el->el_line.cursor <= el->el_line.buffer)
603 el->el_line.cursor--;
609 * Vi list choices for completion or indicate end of file if empty line
612 protected el_action_t
614 vi_list_or_eof(EditLine *el, wint_t c)
617 if (el->el_line.cursor == el->el_line.lastchar) {
618 if (el->el_line.cursor == el->el_line.buffer) {
619 terminal_writec(el, c); /* then do a EOF */
623 * Here we could list completions, but it is an
632 *el->el_line.lastchar = '\0'; /* just in case */
633 return CC_LIST_CHOICES;
636 * Just complain for now.
645 /* vi_kill_line_prev():
646 * Vi cut from beginning of line to cursor
649 protected el_action_t
651 vi_kill_line_prev(EditLine *el, wint_t c __attribute__((__unused__)))
655 cp = el->el_line.buffer;
656 kp = el->el_chared.c_kill.buf;
657 while (cp < el->el_line.cursor)
658 *kp++ = *cp++; /* copy it */
659 el->el_chared.c_kill.last = kp;
660 c_delbefore(el, (int)(el->el_line.cursor - el->el_line.buffer));
661 el->el_line.cursor = el->el_line.buffer; /* zap! */
667 * Vi search history previous
670 protected el_action_t
672 vi_search_prev(EditLine *el, wint_t c __attribute__((__unused__)))
675 return cv_search(el, ED_SEARCH_PREV_HISTORY);
680 * Vi search history next
683 protected el_action_t
685 vi_search_next(EditLine *el, wint_t c __attribute__((__unused__)))
688 return cv_search(el, ED_SEARCH_NEXT_HISTORY);
692 /* vi_repeat_search_next():
693 * Vi repeat current search in the same search direction
696 protected el_action_t
698 vi_repeat_search_next(EditLine *el, wint_t c __attribute__((__unused__)))
701 if (el->el_search.patlen == 0)
704 return cv_repeat_srch(el, el->el_search.patdir);
708 /* vi_repeat_search_prev():
709 * Vi repeat current search in the opposite search direction
713 protected el_action_t
714 vi_repeat_search_prev(EditLine *el, wint_t c __attribute__((__unused__)))
717 if (el->el_search.patlen == 0)
720 return (cv_repeat_srch(el,
721 el->el_search.patdir == ED_SEARCH_PREV_HISTORY ?
722 ED_SEARCH_NEXT_HISTORY : ED_SEARCH_PREV_HISTORY));
727 * Vi move to the character specified next
730 protected el_action_t
732 vi_next_char(EditLine *el, wint_t c __attribute__((__unused__)))
734 return cv_csearch(el, CHAR_FWD, -1, el->el_state.argument, 0);
739 * Vi move to the character specified previous
742 protected el_action_t
744 vi_prev_char(EditLine *el, wint_t c __attribute__((__unused__)))
746 return cv_csearch(el, CHAR_BACK, -1, el->el_state.argument, 0);
750 /* vi_to_next_char():
751 * Vi move up to the character specified next
754 protected el_action_t
756 vi_to_next_char(EditLine *el, wint_t c __attribute__((__unused__)))
758 return cv_csearch(el, CHAR_FWD, -1, el->el_state.argument, 1);
762 /* vi_to_prev_char():
763 * Vi move up to the character specified previous
766 protected el_action_t
768 vi_to_prev_char(EditLine *el, wint_t c __attribute__((__unused__)))
770 return cv_csearch(el, CHAR_BACK, -1, el->el_state.argument, 1);
774 /* vi_repeat_next_char():
775 * Vi repeat current character search in the same search direction
778 protected el_action_t
780 vi_repeat_next_char(EditLine *el, wint_t c __attribute__((__unused__)))
783 return cv_csearch(el, el->el_search.chadir, el->el_search.chacha,
784 el->el_state.argument, el->el_search.chatflg);
788 /* vi_repeat_prev_char():
789 * Vi repeat current character search in the opposite search direction
792 protected el_action_t
794 vi_repeat_prev_char(EditLine *el, wint_t c __attribute__((__unused__)))
797 int dir = el->el_search.chadir;
799 r = cv_csearch(el, -dir, el->el_search.chacha,
800 el->el_state.argument, el->el_search.chatflg);
801 el->el_search.chadir = dir;
807 * Vi go to matching () {} or []
810 protected el_action_t
812 vi_match(EditLine *el, wint_t c __attribute__((__unused__)))
814 const Char match_chars[] = STR("()[]{}");
816 size_t delta, i, count;
819 *el->el_line.lastchar = '\0'; /* just in case */
821 i = Strcspn(el->el_line.cursor, match_chars);
822 o_ch = el->el_line.cursor[i];
825 delta = (size_t)(Strchr(match_chars, o_ch) - match_chars);
826 c_ch = match_chars[delta ^ 1];
828 delta = 1 - (delta & 1) * 2;
830 for (cp = &el->el_line.cursor[i]; count; ) {
832 if (cp < el->el_line.buffer || cp >= el->el_line.lastchar)
836 else if (*cp == c_ch)
840 el->el_line.cursor = cp;
842 if (el->el_chared.c_vcmd.action != NOP) {
843 /* NB posix says char under cursor should NOT be deleted
844 for -ve delta - this is different to netbsd vi. */
846 el->el_line.cursor++;
854 * Vi undo all changes to line
857 protected el_action_t
859 vi_undo_line(EditLine *el, wint_t c __attribute__((__unused__)))
867 * Vi go to specified column
869 * NB netbsd vi goes to screen column 'n', posix says nth character
871 protected el_action_t
873 vi_to_column(EditLine *el, wint_t c __attribute__((__unused__)))
876 el->el_line.cursor = el->el_line.buffer;
877 el->el_state.argument--;
878 return ed_next_char(el, 0);
882 * Vi yank to end of line
885 protected el_action_t
887 vi_yank_end(EditLine *el, wint_t c __attribute__((__unused__)))
890 cv_yank(el, el->el_line.cursor,
891 (int)(el->el_line.lastchar - el->el_line.cursor));
899 protected el_action_t
901 vi_yank(EditLine *el, wint_t c __attribute__((__unused__)))
904 return cv_action(el, YANK);
908 * Vi comment out current command
911 protected el_action_t
913 vi_comment_out(EditLine *el, wint_t c __attribute__((__unused__)))
916 el->el_line.cursor = el->el_line.buffer;
918 *el->el_line.cursor = '#';
920 return ed_newline(el, 0);
924 * Vi include shell alias
926 * NB: posix implies that we should enter insert mode, however
927 * this is against historical precedent...
929 protected el_action_t
931 vi_alias(EditLine *el, wint_t c __attribute__((__unused__)))
934 const char *alias_text;
936 if (el->el_chared.c_aliasfun == NULL)
941 if (el_getc(el, &alias_name[1]) != 1)
944 alias_text = (*el->el_chared.c_aliasfun)(el->el_chared.c_aliasarg,
946 if (alias_text != NULL)
947 FUN(el,push)(el, ct_decode_string(alias_text, &el->el_scratch));
951 /* vi_to_history_line():
952 * Vi go to specified history file line.
955 protected el_action_t
957 vi_to_history_line(EditLine *el, wint_t c __attribute__((__unused__)))
959 int sv_event_no = el->el_history.eventno;
963 if (el->el_history.eventno == 0) {
964 (void) Strncpy(el->el_history.buf, el->el_line.buffer,
966 el->el_history.last = el->el_history.buf +
967 (el->el_line.lastchar - el->el_line.buffer);
970 /* Lack of a 'count' means oldest, not 1 */
971 if (!el->el_state.doingarg) {
972 el->el_history.eventno = 0x7fffffff;
975 /* This is brain dead, all the rest of this code counts
976 * upwards going into the past. Here we need count in the
977 * other direction (to match the output of fc -l).
978 * I could change the world, but this seems to suffice.
980 el->el_history.eventno = 1;
981 if (hist_get(el) == CC_ERROR)
983 el->el_history.eventno = 1 + el->el_history.ev.num
984 - el->el_state.argument;
985 if (el->el_history.eventno < 0) {
986 el->el_history.eventno = sv_event_no;
991 if (rval == CC_ERROR)
992 el->el_history.eventno = sv_event_no;
997 * Vi edit history line with vi
1000 protected el_action_t
1002 vi_histedit(EditLine *el, wint_t c __attribute__((__unused__)))
1008 char tempfile[] = "/tmp/histedit.XXXXXXXXXX";
1013 if (el->el_state.doingarg) {
1014 if (vi_to_history_line(el, 0) == CC_ERROR)
1018 fd = mkstemp(tempfile);
1021 len = (size_t)(el->el_line.lastchar - el->el_line.buffer);
1022 #define TMP_BUFSIZ (EL_BUFSIZ * MB_LEN_MAX)
1023 cp = el_malloc(TMP_BUFSIZ * sizeof(*cp));
1026 line = el_malloc(len * sizeof(*line) + 1);
1029 Strncpy(line, el->el_line.buffer, len);
1031 ct_wcstombs(cp, line, TMP_BUFSIZ - 1);
1032 cp[TMP_BUFSIZ - 1] = '\0';
1035 write(fd, "\n", (size_t)1);
1042 execlp("vi", "vi", tempfile, (char *)NULL);
1046 while (waitpid(pid, &status, 0) != pid)
1048 lseek(fd, (off_t)0, SEEK_SET);
1049 st = read(fd, cp, TMP_BUFSIZ - 1);
1052 len = (size_t)(el->el_line.limit - el->el_line.buffer);
1053 len = ct_mbstowcs(el->el_line.buffer, cp, len);
1054 if (len > 0 && el->el_line.buffer[len - 1] == '\n')
1059 el->el_line.cursor = el->el_line.buffer;
1060 el->el_line.lastchar = el->el_line.buffer + len;
1068 /* return CC_REFRESH; */
1069 return ed_newline(el, 0);
1078 /* vi_history_word():
1079 * Vi append word from previous input line
1081 * Who knows where this one came from!
1082 * '_' in vi means 'entire current line', so 'cc' is a synonym for 'c_'
1084 protected el_action_t
1086 vi_history_word(EditLine *el, wint_t c __attribute__((__unused__)))
1088 const Char *wp = HIST_FIRST(el);
1089 const Char *wep, *wsp;
1099 while (Isspace(*wp))
1104 while (*wp && !Isspace(*wp))
1107 } while ((!el->el_state.doingarg || --el->el_state.argument > 0)
1110 if (wsp == NULL || (el->el_state.doingarg && el->el_state.argument != 0))
1114 len = (int)(wep - wsp);
1115 if (el->el_line.cursor < el->el_line.lastchar)
1116 el->el_line.cursor++;
1117 c_insert(el, len + 1);
1118 cp = el->el_line.cursor;
1119 lim = el->el_line.limit;
1122 while (wsp < wep && cp < lim)
1124 el->el_line.cursor = cp;
1126 el->el_map.current = el->el_map.key;
1131 * Vi redo last non-motion command
1134 protected el_action_t
1136 vi_redo(EditLine *el, wint_t c __attribute__((__unused__)))
1138 c_redo_t *r = &el->el_chared.c_redo;
1140 if (!el->el_state.doingarg && r->count) {
1141 el->el_state.doingarg = 1;
1142 el->el_state.argument = r->count;
1145 el->el_chared.c_vcmd.pos = el->el_line.cursor;
1146 el->el_chared.c_vcmd.action = r->action;
1147 if (r->pos != r->buf) {
1148 if (r->pos + 1 > r->lim)
1150 r->pos = r->lim - 1;
1152 FUN(el,push)(el, r->buf);
1155 el->el_state.thiscmd = r->cmd;
1156 el->el_state.thisch = r->ch;
1157 return (*el->el_map.func[r->cmd])(el, r->ch);