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