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