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