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