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