xref: /openbsd/gnu/lib/libreadline/vi_mode.c (revision 9704b281)
1 /* vi_mode.c -- A vi emulation mode for Bash.
2    Derived from code written by Jeff Sparkes (jsparkes@bnr.ca).  */
3 
4 /* Copyright (C) 1987, 1989, 1992 Free Software Foundation, Inc.
5 
6    This file is part of the GNU Readline Library, a library for
7    reading lines of text with interactive input and history editing.
8 
9    The GNU Readline Library is free software; you can redistribute it
10    and/or modify it under the terms of the GNU General Public License
11    as published by the Free Software Foundation; either version 2, or
12    (at your option) any later version.
13 
14    The GNU Readline Library is distributed in the hope that it will be
15    useful, but WITHOUT ANY WARRANTY; without even the implied warranty
16    of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18 
19    The GNU General Public License is often shipped with GNU software, and
20    is generally kept in a file called COPYING or LICENSE.  If you do not
21    have a copy of the license, write to the Free Software Foundation,
22    59 Temple Place, Suite 330, Boston, MA 02111 USA. */
23 #define READLINE_LIBRARY
24 
25 /* **************************************************************** */
26 /*								    */
27 /*			VI Emulation Mode			    */
28 /*								    */
29 /* **************************************************************** */
30 #include "rlconf.h"
31 
32 #if defined (VI_MODE)
33 
34 #if defined (HAVE_CONFIG_H)
35 #  include <config.h>
36 #endif
37 
38 #include <sys/types.h>
39 
40 #if defined (HAVE_STDLIB_H)
41 #  include <stdlib.h>
42 #else
43 #  include "ansi_stdlib.h"
44 #endif /* HAVE_STDLIB_H */
45 
46 #if defined (HAVE_UNISTD_H)
47 #  include <unistd.h>
48 #endif
49 
50 #include <stdio.h>
51 
52 /* Some standard library routines. */
53 #include "rldefs.h"
54 #include "rlmbutil.h"
55 
56 #include "readline.h"
57 #include "history.h"
58 
59 #include "rlprivate.h"
60 #include "xmalloc.h"
61 
62 #ifndef member
63 #define member(c, s) ((c) ? (char *)strchr ((s), (c)) != (char *)NULL : 0)
64 #endif
65 
66 /* Non-zero means enter insertion mode. */
67 static int _rl_vi_doing_insert;
68 
69 /* Command keys which do movement for xxx_to commands. */
70 static const char *vi_motion = " hl^$0ftFT;,%wbeWBE|";
71 
72 /* Keymap used for vi replace characters.  Created dynamically since
73    rarely used. */
74 static Keymap vi_replace_map;
75 
76 /* The number of characters inserted in the last replace operation. */
77 static int vi_replace_count;
78 
79 /* If non-zero, we have text inserted after a c[motion] command that put
80    us implicitly into insert mode.  Some people want this text to be
81    attached to the command so that it is `redoable' with `.'. */
82 static int vi_continued_command;
83 static char *vi_insert_buffer;
84 static int vi_insert_buffer_size;
85 
86 static int _rl_vi_last_command = 'i';	/* default `.' puts you in insert mode */
87 static int _rl_vi_last_repeat = 1;
88 static int _rl_vi_last_arg_sign = 1;
89 static int _rl_vi_last_motion;
90 #if defined (HANDLE_MULTIBYTE)
91 static char _rl_vi_last_search_mbchar[MB_LEN_MAX];
92 #else
93 static int _rl_vi_last_search_char;
94 #endif
95 static int _rl_vi_last_replacement;
96 
97 static int _rl_vi_last_key_before_insert;
98 
99 static int vi_redoing;
100 
101 /* Text modification commands.  These are the `redoable' commands. */
102 static const char *vi_textmod = "_*\\AaIiCcDdPpYyRrSsXx~";
103 
104 /* Arrays for the saved marks. */
105 static int vi_mark_chars['z' - 'a' + 1];
106 
107 static void _rl_vi_stuff_insert PARAMS((int));
108 static void _rl_vi_save_insert PARAMS((UNDO_LIST *));
109 static int rl_digit_loop1 PARAMS((void));
110 
111 void
_rl_vi_initialize_line()112 _rl_vi_initialize_line ()
113 {
114   register int i;
115 
116   for (i = 0; i < sizeof (vi_mark_chars) / sizeof (int); i++)
117     vi_mark_chars[i] = -1;
118 }
119 
120 void
_rl_vi_reset_last()121 _rl_vi_reset_last ()
122 {
123   _rl_vi_last_command = 'i';
124   _rl_vi_last_repeat = 1;
125   _rl_vi_last_arg_sign = 1;
126   _rl_vi_last_motion = 0;
127 }
128 
129 void
_rl_vi_set_last(key,repeat,sign)130 _rl_vi_set_last (key, repeat, sign)
131      int key, repeat, sign;
132 {
133   _rl_vi_last_command = key;
134   _rl_vi_last_repeat = repeat;
135   _rl_vi_last_arg_sign = sign;
136 }
137 
138 /* Is the command C a VI mode text modification command? */
139 int
_rl_vi_textmod_command(c)140 _rl_vi_textmod_command (c)
141      int c;
142 {
143   return (member (c, vi_textmod));
144 }
145 
146 static void
_rl_vi_stuff_insert(count)147 _rl_vi_stuff_insert (count)
148      int count;
149 {
150   rl_begin_undo_group ();
151   while (count--)
152     rl_insert_text (vi_insert_buffer);
153   rl_end_undo_group ();
154 }
155 
156 /* Bound to `.'.  Called from command mode, so we know that we have to
157    redo a text modification command.  The default for _rl_vi_last_command
158    puts you back into insert mode. */
159 int
rl_vi_redo(count,c)160 rl_vi_redo (count, c)
161      int count, c;
162 {
163   int r;
164 
165   if (!rl_explicit_arg)
166     {
167       rl_numeric_arg = _rl_vi_last_repeat;
168       rl_arg_sign = _rl_vi_last_arg_sign;
169     }
170 
171   r = 0;
172   vi_redoing = 1;
173   /* If we're redoing an insert with `i', stuff in the inserted text
174      and do not go into insertion mode. */
175   if (_rl_vi_last_command == 'i' && vi_insert_buffer && *vi_insert_buffer)
176     {
177       _rl_vi_stuff_insert (count);
178       /* And back up point over the last character inserted. */
179       if (rl_point > 0)
180 	rl_point--;
181     }
182   else
183     r = _rl_dispatch (_rl_vi_last_command, _rl_keymap);
184   vi_redoing = 0;
185 
186   return (r);
187 }
188 
189 /* A placeholder for further expansion. */
190 int
rl_vi_undo(count,key)191 rl_vi_undo (count, key)
192      int count, key;
193 {
194   return (rl_undo_command (count, key));
195 }
196 
197 /* Yank the nth arg from the previous line into this line at point. */
198 int
rl_vi_yank_arg(count,key)199 rl_vi_yank_arg (count, key)
200      int count, key;
201 {
202   /* Readline thinks that the first word on a line is the 0th, while vi
203      thinks the first word on a line is the 1st.  Compensate. */
204   if (rl_explicit_arg)
205     rl_yank_nth_arg (count - 1, 0);
206   else
207     rl_yank_nth_arg ('$', 0);
208 
209   return (0);
210 }
211 
212 /* With an argument, move back that many history lines, else move to the
213    beginning of history. */
214 int
rl_vi_fetch_history(count,c)215 rl_vi_fetch_history (count, c)
216      int count, c;
217 {
218   int wanted;
219 
220   /* Giving an argument of n means we want the nth command in the history
221      file.  The command number is interpreted the same way that the bash
222      `history' command does it -- that is, giving an argument count of 450
223      to this command would get the command listed as number 450 in the
224      output of `history'. */
225   if (rl_explicit_arg)
226     {
227       wanted = history_base + where_history () - count;
228       if (wanted <= 0)
229         rl_beginning_of_history (0, 0);
230       else
231         rl_get_previous_history (wanted, c);
232     }
233   else
234     rl_beginning_of_history (count, 0);
235   return (0);
236 }
237 
238 /* Search again for the last thing searched for. */
239 int
rl_vi_search_again(count,key)240 rl_vi_search_again (count, key)
241      int count, key;
242 {
243   switch (key)
244     {
245     case 'n':
246       rl_noninc_reverse_search_again (count, key);
247       break;
248 
249     case 'N':
250       rl_noninc_forward_search_again (count, key);
251       break;
252     }
253   return (0);
254 }
255 
256 /* Do a vi style search. */
257 int
rl_vi_search(count,key)258 rl_vi_search (count, key)
259      int count, key;
260 {
261   switch (key)
262     {
263     case '?':
264       rl_noninc_forward_search (count, key);
265       break;
266 
267     case '/':
268       rl_noninc_reverse_search (count, key);
269       break;
270 
271     default:
272       rl_ding ();
273       break;
274     }
275   return (0);
276 }
277 
278 /* Completion, from vi's point of view. */
279 int
rl_vi_complete(ignore,key)280 rl_vi_complete (ignore, key)
281      int ignore, key;
282 {
283   if ((rl_point < rl_end) && (!whitespace (rl_line_buffer[rl_point])))
284     {
285       if (!whitespace (rl_line_buffer[rl_point + 1]))
286 	rl_vi_end_word (1, 'E');
287       rl_point++;
288     }
289 
290   if (key == '*')
291     rl_complete_internal ('*');	/* Expansion and replacement. */
292   else if (key == '=')
293     rl_complete_internal ('?');	/* List possible completions. */
294   else if (key == '\\')
295     rl_complete_internal (TAB);	/* Standard Readline completion. */
296   else
297     rl_complete (0, key);
298 
299   if (key == '*' || key == '\\')
300     {
301       _rl_vi_set_last (key, 1, rl_arg_sign);
302       rl_vi_insertion_mode (1, key);
303     }
304   return (0);
305 }
306 
307 /* Tilde expansion for vi mode. */
308 int
rl_vi_tilde_expand(ignore,key)309 rl_vi_tilde_expand (ignore, key)
310      int ignore, key;
311 {
312   rl_tilde_expand (0, key);
313   _rl_vi_set_last (key, 1, rl_arg_sign);	/* XXX */
314   rl_vi_insertion_mode (1, key);
315   return (0);
316 }
317 
318 /* Previous word in vi mode. */
319 int
rl_vi_prev_word(count,key)320 rl_vi_prev_word (count, key)
321      int count, key;
322 {
323   if (count < 0)
324     return (rl_vi_next_word (-count, key));
325 
326   if (rl_point == 0)
327     {
328       rl_ding ();
329       return (0);
330     }
331 
332   if (_rl_uppercase_p (key))
333     rl_vi_bWord (count, key);
334   else
335     rl_vi_bword (count, key);
336 
337   return (0);
338 }
339 
340 /* Next word in vi mode. */
341 int
rl_vi_next_word(count,key)342 rl_vi_next_word (count, key)
343      int count, key;
344 {
345   if (count < 0)
346     return (rl_vi_prev_word (-count, key));
347 
348   if (rl_point >= (rl_end - 1))
349     {
350       rl_ding ();
351       return (0);
352     }
353 
354   if (_rl_uppercase_p (key))
355     rl_vi_fWord (count, key);
356   else
357     rl_vi_fword (count, key);
358   return (0);
359 }
360 
361 /* Move to the end of the ?next? word. */
362 int
rl_vi_end_word(count,key)363 rl_vi_end_word (count, key)
364      int count, key;
365 {
366   if (count < 0)
367     {
368       rl_ding ();
369       return -1;
370     }
371 
372   if (_rl_uppercase_p (key))
373     rl_vi_eWord (count, key);
374   else
375     rl_vi_eword (count, key);
376   return (0);
377 }
378 
379 /* Move forward a word the way that 'W' does. */
380 int
rl_vi_fWord(count,ignore)381 rl_vi_fWord (count, ignore)
382      int count, ignore;
383 {
384   while (count-- && rl_point < (rl_end - 1))
385     {
386       /* Skip until whitespace. */
387       while (!whitespace (rl_line_buffer[rl_point]) && rl_point < rl_end)
388 	rl_point++;
389 
390       /* Now skip whitespace. */
391       while (whitespace (rl_line_buffer[rl_point]) && rl_point < rl_end)
392 	rl_point++;
393     }
394   return (0);
395 }
396 
397 int
rl_vi_bWord(count,ignore)398 rl_vi_bWord (count, ignore)
399      int count, ignore;
400 {
401   while (count-- && rl_point > 0)
402     {
403       /* If we are at the start of a word, move back to whitespace so
404 	 we will go back to the start of the previous word. */
405       if (!whitespace (rl_line_buffer[rl_point]) &&
406 	  whitespace (rl_line_buffer[rl_point - 1]))
407 	rl_point--;
408 
409       while (rl_point > 0 && whitespace (rl_line_buffer[rl_point]))
410 	rl_point--;
411 
412       if (rl_point > 0)
413 	{
414 	  while (--rl_point >= 0 && !whitespace (rl_line_buffer[rl_point]));
415 	  rl_point++;
416 	}
417     }
418   return (0);
419 }
420 
421 int
rl_vi_eWord(count,ignore)422 rl_vi_eWord (count, ignore)
423      int count, ignore;
424 {
425   while (count-- && rl_point < (rl_end - 1))
426     {
427       if (!whitespace (rl_line_buffer[rl_point]))
428 	rl_point++;
429 
430       /* Move to the next non-whitespace character (to the start of the
431 	 next word). */
432       while (++rl_point < rl_end && whitespace (rl_line_buffer[rl_point]));
433 
434       if (rl_point && rl_point < rl_end)
435 	{
436 	  /* Skip whitespace. */
437 	  while (rl_point < rl_end && whitespace (rl_line_buffer[rl_point]))
438 	    rl_point++;
439 
440 	  /* Skip until whitespace. */
441 	  while (rl_point < rl_end && !whitespace (rl_line_buffer[rl_point]))
442 	    rl_point++;
443 
444 	  /* Move back to the last character of the word. */
445 	  rl_point--;
446 	}
447     }
448   return (0);
449 }
450 
451 int
rl_vi_fword(count,ignore)452 rl_vi_fword (count, ignore)
453      int count, ignore;
454 {
455   while (count-- && rl_point < (rl_end - 1))
456     {
457       /* Move to white space (really non-identifer). */
458       if (_rl_isident (rl_line_buffer[rl_point]))
459 	{
460 	  while (_rl_isident (rl_line_buffer[rl_point]) && rl_point < rl_end)
461 	    rl_point++;
462 	}
463       else /* if (!whitespace (rl_line_buffer[rl_point])) */
464 	{
465 	  while (!_rl_isident (rl_line_buffer[rl_point]) &&
466 		 !whitespace (rl_line_buffer[rl_point]) && rl_point < rl_end)
467 	    rl_point++;
468 	}
469 
470       /* Move past whitespace. */
471       while (whitespace (rl_line_buffer[rl_point]) && rl_point < rl_end)
472 	rl_point++;
473     }
474   return (0);
475 }
476 
477 int
rl_vi_bword(count,ignore)478 rl_vi_bword (count, ignore)
479      int count, ignore;
480 {
481   while (count-- && rl_point > 0)
482     {
483       int last_is_ident;
484 
485       /* If we are at the start of a word, move back to whitespace
486 	 so we will go back to the start of the previous word. */
487       if (!whitespace (rl_line_buffer[rl_point]) &&
488 	  whitespace (rl_line_buffer[rl_point - 1]))
489 	rl_point--;
490 
491       /* If this character and the previous character are `opposite', move
492 	 back so we don't get messed up by the rl_point++ down there in
493 	 the while loop.  Without this code, words like `l;' screw up the
494 	 function. */
495       last_is_ident = _rl_isident (rl_line_buffer[rl_point - 1]);
496       if ((_rl_isident (rl_line_buffer[rl_point]) && !last_is_ident) ||
497 	  (!_rl_isident (rl_line_buffer[rl_point]) && last_is_ident))
498 	rl_point--;
499 
500       while (rl_point > 0 && whitespace (rl_line_buffer[rl_point]))
501 	rl_point--;
502 
503       if (rl_point > 0)
504 	{
505 	  if (_rl_isident (rl_line_buffer[rl_point]))
506 	    while (--rl_point >= 0 && _rl_isident (rl_line_buffer[rl_point]));
507 	  else
508 	    while (--rl_point >= 0 && !_rl_isident (rl_line_buffer[rl_point]) &&
509 		   !whitespace (rl_line_buffer[rl_point]));
510 	  rl_point++;
511 	}
512     }
513   return (0);
514 }
515 
516 int
rl_vi_eword(count,ignore)517 rl_vi_eword (count, ignore)
518      int count, ignore;
519 {
520   while (count-- && rl_point < rl_end - 1)
521     {
522       if (!whitespace (rl_line_buffer[rl_point]))
523 	rl_point++;
524 
525       while (rl_point < rl_end && whitespace (rl_line_buffer[rl_point]))
526 	rl_point++;
527 
528       if (rl_point < rl_end)
529 	{
530 	  if (_rl_isident (rl_line_buffer[rl_point]))
531 	    while (++rl_point < rl_end && _rl_isident (rl_line_buffer[rl_point]));
532 	  else
533 	    while (++rl_point < rl_end && !_rl_isident (rl_line_buffer[rl_point])
534 		   && !whitespace (rl_line_buffer[rl_point]));
535 	}
536       rl_point--;
537     }
538   return (0);
539 }
540 
541 int
rl_vi_insert_beg(count,key)542 rl_vi_insert_beg (count, key)
543      int count, key;
544 {
545   rl_beg_of_line (1, key);
546   rl_vi_insertion_mode (1, key);
547   return (0);
548 }
549 
550 int
rl_vi_append_mode(count,key)551 rl_vi_append_mode (count, key)
552      int count, key;
553 {
554   if (rl_point < rl_end)
555     {
556       if (MB_CUR_MAX == 1 || rl_byte_oriented)
557 	rl_point++;
558       else
559         {
560           int point = rl_point;
561           rl_forward_char (1, key);
562           if (point == rl_point)
563             rl_point = rl_end;
564         }
565     }
566   rl_vi_insertion_mode (1, key);
567   return (0);
568 }
569 
570 int
rl_vi_append_eol(count,key)571 rl_vi_append_eol (count, key)
572      int count, key;
573 {
574   rl_end_of_line (1, key);
575   rl_vi_append_mode (1, key);
576   return (0);
577 }
578 
579 /* What to do in the case of C-d. */
580 int
rl_vi_eof_maybe(count,c)581 rl_vi_eof_maybe (count, c)
582      int count, c;
583 {
584   return (rl_newline (1, '\n'));
585 }
586 
587 /* Insertion mode stuff. */
588 
589 /* Switching from one mode to the other really just involves
590    switching keymaps. */
591 int
rl_vi_insertion_mode(count,key)592 rl_vi_insertion_mode (count, key)
593      int count, key;
594 {
595   _rl_keymap = vi_insertion_keymap;
596   _rl_vi_last_key_before_insert = key;
597   return (0);
598 }
599 
600 static void
_rl_vi_save_insert(up)601 _rl_vi_save_insert (up)
602       UNDO_LIST *up;
603 {
604   int len, start, end;
605 
606   if (up == 0)
607     {
608       if (vi_insert_buffer_size >= 1)
609 	vi_insert_buffer[0] = '\0';
610       return;
611     }
612 
613   start = up->start;
614   end = up->end;
615   len = end - start + 1;
616   if (len >= vi_insert_buffer_size)
617     {
618       vi_insert_buffer_size += (len + 32) - (len % 32);
619       vi_insert_buffer = (char *)xrealloc (vi_insert_buffer, vi_insert_buffer_size);
620     }
621   strncpy (vi_insert_buffer, rl_line_buffer + start, len - 1);
622   vi_insert_buffer[len-1] = '\0';
623 }
624 
625 void
_rl_vi_done_inserting()626 _rl_vi_done_inserting ()
627 {
628   if (_rl_vi_doing_insert)
629     {
630       /* The `C', `s', and `S' commands set this. */
631       rl_end_undo_group ();
632       /* Now, the text between rl_undo_list->next->start and
633 	 rl_undo_list->next->end is what was inserted while in insert
634 	 mode.  It gets copied to VI_INSERT_BUFFER because it depends
635 	 on absolute indices into the line which may change (though they
636 	 probably will not). */
637       _rl_vi_doing_insert = 0;
638       _rl_vi_save_insert (rl_undo_list->next);
639       vi_continued_command = 1;
640     }
641   else
642     {
643       if (_rl_vi_last_key_before_insert == 'i' && rl_undo_list)
644         _rl_vi_save_insert (rl_undo_list);
645       /* XXX - Other keys probably need to be checked. */
646       else if (_rl_vi_last_key_before_insert == 'C')
647 	rl_end_undo_group ();
648       while (_rl_undo_group_level > 0)
649 	rl_end_undo_group ();
650       vi_continued_command = 0;
651     }
652 }
653 
654 int
rl_vi_movement_mode(count,key)655 rl_vi_movement_mode (count, key)
656      int count, key;
657 {
658   if (rl_point > 0)
659     rl_backward_char (1, key);
660 
661   _rl_keymap = vi_movement_keymap;
662   _rl_vi_done_inserting ();
663   return (0);
664 }
665 
666 int
rl_vi_arg_digit(count,c)667 rl_vi_arg_digit (count, c)
668      int count, c;
669 {
670   if (c == '0' && rl_numeric_arg == 1 && !rl_explicit_arg)
671     return (rl_beg_of_line (1, c));
672   else
673     return (rl_digit_argument (count, c));
674 }
675 
676 /* Change the case of the next COUNT characters. */
677 #if defined (HANDLE_MULTIBYTE)
678 static int
_rl_vi_change_mbchar_case(count)679 _rl_vi_change_mbchar_case (count)
680      int count;
681 {
682   wchar_t wc;
683   char mb[MB_LEN_MAX+1];
684   int mblen;
685   mbstate_t ps;
686 
687   memset (&ps, 0, sizeof (mbstate_t));
688   if (_rl_adjust_point (rl_line_buffer, rl_point, &ps) > 0)
689     count--;
690   while (count-- && rl_point < rl_end)
691     {
692       mbrtowc (&wc, rl_line_buffer + rl_point, rl_end - rl_point, &ps);
693       if (iswupper (wc))
694 	wc = towlower (wc);
695       else if (iswlower (wc))
696 	wc = towupper (wc);
697       else
698 	{
699 	  /* Just skip over chars neither upper nor lower case */
700 	  rl_forward_char (1, 0);
701 	  continue;
702 	}
703 
704       /* Vi is kind of strange here. */
705       if (wc)
706 	{
707 	  mblen = wctomb (mb, wc);
708 	  if (mblen >= 0)
709 	    mb[mblen] = '\0';
710 	  rl_begin_undo_group ();
711 	  rl_delete (1, 0);
712 	  rl_insert_text (mb);
713 	  rl_end_undo_group ();
714 	  rl_vi_check ();
715 	}
716       else
717         rl_forward_char (1, 0);
718     }
719 
720   return 0;
721 }
722 #endif
723 
724 int
rl_vi_change_case(count,ignore)725 rl_vi_change_case (count, ignore)
726      int count, ignore;
727 {
728   char c = 0;
729 
730   /* Don't try this on an empty line. */
731   if (rl_point >= rl_end)
732     return (0);
733 
734 #if defined (HANDLE_MULTIBYTE)
735   if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
736     return (_rl_vi_change_mbchar_case (count));
737 #endif
738 
739   while (count-- && rl_point < rl_end)
740     {
741       if (_rl_uppercase_p (rl_line_buffer[rl_point]))
742 	c = _rl_to_lower (rl_line_buffer[rl_point]);
743       else if (_rl_lowercase_p (rl_line_buffer[rl_point]))
744 	c = _rl_to_upper (rl_line_buffer[rl_point]);
745       else
746 	{
747 	  /* Just skip over characters neither upper nor lower case. */
748 	  rl_forward_char (1, c);
749 	  continue;
750 	}
751 
752       /* Vi is kind of strange here. */
753       if (c)
754 	{
755 	  rl_begin_undo_group ();
756 	  rl_delete (1, c);
757 	  _rl_insert_char (1, c);
758 	  rl_end_undo_group ();
759 	  rl_vi_check ();
760         }
761       else
762 	rl_forward_char (1, c);
763     }
764   return (0);
765 }
766 
767 int
rl_vi_put(count,key)768 rl_vi_put (count, key)
769      int count, key;
770 {
771   if (!_rl_uppercase_p (key) && (rl_point + 1 <= rl_end))
772     rl_point = _rl_find_next_mbchar (rl_line_buffer, rl_point, 1, MB_FIND_NONZERO);
773 
774   rl_yank (1, key);
775   rl_backward_char (1, key);
776   return (0);
777 }
778 
779 int
rl_vi_check()780 rl_vi_check ()
781 {
782   if (rl_point && rl_point == rl_end)
783     {
784       if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
785 	rl_point = _rl_find_prev_mbchar (rl_line_buffer, rl_point, MB_FIND_NONZERO);
786       else
787         rl_point--;
788     }
789   return (0);
790 }
791 
792 int
rl_vi_column(count,key)793 rl_vi_column (count, key)
794      int count, key;
795 {
796   if (count > rl_end)
797     rl_end_of_line (1, key);
798   else
799     rl_point = count - 1;
800   return (0);
801 }
802 
803 int
rl_vi_domove(key,nextkey)804 rl_vi_domove (key, nextkey)
805      int key, *nextkey;
806 {
807   int c, save;
808   int old_end;
809 
810   rl_mark = rl_point;
811   RL_SETSTATE(RL_STATE_MOREINPUT);
812   c = rl_read_key ();
813   RL_UNSETSTATE(RL_STATE_MOREINPUT);
814   *nextkey = c;
815 
816   if (!member (c, vi_motion))
817     {
818       if (_rl_digit_p (c))
819 	{
820 	  save = rl_numeric_arg;
821 	  rl_numeric_arg = _rl_digit_value (c);
822 	  rl_digit_loop1 ();
823 	  rl_numeric_arg *= save;
824 	  RL_SETSTATE(RL_STATE_MOREINPUT);
825 	  c = rl_read_key ();	/* real command */
826 	  RL_UNSETSTATE(RL_STATE_MOREINPUT);
827 	  *nextkey = c;
828 	}
829       else if (key == c && (key == 'd' || key == 'y' || key == 'c'))
830 	{
831 	  rl_mark = rl_end;
832 	  rl_beg_of_line (1, c);
833 	  _rl_vi_last_motion = c;
834 	  return (0);
835 	}
836       else
837 	return (-1);
838     }
839 
840   _rl_vi_last_motion = c;
841 
842   /* Append a blank character temporarily so that the motion routines
843      work right at the end of the line. */
844   old_end = rl_end;
845   rl_line_buffer[rl_end++] = ' ';
846   rl_line_buffer[rl_end] = '\0';
847 
848   _rl_dispatch (c, _rl_keymap);
849 
850   /* Remove the blank that we added. */
851   rl_end = old_end;
852   rl_line_buffer[rl_end] = '\0';
853   if (rl_point > rl_end)
854     rl_point = rl_end;
855 
856   /* No change in position means the command failed. */
857   if (rl_mark == rl_point)
858     return (-1);
859 
860   /* rl_vi_f[wW]ord () leaves the cursor on the first character of the next
861      word.  If we are not at the end of the line, and we are on a
862      non-whitespace character, move back one (presumably to whitespace). */
863   if ((_rl_to_upper (c) == 'W') && rl_point < rl_end && rl_point > rl_mark &&
864       !whitespace (rl_line_buffer[rl_point]))
865     rl_point--;
866 
867   /* If cw or cW, back up to the end of a word, so the behaviour of ce
868      or cE is the actual result.  Brute-force, no subtlety. */
869   if (key == 'c' && rl_point >= rl_mark && (_rl_to_upper (c) == 'W'))
870     {
871       /* Don't move farther back than where we started. */
872       while (rl_point > rl_mark && whitespace (rl_line_buffer[rl_point]))
873 	rl_point--;
874 
875       /* Posix.2 says that if cw or cW moves the cursor towards the end of
876 	 the line, the character under the cursor should be deleted. */
877       if (rl_point == rl_mark)
878         rl_point++;
879       else
880 	{
881 	  /* Move past the end of the word so that the kill doesn't
882 	     remove the last letter of the previous word.  Only do this
883 	     if we are not at the end of the line. */
884 	  if (rl_point >= 0 && rl_point < (rl_end - 1) && !whitespace (rl_line_buffer[rl_point]))
885 	    rl_point++;
886 	}
887     }
888 
889   if (rl_mark < rl_point)
890     SWAP (rl_point, rl_mark);
891 
892   return (0);
893 }
894 
895 /* A simplified loop for vi. Don't dispatch key at end.
896    Don't recognize minus sign?
897    Should this do rl_save_prompt/rl_restore_prompt? */
898 static int
rl_digit_loop1()899 rl_digit_loop1 ()
900 {
901   int key, c;
902 
903   RL_SETSTATE(RL_STATE_NUMERICARG);
904   while (1)
905     {
906       if (rl_numeric_arg > 1000000)
907 	{
908 	  rl_explicit_arg = rl_numeric_arg = 0;
909 	  rl_ding ();
910 	  rl_clear_message ();
911 	  RL_UNSETSTATE(RL_STATE_NUMERICARG);
912 	  return 1;
913 	}
914       rl_message ("(arg: %d) ", rl_arg_sign * rl_numeric_arg);
915       RL_SETSTATE(RL_STATE_MOREINPUT);
916       key = c = rl_read_key ();
917       RL_UNSETSTATE(RL_STATE_MOREINPUT);
918 
919       if (c >= 0 && _rl_keymap[c].type == ISFUNC &&
920 	  _rl_keymap[c].function == rl_universal_argument)
921 	{
922 	  rl_numeric_arg *= 4;
923 	  continue;
924 	}
925 
926       c = UNMETA (c);
927       if (_rl_digit_p (c))
928 	{
929 	  if (rl_explicit_arg)
930 	    rl_numeric_arg = (rl_numeric_arg * 10) + _rl_digit_value (c);
931 	  else
932 	    rl_numeric_arg = _rl_digit_value (c);
933 	  rl_explicit_arg = 1;
934 	}
935       else
936 	{
937 	  rl_clear_message ();
938 	  rl_stuff_char (key);
939 	  break;
940 	}
941     }
942 
943   RL_UNSETSTATE(RL_STATE_NUMERICARG);
944   return (0);
945 }
946 
947 int
rl_vi_delete_to(count,key)948 rl_vi_delete_to (count, key)
949      int count, key;
950 {
951   int c;
952 
953   if (_rl_uppercase_p (key))
954     rl_stuff_char ('$');
955   else if (vi_redoing)
956     rl_stuff_char (_rl_vi_last_motion);
957 
958   if (rl_vi_domove (key, &c))
959     {
960       rl_ding ();
961       return -1;
962     }
963 
964   /* These are the motion commands that do not require adjusting the
965      mark. */
966   if ((strchr (" l|h^0bB", c) == 0) && (rl_mark < rl_end))
967     rl_mark++;
968 
969   rl_kill_text (rl_point, rl_mark);
970   return (0);
971 }
972 
973 int
rl_vi_change_to(count,key)974 rl_vi_change_to (count, key)
975      int count, key;
976 {
977   int c, start_pos;
978 
979   if (_rl_uppercase_p (key))
980     rl_stuff_char ('$');
981   else if (vi_redoing)
982     rl_stuff_char (_rl_vi_last_motion);
983 
984   start_pos = rl_point;
985 
986   if (rl_vi_domove (key, &c))
987     {
988       rl_ding ();
989       return -1;
990     }
991 
992   /* These are the motion commands that do not require adjusting the
993      mark.  c[wW] are handled by special-case code in rl_vi_domove(),
994      and already leave the mark at the correct location. */
995   if ((strchr (" l|hwW^0bB", c) == 0) && (rl_mark < rl_end))
996     rl_mark++;
997 
998   /* The cursor never moves with c[wW]. */
999   if ((_rl_to_upper (c) == 'W') && rl_point < start_pos)
1000     rl_point = start_pos;
1001 
1002   if (vi_redoing)
1003     {
1004       if (vi_insert_buffer && *vi_insert_buffer)
1005 	rl_begin_undo_group ();
1006       rl_delete_text (rl_point, rl_mark);
1007       if (vi_insert_buffer && *vi_insert_buffer)
1008 	{
1009 	  rl_insert_text (vi_insert_buffer);
1010 	  rl_end_undo_group ();
1011 	}
1012     }
1013   else
1014     {
1015       rl_begin_undo_group ();		/* to make the `u' command work */
1016       rl_kill_text (rl_point, rl_mark);
1017       /* `C' does not save the text inserted for undoing or redoing. */
1018       if (_rl_uppercase_p (key) == 0)
1019         _rl_vi_doing_insert = 1;
1020       _rl_vi_set_last (key, count, rl_arg_sign);
1021       rl_vi_insertion_mode (1, key);
1022     }
1023 
1024   return (0);
1025 }
1026 
1027 int
rl_vi_yank_to(count,key)1028 rl_vi_yank_to (count, key)
1029      int count, key;
1030 {
1031   int c, save = rl_point;
1032 
1033   if (_rl_uppercase_p (key))
1034     rl_stuff_char ('$');
1035 
1036   if (rl_vi_domove (key, &c))
1037     {
1038       rl_ding ();
1039       return -1;
1040     }
1041 
1042   /* These are the motion commands that do not require adjusting the
1043      mark. */
1044   if ((strchr (" l|h^0%bB", c) == 0) && (rl_mark < rl_end))
1045     rl_mark++;
1046 
1047   rl_begin_undo_group ();
1048   rl_kill_text (rl_point, rl_mark);
1049   rl_end_undo_group ();
1050   rl_do_undo ();
1051   rl_point = save;
1052 
1053   return (0);
1054 }
1055 
1056 int
rl_vi_delete(count,key)1057 rl_vi_delete (count, key)
1058      int count, key;
1059 {
1060   int end;
1061 
1062   if (rl_end == 0)
1063     {
1064       rl_ding ();
1065       return -1;
1066     }
1067 
1068   if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
1069     end = _rl_find_next_mbchar (rl_line_buffer, rl_point, count, MB_FIND_NONZERO);
1070   else
1071     end = rl_point + count;
1072 
1073   if (end >= rl_end)
1074     end = rl_end;
1075 
1076   rl_kill_text (rl_point, end);
1077 
1078   if (rl_point > 0 && rl_point == rl_end)
1079     rl_backward_char (1, key);
1080   return (0);
1081 }
1082 
1083 int
rl_vi_back_to_indent(count,key)1084 rl_vi_back_to_indent (count, key)
1085      int count, key;
1086 {
1087   rl_beg_of_line (1, key);
1088   while (rl_point < rl_end && whitespace (rl_line_buffer[rl_point]))
1089     rl_point++;
1090   return (0);
1091 }
1092 
1093 int
rl_vi_first_print(count,key)1094 rl_vi_first_print (count, key)
1095      int count, key;
1096 {
1097   return (rl_vi_back_to_indent (1, key));
1098 }
1099 
1100 int
rl_vi_char_search(count,key)1101 rl_vi_char_search (count, key)
1102      int count, key;
1103 {
1104 #if defined (HANDLE_MULTIBYTE)
1105   static char *target;
1106   static int mb_len;
1107 #else
1108   static char target;
1109 #endif
1110   static int orig_dir, dir;
1111 
1112   if (key == ';' || key == ',')
1113     dir = key == ';' ? orig_dir : -orig_dir;
1114   else
1115     {
1116       if (vi_redoing)
1117 #if defined (HANDLE_MULTIBYTE)
1118 	target = _rl_vi_last_search_mbchar;
1119 #else
1120 	target = _rl_vi_last_search_char;
1121 #endif
1122       else
1123 	{
1124 #if defined (HANDLE_MULTIBYTE)
1125 	  mb_len = _rl_read_mbchar (_rl_vi_last_search_mbchar, MB_LEN_MAX);
1126 	  target = _rl_vi_last_search_mbchar;
1127 #else
1128 	  RL_SETSTATE(RL_STATE_MOREINPUT);
1129 	  _rl_vi_last_search_char = target = rl_read_key ();
1130 	  RL_UNSETSTATE(RL_STATE_MOREINPUT);
1131 #endif
1132 	}
1133 
1134       switch (key)
1135         {
1136         case 't':
1137           orig_dir = dir = FTO;
1138           break;
1139 
1140         case 'T':
1141           orig_dir = dir = BTO;
1142           break;
1143 
1144         case 'f':
1145           orig_dir = dir = FFIND;
1146           break;
1147 
1148         case 'F':
1149           orig_dir = dir = BFIND;
1150           break;
1151         }
1152     }
1153 
1154 #if defined (HANDLE_MULTIBYTE)
1155    return (_rl_char_search_internal (count, dir, target, mb_len));
1156 #else
1157   return (_rl_char_search_internal (count, dir, target));
1158 #endif
1159 }
1160 
1161 /* Match brackets */
1162 int
rl_vi_match(ignore,key)1163 rl_vi_match (ignore, key)
1164      int ignore, key;
1165 {
1166   int count = 1, brack, pos, tmp, pre;
1167 
1168   pos = rl_point;
1169   if ((brack = rl_vi_bracktype (rl_line_buffer[rl_point])) == 0)
1170     {
1171       if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
1172 	{
1173 	  while ((brack = rl_vi_bracktype (rl_line_buffer[rl_point])) == 0)
1174 	    {
1175 	      pre = rl_point;
1176 	      rl_forward_char (1, key);
1177 	      if (pre == rl_point)
1178 	        break;
1179 	    }
1180 	}
1181       else
1182 	while ((brack = rl_vi_bracktype (rl_line_buffer[rl_point])) == 0 &&
1183 		rl_point < rl_end - 1)
1184 	  rl_forward_char (1, key);
1185 
1186       if (brack <= 0)
1187 	{
1188 	  rl_point = pos;
1189 	  rl_ding ();
1190 	  return -1;
1191 	}
1192     }
1193 
1194   pos = rl_point;
1195 
1196   if (brack < 0)
1197     {
1198       while (count)
1199 	{
1200 	  tmp = pos;
1201 	  if (MB_CUR_MAX == 1 || rl_byte_oriented)
1202 	    pos--;
1203 	  else
1204 	    {
1205 	      pos = _rl_find_prev_mbchar (rl_line_buffer, pos, MB_FIND_ANY);
1206 	      if (tmp == pos)
1207 	        pos--;
1208 	    }
1209 	  if (pos >= 0)
1210 	    {
1211 	      int b = rl_vi_bracktype (rl_line_buffer[pos]);
1212 	      if (b == -brack)
1213 		count--;
1214 	      else if (b == brack)
1215 		count++;
1216 	    }
1217 	  else
1218 	    {
1219 	      rl_ding ();
1220 	      return -1;
1221 	    }
1222 	}
1223     }
1224   else
1225     {			/* brack > 0 */
1226       while (count)
1227 	{
1228 	  if (MB_CUR_MAX == 1 || rl_byte_oriented)
1229 	    pos++;
1230 	  else
1231 	    pos = _rl_find_next_mbchar (rl_line_buffer, pos, 1, MB_FIND_ANY);
1232 
1233 	  if (pos < rl_end)
1234 	    {
1235 	      int b = rl_vi_bracktype (rl_line_buffer[pos]);
1236 	      if (b == -brack)
1237 		count--;
1238 	      else if (b == brack)
1239 		count++;
1240 	    }
1241 	  else
1242 	    {
1243 	      rl_ding ();
1244 	      return -1;
1245 	    }
1246 	}
1247     }
1248   rl_point = pos;
1249   return (0);
1250 }
1251 
1252 int
rl_vi_bracktype(c)1253 rl_vi_bracktype (c)
1254      int c;
1255 {
1256   switch (c)
1257     {
1258     case '(': return  1;
1259     case ')': return -1;
1260     case '[': return  2;
1261     case ']': return -2;
1262     case '{': return  3;
1263     case '}': return -3;
1264     default:  return  0;
1265     }
1266 }
1267 
1268 /* XXX - think about reading an entire mbchar with _rl_read_mbchar and
1269    inserting it in one bunch instead of the loop below (like in
1270    rl_vi_char_search or _rl_vi_change_mbchar_case.  Set c to mbchar[0]
1271    for test against 033 or ^C.  Make sure that _rl_read_mbchar does
1272    this right. */
1273 int
rl_vi_change_char(count,key)1274 rl_vi_change_char (count, key)
1275      int count, key;
1276 {
1277   int c;
1278 
1279   if (vi_redoing)
1280     c = _rl_vi_last_replacement;
1281   else
1282     {
1283       RL_SETSTATE(RL_STATE_MOREINPUT);
1284       _rl_vi_last_replacement = c = rl_read_key ();
1285       RL_UNSETSTATE(RL_STATE_MOREINPUT);
1286     }
1287 
1288   if (c == '\033' || c == CTRL ('C'))
1289     return -1;
1290 
1291   while (count-- && rl_point < rl_end)
1292     {
1293       rl_begin_undo_group ();
1294 
1295       rl_delete (1, c);
1296 #if defined (HANDLE_MULTIBYTE)
1297       if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
1298 	while (_rl_insert_char (1, c))
1299 	  {
1300 	    RL_SETSTATE (RL_STATE_MOREINPUT);
1301 	    c = rl_read_key ();
1302 	    RL_UNSETSTATE (RL_STATE_MOREINPUT);
1303 	  }
1304       else
1305 #endif
1306 	_rl_insert_char (1, c);
1307       if (count == 0)
1308 	rl_backward_char (1, c);
1309 
1310       rl_end_undo_group ();
1311     }
1312   return (0);
1313 }
1314 
1315 int
rl_vi_subst(count,key)1316 rl_vi_subst (count, key)
1317      int count, key;
1318 {
1319   /* If we are redoing, rl_vi_change_to will stuff the last motion char */
1320   if (vi_redoing == 0)
1321     rl_stuff_char ((key == 'S') ? 'c' : ' ');	/* `S' == `cc', `s' == `c ' */
1322 
1323   return (rl_vi_change_to (count, 'c'));
1324 }
1325 
1326 int
rl_vi_overstrike(count,key)1327 rl_vi_overstrike (count, key)
1328      int count, key;
1329 {
1330   if (_rl_vi_doing_insert == 0)
1331     {
1332       _rl_vi_doing_insert = 1;
1333       rl_begin_undo_group ();
1334     }
1335 
1336   if (count > 0)
1337     {
1338       _rl_overwrite_char (count, key);
1339       vi_replace_count += count;
1340     }
1341 
1342   return (0);
1343 }
1344 
1345 int
rl_vi_overstrike_delete(count,key)1346 rl_vi_overstrike_delete (count, key)
1347      int count, key;
1348 {
1349   int i, s;
1350 
1351   for (i = 0; i < count; i++)
1352     {
1353       if (vi_replace_count == 0)
1354 	{
1355 	  rl_ding ();
1356 	  break;
1357 	}
1358       s = rl_point;
1359 
1360       if (rl_do_undo ())
1361 	vi_replace_count--;
1362 
1363       if (rl_point == s)
1364 	rl_backward_char (1, key);
1365     }
1366 
1367   if (vi_replace_count == 0 && _rl_vi_doing_insert)
1368     {
1369       rl_end_undo_group ();
1370       rl_do_undo ();
1371       _rl_vi_doing_insert = 0;
1372     }
1373   return (0);
1374 }
1375 
1376 int
rl_vi_replace(count,key)1377 rl_vi_replace (count, key)
1378      int count, key;
1379 {
1380   int i;
1381 
1382   vi_replace_count = 0;
1383 
1384   if (!vi_replace_map)
1385     {
1386       vi_replace_map = rl_make_bare_keymap ();
1387 
1388       for (i = ' '; i < KEYMAP_SIZE; i++)
1389 	vi_replace_map[i].function = rl_vi_overstrike;
1390 
1391       vi_replace_map[RUBOUT].function = rl_vi_overstrike_delete;
1392       vi_replace_map[ESC].function = rl_vi_movement_mode;
1393       vi_replace_map[RETURN].function = rl_newline;
1394       vi_replace_map[NEWLINE].function = rl_newline;
1395 
1396       /* If the normal vi insertion keymap has ^H bound to erase, do the
1397          same here.  Probably should remove the assignment to RUBOUT up
1398          there, but I don't think it will make a difference in real life. */
1399       if (vi_insertion_keymap[CTRL ('H')].type == ISFUNC &&
1400 	  vi_insertion_keymap[CTRL ('H')].function == rl_rubout)
1401 	vi_replace_map[CTRL ('H')].function = rl_vi_overstrike_delete;
1402 
1403     }
1404   _rl_keymap = vi_replace_map;
1405   return (0);
1406 }
1407 
1408 #if 0
1409 /* Try to complete the word we are standing on or the word that ends with
1410    the previous character.  A space matches everything.  Word delimiters are
1411    space and ;. */
1412 int
1413 rl_vi_possible_completions()
1414 {
1415   int save_pos = rl_point;
1416 
1417   if (rl_line_buffer[rl_point] != ' ' && rl_line_buffer[rl_point] != ';')
1418     {
1419       while (rl_point < rl_end && rl_line_buffer[rl_point] != ' ' &&
1420 	     rl_line_buffer[rl_point] != ';')
1421 	rl_point++;
1422     }
1423   else if (rl_line_buffer[rl_point - 1] == ';')
1424     {
1425       rl_ding ();
1426       return (0);
1427     }
1428 
1429   rl_possible_completions ();
1430   rl_point = save_pos;
1431 
1432   return (0);
1433 }
1434 #endif
1435 
1436 /* Functions to save and restore marks. */
1437 int
rl_vi_set_mark(count,key)1438 rl_vi_set_mark (count, key)
1439      int count, key;
1440 {
1441   int ch;
1442 
1443   RL_SETSTATE(RL_STATE_MOREINPUT);
1444   ch = rl_read_key ();
1445   RL_UNSETSTATE(RL_STATE_MOREINPUT);
1446 
1447   if (ch < 'a' || ch > 'z')
1448     {
1449       rl_ding ();
1450       return -1;
1451     }
1452   ch -= 'a';
1453   vi_mark_chars[ch] = rl_point;
1454   return 0;
1455 }
1456 
1457 int
rl_vi_goto_mark(count,key)1458 rl_vi_goto_mark (count, key)
1459      int count, key;
1460 {
1461   int ch;
1462 
1463   RL_SETSTATE(RL_STATE_MOREINPUT);
1464   ch = rl_read_key ();
1465   RL_UNSETSTATE(RL_STATE_MOREINPUT);
1466 
1467   if (ch == '`')
1468     {
1469       rl_point = rl_mark;
1470       return 0;
1471     }
1472   else if (ch < 'a' || ch > 'z')
1473     {
1474       rl_ding ();
1475       return -1;
1476     }
1477 
1478   ch -= 'a';
1479   if (vi_mark_chars[ch] == -1)
1480     {
1481       rl_ding ();
1482       return -1;
1483     }
1484   rl_point = vi_mark_chars[ch];
1485   return 0;
1486 }
1487 
1488 #endif /* VI_MODE */
1489