1/* $FreeBSD$ */
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
891  if (c < 0)
892    {
893      *nextkey = 0;
894      return -1;
895    }
896
897  *nextkey = c;
898
899  if (!member (c, vi_motion))
900    {
901      if (_rl_digit_p (c))
902	{
903	  save = rl_numeric_arg;
904	  rl_numeric_arg = _rl_digit_value (c);
905	  rl_explicit_arg = 1;
906	  RL_SETSTATE (RL_STATE_NUMERICARG|RL_STATE_VIMOTION);
907	  rl_digit_loop1 ();
908	  RL_UNSETSTATE (RL_STATE_VIMOTION);
909	  rl_numeric_arg *= save;
910	  RL_SETSTATE(RL_STATE_MOREINPUT);
911	  c = rl_read_key ();	/* real command */
912	  RL_UNSETSTATE(RL_STATE_MOREINPUT);
913	  if (c < 0)
914	    {
915	      *nextkey = 0;
916	      return -1;
917	    }
918	  *nextkey = c;
919	}
920      else if (key == c && (key == 'd' || key == 'y' || key == 'c'))
921	{
922	  rl_mark = rl_end;
923	  rl_beg_of_line (1, c);
924	  _rl_vi_last_motion = c;
925	  return (0);
926	}
927      else
928	return (-1);
929    }
930
931  _rl_vi_last_motion = c;
932
933  /* Append a blank character temporarily so that the motion routines
934     work right at the end of the line. */
935  old_end = rl_end;
936  rl_line_buffer[rl_end++] = ' ';
937  rl_line_buffer[rl_end] = '\0';
938
939  _rl_dispatch (c, _rl_keymap);
940
941  /* Remove the blank that we added. */
942  rl_end = old_end;
943  rl_line_buffer[rl_end] = '\0';
944  if (rl_point > rl_end)
945    rl_point = rl_end;
946
947  /* No change in position means the command failed. */
948  if (rl_mark == rl_point)
949    return (-1);
950
951  /* rl_vi_f[wW]ord () leaves the cursor on the first character of the next
952     word.  If we are not at the end of the line, and we are on a
953     non-whitespace character, move back one (presumably to whitespace). */
954  if ((_rl_to_upper (c) == 'W') && rl_point < rl_end && rl_point > rl_mark &&
955      !whitespace (rl_line_buffer[rl_point]))
956    rl_point--;
957
958  /* If cw or cW, back up to the end of a word, so the behaviour of ce
959     or cE is the actual result.  Brute-force, no subtlety. */
960  if (key == 'c' && rl_point >= rl_mark && (_rl_to_upper (c) == 'W'))
961    {
962      /* Don't move farther back than where we started. */
963      while (rl_point > rl_mark && whitespace (rl_line_buffer[rl_point]))
964	rl_point--;
965
966      /* Posix.2 says that if cw or cW moves the cursor towards the end of
967	 the line, the character under the cursor should be deleted. */
968      if (rl_point == rl_mark)
969        rl_point++;
970      else
971	{
972	  /* Move past the end of the word so that the kill doesn't
973	     remove the last letter of the previous word.  Only do this
974	     if we are not at the end of the line. */
975	  if (rl_point >= 0 && rl_point < (rl_end - 1) && !whitespace (rl_line_buffer[rl_point]))
976	    rl_point++;
977	}
978    }
979
980  if (rl_mark < rl_point)
981    SWAP (rl_point, rl_mark);
982
983  return (0);
984}
985
986/* Process C as part of the current numeric argument.  Return -1 if the
987   argument should be aborted, 0 if we should not read any more chars, and
988   1 if we should continue to read chars. */
989static int
990_rl_vi_arg_dispatch (c)
991     int c;
992{
993  int key;
994
995  key = c;
996  if (c >= 0 && _rl_keymap[c].type == ISFUNC && _rl_keymap[c].function == rl_universal_argument)
997    {
998      rl_numeric_arg *= 4;
999      return 1;
1000    }
1001
1002  c = UNMETA (c);
1003
1004  if (_rl_digit_p (c))
1005    {
1006      if (rl_explicit_arg)
1007	rl_numeric_arg = (rl_numeric_arg * 10) + _rl_digit_value (c);
1008      else
1009	rl_numeric_arg = _rl_digit_value (c);
1010      rl_explicit_arg = 1;
1011      return 1;
1012    }
1013  else
1014    {
1015      rl_clear_message ();
1016      rl_stuff_char (key);
1017      return 0;
1018    }
1019}
1020
1021/* A simplified loop for vi. Don't dispatch key at end.
1022   Don't recognize minus sign?
1023   Should this do rl_save_prompt/rl_restore_prompt? */
1024static int
1025rl_digit_loop1 ()
1026{
1027  int c, r;
1028
1029  while (1)
1030    {
1031      if (_rl_arg_overflow ())
1032	return 1;
1033
1034      c = _rl_arg_getchar ();
1035
1036      r = _rl_vi_arg_dispatch (c);
1037      if (r <= 0)
1038	break;
1039    }
1040
1041  RL_UNSETSTATE(RL_STATE_NUMERICARG);
1042  return (0);
1043}
1044
1045int
1046rl_vi_delete_to (count, key)
1047     int count, key;
1048{
1049  int c;
1050
1051  if (_rl_uppercase_p (key))
1052    rl_stuff_char ('$');
1053  else if (vi_redoing)
1054    rl_stuff_char (_rl_vi_last_motion);
1055
1056  if (rl_vi_domove (key, &c))
1057    {
1058      rl_ding ();
1059      return -1;
1060    }
1061
1062  /* These are the motion commands that do not require adjusting the
1063     mark. */
1064  if ((strchr (" l|h^0bB", c) == 0) && (rl_mark < rl_end))
1065    rl_mark++;
1066
1067  rl_kill_text (rl_point, rl_mark);
1068  return (0);
1069}
1070
1071int
1072rl_vi_change_to (count, key)
1073     int count, key;
1074{
1075  int c, start_pos;
1076
1077  if (_rl_uppercase_p (key))
1078    rl_stuff_char ('$');
1079  else if (vi_redoing)
1080    rl_stuff_char (_rl_vi_last_motion);
1081
1082  start_pos = rl_point;
1083
1084  if (rl_vi_domove (key, &c))
1085    {
1086      rl_ding ();
1087      return -1;
1088    }
1089
1090  /* These are the motion commands that do not require adjusting the
1091     mark.  c[wW] are handled by special-case code in rl_vi_domove(),
1092     and already leave the mark at the correct location. */
1093  if ((strchr (" l|hwW^0bB", c) == 0) && (rl_mark < rl_end))
1094    rl_mark++;
1095
1096  /* The cursor never moves with c[wW]. */
1097  if ((_rl_to_upper (c) == 'W') && rl_point < start_pos)
1098    rl_point = start_pos;
1099
1100  if (vi_redoing)
1101    {
1102      if (vi_insert_buffer && *vi_insert_buffer)
1103	rl_begin_undo_group ();
1104      rl_delete_text (rl_point, rl_mark);
1105      if (vi_insert_buffer && *vi_insert_buffer)
1106	{
1107	  rl_insert_text (vi_insert_buffer);
1108	  rl_end_undo_group ();
1109	}
1110    }
1111  else
1112    {
1113      rl_begin_undo_group ();		/* to make the `u' command work */
1114      rl_kill_text (rl_point, rl_mark);
1115      /* `C' does not save the text inserted for undoing or redoing. */
1116      if (_rl_uppercase_p (key) == 0)
1117        _rl_vi_doing_insert = 1;
1118      rl_vi_start_inserting (key, rl_numeric_arg, rl_arg_sign);
1119    }
1120
1121  return (0);
1122}
1123
1124int
1125rl_vi_yank_to (count, key)
1126     int count, key;
1127{
1128  int c, save;
1129
1130  save = rl_point;
1131  if (_rl_uppercase_p (key))
1132    rl_stuff_char ('$');
1133
1134  if (rl_vi_domove (key, &c))
1135    {
1136      rl_ding ();
1137      return -1;
1138    }
1139
1140  /* These are the motion commands that do not require adjusting the
1141     mark. */
1142  if ((strchr (" l|h^0%bB", c) == 0) && (rl_mark < rl_end))
1143    rl_mark++;
1144
1145  rl_begin_undo_group ();
1146  rl_kill_text (rl_point, rl_mark);
1147  rl_end_undo_group ();
1148  rl_do_undo ();
1149  rl_point = save;
1150
1151  return (0);
1152}
1153
1154int
1155rl_vi_rubout (count, key)
1156     int count, key;
1157{
1158  int opoint;
1159
1160  if (count < 0)
1161    return (rl_vi_delete (-count, key));
1162
1163  if (rl_point == 0)
1164    {
1165      rl_ding ();
1166      return -1;
1167    }
1168
1169  opoint = rl_point;
1170  if (count > 1 && MB_CUR_MAX > 1 && rl_byte_oriented == 0)
1171    rl_backward_char (count, key);
1172  else if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
1173    rl_point = _rl_find_prev_mbchar (rl_line_buffer, rl_point, MB_FIND_NONZERO);
1174  else
1175    rl_point -= count;
1176
1177  if (rl_point < 0)
1178    rl_point = 0;
1179
1180  rl_kill_text (rl_point, opoint);
1181
1182  return (0);
1183}
1184
1185int
1186rl_vi_delete (count, key)
1187     int count, key;
1188{
1189  int end;
1190
1191  if (count < 0)
1192    return (rl_vi_rubout (-count, key));
1193
1194  if (rl_end == 0)
1195    {
1196      rl_ding ();
1197      return -1;
1198    }
1199
1200  if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
1201    end = _rl_find_next_mbchar (rl_line_buffer, rl_point, count, MB_FIND_NONZERO);
1202  else
1203    end = rl_point + count;
1204
1205  if (end >= rl_end)
1206    end = rl_end;
1207
1208  rl_kill_text (rl_point, end);
1209
1210  if (rl_point > 0 && rl_point == rl_end)
1211    rl_backward_char (1, key);
1212
1213  return (0);
1214}
1215
1216int
1217rl_vi_back_to_indent (count, key)
1218     int count, key;
1219{
1220  rl_beg_of_line (1, key);
1221  while (rl_point < rl_end && whitespace (rl_line_buffer[rl_point]))
1222    rl_point++;
1223  return (0);
1224}
1225
1226int
1227rl_vi_first_print (count, key)
1228     int count, key;
1229{
1230  return (rl_vi_back_to_indent (1, key));
1231}
1232
1233static int _rl_cs_dir, _rl_cs_orig_dir;
1234
1235#if defined (READLINE_CALLBACKS)
1236static int
1237_rl_vi_callback_char_search (data)
1238     _rl_callback_generic_arg *data;
1239{
1240  int c;
1241#if defined (HANDLE_MULTIBYTE)
1242  c = _rl_vi_last_search_mblen = _rl_read_mbchar (_rl_vi_last_search_mbchar, MB_LEN_MAX);
1243#else
1244  RL_SETSTATE(RL_STATE_MOREINPUT);
1245  c = rl_read_key ();
1246  RL_UNSETSTATE(RL_STATE_MOREINPUT);
1247#endif
1248
1249  if (c <= 0)
1250    return -1;
1251
1252#if !defined (HANDLE_MULTIBYTE)
1253  _rl_vi_last_search_char = c;
1254#endif
1255
1256  _rl_callback_func = 0;
1257  _rl_want_redisplay = 1;
1258
1259#if defined (HANDLE_MULTIBYTE)
1260  return (_rl_char_search_internal (data->count, _rl_cs_dir, _rl_vi_last_search_mbchar, _rl_vi_last_search_mblen));
1261#else
1262  return (_rl_char_search_internal (data->count, _rl_cs_dir, _rl_vi_last_search_char));
1263#endif
1264}
1265#endif
1266
1267int
1268rl_vi_char_search (count, key)
1269     int count, key;
1270{
1271  int c;
1272#if defined (HANDLE_MULTIBYTE)
1273  static char *target;
1274  static int tlen;
1275#else
1276  static char target;
1277#endif
1278
1279  if (key == ';' || key == ',')
1280    _rl_cs_dir = (key == ';') ? _rl_cs_orig_dir : -_rl_cs_orig_dir;
1281  else
1282    {
1283      switch (key)
1284        {
1285        case 't':
1286          _rl_cs_orig_dir = _rl_cs_dir = FTO;
1287          break;
1288
1289        case 'T':
1290          _rl_cs_orig_dir = _rl_cs_dir = BTO;
1291          break;
1292
1293        case 'f':
1294          _rl_cs_orig_dir = _rl_cs_dir = FFIND;
1295          break;
1296
1297        case 'F':
1298          _rl_cs_orig_dir = _rl_cs_dir = BFIND;
1299          break;
1300        }
1301
1302      if (vi_redoing)
1303	{
1304	  /* set target and tlen below */
1305	}
1306#if defined (READLINE_CALLBACKS)
1307      else if (RL_ISSTATE (RL_STATE_CALLBACK))
1308        {
1309          _rl_callback_data = _rl_callback_data_alloc (count);
1310          _rl_callback_data->i1 = _rl_cs_dir;
1311          _rl_callback_func = _rl_vi_callback_char_search;
1312          return (0);
1313        }
1314#endif
1315      else
1316	{
1317#if defined (HANDLE_MULTIBYTE)
1318	  c = _rl_read_mbchar (_rl_vi_last_search_mbchar, MB_LEN_MAX);
1319	  if (c <= 0)
1320	    return -1;
1321	  _rl_vi_last_search_mblen = c;
1322#else
1323	  RL_SETSTATE(RL_STATE_MOREINPUT);
1324	  c = rl_read_key ();
1325	  RL_UNSETSTATE(RL_STATE_MOREINPUT);
1326	  if (c < 0)
1327	    return -1;
1328	  _rl_vi_last_search_char = c;
1329#endif
1330	}
1331    }
1332
1333#if defined (HANDLE_MULTIBYTE)
1334  target = _rl_vi_last_search_mbchar;
1335  tlen = _rl_vi_last_search_mblen;
1336#else
1337  target = _rl_vi_last_search_char;
1338#endif
1339
1340#if defined (HANDLE_MULTIBYTE)
1341  return (_rl_char_search_internal (count, _rl_cs_dir, target, tlen));
1342#else
1343  return (_rl_char_search_internal (count, _rl_cs_dir, target));
1344#endif
1345}
1346
1347/* Match brackets */
1348int
1349rl_vi_match (ignore, key)
1350     int ignore, key;
1351{
1352  int count = 1, brack, pos, tmp, pre;
1353
1354  pos = rl_point;
1355  if ((brack = rl_vi_bracktype (rl_line_buffer[rl_point])) == 0)
1356    {
1357      if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
1358	{
1359	  while ((brack = rl_vi_bracktype (rl_line_buffer[rl_point])) == 0)
1360	    {
1361	      pre = rl_point;
1362	      rl_forward_char (1, key);
1363	      if (pre == rl_point)
1364	        break;
1365	    }
1366	}
1367      else
1368	while ((brack = rl_vi_bracktype (rl_line_buffer[rl_point])) == 0 &&
1369		rl_point < rl_end - 1)
1370	  rl_forward_char (1, key);
1371
1372      if (brack <= 0)
1373	{
1374	  rl_point = pos;
1375	  rl_ding ();
1376	  return -1;
1377	}
1378    }
1379
1380  pos = rl_point;
1381
1382  if (brack < 0)
1383    {
1384      while (count)
1385	{
1386	  tmp = pos;
1387	  if (MB_CUR_MAX == 1 || rl_byte_oriented)
1388	    pos--;
1389	  else
1390	    {
1391	      pos = _rl_find_prev_mbchar (rl_line_buffer, pos, MB_FIND_ANY);
1392	      if (tmp == pos)
1393	        pos--;
1394	    }
1395	  if (pos >= 0)
1396	    {
1397	      int b = rl_vi_bracktype (rl_line_buffer[pos]);
1398	      if (b == -brack)
1399		count--;
1400	      else if (b == brack)
1401		count++;
1402	    }
1403	  else
1404	    {
1405	      rl_ding ();
1406	      return -1;
1407	    }
1408	}
1409    }
1410  else
1411    {			/* brack > 0 */
1412      while (count)
1413	{
1414	  if (MB_CUR_MAX == 1 || rl_byte_oriented)
1415	    pos++;
1416	  else
1417	    pos = _rl_find_next_mbchar (rl_line_buffer, pos, 1, MB_FIND_ANY);
1418
1419	  if (pos < rl_end)
1420	    {
1421	      int b = rl_vi_bracktype (rl_line_buffer[pos]);
1422	      if (b == -brack)
1423		count--;
1424	      else if (b == brack)
1425		count++;
1426	    }
1427	  else
1428	    {
1429	      rl_ding ();
1430	      return -1;
1431	    }
1432	}
1433    }
1434  rl_point = pos;
1435  return (0);
1436}
1437
1438int
1439rl_vi_bracktype (c)
1440     int c;
1441{
1442  switch (c)
1443    {
1444    case '(': return  1;
1445    case ')': return -1;
1446    case '[': return  2;
1447    case ']': return -2;
1448    case '{': return  3;
1449    case '}': return -3;
1450    default:  return  0;
1451    }
1452}
1453
1454static int
1455_rl_vi_change_char (count, c, mb)
1456     int count, c;
1457     char *mb;
1458{
1459  int p;
1460
1461  if (c == '\033' || c == CTRL ('C'))
1462    return -1;
1463
1464  rl_begin_undo_group ();
1465  while (count-- && rl_point < rl_end)
1466    {
1467      p = rl_point;
1468      rl_vi_delete (1, c);
1469      if (rl_point < p)		/* Did we retreat at EOL? */
1470	rl_point++;
1471#if defined (HANDLE_MULTIBYTE)
1472      if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
1473	rl_insert_text (mb);
1474      else
1475#endif
1476	_rl_insert_char (1, c);
1477    }
1478
1479  /* The cursor shall be left on the last character changed. */
1480  rl_backward_char (1, c);
1481
1482  rl_end_undo_group ();
1483
1484  return (0);
1485}
1486
1487static int
1488_rl_vi_callback_getchar (mb, mlen)
1489     char *mb;
1490     int mlen;
1491{
1492  int c;
1493
1494  RL_SETSTATE(RL_STATE_MOREINPUT);
1495  c = rl_read_key ();
1496  RL_UNSETSTATE(RL_STATE_MOREINPUT);
1497
1498  if (c < 0)
1499    return -1;
1500
1501#if defined (HANDLE_MULTIBYTE)
1502  if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
1503    c = _rl_read_mbstring (c, mb, mlen);
1504#endif
1505
1506  return c;
1507}
1508
1509#if defined (READLINE_CALLBACKS)
1510static int
1511_rl_vi_callback_change_char (data)
1512     _rl_callback_generic_arg *data;
1513{
1514  int c;
1515  char mb[MB_LEN_MAX];
1516
1517  _rl_vi_last_replacement = c = _rl_vi_callback_getchar (mb, MB_LEN_MAX);
1518
1519  if (c < 0)
1520    return -1;
1521
1522  _rl_callback_func = 0;
1523  _rl_want_redisplay = 1;
1524
1525  return (_rl_vi_change_char (data->count, c, mb));
1526}
1527#endif
1528
1529int
1530rl_vi_change_char (count, key)
1531     int count, key;
1532{
1533  int c;
1534  char mb[MB_LEN_MAX];
1535
1536  if (vi_redoing)
1537    {
1538      c = _rl_vi_last_replacement;
1539      mb[0] = c;
1540      mb[1] = '\0';
1541    }
1542#if defined (READLINE_CALLBACKS)
1543  else if (RL_ISSTATE (RL_STATE_CALLBACK))
1544    {
1545      _rl_callback_data = _rl_callback_data_alloc (count);
1546      _rl_callback_func = _rl_vi_callback_change_char;
1547      return (0);
1548    }
1549#endif
1550  else
1551    _rl_vi_last_replacement = c = _rl_vi_callback_getchar (mb, MB_LEN_MAX);
1552
1553  if (c < 0)
1554    return -1;
1555
1556  return (_rl_vi_change_char (count, c, mb));
1557}
1558
1559int
1560rl_vi_subst (count, key)
1561     int count, key;
1562{
1563  /* If we are redoing, rl_vi_change_to will stuff the last motion char */
1564  if (vi_redoing == 0)
1565    rl_stuff_char ((key == 'S') ? 'c' : 'l');	/* `S' == `cc', `s' == `cl' */
1566
1567  return (rl_vi_change_to (count, 'c'));
1568}
1569
1570int
1571rl_vi_overstrike (count, key)
1572     int count, key;
1573{
1574  if (_rl_vi_doing_insert == 0)
1575    {
1576      _rl_vi_doing_insert = 1;
1577      rl_begin_undo_group ();
1578    }
1579
1580  if (count > 0)
1581    {
1582      _rl_overwrite_char (count, key);
1583      vi_replace_count += count;
1584    }
1585
1586  return (0);
1587}
1588
1589int
1590rl_vi_overstrike_delete (count, key)
1591     int count, key;
1592{
1593  int i, s;
1594
1595  for (i = 0; i < count; i++)
1596    {
1597      if (vi_replace_count == 0)
1598	{
1599	  rl_ding ();
1600	  break;
1601	}
1602      s = rl_point;
1603
1604      if (rl_do_undo ())
1605	vi_replace_count--;
1606
1607      if (rl_point == s)
1608	rl_backward_char (1, key);
1609    }
1610
1611  if (vi_replace_count == 0 && _rl_vi_doing_insert)
1612    {
1613      rl_end_undo_group ();
1614      rl_do_undo ();
1615      _rl_vi_doing_insert = 0;
1616    }
1617  return (0);
1618}
1619
1620int
1621rl_vi_replace (count, key)
1622     int count, key;
1623{
1624  int i;
1625
1626  vi_replace_count = 0;
1627
1628  if (!vi_replace_map)
1629    {
1630      vi_replace_map = rl_make_bare_keymap ();
1631
1632      for (i = ' '; i < KEYMAP_SIZE; i++)
1633	vi_replace_map[i].function = rl_vi_overstrike;
1634
1635      vi_replace_map[RUBOUT].function = rl_vi_overstrike_delete;
1636      vi_replace_map[ESC].function = rl_vi_movement_mode;
1637      vi_replace_map[RETURN].function = rl_newline;
1638      vi_replace_map[NEWLINE].function = rl_newline;
1639
1640      /* If the normal vi insertion keymap has ^H bound to erase, do the
1641         same here.  Probably should remove the assignment to RUBOUT up
1642         there, but I don't think it will make a difference in real life. */
1643      if (vi_insertion_keymap[CTRL ('H')].type == ISFUNC &&
1644	  vi_insertion_keymap[CTRL ('H')].function == rl_rubout)
1645	vi_replace_map[CTRL ('H')].function = rl_vi_overstrike_delete;
1646
1647    }
1648  _rl_keymap = vi_replace_map;
1649  return (0);
1650}
1651
1652#if 0
1653/* Try to complete the word we are standing on or the word that ends with
1654   the previous character.  A space matches everything.  Word delimiters are
1655   space and ;. */
1656int
1657rl_vi_possible_completions()
1658{
1659  int save_pos = rl_point;
1660
1661  if (rl_line_buffer[rl_point] != ' ' && rl_line_buffer[rl_point] != ';')
1662    {
1663      while (rl_point < rl_end && rl_line_buffer[rl_point] != ' ' &&
1664	     rl_line_buffer[rl_point] != ';')
1665	rl_point++;
1666    }
1667  else if (rl_line_buffer[rl_point - 1] == ';')
1668    {
1669      rl_ding ();
1670      return (0);
1671    }
1672
1673  rl_possible_completions ();
1674  rl_point = save_pos;
1675
1676  return (0);
1677}
1678#endif
1679
1680/* Functions to save and restore marks. */
1681static int
1682_rl_vi_set_mark ()
1683{
1684  int ch;
1685
1686  RL_SETSTATE(RL_STATE_MOREINPUT);
1687  ch = rl_read_key ();
1688  RL_UNSETSTATE(RL_STATE_MOREINPUT);
1689
1690  if (ch < 0 || ch < 'a' || ch > 'z')	/* make test against 0 explicit */
1691    {
1692      rl_ding ();
1693      return -1;
1694    }
1695  ch -= 'a';
1696  vi_mark_chars[ch] = rl_point;
1697  return 0;
1698}
1699
1700#if defined (READLINE_CALLBACKS)
1701static int
1702_rl_vi_callback_set_mark (data)
1703     _rl_callback_generic_arg *data;
1704{
1705  _rl_callback_func = 0;
1706  _rl_want_redisplay = 1;
1707
1708  return (_rl_vi_set_mark ());
1709}
1710#endif
1711
1712int
1713rl_vi_set_mark (count, key)
1714     int count, key;
1715{
1716#if defined (READLINE_CALLBACKS)
1717  if (RL_ISSTATE (RL_STATE_CALLBACK))
1718    {
1719      _rl_callback_data = 0;
1720      _rl_callback_func = _rl_vi_callback_set_mark;
1721      return (0);
1722    }
1723#endif
1724
1725  return (_rl_vi_set_mark ());
1726}
1727
1728static int
1729_rl_vi_goto_mark ()
1730{
1731  int ch;
1732
1733  RL_SETSTATE(RL_STATE_MOREINPUT);
1734  ch = rl_read_key ();
1735  RL_UNSETSTATE(RL_STATE_MOREINPUT);
1736
1737  if (ch == '`')
1738    {
1739      rl_point = rl_mark;
1740      return 0;
1741    }
1742  else if (ch < 0 || ch < 'a' || ch > 'z')	/* make test against 0 explicit */
1743    {
1744      rl_ding ();
1745      return -1;
1746    }
1747
1748  ch -= 'a';
1749  if (vi_mark_chars[ch] == -1)
1750    {
1751      rl_ding ();
1752      return -1;
1753    }
1754  rl_point = vi_mark_chars[ch];
1755  return 0;
1756}
1757
1758#if defined (READLINE_CALLBACKS)
1759static int
1760_rl_vi_callback_goto_mark (data)
1761     _rl_callback_generic_arg *data;
1762{
1763  _rl_callback_func = 0;
1764  _rl_want_redisplay = 1;
1765
1766  return (_rl_vi_goto_mark ());
1767}
1768#endif
1769
1770int
1771rl_vi_goto_mark (count, key)
1772     int count, key;
1773{
1774#if defined (READLINE_CALLBACKS)
1775  if (RL_ISSTATE (RL_STATE_CALLBACK))
1776    {
1777      _rl_callback_data = 0;
1778      _rl_callback_func = _rl_vi_callback_goto_mark;
1779      return (0);
1780    }
1781#endif
1782
1783  return (_rl_vi_goto_mark ());
1784}
1785#endif /* VI_MODE */
1786