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. */
67static int _rl_vi_doing_insert;
68
69/* Command keys which do movement for xxx_to commands. */
70static const char *vi_motion = " hl^$0ftFT;,%wbeWBE|";
71
72/* Keymap used for vi replace characters.  Created dynamically since
73   rarely used. */
74static Keymap vi_replace_map;
75
76/* The number of characters inserted in the last replace operation. */
77static 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 `.'. */
82static int vi_continued_command;
83static char *vi_insert_buffer;
84static int vi_insert_buffer_size;
85
86static int _rl_vi_last_command = 'i';	/* default `.' puts you in insert mode */
87static int _rl_vi_last_repeat = 1;
88static int _rl_vi_last_arg_sign = 1;
89static int _rl_vi_last_motion;
90#if defined (HANDLE_MULTIBYTE)
91static char _rl_vi_last_search_mbchar[MB_LEN_MAX];
92#else
93static int _rl_vi_last_search_char;
94#endif
95static int _rl_vi_last_replacement;
96
97static int _rl_vi_last_key_before_insert;
98
99static int vi_redoing;
100
101/* Text modification commands.  These are the `redoable' commands. */
102static const char *vi_textmod = "_*\\AaIiCcDdPpYyRrSsXx~";
103
104/* Arrays for the saved marks. */
105static int vi_mark_chars['z' - 'a' + 1];
106
107static void _rl_vi_stuff_insert PARAMS((int));
108static void _rl_vi_save_insert PARAMS((UNDO_LIST *));
109static int rl_digit_loop1 PARAMS((void));
110
111void
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
120void
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
129void
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? */
139int
140_rl_vi_textmod_command (c)
141     int c;
142{
143  return (member (c, vi_textmod));
144}
145
146static void
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. */
159int
160rl_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. */
190int
191rl_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. */
198int
199rl_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. */
214int
215rl_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. */
239int
240rl_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. */
257int
258rl_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. */
279int
280rl_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. */
308int
309rl_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. */
319int
320rl_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. */
341int
342rl_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. */
362int
363rl_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. */
380int
381rl_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
397int
398rl_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
421int
422rl_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
451int
452rl_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
477int
478rl_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
516int
517rl_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
541int
542rl_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
550int
551rl_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
570int
571rl_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. */
580int
581rl_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. */
591int
592rl_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
600static void
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
625void
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
654int
655rl_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
666int
667rl_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)
678static int
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
724int
725rl_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
767int
768rl_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
779int
780rl_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
792int
793rl_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
803int
804rl_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? */
898static int
899rl_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
947int
948rl_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
973int
974rl_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
1027int
1028rl_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
1056int
1057rl_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
1083int
1084rl_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
1093int
1094rl_vi_first_print (count, key)
1095     int count, key;
1096{
1097  return (rl_vi_back_to_indent (1, key));
1098}
1099
1100int
1101rl_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 */
1162int
1163rl_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
1252int
1253rl_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. */
1273int
1274rl_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
1315int
1316rl_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
1326int
1327rl_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
1345int
1346rl_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
1376int
1377rl_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 ;. */
1412int
1413rl_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. */
1437int
1438rl_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
1457int
1458rl_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