vi_mode.c revision 75406
121308Sache/* vi_mode.c -- A vi emulation mode for Bash.
221308Sache   Derived from code written by Jeff Sparkes (jsparkes@bnr.ca).  */
321308Sache
421308Sache/* Copyright (C) 1987, 1989, 1992 Free Software Foundation, Inc.
521308Sache
621308Sache   This file is part of the GNU Readline Library, a library for
721308Sache   reading lines of text with interactive input and history editing.
821308Sache
921308Sache   The GNU Readline Library is free software; you can redistribute it
1021308Sache   and/or modify it under the terms of the GNU General Public License
1158310Sache   as published by the Free Software Foundation; either version 2, or
1221308Sache   (at your option) any later version.
1321308Sache
1421308Sache   The GNU Readline Library is distributed in the hope that it will be
1521308Sache   useful, but WITHOUT ANY WARRANTY; without even the implied warranty
1621308Sache   of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1721308Sache   GNU General Public License for more details.
1821308Sache
1921308Sache   The GNU General Public License is often shipped with GNU software, and
2021308Sache   is generally kept in a file called COPYING or LICENSE.  If you do not
2121308Sache   have a copy of the license, write to the Free Software Foundation,
2258310Sache   59 Temple Place, Suite 330, Boston, MA 02111 USA. */
2321308Sache#define READLINE_LIBRARY
2421308Sache
2521308Sache/* **************************************************************** */
2621308Sache/*								    */
2721308Sache/*			VI Emulation Mode			    */
2821308Sache/*								    */
2921308Sache/* **************************************************************** */
3021308Sache#include "rlconf.h"
3121308Sache
3221308Sache#if defined (VI_MODE)
3321308Sache
3421308Sache#if defined (HAVE_CONFIG_H)
3521308Sache#  include <config.h>
3621308Sache#endif
3721308Sache
3821308Sache#include <sys/types.h>
3921308Sache
4021308Sache#if defined (HAVE_STDLIB_H)
4121308Sache#  include <stdlib.h>
4221308Sache#else
4321308Sache#  include "ansi_stdlib.h"
4421308Sache#endif /* HAVE_STDLIB_H */
4521308Sache
4621308Sache#if defined (HAVE_UNISTD_H)
4721308Sache#  include <unistd.h>
4821308Sache#endif
4921308Sache
5021308Sache#include <stdio.h>
5121308Sache
5221308Sache/* Some standard library routines. */
5321308Sache#include "rldefs.h"
5421308Sache#include "readline.h"
5521308Sache#include "history.h"
5621308Sache
5758310Sache#include "rlprivate.h"
5858310Sache#include "xmalloc.h"
5958310Sache
6021308Sache#ifndef _rl_digit_p
6121308Sache#define _rl_digit_p(c)  ((c) >= '0' && (c) <= '9')
6221308Sache#endif
6321308Sache
6421308Sache#ifndef _rl_digit_value
6521308Sache#define _rl_digit_value(c) ((c) - '0')
6621308Sache#endif
6721308Sache
6821308Sache#ifndef member
6921308Sache#define member(c, s) ((c) ? (char *)strchr ((s), (c)) != (char *)NULL : 0)
7021308Sache#endif
7121308Sache
7221308Sache#ifndef isident
7321308Sache#define isident(c) ((_rl_pure_alphabetic (c) || _rl_digit_p (c) || c == '_'))
7421308Sache#endif
7521308Sache
7621308Sache#ifndef exchange
7721308Sache#define exchange(x, y) do {int temp = x; x = y; y = temp;} while (0)
7821308Sache#endif
7921308Sache
8021308Sache/* Non-zero means enter insertion mode. */
8121308Sachestatic int _rl_vi_doing_insert;
8221308Sache
8321308Sache/* Command keys which do movement for xxx_to commands. */
8475406Sachestatic const char *vi_motion = " hl^$0ftFt;,%wbeWBE|";
8521308Sache
8621308Sache/* Keymap used for vi replace characters.  Created dynamically since
8721308Sache   rarely used. */
8821308Sachestatic Keymap vi_replace_map;
8921308Sache
9021308Sache/* The number of characters inserted in the last replace operation. */
9121308Sachestatic int vi_replace_count;
9221308Sache
9321308Sache/* If non-zero, we have text inserted after a c[motion] command that put
9421308Sache   us implicitly into insert mode.  Some people want this text to be
9521308Sache   attached to the command so that it is `redoable' with `.'. */
9621308Sachestatic int vi_continued_command;
9721308Sachestatic char *vi_insert_buffer;
9821308Sachestatic int vi_insert_buffer_size;
9921308Sache
10021308Sachestatic int _rl_vi_last_command = 'i';	/* default `.' puts you in insert mode */
10121308Sachestatic int _rl_vi_last_repeat = 1;
10221308Sachestatic int _rl_vi_last_arg_sign = 1;
10321308Sachestatic int _rl_vi_last_motion;
10421308Sachestatic int _rl_vi_last_search_char;
10521308Sachestatic int _rl_vi_last_replacement;
10621308Sache
10721308Sachestatic int _rl_vi_last_key_before_insert;
10821308Sache
10921308Sachestatic int vi_redoing;
11021308Sache
11121308Sache/* Text modification commands.  These are the `redoable' commands. */
11275406Sachestatic const char *vi_textmod = "_*\\AaIiCcDdPpYyRrSsXx~";
11321308Sache
11421308Sache/* Arrays for the saved marks. */
11521308Sachestatic int vi_mark_chars[27];
11621308Sache
11758310Sachestatic int rl_digit_loop1 __P((void));
11821308Sache
11921308Sachevoid
12021308Sache_rl_vi_initialize_line ()
12121308Sache{
12221308Sache  register int i;
12321308Sache
12421308Sache  for (i = 0; i < sizeof (vi_mark_chars) / sizeof (int); i++)
12521308Sache    vi_mark_chars[i] = -1;
12621308Sache}
12721308Sache
12821308Sachevoid
12921308Sache_rl_vi_reset_last ()
13021308Sache{
13121308Sache  _rl_vi_last_command = 'i';
13221308Sache  _rl_vi_last_repeat = 1;
13321308Sache  _rl_vi_last_arg_sign = 1;
13421308Sache  _rl_vi_last_motion = 0;
13521308Sache}
13621308Sache
13721308Sachevoid
13821308Sache_rl_vi_set_last (key, repeat, sign)
13921308Sache     int key, repeat, sign;
14021308Sache{
14121308Sache  _rl_vi_last_command = key;
14221308Sache  _rl_vi_last_repeat = repeat;
14321308Sache  _rl_vi_last_arg_sign = sign;
14421308Sache}
14521308Sache
14621308Sache/* Is the command C a VI mode text modification command? */
14721308Sacheint
14821308Sache_rl_vi_textmod_command (c)
14921308Sache     int c;
15021308Sache{
15121308Sache  return (member (c, vi_textmod));
15221308Sache}
15321308Sache
15421308Sachestatic void
15521308Sache_rl_vi_stuff_insert (count)
15621308Sache     int count;
15721308Sache{
15821308Sache  rl_begin_undo_group ();
15921308Sache  while (count--)
16021308Sache    rl_insert_text (vi_insert_buffer);
16121308Sache  rl_end_undo_group ();
16221308Sache}
16321308Sache
16421308Sache/* Bound to `.'.  Called from command mode, so we know that we have to
16521308Sache   redo a text modification command.  The default for _rl_vi_last_command
16621308Sache   puts you back into insert mode. */
16721308Sacheint
16821308Sacherl_vi_redo (count, c)
16921308Sache     int count, c;
17021308Sache{
17121308Sache  if (!rl_explicit_arg)
17221308Sache    {
17321308Sache      rl_numeric_arg = _rl_vi_last_repeat;
17421308Sache      rl_arg_sign = _rl_vi_last_arg_sign;
17521308Sache    }
17621308Sache
17721308Sache  vi_redoing = 1;
17821308Sache  /* If we're redoing an insert with `i', stuff in the inserted text
17921308Sache     and do not go into insertion mode. */
18021308Sache  if (_rl_vi_last_command == 'i' && vi_insert_buffer && *vi_insert_buffer)
18121308Sache    {
18221308Sache      _rl_vi_stuff_insert (count);
18321308Sache      /* And back up point over the last character inserted. */
18421308Sache      if (rl_point > 0)
18521308Sache	rl_point--;
18621308Sache    }
18721308Sache  else
18821308Sache    _rl_dispatch (_rl_vi_last_command, _rl_keymap);
18921308Sache  vi_redoing = 0;
19021308Sache
19121308Sache  return (0);
19221308Sache}
19321308Sache
19421308Sache/* A placeholder for further expansion. */
19521308Sacheint
19621308Sacherl_vi_undo (count, key)
19721308Sache     int count, key;
19821308Sache{
19921308Sache  return (rl_undo_command (count, key));
20021308Sache}
20121308Sache
20221308Sache/* Yank the nth arg from the previous line into this line at point. */
20321308Sacheint
20421308Sacherl_vi_yank_arg (count, key)
20521308Sache     int count, key;
20621308Sache{
20721308Sache  /* Readline thinks that the first word on a line is the 0th, while vi
20821308Sache     thinks the first word on a line is the 1st.  Compensate. */
20921308Sache  if (rl_explicit_arg)
21021308Sache    rl_yank_nth_arg (count - 1, 0);
21121308Sache  else
21221308Sache    rl_yank_nth_arg ('$', 0);
21321308Sache
21421308Sache  return (0);
21521308Sache}
21621308Sache
21721308Sache/* With an argument, move back that many history lines, else move to the
21821308Sache   beginning of history. */
21921308Sacheint
22021308Sacherl_vi_fetch_history (count, c)
22121308Sache     int count, c;
22221308Sache{
22321308Sache  int wanted;
22421308Sache
22521308Sache  /* Giving an argument of n means we want the nth command in the history
22621308Sache     file.  The command number is interpreted the same way that the bash
22721308Sache     `history' command does it -- that is, giving an argument count of 450
22821308Sache     to this command would get the command listed as number 450 in the
22921308Sache     output of `history'. */
23021308Sache  if (rl_explicit_arg)
23121308Sache    {
23221308Sache      wanted = history_base + where_history () - count;
23321308Sache      if (wanted <= 0)
23421308Sache        rl_beginning_of_history (0, 0);
23521308Sache      else
23621308Sache        rl_get_previous_history (wanted, c);
23721308Sache    }
23821308Sache  else
23921308Sache    rl_beginning_of_history (count, 0);
24021308Sache  return (0);
24121308Sache}
24221308Sache
24321308Sache/* Search again for the last thing searched for. */
24421308Sacheint
24521308Sacherl_vi_search_again (count, key)
24621308Sache     int count, key;
24721308Sache{
24821308Sache  switch (key)
24921308Sache    {
25021308Sache    case 'n':
25121308Sache      rl_noninc_reverse_search_again (count, key);
25221308Sache      break;
25321308Sache
25421308Sache    case 'N':
25521308Sache      rl_noninc_forward_search_again (count, key);
25621308Sache      break;
25721308Sache    }
25821308Sache  return (0);
25921308Sache}
26021308Sache
26121308Sache/* Do a vi style search. */
26221308Sacheint
26321308Sacherl_vi_search (count, key)
26421308Sache     int count, key;
26521308Sache{
26621308Sache  switch (key)
26721308Sache    {
26821308Sache    case '?':
26921308Sache      rl_noninc_forward_search (count, key);
27021308Sache      break;
27121308Sache
27221308Sache    case '/':
27321308Sache      rl_noninc_reverse_search (count, key);
27421308Sache      break;
27521308Sache
27621308Sache    default:
27775406Sache      rl_ding ();
27821308Sache      break;
27921308Sache    }
28021308Sache  return (0);
28121308Sache}
28221308Sache
28321308Sache/* Completion, from vi's point of view. */
28421308Sacheint
28521308Sacherl_vi_complete (ignore, key)
28621308Sache     int ignore, key;
28721308Sache{
28821308Sache  if ((rl_point < rl_end) && (!whitespace (rl_line_buffer[rl_point])))
28921308Sache    {
29021308Sache      if (!whitespace (rl_line_buffer[rl_point + 1]))
29121308Sache	rl_vi_end_word (1, 'E');
29221308Sache      rl_point++;
29321308Sache    }
29421308Sache
29521308Sache  if (key == '*')
29621308Sache    rl_complete_internal ('*');	/* Expansion and replacement. */
29721308Sache  else if (key == '=')
29821308Sache    rl_complete_internal ('?');	/* List possible completions. */
29921308Sache  else if (key == '\\')
30021308Sache    rl_complete_internal (TAB);	/* Standard Readline completion. */
30121308Sache  else
30221308Sache    rl_complete (0, key);
30321308Sache
30421308Sache  if (key == '*' || key == '\\')
30521308Sache    {
30621308Sache      _rl_vi_set_last (key, 1, rl_arg_sign);
30721308Sache      rl_vi_insertion_mode (1, key);
30821308Sache    }
30921308Sache  return (0);
31021308Sache}
31121308Sache
31221308Sache/* Tilde expansion for vi mode. */
31321308Sacheint
31421308Sacherl_vi_tilde_expand (ignore, key)
31521308Sache     int ignore, key;
31621308Sache{
31721308Sache  rl_tilde_expand (0, key);
31821308Sache  _rl_vi_set_last (key, 1, rl_arg_sign);	/* XXX */
31921308Sache  rl_vi_insertion_mode (1, key);
32021308Sache  return (0);
32121308Sache}
32221308Sache
32321308Sache/* Previous word in vi mode. */
32421308Sacheint
32521308Sacherl_vi_prev_word (count, key)
32621308Sache     int count, key;
32721308Sache{
32821308Sache  if (count < 0)
32921308Sache    return (rl_vi_next_word (-count, key));
33021308Sache
33121308Sache  if (rl_point == 0)
33221308Sache    {
33375406Sache      rl_ding ();
33421308Sache      return (0);
33521308Sache    }
33621308Sache
33721308Sache  if (_rl_uppercase_p (key))
33847558Sache    rl_vi_bWord (count, key);
33921308Sache  else
34047558Sache    rl_vi_bword (count, key);
34121308Sache
34221308Sache  return (0);
34321308Sache}
34421308Sache
34521308Sache/* Next word in vi mode. */
34621308Sacheint
34721308Sacherl_vi_next_word (count, key)
34821308Sache     int count, key;
34921308Sache{
35021308Sache  if (count < 0)
35121308Sache    return (rl_vi_prev_word (-count, key));
35221308Sache
35321308Sache  if (rl_point >= (rl_end - 1))
35421308Sache    {
35575406Sache      rl_ding ();
35621308Sache      return (0);
35721308Sache    }
35821308Sache
35921308Sache  if (_rl_uppercase_p (key))
36047558Sache    rl_vi_fWord (count, key);
36121308Sache  else
36247558Sache    rl_vi_fword (count, key);
36321308Sache  return (0);
36421308Sache}
36521308Sache
36621308Sache/* Move to the end of the ?next? word. */
36721308Sacheint
36821308Sacherl_vi_end_word (count, key)
36921308Sache     int count, key;
37021308Sache{
37121308Sache  if (count < 0)
37221308Sache    {
37375406Sache      rl_ding ();
37421308Sache      return -1;
37521308Sache    }
37621308Sache
37721308Sache  if (_rl_uppercase_p (key))
37847558Sache    rl_vi_eWord (count, key);
37921308Sache  else
38047558Sache    rl_vi_eword (count, key);
38121308Sache  return (0);
38221308Sache}
38321308Sache
38421308Sache/* Move forward a word the way that 'W' does. */
38521308Sacheint
38647558Sacherl_vi_fWord (count, ignore)
38747558Sache     int count, ignore;
38821308Sache{
38921308Sache  while (count-- && rl_point < (rl_end - 1))
39021308Sache    {
39121308Sache      /* Skip until whitespace. */
39221308Sache      while (!whitespace (rl_line_buffer[rl_point]) && rl_point < rl_end)
39321308Sache	rl_point++;
39421308Sache
39521308Sache      /* Now skip whitespace. */
39621308Sache      while (whitespace (rl_line_buffer[rl_point]) && rl_point < rl_end)
39721308Sache	rl_point++;
39821308Sache    }
39921308Sache  return (0);
40021308Sache}
40121308Sache
40221308Sacheint
40347558Sacherl_vi_bWord (count, ignore)
40447558Sache     int count, ignore;
40521308Sache{
40621308Sache  while (count-- && rl_point > 0)
40721308Sache    {
40821308Sache      /* If we are at the start of a word, move back to whitespace so
40921308Sache	 we will go back to the start of the previous word. */
41021308Sache      if (!whitespace (rl_line_buffer[rl_point]) &&
41121308Sache	  whitespace (rl_line_buffer[rl_point - 1]))
41221308Sache	rl_point--;
41321308Sache
41421308Sache      while (rl_point > 0 && whitespace (rl_line_buffer[rl_point]))
41521308Sache	rl_point--;
41621308Sache
41721308Sache      if (rl_point > 0)
41821308Sache	{
41921308Sache	  while (--rl_point >= 0 && !whitespace (rl_line_buffer[rl_point]));
42021308Sache	  rl_point++;
42121308Sache	}
42221308Sache    }
42321308Sache  return (0);
42421308Sache}
42521308Sache
42621308Sacheint
42747558Sacherl_vi_eWord (count, ignore)
42847558Sache     int count, ignore;
42921308Sache{
43021308Sache  while (count-- && rl_point < (rl_end - 1))
43121308Sache    {
43221308Sache      if (!whitespace (rl_line_buffer[rl_point]))
43321308Sache	rl_point++;
43421308Sache
43521308Sache      /* Move to the next non-whitespace character (to the start of the
43621308Sache	 next word). */
43721308Sache      while (++rl_point < rl_end && whitespace (rl_line_buffer[rl_point]));
43821308Sache
43921308Sache      if (rl_point && rl_point < rl_end)
44021308Sache	{
44121308Sache	  /* Skip whitespace. */
44221308Sache	  while (rl_point < rl_end && whitespace (rl_line_buffer[rl_point]))
44321308Sache	    rl_point++;
44421308Sache
44521308Sache	  /* Skip until whitespace. */
44621308Sache	  while (rl_point < rl_end && !whitespace (rl_line_buffer[rl_point]))
44721308Sache	    rl_point++;
44821308Sache
44921308Sache	  /* Move back to the last character of the word. */
45021308Sache	  rl_point--;
45121308Sache	}
45221308Sache    }
45321308Sache  return (0);
45421308Sache}
45521308Sache
45621308Sacheint
45747558Sacherl_vi_fword (count, ignore)
45847558Sache     int count, ignore;
45921308Sache{
46021308Sache  while (count-- && rl_point < (rl_end - 1))
46121308Sache    {
46221308Sache      /* Move to white space (really non-identifer). */
46321308Sache      if (isident (rl_line_buffer[rl_point]))
46421308Sache	{
46521308Sache	  while (isident (rl_line_buffer[rl_point]) && rl_point < rl_end)
46621308Sache	    rl_point++;
46721308Sache	}
46821308Sache      else /* if (!whitespace (rl_line_buffer[rl_point])) */
46921308Sache	{
47021308Sache	  while (!isident (rl_line_buffer[rl_point]) &&
47121308Sache		 !whitespace (rl_line_buffer[rl_point]) && rl_point < rl_end)
47221308Sache	    rl_point++;
47321308Sache	}
47421308Sache
47521308Sache      /* Move past whitespace. */
47621308Sache      while (whitespace (rl_line_buffer[rl_point]) && rl_point < rl_end)
47721308Sache	rl_point++;
47821308Sache    }
47921308Sache  return (0);
48021308Sache}
48121308Sache
48221308Sacheint
48347558Sacherl_vi_bword (count, ignore)
48447558Sache     int count, ignore;
48521308Sache{
48621308Sache  while (count-- && rl_point > 0)
48721308Sache    {
48821308Sache      int last_is_ident;
48921308Sache
49021308Sache      /* If we are at the start of a word, move back to whitespace
49121308Sache	 so we will go back to the start of the previous word. */
49221308Sache      if (!whitespace (rl_line_buffer[rl_point]) &&
49321308Sache	  whitespace (rl_line_buffer[rl_point - 1]))
49421308Sache	rl_point--;
49521308Sache
49621308Sache      /* If this character and the previous character are `opposite', move
49721308Sache	 back so we don't get messed up by the rl_point++ down there in
49821308Sache	 the while loop.  Without this code, words like `l;' screw up the
49921308Sache	 function. */
50021308Sache      last_is_ident = isident (rl_line_buffer[rl_point - 1]);
50121308Sache      if ((isident (rl_line_buffer[rl_point]) && !last_is_ident) ||
50221308Sache	  (!isident (rl_line_buffer[rl_point]) && last_is_ident))
50321308Sache	rl_point--;
50421308Sache
50521308Sache      while (rl_point > 0 && whitespace (rl_line_buffer[rl_point]))
50621308Sache	rl_point--;
50721308Sache
50821308Sache      if (rl_point > 0)
50921308Sache	{
51021308Sache	  if (isident (rl_line_buffer[rl_point]))
51121308Sache	    while (--rl_point >= 0 && isident (rl_line_buffer[rl_point]));
51221308Sache	  else
51321308Sache	    while (--rl_point >= 0 && !isident (rl_line_buffer[rl_point]) &&
51421308Sache		   !whitespace (rl_line_buffer[rl_point]));
51521308Sache	  rl_point++;
51621308Sache	}
51721308Sache    }
51821308Sache  return (0);
51921308Sache}
52021308Sache
52121308Sacheint
52247558Sacherl_vi_eword (count, ignore)
52347558Sache     int count, ignore;
52421308Sache{
52521308Sache  while (count-- && rl_point < rl_end - 1)
52621308Sache    {
52721308Sache      if (!whitespace (rl_line_buffer[rl_point]))
52821308Sache	rl_point++;
52921308Sache
53021308Sache      while (rl_point < rl_end && whitespace (rl_line_buffer[rl_point]))
53121308Sache	rl_point++;
53221308Sache
53321308Sache      if (rl_point < rl_end)
53421308Sache	{
53521308Sache	  if (isident (rl_line_buffer[rl_point]))
53621308Sache	    while (++rl_point < rl_end && isident (rl_line_buffer[rl_point]));
53721308Sache	  else
53821308Sache	    while (++rl_point < rl_end && !isident (rl_line_buffer[rl_point])
53921308Sache		   && !whitespace (rl_line_buffer[rl_point]));
54021308Sache	}
54121308Sache      rl_point--;
54221308Sache    }
54321308Sache  return (0);
54421308Sache}
54521308Sache
54621308Sacheint
54721308Sacherl_vi_insert_beg (count, key)
54821308Sache     int count, key;
54921308Sache{
55021308Sache  rl_beg_of_line (1, key);
55121308Sache  rl_vi_insertion_mode (1, key);
55221308Sache  return (0);
55321308Sache}
55421308Sache
55521308Sacheint
55621308Sacherl_vi_append_mode (count, key)
55721308Sache     int count, key;
55821308Sache{
55921308Sache  if (rl_point < rl_end)
56021308Sache    rl_point++;
56121308Sache  rl_vi_insertion_mode (1, key);
56221308Sache  return (0);
56321308Sache}
56421308Sache
56521308Sacheint
56621308Sacherl_vi_append_eol (count, key)
56721308Sache     int count, key;
56821308Sache{
56921308Sache  rl_end_of_line (1, key);
57021308Sache  rl_vi_append_mode (1, key);
57121308Sache  return (0);
57221308Sache}
57321308Sache
57421308Sache/* What to do in the case of C-d. */
57521308Sacheint
57621308Sacherl_vi_eof_maybe (count, c)
57721308Sache     int count, c;
57821308Sache{
57921308Sache  return (rl_newline (1, '\n'));
58021308Sache}
58121308Sache
58221308Sache/* Insertion mode stuff. */
58321308Sache
58421308Sache/* Switching from one mode to the other really just involves
58521308Sache   switching keymaps. */
58621308Sacheint
58721308Sacherl_vi_insertion_mode (count, key)
58821308Sache     int count, key;
58921308Sache{
59021308Sache  _rl_keymap = vi_insertion_keymap;
59121308Sache  _rl_vi_last_key_before_insert = key;
59221308Sache  return (0);
59321308Sache}
59421308Sache
59521308Sachestatic void
59621308Sache_rl_vi_save_insert (up)
59721308Sache      UNDO_LIST *up;
59821308Sache{
59921308Sache  int len, start, end;
60021308Sache
60135486Sache  if (up == 0)
60235486Sache    {
60335486Sache      if (vi_insert_buffer_size >= 1)
60435486Sache	vi_insert_buffer[0] = '\0';
60535486Sache      return;
60635486Sache    }
60735486Sache
60821308Sache  start = up->start;
60921308Sache  end = up->end;
61021308Sache  len = end - start + 1;
61121308Sache  if (len >= vi_insert_buffer_size)
61221308Sache    {
61321308Sache      vi_insert_buffer_size += (len + 32) - (len % 32);
61421308Sache      vi_insert_buffer = xrealloc (vi_insert_buffer, vi_insert_buffer_size);
61521308Sache    }
61621308Sache  strncpy (vi_insert_buffer, rl_line_buffer + start, len - 1);
61721308Sache  vi_insert_buffer[len-1] = '\0';
61821308Sache}
61921308Sache
62021308Sachevoid
62121308Sache_rl_vi_done_inserting ()
62221308Sache{
62321308Sache  if (_rl_vi_doing_insert)
62421308Sache    {
62521308Sache      rl_end_undo_group ();
62621308Sache      /* Now, the text between rl_undo_list->next->start and
62721308Sache	 rl_undo_list->next->end is what was inserted while in insert
62821308Sache	 mode.  It gets copied to VI_INSERT_BUFFER because it depends
62921308Sache	 on absolute indices into the line which may change (though they
63021308Sache	 probably will not). */
63121308Sache      _rl_vi_doing_insert = 0;
63221308Sache      _rl_vi_save_insert (rl_undo_list->next);
63321308Sache      vi_continued_command = 1;
63421308Sache    }
63521308Sache  else
63621308Sache    {
63721308Sache      if (_rl_vi_last_key_before_insert == 'i' && rl_undo_list)
63821308Sache        _rl_vi_save_insert (rl_undo_list);
63921308Sache      /* XXX - Other keys probably need to be checked. */
64021308Sache      else if (_rl_vi_last_key_before_insert == 'C')
64121308Sache	rl_end_undo_group ();
64221308Sache      while (_rl_undo_group_level > 0)
64321308Sache	rl_end_undo_group ();
64421308Sache      vi_continued_command = 0;
64521308Sache    }
64621308Sache}
64721308Sache
64821308Sacheint
64921308Sacherl_vi_movement_mode (count, key)
65021308Sache     int count, key;
65121308Sache{
65221308Sache  if (rl_point > 0)
65321308Sache    rl_backward (1, key);
65421308Sache
65521308Sache  _rl_keymap = vi_movement_keymap;
65621308Sache  _rl_vi_done_inserting ();
65721308Sache  return (0);
65821308Sache}
65921308Sache
66021308Sacheint
66121308Sacherl_vi_arg_digit (count, c)
66221308Sache     int count, c;
66321308Sache{
66421308Sache  if (c == '0' && rl_numeric_arg == 1 && !rl_explicit_arg)
66521308Sache    return (rl_beg_of_line (1, c));
66621308Sache  else
66721308Sache    return (rl_digit_argument (count, c));
66821308Sache}
66921308Sache
67021308Sacheint
67121308Sacherl_vi_change_case (count, ignore)
67221308Sache     int count, ignore;
67321308Sache{
67421308Sache  char c = 0;
67521308Sache
67621308Sache  /* Don't try this on an empty line. */
67721308Sache  if (rl_point >= rl_end)
67821308Sache    return (0);
67921308Sache
68021308Sache  while (count-- && rl_point < rl_end)
68121308Sache    {
68221308Sache      if (_rl_uppercase_p (rl_line_buffer[rl_point]))
68321308Sache	c = _rl_to_lower (rl_line_buffer[rl_point]);
68421308Sache      else if (_rl_lowercase_p (rl_line_buffer[rl_point]))
68521308Sache	c = _rl_to_upper (rl_line_buffer[rl_point]);
68621308Sache      else
68721308Sache	{
68821308Sache	  /* Just skip over characters neither upper nor lower case. */
68921308Sache	  rl_forward (1, c);
69021308Sache	  continue;
69121308Sache	}
69221308Sache
69321308Sache      /* Vi is kind of strange here. */
69421308Sache      if (c)
69521308Sache	{
69621308Sache	  rl_begin_undo_group ();
69721308Sache	  rl_delete (1, c);
69821308Sache	  rl_insert (1, c);
69921308Sache	  rl_end_undo_group ();
70021308Sache	  rl_vi_check ();
70121308Sache        }
70221308Sache      else
70321308Sache	rl_forward (1, c);
70421308Sache    }
70521308Sache  return (0);
70621308Sache}
70721308Sache
70821308Sacheint
70921308Sacherl_vi_put (count, key)
71021308Sache     int count, key;
71121308Sache{
71221308Sache  if (!_rl_uppercase_p (key) && (rl_point + 1 <= rl_end))
71321308Sache    rl_point++;
71421308Sache
71547558Sache  rl_yank (1, key);
71621308Sache  rl_backward (1, key);
71721308Sache  return (0);
71821308Sache}
71921308Sache
72021308Sacheint
72121308Sacherl_vi_check ()
72221308Sache{
72321308Sache  if (rl_point && rl_point == rl_end)
72421308Sache    rl_point--;
72521308Sache  return (0);
72621308Sache}
72721308Sache
72821308Sacheint
72921308Sacherl_vi_column (count, key)
73021308Sache     int count, key;
73121308Sache{
73221308Sache  if (count > rl_end)
73321308Sache    rl_end_of_line (1, key);
73421308Sache  else
73521308Sache    rl_point = count - 1;
73621308Sache  return (0);
73721308Sache}
73821308Sache
73921308Sacheint
74021308Sacherl_vi_domove (key, nextkey)
74121308Sache     int key, *nextkey;
74221308Sache{
74321308Sache  int c, save;
74421308Sache  int old_end;
74521308Sache
74621308Sache  rl_mark = rl_point;
74775406Sache  RL_SETSTATE(RL_STATE_MOREINPUT);
74821308Sache  c = rl_read_key ();
74975406Sache  RL_UNSETSTATE(RL_STATE_MOREINPUT);
75021308Sache  *nextkey = c;
75121308Sache
75221308Sache  if (!member (c, vi_motion))
75321308Sache    {
75421308Sache      if (_rl_digit_p (c))
75521308Sache	{
75621308Sache	  save = rl_numeric_arg;
75721308Sache	  rl_numeric_arg = _rl_digit_value (c);
75821308Sache	  rl_digit_loop1 ();
75921308Sache	  rl_numeric_arg *= save;
76075406Sache	  RL_SETSTATE(RL_STATE_MOREINPUT);
76121308Sache	  c = rl_read_key ();	/* real command */
76275406Sache	  RL_UNSETSTATE(RL_STATE_MOREINPUT);
76321308Sache	  *nextkey = c;
76421308Sache	}
76521308Sache      else if (key == c && (key == 'd' || key == 'y' || key == 'c'))
76621308Sache	{
76721308Sache	  rl_mark = rl_end;
76821308Sache	  rl_beg_of_line (1, c);
76921308Sache	  _rl_vi_last_motion = c;
77021308Sache	  return (0);
77121308Sache	}
77221308Sache      else
77321308Sache	return (-1);
77421308Sache    }
77521308Sache
77621308Sache  _rl_vi_last_motion = c;
77721308Sache
77821308Sache  /* Append a blank character temporarily so that the motion routines
77921308Sache     work right at the end of the line. */
78021308Sache  old_end = rl_end;
78121308Sache  rl_line_buffer[rl_end++] = ' ';
78221308Sache  rl_line_buffer[rl_end] = '\0';
78321308Sache
78421308Sache  _rl_dispatch (c, _rl_keymap);
78521308Sache
78621308Sache  /* Remove the blank that we added. */
78721308Sache  rl_end = old_end;
78821308Sache  rl_line_buffer[rl_end] = '\0';
78921308Sache  if (rl_point > rl_end)
79021308Sache    rl_point = rl_end;
79121308Sache
79221308Sache  /* No change in position means the command failed. */
79321308Sache  if (rl_mark == rl_point)
79421308Sache    return (-1);
79521308Sache
79621308Sache  /* rl_vi_f[wW]ord () leaves the cursor on the first character of the next
79721308Sache     word.  If we are not at the end of the line, and we are on a
79821308Sache     non-whitespace character, move back one (presumably to whitespace). */
79921308Sache  if ((_rl_to_upper (c) == 'W') && rl_point < rl_end && rl_point > rl_mark &&
80021308Sache      !whitespace (rl_line_buffer[rl_point]))
80121308Sache    rl_point--;
80221308Sache
80321308Sache  /* If cw or cW, back up to the end of a word, so the behaviour of ce
80421308Sache     or cE is the actual result.  Brute-force, no subtlety. */
80521308Sache  if (key == 'c' && rl_point >= rl_mark && (_rl_to_upper (c) == 'W'))
80621308Sache    {
80721308Sache      /* Don't move farther back than where we started. */
80821308Sache      while (rl_point > rl_mark && whitespace (rl_line_buffer[rl_point]))
80921308Sache	rl_point--;
81021308Sache
81121308Sache      /* Posix.2 says that if cw or cW moves the cursor towards the end of
81221308Sache	 the line, the character under the cursor should be deleted. */
81321308Sache      if (rl_point == rl_mark)
81421308Sache        rl_point++;
81521308Sache      else
81621308Sache	{
81721308Sache	  /* Move past the end of the word so that the kill doesn't
81821308Sache	     remove the last letter of the previous word.  Only do this
81921308Sache	     if we are not at the end of the line. */
82021308Sache	  if (rl_point >= 0 && rl_point < (rl_end - 1) && !whitespace (rl_line_buffer[rl_point]))
82121308Sache	    rl_point++;
82221308Sache	}
82321308Sache    }
82421308Sache
82521308Sache  if (rl_mark < rl_point)
82621308Sache    exchange (rl_point, rl_mark);
82721308Sache
82821308Sache  return (0);
82921308Sache}
83021308Sache
83121308Sache/* A simplified loop for vi. Don't dispatch key at end.
83275406Sache   Don't recognize minus sign?
83375406Sache   Should this do rl_save_prompt/rl_restore_prompt? */
83421308Sachestatic int
83521308Sacherl_digit_loop1 ()
83621308Sache{
83721308Sache  int key, c;
83821308Sache
83975406Sache  RL_SETSTATE(RL_STATE_NUMERICARG);
84021308Sache  while (1)
84121308Sache    {
84275406Sache      if (rl_numeric_arg > 1000000)
84375406Sache	{
84475406Sache	  rl_explicit_arg = rl_numeric_arg = 0;
84575406Sache	  rl_ding ();
84675406Sache	  rl_clear_message ();
84775406Sache	  RL_UNSETSTATE(RL_STATE_NUMERICARG);
84875406Sache	  return 1;
84975406Sache	}
85021308Sache      rl_message ("(arg: %d) ", rl_arg_sign * rl_numeric_arg, 0);
85175406Sache      RL_SETSTATE(RL_STATE_MOREINPUT);
85221308Sache      key = c = rl_read_key ();
85375406Sache      RL_UNSETSTATE(RL_STATE_MOREINPUT);
85421308Sache
85521308Sache      if (_rl_keymap[c].type == ISFUNC &&
85621308Sache	  _rl_keymap[c].function == rl_universal_argument)
85721308Sache	{
85821308Sache	  rl_numeric_arg *= 4;
85921308Sache	  continue;
86021308Sache	}
86121308Sache
86221308Sache      c = UNMETA (c);
86321308Sache      if (_rl_digit_p (c))
86421308Sache	{
86521308Sache	  if (rl_explicit_arg)
86621308Sache	    rl_numeric_arg = (rl_numeric_arg * 10) + _rl_digit_value (c);
86721308Sache	  else
86821308Sache	    rl_numeric_arg = _rl_digit_value (c);
86921308Sache	  rl_explicit_arg = 1;
87021308Sache	}
87121308Sache      else
87221308Sache	{
87321308Sache	  rl_clear_message ();
87421308Sache	  rl_stuff_char (key);
87521308Sache	  break;
87621308Sache	}
87721308Sache    }
87875406Sache
87975406Sache  RL_UNSETSTATE(RL_STATE_NUMERICARG);
88021308Sache  return (0);
88121308Sache}
88221308Sache
88321308Sacheint
88421308Sacherl_vi_delete_to (count, key)
88521308Sache     int count, key;
88621308Sache{
88721308Sache  int c;
88821308Sache
88921308Sache  if (_rl_uppercase_p (key))
89021308Sache    rl_stuff_char ('$');
89121308Sache  else if (vi_redoing)
89221308Sache    rl_stuff_char (_rl_vi_last_motion);
89321308Sache
89421308Sache  if (rl_vi_domove (key, &c))
89521308Sache    {
89675406Sache      rl_ding ();
89721308Sache      return -1;
89821308Sache    }
89921308Sache
90021308Sache  /* These are the motion commands that do not require adjusting the
90121308Sache     mark. */
90221308Sache  if ((strchr (" l|h^0bB", c) == 0) && (rl_mark < rl_end))
90321308Sache    rl_mark++;
90421308Sache
90521308Sache  rl_kill_text (rl_point, rl_mark);
90621308Sache  return (0);
90721308Sache}
90821308Sache
90921308Sacheint
91021308Sacherl_vi_change_to (count, key)
91121308Sache     int count, key;
91221308Sache{
91321308Sache  int c, start_pos;
91421308Sache
91521308Sache  if (_rl_uppercase_p (key))
91621308Sache    rl_stuff_char ('$');
91721308Sache  else if (vi_redoing)
91821308Sache    rl_stuff_char (_rl_vi_last_motion);
91921308Sache
92021308Sache  start_pos = rl_point;
92121308Sache
92221308Sache  if (rl_vi_domove (key, &c))
92321308Sache    {
92475406Sache      rl_ding ();
92521308Sache      return -1;
92621308Sache    }
92721308Sache
92821308Sache  /* These are the motion commands that do not require adjusting the
92921308Sache     mark.  c[wW] are handled by special-case code in rl_vi_domove(),
93021308Sache     and already leave the mark at the correct location. */
93121308Sache  if ((strchr (" l|hwW^0bB", c) == 0) && (rl_mark < rl_end))
93221308Sache    rl_mark++;
93321308Sache
93421308Sache  /* The cursor never moves with c[wW]. */
93521308Sache  if ((_rl_to_upper (c) == 'W') && rl_point < start_pos)
93621308Sache    rl_point = start_pos;
93721308Sache
93821308Sache  if (vi_redoing)
93921308Sache    {
94021308Sache      if (vi_insert_buffer && *vi_insert_buffer)
94121308Sache	rl_begin_undo_group ();
94221308Sache      rl_delete_text (rl_point, rl_mark);
94321308Sache      if (vi_insert_buffer && *vi_insert_buffer)
94421308Sache	{
94521308Sache	  rl_insert_text (vi_insert_buffer);
94621308Sache	  rl_end_undo_group ();
94721308Sache	}
94821308Sache    }
94921308Sache  else
95021308Sache    {
95121308Sache      rl_begin_undo_group ();		/* to make the `u' command work */
95221308Sache      rl_kill_text (rl_point, rl_mark);
95321308Sache      /* `C' does not save the text inserted for undoing or redoing. */
95421308Sache      if (_rl_uppercase_p (key) == 0)
95521308Sache        _rl_vi_doing_insert = 1;
95621308Sache      _rl_vi_set_last (key, count, rl_arg_sign);
95721308Sache      rl_vi_insertion_mode (1, key);
95821308Sache    }
95921308Sache
96021308Sache  return (0);
96121308Sache}
96221308Sache
96321308Sacheint
96421308Sacherl_vi_yank_to (count, key)
96521308Sache     int count, key;
96621308Sache{
96721308Sache  int c, save = rl_point;
96821308Sache
96921308Sache  if (_rl_uppercase_p (key))
97021308Sache    rl_stuff_char ('$');
97121308Sache
97221308Sache  if (rl_vi_domove (key, &c))
97321308Sache    {
97475406Sache      rl_ding ();
97521308Sache      return -1;
97621308Sache    }
97721308Sache
97821308Sache  /* These are the motion commands that do not require adjusting the
97921308Sache     mark. */
98021308Sache  if ((strchr (" l|h^0%bB", c) == 0) && (rl_mark < rl_end))
98121308Sache    rl_mark++;
98221308Sache
98321308Sache  rl_begin_undo_group ();
98421308Sache  rl_kill_text (rl_point, rl_mark);
98521308Sache  rl_end_undo_group ();
98621308Sache  rl_do_undo ();
98721308Sache  rl_point = save;
98821308Sache
98921308Sache  return (0);
99021308Sache}
99121308Sache
99221308Sacheint
99321308Sacherl_vi_delete (count, key)
99421308Sache     int count, key;
99521308Sache{
99621308Sache  int end;
99721308Sache
99821308Sache  if (rl_end == 0)
99921308Sache    {
100075406Sache      rl_ding ();
100121308Sache      return -1;
100221308Sache    }
100321308Sache
100421308Sache  end = rl_point + count;
100521308Sache
100621308Sache  if (end >= rl_end)
100721308Sache    end = rl_end;
100821308Sache
100921308Sache  rl_kill_text (rl_point, end);
101021308Sache
101121308Sache  if (rl_point > 0 && rl_point == rl_end)
101221308Sache    rl_backward (1, key);
101321308Sache  return (0);
101421308Sache}
101521308Sache
101621308Sacheint
101721308Sacherl_vi_back_to_indent (count, key)
101821308Sache     int count, key;
101921308Sache{
102021308Sache  rl_beg_of_line (1, key);
102121308Sache  while (rl_point < rl_end && whitespace (rl_line_buffer[rl_point]))
102221308Sache    rl_point++;
102321308Sache  return (0);
102421308Sache}
102521308Sache
102621308Sacheint
102721308Sacherl_vi_first_print (count, key)
102821308Sache     int count, key;
102921308Sache{
103021308Sache  return (rl_vi_back_to_indent (1, key));
103121308Sache}
103221308Sache
103321308Sacheint
103421308Sacherl_vi_char_search (count, key)
103521308Sache     int count, key;
103621308Sache{
103721308Sache  static char target;
103821308Sache  static int orig_dir, dir;
103921308Sache
104021308Sache  if (key == ';' || key == ',')
104121308Sache    dir = key == ';' ? orig_dir : -orig_dir;
104221308Sache  else
104321308Sache    {
104421308Sache      if (vi_redoing)
104521308Sache	target = _rl_vi_last_search_char;
104621308Sache      else
104775406Sache	{
104875406Sache	  RL_SETSTATE(RL_STATE_MOREINPUT);
104975406Sache	  _rl_vi_last_search_char = target = rl_read_key ();
105075406Sache	  RL_UNSETSTATE(RL_STATE_MOREINPUT);
105175406Sache	}
105221308Sache
105321308Sache      switch (key)
105421308Sache        {
105521308Sache        case 't':
105621308Sache          orig_dir = dir = FTO;
105721308Sache          break;
105821308Sache
105921308Sache        case 'T':
106021308Sache          orig_dir = dir = BTO;
106121308Sache          break;
106221308Sache
106321308Sache        case 'f':
106421308Sache          orig_dir = dir = FFIND;
106521308Sache          break;
106621308Sache
106721308Sache        case 'F':
106821308Sache          orig_dir = dir = BFIND;
106921308Sache          break;
107021308Sache        }
107121308Sache    }
107221308Sache
107321308Sache  return (_rl_char_search_internal (count, dir, target));
107421308Sache}
107521308Sache
107621308Sache/* Match brackets */
107721308Sacheint
107821308Sacherl_vi_match (ignore, key)
107921308Sache     int ignore, key;
108021308Sache{
108121308Sache  int count = 1, brack, pos;
108221308Sache
108321308Sache  pos = rl_point;
108421308Sache  if ((brack = rl_vi_bracktype (rl_line_buffer[rl_point])) == 0)
108521308Sache    {
108621308Sache      while ((brack = rl_vi_bracktype (rl_line_buffer[rl_point])) == 0 &&
108721308Sache	     rl_point < rl_end - 1)
108821308Sache	rl_forward (1, key);
108921308Sache
109021308Sache      if (brack <= 0)
109121308Sache	{
109221308Sache	  rl_point = pos;
109375406Sache	  rl_ding ();
109421308Sache	  return -1;
109521308Sache	}
109621308Sache    }
109721308Sache
109821308Sache  pos = rl_point;
109921308Sache
110021308Sache  if (brack < 0)
110121308Sache    {
110221308Sache      while (count)
110321308Sache	{
110421308Sache	  if (--pos >= 0)
110521308Sache	    {
110621308Sache	      int b = rl_vi_bracktype (rl_line_buffer[pos]);
110721308Sache	      if (b == -brack)
110821308Sache		count--;
110921308Sache	      else if (b == brack)
111021308Sache		count++;
111121308Sache	    }
111221308Sache	  else
111321308Sache	    {
111475406Sache	      rl_ding ();
111521308Sache	      return -1;
111621308Sache	    }
111721308Sache	}
111821308Sache    }
111921308Sache  else
112021308Sache    {			/* brack > 0 */
112121308Sache      while (count)
112221308Sache	{
112321308Sache	  if (++pos < rl_end)
112421308Sache	    {
112521308Sache	      int b = rl_vi_bracktype (rl_line_buffer[pos]);
112621308Sache	      if (b == -brack)
112721308Sache		count--;
112821308Sache	      else if (b == brack)
112921308Sache		count++;
113021308Sache	    }
113121308Sache	  else
113221308Sache	    {
113375406Sache	      rl_ding ();
113421308Sache	      return -1;
113521308Sache	    }
113621308Sache	}
113721308Sache    }
113821308Sache  rl_point = pos;
113921308Sache  return (0);
114021308Sache}
114121308Sache
114221308Sacheint
114321308Sacherl_vi_bracktype (c)
114421308Sache     int c;
114521308Sache{
114621308Sache  switch (c)
114721308Sache    {
114821308Sache    case '(': return  1;
114921308Sache    case ')': return -1;
115021308Sache    case '[': return  2;
115121308Sache    case ']': return -2;
115221308Sache    case '{': return  3;
115321308Sache    case '}': return -3;
115421308Sache    default:  return  0;
115521308Sache    }
115621308Sache}
115721308Sache
115821308Sacheint
115921308Sacherl_vi_change_char (count, key)
116021308Sache     int count, key;
116121308Sache{
116221308Sache  int c;
116321308Sache
116421308Sache  if (vi_redoing)
116521308Sache    c = _rl_vi_last_replacement;
116621308Sache  else
116775406Sache    {
116875406Sache      RL_SETSTATE(RL_STATE_MOREINPUT);
116975406Sache      _rl_vi_last_replacement = c = rl_read_key ();
117075406Sache      RL_UNSETSTATE(RL_STATE_MOREINPUT);
117175406Sache    }
117221308Sache
117321308Sache  if (c == '\033' || c == CTRL ('C'))
117421308Sache    return -1;
117521308Sache
117621308Sache  while (count-- && rl_point < rl_end)
117721308Sache    {
117821308Sache      rl_begin_undo_group ();
117921308Sache
118021308Sache      rl_delete (1, c);
118121308Sache      rl_insert (1, c);
118221308Sache      if (count == 0)
118321308Sache	rl_backward (1, c);
118421308Sache
118521308Sache      rl_end_undo_group ();
118621308Sache    }
118721308Sache  return (0);
118821308Sache}
118921308Sache
119021308Sacheint
119121308Sacherl_vi_subst (count, key)
119221308Sache     int count, key;
119321308Sache{
119421308Sache  rl_begin_undo_group ();
119521308Sache
119621308Sache  if (_rl_uppercase_p (key))
119721308Sache    {
119821308Sache      rl_beg_of_line (1, key);
119921308Sache      rl_kill_line (1, key);
120021308Sache    }
120121308Sache  else
120221308Sache    rl_delete_text (rl_point, rl_point+count);
120321308Sache
120421308Sache  rl_end_undo_group ();
120521308Sache
120621308Sache  _rl_vi_set_last (key, count, rl_arg_sign);
120721308Sache
120821308Sache  if (vi_redoing)
120921308Sache    {
121021308Sache      int o = _rl_doing_an_undo;
121121308Sache
121221308Sache      _rl_doing_an_undo = 1;
121321308Sache      if (vi_insert_buffer && *vi_insert_buffer)
121421308Sache	rl_insert_text (vi_insert_buffer);
121521308Sache      _rl_doing_an_undo = o;
121621308Sache    }
121721308Sache  else
121821308Sache    {
121921308Sache      rl_begin_undo_group ();
122021308Sache      _rl_vi_doing_insert = 1;
122121308Sache      rl_vi_insertion_mode (1, key);
122221308Sache    }
122321308Sache
122421308Sache  return (0);
122521308Sache}
122621308Sache
122721308Sacheint
122821308Sacherl_vi_overstrike (count, key)
122921308Sache     int count, key;
123021308Sache{
123121308Sache  int i;
123221308Sache
123321308Sache  if (_rl_vi_doing_insert == 0)
123421308Sache    {
123521308Sache      _rl_vi_doing_insert = 1;
123621308Sache      rl_begin_undo_group ();
123721308Sache    }
123821308Sache
123921308Sache  for (i = 0; i < count; i++)
124021308Sache    {
124121308Sache      vi_replace_count++;
124221308Sache      rl_begin_undo_group ();
124321308Sache
124421308Sache      if (rl_point < rl_end)
124521308Sache	{
124621308Sache	  rl_delete (1, key);
124721308Sache	  rl_insert (1, key);
124821308Sache	}
124921308Sache      else
125021308Sache	rl_insert (1, key);
125121308Sache
125221308Sache      rl_end_undo_group ();
125321308Sache    }
125421308Sache  return (0);
125521308Sache}
125621308Sache
125721308Sacheint
125821308Sacherl_vi_overstrike_delete (count, key)
125921308Sache     int count, key;
126021308Sache{
126121308Sache  int i, s;
126221308Sache
126321308Sache  for (i = 0; i < count; i++)
126421308Sache    {
126521308Sache      if (vi_replace_count == 0)
126621308Sache	{
126775406Sache	  rl_ding ();
126821308Sache	  break;
126921308Sache	}
127021308Sache      s = rl_point;
127121308Sache
127221308Sache      if (rl_do_undo ())
127321308Sache	vi_replace_count--;
127421308Sache
127521308Sache      if (rl_point == s)
127621308Sache	rl_backward (1, key);
127721308Sache    }
127821308Sache
127921308Sache  if (vi_replace_count == 0 && _rl_vi_doing_insert)
128021308Sache    {
128121308Sache      rl_end_undo_group ();
128221308Sache      rl_do_undo ();
128321308Sache      _rl_vi_doing_insert = 0;
128421308Sache    }
128521308Sache  return (0);
128621308Sache}
128721308Sache
128821308Sacheint
128921308Sacherl_vi_replace (count, key)
129021308Sache     int count, key;
129121308Sache{
129221308Sache  int i;
129321308Sache
129421308Sache  vi_replace_count = 0;
129521308Sache
129621308Sache  if (!vi_replace_map)
129721308Sache    {
129821308Sache      vi_replace_map = rl_make_bare_keymap ();
129921308Sache
130021308Sache      for (i = ' '; i < KEYMAP_SIZE; i++)
130121308Sache	vi_replace_map[i].function = rl_vi_overstrike;
130221308Sache
130321308Sache      vi_replace_map[RUBOUT].function = rl_vi_overstrike_delete;
130421308Sache      vi_replace_map[ESC].function = rl_vi_movement_mode;
130521308Sache      vi_replace_map[RETURN].function = rl_newline;
130621308Sache      vi_replace_map[NEWLINE].function = rl_newline;
130721308Sache
130821308Sache      /* If the normal vi insertion keymap has ^H bound to erase, do the
130921308Sache         same here.  Probably should remove the assignment to RUBOUT up
131021308Sache         there, but I don't think it will make a difference in real life. */
131121308Sache      if (vi_insertion_keymap[CTRL ('H')].type == ISFUNC &&
131221308Sache	  vi_insertion_keymap[CTRL ('H')].function == rl_rubout)
131321308Sache	vi_replace_map[CTRL ('H')].function = rl_vi_overstrike_delete;
131421308Sache
131521308Sache    }
131621308Sache  _rl_keymap = vi_replace_map;
131721308Sache  return (0);
131821308Sache}
131921308Sache
132021308Sache#if 0
132121308Sache/* Try to complete the word we are standing on or the word that ends with
132221308Sache   the previous character.  A space matches everything.  Word delimiters are
132321308Sache   space and ;. */
132421308Sacheint
132521308Sacherl_vi_possible_completions()
132621308Sache{
132721308Sache  int save_pos = rl_point;
132821308Sache
132921308Sache  if (rl_line_buffer[rl_point] != ' ' && rl_line_buffer[rl_point] != ';')
133021308Sache    {
133121308Sache      while (rl_point < rl_end && rl_line_buffer[rl_point] != ' ' &&
133221308Sache	     rl_line_buffer[rl_point] != ';')
133321308Sache	rl_point++;
133421308Sache    }
133521308Sache  else if (rl_line_buffer[rl_point - 1] == ';')
133621308Sache    {
133775406Sache      rl_ding ();
133821308Sache      return (0);
133921308Sache    }
134021308Sache
134121308Sache  rl_possible_completions ();
134221308Sache  rl_point = save_pos;
134321308Sache
134421308Sache  return (0);
134521308Sache}
134621308Sache#endif
134721308Sache
134821308Sache/* Functions to save and restore marks. */
134921308Sacheint
135021308Sacherl_vi_set_mark (count, key)
135121308Sache     int count, key;
135221308Sache{
135321308Sache  int ch;
135421308Sache
135575406Sache  RL_SETSTATE(RL_STATE_MOREINPUT);
135621308Sache  ch = rl_read_key ();
135775406Sache  RL_UNSETSTATE(RL_STATE_MOREINPUT);
135875406Sache
135921308Sache  if (_rl_lowercase_p (ch) == 0)
136021308Sache    {
136175406Sache      rl_ding ();
136221308Sache      return -1;
136321308Sache    }
136421308Sache  ch -= 'a';
136521308Sache  vi_mark_chars[ch] = rl_point;
136621308Sache  return 0;
136721308Sache}
136821308Sache
136921308Sacheint
137021308Sacherl_vi_goto_mark (count, key)
137121308Sache     int count, key;
137221308Sache{
137321308Sache  int ch;
137421308Sache
137575406Sache  RL_SETSTATE(RL_STATE_MOREINPUT);
137621308Sache  ch = rl_read_key ();
137775406Sache  RL_UNSETSTATE(RL_STATE_MOREINPUT);
137875406Sache
137921308Sache  if (ch == '`')
138021308Sache    {
138121308Sache      rl_point = rl_mark;
138221308Sache      return 0;
138321308Sache    }
138421308Sache  else if (_rl_lowercase_p (ch) == 0)
138521308Sache    {
138675406Sache      rl_ding ();
138721308Sache      return -1;
138821308Sache    }
138921308Sache
139021308Sache  ch -= 'a';
139121308Sache  if (vi_mark_chars[ch] == -1)
139221308Sache    {
139375406Sache      rl_ding ();
139421308Sache      return -1;
139521308Sache    }
139621308Sache  rl_point = vi_mark_chars[ch];
139721308Sache  return 0;
139821308Sache}
139921308Sache
140021308Sache#endif /* VI_MODE */
1401