vi_mode.c revision 119611
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];
684  mbstate_t ps;
685
686  memset (&ps, 0, sizeof (mbstate_t));
687  if (_rl_adjust_point (rl_line_buffer, rl_point, &ps) > 0)
688    count--;
689  while (count-- && rl_point < rl_end)
690    {
691      mbrtowc (&wc, rl_line_buffer + rl_point, rl_end - rl_point, &ps);
692      if (iswupper (wc))
693	wc = towlower (wc);
694      else if (iswlower (wc))
695	wc = towupper (wc);
696      else
697	{
698	  /* Just skip over chars neither upper nor lower case */
699	  rl_forward_char (1, 0);
700	  continue;
701	}
702
703      /* Vi is kind of strange here. */
704      if (wc)
705	{
706	  wctomb (mb, wc);
707	  rl_begin_undo_group ();
708	  rl_delete (1, 0);
709	  rl_insert_text (mb);
710	  rl_end_undo_group ();
711	  rl_vi_check ();
712	}
713      else
714        rl_forward_char (1, 0);
715    }
716
717  return 0;
718}
719#endif
720
721int
722rl_vi_change_case (count, ignore)
723     int count, ignore;
724{
725  char c = 0;
726
727  /* Don't try this on an empty line. */
728  if (rl_point >= rl_end)
729    return (0);
730
731#if defined (HANDLE_MULTIBYTE)
732  if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
733    return (_rl_vi_change_mbchar_case (count));
734#endif
735
736  while (count-- && rl_point < rl_end)
737    {
738      if (_rl_uppercase_p (rl_line_buffer[rl_point]))
739	c = _rl_to_lower (rl_line_buffer[rl_point]);
740      else if (_rl_lowercase_p (rl_line_buffer[rl_point]))
741	c = _rl_to_upper (rl_line_buffer[rl_point]);
742      else
743	{
744	  /* Just skip over characters neither upper nor lower case. */
745	  rl_forward_char (1, c);
746	  continue;
747	}
748
749      /* Vi is kind of strange here. */
750      if (c)
751	{
752	  rl_begin_undo_group ();
753	  rl_delete (1, c);
754	  _rl_insert_char (1, c);
755	  rl_end_undo_group ();
756	  rl_vi_check ();
757        }
758      else
759	rl_forward_char (1, c);
760    }
761  return (0);
762}
763
764int
765rl_vi_put (count, key)
766     int count, key;
767{
768  if (!_rl_uppercase_p (key) && (rl_point + 1 <= rl_end))
769    rl_point = _rl_find_next_mbchar (rl_line_buffer, rl_point, 1, MB_FIND_NONZERO);
770
771  rl_yank (1, key);
772  rl_backward_char (1, key);
773  return (0);
774}
775
776int
777rl_vi_check ()
778{
779  if (rl_point && rl_point == rl_end)
780    {
781      if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
782	rl_point = _rl_find_prev_mbchar (rl_line_buffer, rl_point, MB_FIND_NONZERO);
783      else
784        rl_point--;
785    }
786  return (0);
787}
788
789int
790rl_vi_column (count, key)
791     int count, key;
792{
793  if (count > rl_end)
794    rl_end_of_line (1, key);
795  else
796    rl_point = count - 1;
797  return (0);
798}
799
800int
801rl_vi_domove (key, nextkey)
802     int key, *nextkey;
803{
804  int c, save;
805  int old_end;
806
807  rl_mark = rl_point;
808  RL_SETSTATE(RL_STATE_MOREINPUT);
809  c = rl_read_key ();
810  RL_UNSETSTATE(RL_STATE_MOREINPUT);
811  *nextkey = c;
812
813  if (!member (c, vi_motion))
814    {
815      if (_rl_digit_p (c))
816	{
817	  save = rl_numeric_arg;
818	  rl_numeric_arg = _rl_digit_value (c);
819	  rl_digit_loop1 ();
820	  rl_numeric_arg *= save;
821	  RL_SETSTATE(RL_STATE_MOREINPUT);
822	  c = rl_read_key ();	/* real command */
823	  RL_UNSETSTATE(RL_STATE_MOREINPUT);
824	  *nextkey = c;
825	}
826      else if (key == c && (key == 'd' || key == 'y' || key == 'c'))
827	{
828	  rl_mark = rl_end;
829	  rl_beg_of_line (1, c);
830	  _rl_vi_last_motion = c;
831	  return (0);
832	}
833      else
834	return (-1);
835    }
836
837  _rl_vi_last_motion = c;
838
839  /* Append a blank character temporarily so that the motion routines
840     work right at the end of the line. */
841  old_end = rl_end;
842  rl_line_buffer[rl_end++] = ' ';
843  rl_line_buffer[rl_end] = '\0';
844
845  _rl_dispatch (c, _rl_keymap);
846
847  /* Remove the blank that we added. */
848  rl_end = old_end;
849  rl_line_buffer[rl_end] = '\0';
850  if (rl_point > rl_end)
851    rl_point = rl_end;
852
853  /* No change in position means the command failed. */
854  if (rl_mark == rl_point)
855    return (-1);
856
857  /* rl_vi_f[wW]ord () leaves the cursor on the first character of the next
858     word.  If we are not at the end of the line, and we are on a
859     non-whitespace character, move back one (presumably to whitespace). */
860  if ((_rl_to_upper (c) == 'W') && rl_point < rl_end && rl_point > rl_mark &&
861      !whitespace (rl_line_buffer[rl_point]))
862    rl_point--;
863
864  /* If cw or cW, back up to the end of a word, so the behaviour of ce
865     or cE is the actual result.  Brute-force, no subtlety. */
866  if (key == 'c' && rl_point >= rl_mark && (_rl_to_upper (c) == 'W'))
867    {
868      /* Don't move farther back than where we started. */
869      while (rl_point > rl_mark && whitespace (rl_line_buffer[rl_point]))
870	rl_point--;
871
872      /* Posix.2 says that if cw or cW moves the cursor towards the end of
873	 the line, the character under the cursor should be deleted. */
874      if (rl_point == rl_mark)
875        rl_point++;
876      else
877	{
878	  /* Move past the end of the word so that the kill doesn't
879	     remove the last letter of the previous word.  Only do this
880	     if we are not at the end of the line. */
881	  if (rl_point >= 0 && rl_point < (rl_end - 1) && !whitespace (rl_line_buffer[rl_point]))
882	    rl_point++;
883	}
884    }
885
886  if (rl_mark < rl_point)
887    SWAP (rl_point, rl_mark);
888
889  return (0);
890}
891
892/* A simplified loop for vi. Don't dispatch key at end.
893   Don't recognize minus sign?
894   Should this do rl_save_prompt/rl_restore_prompt? */
895static int
896rl_digit_loop1 ()
897{
898  int key, c;
899
900  RL_SETSTATE(RL_STATE_NUMERICARG);
901  while (1)
902    {
903      if (rl_numeric_arg > 1000000)
904	{
905	  rl_explicit_arg = rl_numeric_arg = 0;
906	  rl_ding ();
907	  rl_clear_message ();
908	  RL_UNSETSTATE(RL_STATE_NUMERICARG);
909	  return 1;
910	}
911      rl_message ("(arg: %d) ", rl_arg_sign * rl_numeric_arg);
912      RL_SETSTATE(RL_STATE_MOREINPUT);
913      key = c = rl_read_key ();
914      RL_UNSETSTATE(RL_STATE_MOREINPUT);
915
916      if (c >= 0 && _rl_keymap[c].type == ISFUNC &&
917	  _rl_keymap[c].function == rl_universal_argument)
918	{
919	  rl_numeric_arg *= 4;
920	  continue;
921	}
922
923      c = UNMETA (c);
924      if (_rl_digit_p (c))
925	{
926	  if (rl_explicit_arg)
927	    rl_numeric_arg = (rl_numeric_arg * 10) + _rl_digit_value (c);
928	  else
929	    rl_numeric_arg = _rl_digit_value (c);
930	  rl_explicit_arg = 1;
931	}
932      else
933	{
934	  rl_clear_message ();
935	  rl_stuff_char (key);
936	  break;
937	}
938    }
939
940  RL_UNSETSTATE(RL_STATE_NUMERICARG);
941  return (0);
942}
943
944int
945rl_vi_delete_to (count, key)
946     int count, key;
947{
948  int c;
949
950  if (_rl_uppercase_p (key))
951    rl_stuff_char ('$');
952  else if (vi_redoing)
953    rl_stuff_char (_rl_vi_last_motion);
954
955  if (rl_vi_domove (key, &c))
956    {
957      rl_ding ();
958      return -1;
959    }
960
961  /* These are the motion commands that do not require adjusting the
962     mark. */
963  if ((strchr (" l|h^0bB", c) == 0) && (rl_mark < rl_end))
964    rl_mark++;
965
966  rl_kill_text (rl_point, rl_mark);
967  return (0);
968}
969
970int
971rl_vi_change_to (count, key)
972     int count, key;
973{
974  int c, start_pos;
975
976  if (_rl_uppercase_p (key))
977    rl_stuff_char ('$');
978  else if (vi_redoing)
979    rl_stuff_char (_rl_vi_last_motion);
980
981  start_pos = rl_point;
982
983  if (rl_vi_domove (key, &c))
984    {
985      rl_ding ();
986      return -1;
987    }
988
989  /* These are the motion commands that do not require adjusting the
990     mark.  c[wW] are handled by special-case code in rl_vi_domove(),
991     and already leave the mark at the correct location. */
992  if ((strchr (" l|hwW^0bB", c) == 0) && (rl_mark < rl_end))
993    rl_mark++;
994
995  /* The cursor never moves with c[wW]. */
996  if ((_rl_to_upper (c) == 'W') && rl_point < start_pos)
997    rl_point = start_pos;
998
999  if (vi_redoing)
1000    {
1001      if (vi_insert_buffer && *vi_insert_buffer)
1002	rl_begin_undo_group ();
1003      rl_delete_text (rl_point, rl_mark);
1004      if (vi_insert_buffer && *vi_insert_buffer)
1005	{
1006	  rl_insert_text (vi_insert_buffer);
1007	  rl_end_undo_group ();
1008	}
1009    }
1010  else
1011    {
1012      rl_begin_undo_group ();		/* to make the `u' command work */
1013      rl_kill_text (rl_point, rl_mark);
1014      /* `C' does not save the text inserted for undoing or redoing. */
1015      if (_rl_uppercase_p (key) == 0)
1016        _rl_vi_doing_insert = 1;
1017      _rl_vi_set_last (key, count, rl_arg_sign);
1018      rl_vi_insertion_mode (1, key);
1019    }
1020
1021  return (0);
1022}
1023
1024int
1025rl_vi_yank_to (count, key)
1026     int count, key;
1027{
1028  int c, save = rl_point;
1029
1030  if (_rl_uppercase_p (key))
1031    rl_stuff_char ('$');
1032
1033  if (rl_vi_domove (key, &c))
1034    {
1035      rl_ding ();
1036      return -1;
1037    }
1038
1039  /* These are the motion commands that do not require adjusting the
1040     mark. */
1041  if ((strchr (" l|h^0%bB", c) == 0) && (rl_mark < rl_end))
1042    rl_mark++;
1043
1044  rl_begin_undo_group ();
1045  rl_kill_text (rl_point, rl_mark);
1046  rl_end_undo_group ();
1047  rl_do_undo ();
1048  rl_point = save;
1049
1050  return (0);
1051}
1052
1053int
1054rl_vi_delete (count, key)
1055     int count, key;
1056{
1057  int end;
1058
1059  if (rl_end == 0)
1060    {
1061      rl_ding ();
1062      return -1;
1063    }
1064
1065  if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
1066    end = _rl_find_next_mbchar (rl_line_buffer, rl_point, count, MB_FIND_NONZERO);
1067  else
1068    end = rl_point + count;
1069
1070  if (end >= rl_end)
1071    end = rl_end;
1072
1073  rl_kill_text (rl_point, end);
1074
1075  if (rl_point > 0 && rl_point == rl_end)
1076    rl_backward_char (1, key);
1077  return (0);
1078}
1079
1080int
1081rl_vi_back_to_indent (count, key)
1082     int count, key;
1083{
1084  rl_beg_of_line (1, key);
1085  while (rl_point < rl_end && whitespace (rl_line_buffer[rl_point]))
1086    rl_point++;
1087  return (0);
1088}
1089
1090int
1091rl_vi_first_print (count, key)
1092     int count, key;
1093{
1094  return (rl_vi_back_to_indent (1, key));
1095}
1096
1097int
1098rl_vi_char_search (count, key)
1099     int count, key;
1100{
1101#if defined (HANDLE_MULTIBYTE)
1102  static char *target;
1103  static int mb_len;
1104#else
1105  static char target;
1106#endif
1107  static int orig_dir, dir;
1108
1109  if (key == ';' || key == ',')
1110    dir = key == ';' ? orig_dir : -orig_dir;
1111  else
1112    {
1113      if (vi_redoing)
1114#if defined (HANDLE_MULTIBYTE)
1115	target = _rl_vi_last_search_mbchar;
1116#else
1117	target = _rl_vi_last_search_char;
1118#endif
1119      else
1120	{
1121#if defined (HANDLE_MULTIBYTE)
1122	  mb_len = _rl_read_mbchar (_rl_vi_last_search_mbchar, MB_LEN_MAX);
1123	  target = _rl_vi_last_search_mbchar;
1124#else
1125	  RL_SETSTATE(RL_STATE_MOREINPUT);
1126	  _rl_vi_last_search_char = target = rl_read_key ();
1127	  RL_UNSETSTATE(RL_STATE_MOREINPUT);
1128#endif
1129	}
1130
1131      switch (key)
1132        {
1133        case 't':
1134          orig_dir = dir = FTO;
1135          break;
1136
1137        case 'T':
1138          orig_dir = dir = BTO;
1139          break;
1140
1141        case 'f':
1142          orig_dir = dir = FFIND;
1143          break;
1144
1145        case 'F':
1146          orig_dir = dir = BFIND;
1147          break;
1148        }
1149    }
1150
1151#if defined (HANDLE_MULTIBYTE)
1152   return (_rl_char_search_internal (count, dir, target, mb_len));
1153#else
1154  return (_rl_char_search_internal (count, dir, target));
1155#endif
1156}
1157
1158/* Match brackets */
1159int
1160rl_vi_match (ignore, key)
1161     int ignore, key;
1162{
1163  int count = 1, brack, pos, tmp, pre;
1164
1165  pos = rl_point;
1166  if ((brack = rl_vi_bracktype (rl_line_buffer[rl_point])) == 0)
1167    {
1168      if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
1169	{
1170	  while ((brack = rl_vi_bracktype (rl_line_buffer[rl_point])) == 0)
1171	    {
1172	      pre = rl_point;
1173	      rl_forward_char (1, key);
1174	      if (pre == rl_point)
1175	        break;
1176	    }
1177	}
1178      else
1179	while ((brack = rl_vi_bracktype (rl_line_buffer[rl_point])) == 0 &&
1180		rl_point < rl_end - 1)
1181	  rl_forward_char (1, key);
1182
1183      if (brack <= 0)
1184	{
1185	  rl_point = pos;
1186	  rl_ding ();
1187	  return -1;
1188	}
1189    }
1190
1191  pos = rl_point;
1192
1193  if (brack < 0)
1194    {
1195      while (count)
1196	{
1197	  tmp = pos;
1198	  if (MB_CUR_MAX == 1 || rl_byte_oriented)
1199	    pos--;
1200	  else
1201	    {
1202	      pos = _rl_find_prev_mbchar (rl_line_buffer, pos, MB_FIND_ANY);
1203	      if (tmp == pos)
1204	        pos--;
1205	    }
1206	  if (pos >= 0)
1207	    {
1208	      int b = rl_vi_bracktype (rl_line_buffer[pos]);
1209	      if (b == -brack)
1210		count--;
1211	      else if (b == brack)
1212		count++;
1213	    }
1214	  else
1215	    {
1216	      rl_ding ();
1217	      return -1;
1218	    }
1219	}
1220    }
1221  else
1222    {			/* brack > 0 */
1223      while (count)
1224	{
1225	  if (MB_CUR_MAX == 1 || rl_byte_oriented)
1226	    pos++;
1227	  else
1228	    pos = _rl_find_next_mbchar (rl_line_buffer, pos, 1, MB_FIND_ANY);
1229
1230	  if (pos < rl_end)
1231	    {
1232	      int b = rl_vi_bracktype (rl_line_buffer[pos]);
1233	      if (b == -brack)
1234		count--;
1235	      else if (b == brack)
1236		count++;
1237	    }
1238	  else
1239	    {
1240	      rl_ding ();
1241	      return -1;
1242	    }
1243	}
1244    }
1245  rl_point = pos;
1246  return (0);
1247}
1248
1249int
1250rl_vi_bracktype (c)
1251     int c;
1252{
1253  switch (c)
1254    {
1255    case '(': return  1;
1256    case ')': return -1;
1257    case '[': return  2;
1258    case ']': return -2;
1259    case '{': return  3;
1260    case '}': return -3;
1261    default:  return  0;
1262    }
1263}
1264
1265/* XXX - think about reading an entire mbchar with _rl_read_mbchar and
1266   inserting it in one bunch instead of the loop below (like in
1267   rl_vi_char_search or _rl_vi_change_mbchar_case.  Set c to mbchar[0]
1268   for test against 033 or ^C.  Make sure that _rl_read_mbchar does
1269   this right. */
1270int
1271rl_vi_change_char (count, key)
1272     int count, key;
1273{
1274  int c;
1275
1276  if (vi_redoing)
1277    c = _rl_vi_last_replacement;
1278  else
1279    {
1280      RL_SETSTATE(RL_STATE_MOREINPUT);
1281      _rl_vi_last_replacement = c = rl_read_key ();
1282      RL_UNSETSTATE(RL_STATE_MOREINPUT);
1283    }
1284
1285  if (c == '\033' || c == CTRL ('C'))
1286    return -1;
1287
1288  while (count-- && rl_point < rl_end)
1289    {
1290      rl_begin_undo_group ();
1291
1292      rl_delete (1, c);
1293#if defined (HANDLE_MULTIBYTE)
1294      if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
1295	while (_rl_insert_char (1, c))
1296	  {
1297	    RL_SETSTATE (RL_STATE_MOREINPUT);
1298	    c = rl_read_key ();
1299	    RL_UNSETSTATE (RL_STATE_MOREINPUT);
1300	  }
1301      else
1302#endif
1303	_rl_insert_char (1, c);
1304      if (count == 0)
1305	rl_backward_char (1, c);
1306
1307      rl_end_undo_group ();
1308    }
1309  return (0);
1310}
1311
1312int
1313rl_vi_subst (count, key)
1314     int count, key;
1315{
1316  /* If we are redoing, rl_vi_change_to will stuff the last motion char */
1317  if (vi_redoing == 0)
1318    rl_stuff_char ((key == 'S') ? 'c' : ' ');	/* `S' == `cc', `s' == `c ' */
1319
1320  return (rl_vi_change_to (count, 'c'));
1321}
1322
1323int
1324rl_vi_overstrike (count, key)
1325     int count, key;
1326{
1327  if (_rl_vi_doing_insert == 0)
1328    {
1329      _rl_vi_doing_insert = 1;
1330      rl_begin_undo_group ();
1331    }
1332
1333  if (count > 0)
1334    {
1335      _rl_overwrite_char (count, key);
1336      vi_replace_count += count;
1337    }
1338
1339  return (0);
1340}
1341
1342int
1343rl_vi_overstrike_delete (count, key)
1344     int count, key;
1345{
1346  int i, s;
1347
1348  for (i = 0; i < count; i++)
1349    {
1350      if (vi_replace_count == 0)
1351	{
1352	  rl_ding ();
1353	  break;
1354	}
1355      s = rl_point;
1356
1357      if (rl_do_undo ())
1358	vi_replace_count--;
1359
1360      if (rl_point == s)
1361	rl_backward_char (1, key);
1362    }
1363
1364  if (vi_replace_count == 0 && _rl_vi_doing_insert)
1365    {
1366      rl_end_undo_group ();
1367      rl_do_undo ();
1368      _rl_vi_doing_insert = 0;
1369    }
1370  return (0);
1371}
1372
1373int
1374rl_vi_replace (count, key)
1375     int count, key;
1376{
1377  int i;
1378
1379  vi_replace_count = 0;
1380
1381  if (!vi_replace_map)
1382    {
1383      vi_replace_map = rl_make_bare_keymap ();
1384
1385      for (i = ' '; i < KEYMAP_SIZE; i++)
1386	vi_replace_map[i].function = rl_vi_overstrike;
1387
1388      vi_replace_map[RUBOUT].function = rl_vi_overstrike_delete;
1389      vi_replace_map[ESC].function = rl_vi_movement_mode;
1390      vi_replace_map[RETURN].function = rl_newline;
1391      vi_replace_map[NEWLINE].function = rl_newline;
1392
1393      /* If the normal vi insertion keymap has ^H bound to erase, do the
1394         same here.  Probably should remove the assignment to RUBOUT up
1395         there, but I don't think it will make a difference in real life. */
1396      if (vi_insertion_keymap[CTRL ('H')].type == ISFUNC &&
1397	  vi_insertion_keymap[CTRL ('H')].function == rl_rubout)
1398	vi_replace_map[CTRL ('H')].function = rl_vi_overstrike_delete;
1399
1400    }
1401  _rl_keymap = vi_replace_map;
1402  return (0);
1403}
1404
1405#if 0
1406/* Try to complete the word we are standing on or the word that ends with
1407   the previous character.  A space matches everything.  Word delimiters are
1408   space and ;. */
1409int
1410rl_vi_possible_completions()
1411{
1412  int save_pos = rl_point;
1413
1414  if (rl_line_buffer[rl_point] != ' ' && rl_line_buffer[rl_point] != ';')
1415    {
1416      while (rl_point < rl_end && rl_line_buffer[rl_point] != ' ' &&
1417	     rl_line_buffer[rl_point] != ';')
1418	rl_point++;
1419    }
1420  else if (rl_line_buffer[rl_point - 1] == ';')
1421    {
1422      rl_ding ();
1423      return (0);
1424    }
1425
1426  rl_possible_completions ();
1427  rl_point = save_pos;
1428
1429  return (0);
1430}
1431#endif
1432
1433/* Functions to save and restore marks. */
1434int
1435rl_vi_set_mark (count, key)
1436     int count, key;
1437{
1438  int ch;
1439
1440  RL_SETSTATE(RL_STATE_MOREINPUT);
1441  ch = rl_read_key ();
1442  RL_UNSETSTATE(RL_STATE_MOREINPUT);
1443
1444  if (ch < 'a' || ch > 'z')
1445    {
1446      rl_ding ();
1447      return -1;
1448    }
1449  ch -= 'a';
1450  vi_mark_chars[ch] = rl_point;
1451  return 0;
1452}
1453
1454int
1455rl_vi_goto_mark (count, key)
1456     int count, key;
1457{
1458  int ch;
1459
1460  RL_SETSTATE(RL_STATE_MOREINPUT);
1461  ch = rl_read_key ();
1462  RL_UNSETSTATE(RL_STATE_MOREINPUT);
1463
1464  if (ch == '`')
1465    {
1466      rl_point = rl_mark;
1467      return 0;
1468    }
1469  else if (ch < 'a' || ch > 'z')
1470    {
1471      rl_ding ();
1472      return -1;
1473    }
1474
1475  ch -= 'a';
1476  if (vi_mark_chars[ch] == -1)
1477    {
1478      rl_ding ();
1479      return -1;
1480    }
1481  rl_point = vi_mark_chars[ch];
1482  return 0;
1483}
1484
1485#endif /* VI_MODE */
1486