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