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