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