vi_mode.c revision 119610
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"
54119610Sache#include "rlmbutil.h"
55119610Sache
5621308Sache#include "readline.h"
5721308Sache#include "history.h"
5821308Sache
5958310Sache#include "rlprivate.h"
6058310Sache#include "xmalloc.h"
6158310Sache
6221308Sache#ifndef member
6321308Sache#define member(c, s) ((c) ? (char *)strchr ((s), (c)) != (char *)NULL : 0)
6421308Sache#endif
6521308Sache
6621308Sache/* Non-zero means enter insertion mode. */
6721308Sachestatic int _rl_vi_doing_insert;
6821308Sache
6921308Sache/* Command keys which do movement for xxx_to commands. */
70119610Sachestatic const char *vi_motion = " hl^$0ftFT;,%wbeWBE|";
7121308Sache
7221308Sache/* Keymap used for vi replace characters.  Created dynamically since
7321308Sache   rarely used. */
7421308Sachestatic Keymap vi_replace_map;
7521308Sache
7621308Sache/* The number of characters inserted in the last replace operation. */
7721308Sachestatic int vi_replace_count;
7821308Sache
7921308Sache/* If non-zero, we have text inserted after a c[motion] command that put
8021308Sache   us implicitly into insert mode.  Some people want this text to be
8121308Sache   attached to the command so that it is `redoable' with `.'. */
8221308Sachestatic int vi_continued_command;
8321308Sachestatic char *vi_insert_buffer;
8421308Sachestatic int vi_insert_buffer_size;
8521308Sache
8621308Sachestatic int _rl_vi_last_command = 'i';	/* default `.' puts you in insert mode */
8721308Sachestatic int _rl_vi_last_repeat = 1;
8821308Sachestatic int _rl_vi_last_arg_sign = 1;
8921308Sachestatic int _rl_vi_last_motion;
90119610Sache#if defined (HANDLE_MULTIBYTE)
91119610Sachestatic char _rl_vi_last_search_mbchar[MB_LEN_MAX];
92119610Sache#else
9321308Sachestatic int _rl_vi_last_search_char;
94119610Sache#endif
9521308Sachestatic int _rl_vi_last_replacement;
9621308Sache
9721308Sachestatic int _rl_vi_last_key_before_insert;
9821308Sache
9921308Sachestatic int vi_redoing;
10021308Sache
10121308Sache/* Text modification commands.  These are the `redoable' commands. */
10275406Sachestatic const char *vi_textmod = "_*\\AaIiCcDdPpYyRrSsXx~";
10321308Sache
10421308Sache/* Arrays for the saved marks. */
105119610Sachestatic int vi_mark_chars['z' - 'a' + 1];
10621308Sache
107119610Sachestatic void _rl_vi_stuff_insert PARAMS((int));
108119610Sachestatic void _rl_vi_save_insert PARAMS((UNDO_LIST *));
109119610Sachestatic int rl_digit_loop1 PARAMS((void));
11021308Sache
11121308Sachevoid
11221308Sache_rl_vi_initialize_line ()
11321308Sache{
11421308Sache  register int i;
11521308Sache
11621308Sache  for (i = 0; i < sizeof (vi_mark_chars) / sizeof (int); i++)
11721308Sache    vi_mark_chars[i] = -1;
11821308Sache}
11921308Sache
12021308Sachevoid
12121308Sache_rl_vi_reset_last ()
12221308Sache{
12321308Sache  _rl_vi_last_command = 'i';
12421308Sache  _rl_vi_last_repeat = 1;
12521308Sache  _rl_vi_last_arg_sign = 1;
12621308Sache  _rl_vi_last_motion = 0;
12721308Sache}
12821308Sache
12921308Sachevoid
13021308Sache_rl_vi_set_last (key, repeat, sign)
13121308Sache     int key, repeat, sign;
13221308Sache{
13321308Sache  _rl_vi_last_command = key;
13421308Sache  _rl_vi_last_repeat = repeat;
13521308Sache  _rl_vi_last_arg_sign = sign;
13621308Sache}
13721308Sache
13821308Sache/* Is the command C a VI mode text modification command? */
13921308Sacheint
14021308Sache_rl_vi_textmod_command (c)
14121308Sache     int c;
14221308Sache{
14321308Sache  return (member (c, vi_textmod));
14421308Sache}
14521308Sache
14621308Sachestatic void
14721308Sache_rl_vi_stuff_insert (count)
14821308Sache     int count;
14921308Sache{
15021308Sache  rl_begin_undo_group ();
15121308Sache  while (count--)
15221308Sache    rl_insert_text (vi_insert_buffer);
15321308Sache  rl_end_undo_group ();
15421308Sache}
15521308Sache
15621308Sache/* Bound to `.'.  Called from command mode, so we know that we have to
15721308Sache   redo a text modification command.  The default for _rl_vi_last_command
15821308Sache   puts you back into insert mode. */
15921308Sacheint
16021308Sacherl_vi_redo (count, c)
16121308Sache     int count, c;
16221308Sache{
163119610Sache  int r;
164119610Sache
16521308Sache  if (!rl_explicit_arg)
16621308Sache    {
16721308Sache      rl_numeric_arg = _rl_vi_last_repeat;
16821308Sache      rl_arg_sign = _rl_vi_last_arg_sign;
16921308Sache    }
17021308Sache
171119610Sache  r = 0;
17221308Sache  vi_redoing = 1;
17321308Sache  /* If we're redoing an insert with `i', stuff in the inserted text
17421308Sache     and do not go into insertion mode. */
17521308Sache  if (_rl_vi_last_command == 'i' && vi_insert_buffer && *vi_insert_buffer)
17621308Sache    {
17721308Sache      _rl_vi_stuff_insert (count);
17821308Sache      /* And back up point over the last character inserted. */
17921308Sache      if (rl_point > 0)
18021308Sache	rl_point--;
18121308Sache    }
18221308Sache  else
183119610Sache    r = _rl_dispatch (_rl_vi_last_command, _rl_keymap);
18421308Sache  vi_redoing = 0;
18521308Sache
186119610Sache  return (r);
18721308Sache}
18821308Sache
18921308Sache/* A placeholder for further expansion. */
19021308Sacheint
19121308Sacherl_vi_undo (count, key)
19221308Sache     int count, key;
19321308Sache{
19421308Sache  return (rl_undo_command (count, key));
19521308Sache}
19621308Sache
19721308Sache/* Yank the nth arg from the previous line into this line at point. */
19821308Sacheint
19921308Sacherl_vi_yank_arg (count, key)
20021308Sache     int count, key;
20121308Sache{
20221308Sache  /* Readline thinks that the first word on a line is the 0th, while vi
20321308Sache     thinks the first word on a line is the 1st.  Compensate. */
20421308Sache  if (rl_explicit_arg)
20521308Sache    rl_yank_nth_arg (count - 1, 0);
20621308Sache  else
20721308Sache    rl_yank_nth_arg ('$', 0);
20821308Sache
20921308Sache  return (0);
21021308Sache}
21121308Sache
21221308Sache/* With an argument, move back that many history lines, else move to the
21321308Sache   beginning of history. */
21421308Sacheint
21521308Sacherl_vi_fetch_history (count, c)
21621308Sache     int count, c;
21721308Sache{
21821308Sache  int wanted;
21921308Sache
22021308Sache  /* Giving an argument of n means we want the nth command in the history
22121308Sache     file.  The command number is interpreted the same way that the bash
22221308Sache     `history' command does it -- that is, giving an argument count of 450
22321308Sache     to this command would get the command listed as number 450 in the
22421308Sache     output of `history'. */
22521308Sache  if (rl_explicit_arg)
22621308Sache    {
22721308Sache      wanted = history_base + where_history () - count;
22821308Sache      if (wanted <= 0)
22921308Sache        rl_beginning_of_history (0, 0);
23021308Sache      else
23121308Sache        rl_get_previous_history (wanted, c);
23221308Sache    }
23321308Sache  else
23421308Sache    rl_beginning_of_history (count, 0);
23521308Sache  return (0);
23621308Sache}
23721308Sache
23821308Sache/* Search again for the last thing searched for. */
23921308Sacheint
24021308Sacherl_vi_search_again (count, key)
24121308Sache     int count, key;
24221308Sache{
24321308Sache  switch (key)
24421308Sache    {
24521308Sache    case 'n':
24621308Sache      rl_noninc_reverse_search_again (count, key);
24721308Sache      break;
24821308Sache
24921308Sache    case 'N':
25021308Sache      rl_noninc_forward_search_again (count, key);
25121308Sache      break;
25221308Sache    }
25321308Sache  return (0);
25421308Sache}
25521308Sache
25621308Sache/* Do a vi style search. */
25721308Sacheint
25821308Sacherl_vi_search (count, key)
25921308Sache     int count, key;
26021308Sache{
26121308Sache  switch (key)
26221308Sache    {
26321308Sache    case '?':
26421308Sache      rl_noninc_forward_search (count, key);
26521308Sache      break;
26621308Sache
26721308Sache    case '/':
26821308Sache      rl_noninc_reverse_search (count, key);
26921308Sache      break;
27021308Sache
27121308Sache    default:
27275406Sache      rl_ding ();
27321308Sache      break;
27421308Sache    }
27521308Sache  return (0);
27621308Sache}
27721308Sache
27821308Sache/* Completion, from vi's point of view. */
27921308Sacheint
28021308Sacherl_vi_complete (ignore, key)
28121308Sache     int ignore, key;
28221308Sache{
28321308Sache  if ((rl_point < rl_end) && (!whitespace (rl_line_buffer[rl_point])))
28421308Sache    {
28521308Sache      if (!whitespace (rl_line_buffer[rl_point + 1]))
28621308Sache	rl_vi_end_word (1, 'E');
28721308Sache      rl_point++;
28821308Sache    }
28921308Sache
29021308Sache  if (key == '*')
29121308Sache    rl_complete_internal ('*');	/* Expansion and replacement. */
29221308Sache  else if (key == '=')
29321308Sache    rl_complete_internal ('?');	/* List possible completions. */
29421308Sache  else if (key == '\\')
29521308Sache    rl_complete_internal (TAB);	/* Standard Readline completion. */
29621308Sache  else
29721308Sache    rl_complete (0, key);
29821308Sache
29921308Sache  if (key == '*' || key == '\\')
30021308Sache    {
30121308Sache      _rl_vi_set_last (key, 1, rl_arg_sign);
30221308Sache      rl_vi_insertion_mode (1, key);
30321308Sache    }
30421308Sache  return (0);
30521308Sache}
30621308Sache
30721308Sache/* Tilde expansion for vi mode. */
30821308Sacheint
30921308Sacherl_vi_tilde_expand (ignore, key)
31021308Sache     int ignore, key;
31121308Sache{
31221308Sache  rl_tilde_expand (0, key);
31321308Sache  _rl_vi_set_last (key, 1, rl_arg_sign);	/* XXX */
31421308Sache  rl_vi_insertion_mode (1, key);
31521308Sache  return (0);
31621308Sache}
31721308Sache
31821308Sache/* Previous word in vi mode. */
31921308Sacheint
32021308Sacherl_vi_prev_word (count, key)
32121308Sache     int count, key;
32221308Sache{
32321308Sache  if (count < 0)
32421308Sache    return (rl_vi_next_word (-count, key));
32521308Sache
32621308Sache  if (rl_point == 0)
32721308Sache    {
32875406Sache      rl_ding ();
32921308Sache      return (0);
33021308Sache    }
33121308Sache
33221308Sache  if (_rl_uppercase_p (key))
33347558Sache    rl_vi_bWord (count, key);
33421308Sache  else
33547558Sache    rl_vi_bword (count, key);
33621308Sache
33721308Sache  return (0);
33821308Sache}
33921308Sache
34021308Sache/* Next word in vi mode. */
34121308Sacheint
34221308Sacherl_vi_next_word (count, key)
34321308Sache     int count, key;
34421308Sache{
34521308Sache  if (count < 0)
34621308Sache    return (rl_vi_prev_word (-count, key));
34721308Sache
34821308Sache  if (rl_point >= (rl_end - 1))
34921308Sache    {
35075406Sache      rl_ding ();
35121308Sache      return (0);
35221308Sache    }
35321308Sache
35421308Sache  if (_rl_uppercase_p (key))
35547558Sache    rl_vi_fWord (count, key);
35621308Sache  else
35747558Sache    rl_vi_fword (count, key);
35821308Sache  return (0);
35921308Sache}
36021308Sache
36121308Sache/* Move to the end of the ?next? word. */
36221308Sacheint
36321308Sacherl_vi_end_word (count, key)
36421308Sache     int count, key;
36521308Sache{
36621308Sache  if (count < 0)
36721308Sache    {
36875406Sache      rl_ding ();
36921308Sache      return -1;
37021308Sache    }
37121308Sache
37221308Sache  if (_rl_uppercase_p (key))
37347558Sache    rl_vi_eWord (count, key);
37421308Sache  else
37547558Sache    rl_vi_eword (count, key);
37621308Sache  return (0);
37721308Sache}
37821308Sache
37921308Sache/* Move forward a word the way that 'W' does. */
38021308Sacheint
38147558Sacherl_vi_fWord (count, ignore)
38247558Sache     int count, ignore;
38321308Sache{
38421308Sache  while (count-- && rl_point < (rl_end - 1))
38521308Sache    {
38621308Sache      /* Skip until whitespace. */
38721308Sache      while (!whitespace (rl_line_buffer[rl_point]) && rl_point < rl_end)
38821308Sache	rl_point++;
38921308Sache
39021308Sache      /* Now skip whitespace. */
39121308Sache      while (whitespace (rl_line_buffer[rl_point]) && rl_point < rl_end)
39221308Sache	rl_point++;
39321308Sache    }
39421308Sache  return (0);
39521308Sache}
39621308Sache
39721308Sacheint
39847558Sacherl_vi_bWord (count, ignore)
39947558Sache     int count, ignore;
40021308Sache{
40121308Sache  while (count-- && rl_point > 0)
40221308Sache    {
40321308Sache      /* If we are at the start of a word, move back to whitespace so
40421308Sache	 we will go back to the start of the previous word. */
40521308Sache      if (!whitespace (rl_line_buffer[rl_point]) &&
40621308Sache	  whitespace (rl_line_buffer[rl_point - 1]))
40721308Sache	rl_point--;
40821308Sache
40921308Sache      while (rl_point > 0 && whitespace (rl_line_buffer[rl_point]))
41021308Sache	rl_point--;
41121308Sache
41221308Sache      if (rl_point > 0)
41321308Sache	{
41421308Sache	  while (--rl_point >= 0 && !whitespace (rl_line_buffer[rl_point]));
41521308Sache	  rl_point++;
41621308Sache	}
41721308Sache    }
41821308Sache  return (0);
41921308Sache}
42021308Sache
42121308Sacheint
42247558Sacherl_vi_eWord (count, ignore)
42347558Sache     int count, ignore;
42421308Sache{
42521308Sache  while (count-- && rl_point < (rl_end - 1))
42621308Sache    {
42721308Sache      if (!whitespace (rl_line_buffer[rl_point]))
42821308Sache	rl_point++;
42921308Sache
43021308Sache      /* Move to the next non-whitespace character (to the start of the
43121308Sache	 next word). */
43221308Sache      while (++rl_point < rl_end && whitespace (rl_line_buffer[rl_point]));
43321308Sache
43421308Sache      if (rl_point && rl_point < rl_end)
43521308Sache	{
43621308Sache	  /* Skip whitespace. */
43721308Sache	  while (rl_point < rl_end && whitespace (rl_line_buffer[rl_point]))
43821308Sache	    rl_point++;
43921308Sache
44021308Sache	  /* Skip until whitespace. */
44121308Sache	  while (rl_point < rl_end && !whitespace (rl_line_buffer[rl_point]))
44221308Sache	    rl_point++;
44321308Sache
44421308Sache	  /* Move back to the last character of the word. */
44521308Sache	  rl_point--;
44621308Sache	}
44721308Sache    }
44821308Sache  return (0);
44921308Sache}
45021308Sache
45121308Sacheint
45247558Sacherl_vi_fword (count, ignore)
45347558Sache     int count, ignore;
45421308Sache{
45521308Sache  while (count-- && rl_point < (rl_end - 1))
45621308Sache    {
45721308Sache      /* Move to white space (really non-identifer). */
458119610Sache      if (_rl_isident (rl_line_buffer[rl_point]))
45921308Sache	{
460119610Sache	  while (_rl_isident (rl_line_buffer[rl_point]) && rl_point < rl_end)
46121308Sache	    rl_point++;
46221308Sache	}
46321308Sache      else /* if (!whitespace (rl_line_buffer[rl_point])) */
46421308Sache	{
465119610Sache	  while (!_rl_isident (rl_line_buffer[rl_point]) &&
46621308Sache		 !whitespace (rl_line_buffer[rl_point]) && rl_point < rl_end)
46721308Sache	    rl_point++;
46821308Sache	}
46921308Sache
47021308Sache      /* Move past whitespace. */
47121308Sache      while (whitespace (rl_line_buffer[rl_point]) && rl_point < rl_end)
47221308Sache	rl_point++;
47321308Sache    }
47421308Sache  return (0);
47521308Sache}
47621308Sache
47721308Sacheint
47847558Sacherl_vi_bword (count, ignore)
47947558Sache     int count, ignore;
48021308Sache{
48121308Sache  while (count-- && rl_point > 0)
48221308Sache    {
48321308Sache      int last_is_ident;
48421308Sache
48521308Sache      /* If we are at the start of a word, move back to whitespace
48621308Sache	 so we will go back to the start of the previous word. */
48721308Sache      if (!whitespace (rl_line_buffer[rl_point]) &&
48821308Sache	  whitespace (rl_line_buffer[rl_point - 1]))
48921308Sache	rl_point--;
49021308Sache
49121308Sache      /* If this character and the previous character are `opposite', move
49221308Sache	 back so we don't get messed up by the rl_point++ down there in
49321308Sache	 the while loop.  Without this code, words like `l;' screw up the
49421308Sache	 function. */
495119610Sache      last_is_ident = _rl_isident (rl_line_buffer[rl_point - 1]);
496119610Sache      if ((_rl_isident (rl_line_buffer[rl_point]) && !last_is_ident) ||
497119610Sache	  (!_rl_isident (rl_line_buffer[rl_point]) && last_is_ident))
49821308Sache	rl_point--;
49921308Sache
50021308Sache      while (rl_point > 0 && whitespace (rl_line_buffer[rl_point]))
50121308Sache	rl_point--;
50221308Sache
50321308Sache      if (rl_point > 0)
50421308Sache	{
505119610Sache	  if (_rl_isident (rl_line_buffer[rl_point]))
506119610Sache	    while (--rl_point >= 0 && _rl_isident (rl_line_buffer[rl_point]));
50721308Sache	  else
508119610Sache	    while (--rl_point >= 0 && !_rl_isident (rl_line_buffer[rl_point]) &&
50921308Sache		   !whitespace (rl_line_buffer[rl_point]));
51021308Sache	  rl_point++;
51121308Sache	}
51221308Sache    }
51321308Sache  return (0);
51421308Sache}
51521308Sache
51621308Sacheint
51747558Sacherl_vi_eword (count, ignore)
51847558Sache     int count, ignore;
51921308Sache{
52021308Sache  while (count-- && rl_point < rl_end - 1)
52121308Sache    {
52221308Sache      if (!whitespace (rl_line_buffer[rl_point]))
52321308Sache	rl_point++;
52421308Sache
52521308Sache      while (rl_point < rl_end && whitespace (rl_line_buffer[rl_point]))
52621308Sache	rl_point++;
52721308Sache
52821308Sache      if (rl_point < rl_end)
52921308Sache	{
530119610Sache	  if (_rl_isident (rl_line_buffer[rl_point]))
531119610Sache	    while (++rl_point < rl_end && _rl_isident (rl_line_buffer[rl_point]));
53221308Sache	  else
533119610Sache	    while (++rl_point < rl_end && !_rl_isident (rl_line_buffer[rl_point])
53421308Sache		   && !whitespace (rl_line_buffer[rl_point]));
53521308Sache	}
53621308Sache      rl_point--;
53721308Sache    }
53821308Sache  return (0);
53921308Sache}
54021308Sache
54121308Sacheint
54221308Sacherl_vi_insert_beg (count, key)
54321308Sache     int count, key;
54421308Sache{
54521308Sache  rl_beg_of_line (1, key);
54621308Sache  rl_vi_insertion_mode (1, key);
54721308Sache  return (0);
54821308Sache}
54921308Sache
55021308Sacheint
55121308Sacherl_vi_append_mode (count, key)
55221308Sache     int count, key;
55321308Sache{
55421308Sache  if (rl_point < rl_end)
555119610Sache    {
556119610Sache      if (MB_CUR_MAX == 1 || rl_byte_oriented)
557119610Sache	rl_point++;
558119610Sache      else
559119610Sache        {
560119610Sache          int point = rl_point;
561119610Sache          rl_forward_char (1, key);
562119610Sache          if (point == rl_point)
563119610Sache            rl_point = rl_end;
564119610Sache        }
565119610Sache    }
56621308Sache  rl_vi_insertion_mode (1, key);
56721308Sache  return (0);
56821308Sache}
56921308Sache
57021308Sacheint
57121308Sacherl_vi_append_eol (count, key)
57221308Sache     int count, key;
57321308Sache{
57421308Sache  rl_end_of_line (1, key);
57521308Sache  rl_vi_append_mode (1, key);
57621308Sache  return (0);
57721308Sache}
57821308Sache
57921308Sache/* What to do in the case of C-d. */
58021308Sacheint
58121308Sacherl_vi_eof_maybe (count, c)
58221308Sache     int count, c;
58321308Sache{
58421308Sache  return (rl_newline (1, '\n'));
58521308Sache}
58621308Sache
58721308Sache/* Insertion mode stuff. */
58821308Sache
58921308Sache/* Switching from one mode to the other really just involves
59021308Sache   switching keymaps. */
59121308Sacheint
59221308Sacherl_vi_insertion_mode (count, key)
59321308Sache     int count, key;
59421308Sache{
59521308Sache  _rl_keymap = vi_insertion_keymap;
59621308Sache  _rl_vi_last_key_before_insert = key;
59721308Sache  return (0);
59821308Sache}
59921308Sache
60021308Sachestatic void
60121308Sache_rl_vi_save_insert (up)
60221308Sache      UNDO_LIST *up;
60321308Sache{
60421308Sache  int len, start, end;
60521308Sache
60635486Sache  if (up == 0)
60735486Sache    {
60835486Sache      if (vi_insert_buffer_size >= 1)
60935486Sache	vi_insert_buffer[0] = '\0';
61035486Sache      return;
61135486Sache    }
61235486Sache
61321308Sache  start = up->start;
61421308Sache  end = up->end;
61521308Sache  len = end - start + 1;
61621308Sache  if (len >= vi_insert_buffer_size)
61721308Sache    {
61821308Sache      vi_insert_buffer_size += (len + 32) - (len % 32);
619119610Sache      vi_insert_buffer = (char *)xrealloc (vi_insert_buffer, vi_insert_buffer_size);
62021308Sache    }
62121308Sache  strncpy (vi_insert_buffer, rl_line_buffer + start, len - 1);
62221308Sache  vi_insert_buffer[len-1] = '\0';
62321308Sache}
62421308Sache
62521308Sachevoid
62621308Sache_rl_vi_done_inserting ()
62721308Sache{
62821308Sache  if (_rl_vi_doing_insert)
62921308Sache    {
630119610Sache      /* The `C', `s', and `S' commands set this. */
63121308Sache      rl_end_undo_group ();
63221308Sache      /* Now, the text between rl_undo_list->next->start and
63321308Sache	 rl_undo_list->next->end is what was inserted while in insert
63421308Sache	 mode.  It gets copied to VI_INSERT_BUFFER because it depends
63521308Sache	 on absolute indices into the line which may change (though they
63621308Sache	 probably will not). */
63721308Sache      _rl_vi_doing_insert = 0;
63821308Sache      _rl_vi_save_insert (rl_undo_list->next);
63921308Sache      vi_continued_command = 1;
64021308Sache    }
64121308Sache  else
64221308Sache    {
64321308Sache      if (_rl_vi_last_key_before_insert == 'i' && rl_undo_list)
64421308Sache        _rl_vi_save_insert (rl_undo_list);
64521308Sache      /* XXX - Other keys probably need to be checked. */
64621308Sache      else if (_rl_vi_last_key_before_insert == 'C')
64721308Sache	rl_end_undo_group ();
64821308Sache      while (_rl_undo_group_level > 0)
64921308Sache	rl_end_undo_group ();
65021308Sache      vi_continued_command = 0;
65121308Sache    }
65221308Sache}
65321308Sache
65421308Sacheint
65521308Sacherl_vi_movement_mode (count, key)
65621308Sache     int count, key;
65721308Sache{
65821308Sache  if (rl_point > 0)
659119610Sache    rl_backward_char (1, key);
66021308Sache
66121308Sache  _rl_keymap = vi_movement_keymap;
66221308Sache  _rl_vi_done_inserting ();
66321308Sache  return (0);
66421308Sache}
66521308Sache
66621308Sacheint
66721308Sacherl_vi_arg_digit (count, c)
66821308Sache     int count, c;
66921308Sache{
67021308Sache  if (c == '0' && rl_numeric_arg == 1 && !rl_explicit_arg)
67121308Sache    return (rl_beg_of_line (1, c));
67221308Sache  else
67321308Sache    return (rl_digit_argument (count, c));
67421308Sache}
67521308Sache
676119610Sache/* Change the case of the next COUNT characters. */
677119610Sache#if defined (HANDLE_MULTIBYTE)
678119610Sachestatic int
679119610Sache_rl_vi_change_mbchar_case (count)
680119610Sache     int count;
681119610Sache{
682119610Sache  wchar_t wc;
683119610Sache  char mb[MB_LEN_MAX];
684119610Sache  mbstate_t ps;
685119610Sache
686119610Sache  memset (&ps, 0, sizeof (mbstate_t));
687119610Sache  if (_rl_adjust_point (rl_line_buffer, rl_point, &ps) > 0)
688119610Sache    count--;
689119610Sache  while (count-- && rl_point < rl_end)
690119610Sache    {
691119610Sache      mbrtowc (&wc, rl_line_buffer + rl_point, rl_end - rl_point, &ps);
692119610Sache      if (iswupper (wc))
693119610Sache	wc = towlower (wc);
694119610Sache      else if (iswlower (wc))
695119610Sache	wc = towupper (wc);
696119610Sache      else
697119610Sache	{
698119610Sache	  /* Just skip over chars neither upper nor lower case */
699119610Sache	  rl_forward_char (1, 0);
700119610Sache	  continue;
701119610Sache	}
702119610Sache
703119610Sache      /* Vi is kind of strange here. */
704119610Sache      if (wc)
705119610Sache	{
706119610Sache	  wctomb (mb, wc);
707119610Sache	  rl_begin_undo_group ();
708119610Sache	  rl_delete (1, 0);
709119610Sache	  rl_insert_text (mb);
710119610Sache	  rl_end_undo_group ();
711119610Sache	  rl_vi_check ();
712119610Sache	}
713119610Sache      else
714119610Sache        rl_forward_char (1, 0);
715119610Sache    }
716119610Sache
717119610Sache  return 0;
718119610Sache}
719119610Sache#endif
720119610Sache
72121308Sacheint
72221308Sacherl_vi_change_case (count, ignore)
72321308Sache     int count, ignore;
72421308Sache{
72521308Sache  char c = 0;
72621308Sache
72721308Sache  /* Don't try this on an empty line. */
72821308Sache  if (rl_point >= rl_end)
72921308Sache    return (0);
73021308Sache
731119610Sache#if defined (HANDLE_MULTIBYTE)
732119610Sache  if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
733119610Sache    return (_rl_vi_change_mbchar_case (count));
734119610Sache#endif
735119610Sache
73621308Sache  while (count-- && rl_point < rl_end)
73721308Sache    {
73821308Sache      if (_rl_uppercase_p (rl_line_buffer[rl_point]))
73921308Sache	c = _rl_to_lower (rl_line_buffer[rl_point]);
74021308Sache      else if (_rl_lowercase_p (rl_line_buffer[rl_point]))
74121308Sache	c = _rl_to_upper (rl_line_buffer[rl_point]);
74221308Sache      else
74321308Sache	{
74421308Sache	  /* Just skip over characters neither upper nor lower case. */
745119610Sache	  rl_forward_char (1, c);
74621308Sache	  continue;
74721308Sache	}
74821308Sache
74921308Sache      /* Vi is kind of strange here. */
75021308Sache      if (c)
75121308Sache	{
75221308Sache	  rl_begin_undo_group ();
75321308Sache	  rl_delete (1, c);
754119610Sache	  _rl_insert_char (1, c);
75521308Sache	  rl_end_undo_group ();
75621308Sache	  rl_vi_check ();
75721308Sache        }
75821308Sache      else
759119610Sache	rl_forward_char (1, c);
76021308Sache    }
76121308Sache  return (0);
76221308Sache}
76321308Sache
76421308Sacheint
76521308Sacherl_vi_put (count, key)
76621308Sache     int count, key;
76721308Sache{
76821308Sache  if (!_rl_uppercase_p (key) && (rl_point + 1 <= rl_end))
769119610Sache    rl_point = _rl_find_next_mbchar (rl_line_buffer, rl_point, 1, MB_FIND_NONZERO);
77021308Sache
77147558Sache  rl_yank (1, key);
772119610Sache  rl_backward_char (1, key);
77321308Sache  return (0);
77421308Sache}
77521308Sache
77621308Sacheint
77721308Sacherl_vi_check ()
77821308Sache{
77921308Sache  if (rl_point && rl_point == rl_end)
780119610Sache    {
781119610Sache      if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
782119610Sache	rl_point = _rl_find_prev_mbchar (rl_line_buffer, rl_point, MB_FIND_NONZERO);
783119610Sache      else
784119610Sache        rl_point--;
785119610Sache    }
78621308Sache  return (0);
78721308Sache}
78821308Sache
78921308Sacheint
79021308Sacherl_vi_column (count, key)
79121308Sache     int count, key;
79221308Sache{
79321308Sache  if (count > rl_end)
79421308Sache    rl_end_of_line (1, key);
79521308Sache  else
79621308Sache    rl_point = count - 1;
79721308Sache  return (0);
79821308Sache}
79921308Sache
80021308Sacheint
80121308Sacherl_vi_domove (key, nextkey)
80221308Sache     int key, *nextkey;
80321308Sache{
80421308Sache  int c, save;
80521308Sache  int old_end;
80621308Sache
80721308Sache  rl_mark = rl_point;
80875406Sache  RL_SETSTATE(RL_STATE_MOREINPUT);
80921308Sache  c = rl_read_key ();
81075406Sache  RL_UNSETSTATE(RL_STATE_MOREINPUT);
81121308Sache  *nextkey = c;
81221308Sache
81321308Sache  if (!member (c, vi_motion))
81421308Sache    {
81521308Sache      if (_rl_digit_p (c))
81621308Sache	{
81721308Sache	  save = rl_numeric_arg;
81821308Sache	  rl_numeric_arg = _rl_digit_value (c);
81921308Sache	  rl_digit_loop1 ();
82021308Sache	  rl_numeric_arg *= save;
82175406Sache	  RL_SETSTATE(RL_STATE_MOREINPUT);
82221308Sache	  c = rl_read_key ();	/* real command */
82375406Sache	  RL_UNSETSTATE(RL_STATE_MOREINPUT);
82421308Sache	  *nextkey = c;
82521308Sache	}
82621308Sache      else if (key == c && (key == 'd' || key == 'y' || key == 'c'))
82721308Sache	{
82821308Sache	  rl_mark = rl_end;
82921308Sache	  rl_beg_of_line (1, c);
83021308Sache	  _rl_vi_last_motion = c;
83121308Sache	  return (0);
83221308Sache	}
83321308Sache      else
83421308Sache	return (-1);
83521308Sache    }
83621308Sache
83721308Sache  _rl_vi_last_motion = c;
83821308Sache
83921308Sache  /* Append a blank character temporarily so that the motion routines
84021308Sache     work right at the end of the line. */
84121308Sache  old_end = rl_end;
84221308Sache  rl_line_buffer[rl_end++] = ' ';
84321308Sache  rl_line_buffer[rl_end] = '\0';
84421308Sache
84521308Sache  _rl_dispatch (c, _rl_keymap);
84621308Sache
84721308Sache  /* Remove the blank that we added. */
84821308Sache  rl_end = old_end;
84921308Sache  rl_line_buffer[rl_end] = '\0';
85021308Sache  if (rl_point > rl_end)
85121308Sache    rl_point = rl_end;
85221308Sache
85321308Sache  /* No change in position means the command failed. */
85421308Sache  if (rl_mark == rl_point)
85521308Sache    return (-1);
85621308Sache
85721308Sache  /* rl_vi_f[wW]ord () leaves the cursor on the first character of the next
85821308Sache     word.  If we are not at the end of the line, and we are on a
85921308Sache     non-whitespace character, move back one (presumably to whitespace). */
86021308Sache  if ((_rl_to_upper (c) == 'W') && rl_point < rl_end && rl_point > rl_mark &&
86121308Sache      !whitespace (rl_line_buffer[rl_point]))
86221308Sache    rl_point--;
86321308Sache
86421308Sache  /* If cw or cW, back up to the end of a word, so the behaviour of ce
86521308Sache     or cE is the actual result.  Brute-force, no subtlety. */
86621308Sache  if (key == 'c' && rl_point >= rl_mark && (_rl_to_upper (c) == 'W'))
86721308Sache    {
86821308Sache      /* Don't move farther back than where we started. */
86921308Sache      while (rl_point > rl_mark && whitespace (rl_line_buffer[rl_point]))
87021308Sache	rl_point--;
87121308Sache
87221308Sache      /* Posix.2 says that if cw or cW moves the cursor towards the end of
87321308Sache	 the line, the character under the cursor should be deleted. */
87421308Sache      if (rl_point == rl_mark)
87521308Sache        rl_point++;
87621308Sache      else
87721308Sache	{
87821308Sache	  /* Move past the end of the word so that the kill doesn't
87921308Sache	     remove the last letter of the previous word.  Only do this
88021308Sache	     if we are not at the end of the line. */
88121308Sache	  if (rl_point >= 0 && rl_point < (rl_end - 1) && !whitespace (rl_line_buffer[rl_point]))
88221308Sache	    rl_point++;
88321308Sache	}
88421308Sache    }
88521308Sache
88621308Sache  if (rl_mark < rl_point)
887119610Sache    SWAP (rl_point, rl_mark);
88821308Sache
88921308Sache  return (0);
89021308Sache}
89121308Sache
89221308Sache/* A simplified loop for vi. Don't dispatch key at end.
89375406Sache   Don't recognize minus sign?
89475406Sache   Should this do rl_save_prompt/rl_restore_prompt? */
89521308Sachestatic int
89621308Sacherl_digit_loop1 ()
89721308Sache{
89821308Sache  int key, c;
89921308Sache
90075406Sache  RL_SETSTATE(RL_STATE_NUMERICARG);
90121308Sache  while (1)
90221308Sache    {
90375406Sache      if (rl_numeric_arg > 1000000)
90475406Sache	{
90575406Sache	  rl_explicit_arg = rl_numeric_arg = 0;
90675406Sache	  rl_ding ();
90775406Sache	  rl_clear_message ();
90875406Sache	  RL_UNSETSTATE(RL_STATE_NUMERICARG);
90975406Sache	  return 1;
91075406Sache	}
911119610Sache      rl_message ("(arg: %d) ", rl_arg_sign * rl_numeric_arg);
91275406Sache      RL_SETSTATE(RL_STATE_MOREINPUT);
91321308Sache      key = c = rl_read_key ();
91475406Sache      RL_UNSETSTATE(RL_STATE_MOREINPUT);
91521308Sache
916119610Sache      if (c >= 0 && _rl_keymap[c].type == ISFUNC &&
91721308Sache	  _rl_keymap[c].function == rl_universal_argument)
91821308Sache	{
91921308Sache	  rl_numeric_arg *= 4;
92021308Sache	  continue;
92121308Sache	}
92221308Sache
92321308Sache      c = UNMETA (c);
92421308Sache      if (_rl_digit_p (c))
92521308Sache	{
92621308Sache	  if (rl_explicit_arg)
92721308Sache	    rl_numeric_arg = (rl_numeric_arg * 10) + _rl_digit_value (c);
92821308Sache	  else
92921308Sache	    rl_numeric_arg = _rl_digit_value (c);
93021308Sache	  rl_explicit_arg = 1;
93121308Sache	}
93221308Sache      else
93321308Sache	{
93421308Sache	  rl_clear_message ();
93521308Sache	  rl_stuff_char (key);
93621308Sache	  break;
93721308Sache	}
93821308Sache    }
93975406Sache
94075406Sache  RL_UNSETSTATE(RL_STATE_NUMERICARG);
94121308Sache  return (0);
94221308Sache}
94321308Sache
94421308Sacheint
94521308Sacherl_vi_delete_to (count, key)
94621308Sache     int count, key;
94721308Sache{
94821308Sache  int c;
94921308Sache
95021308Sache  if (_rl_uppercase_p (key))
95121308Sache    rl_stuff_char ('$');
95221308Sache  else if (vi_redoing)
95321308Sache    rl_stuff_char (_rl_vi_last_motion);
95421308Sache
95521308Sache  if (rl_vi_domove (key, &c))
95621308Sache    {
95775406Sache      rl_ding ();
95821308Sache      return -1;
95921308Sache    }
96021308Sache
96121308Sache  /* These are the motion commands that do not require adjusting the
96221308Sache     mark. */
96321308Sache  if ((strchr (" l|h^0bB", c) == 0) && (rl_mark < rl_end))
96421308Sache    rl_mark++;
96521308Sache
96621308Sache  rl_kill_text (rl_point, rl_mark);
96721308Sache  return (0);
96821308Sache}
96921308Sache
97021308Sacheint
97121308Sacherl_vi_change_to (count, key)
97221308Sache     int count, key;
97321308Sache{
97421308Sache  int c, start_pos;
97521308Sache
97621308Sache  if (_rl_uppercase_p (key))
97721308Sache    rl_stuff_char ('$');
97821308Sache  else if (vi_redoing)
97921308Sache    rl_stuff_char (_rl_vi_last_motion);
98021308Sache
98121308Sache  start_pos = rl_point;
98221308Sache
98321308Sache  if (rl_vi_domove (key, &c))
98421308Sache    {
98575406Sache      rl_ding ();
98621308Sache      return -1;
98721308Sache    }
98821308Sache
98921308Sache  /* These are the motion commands that do not require adjusting the
99021308Sache     mark.  c[wW] are handled by special-case code in rl_vi_domove(),
99121308Sache     and already leave the mark at the correct location. */
99221308Sache  if ((strchr (" l|hwW^0bB", c) == 0) && (rl_mark < rl_end))
99321308Sache    rl_mark++;
99421308Sache
99521308Sache  /* The cursor never moves with c[wW]. */
99621308Sache  if ((_rl_to_upper (c) == 'W') && rl_point < start_pos)
99721308Sache    rl_point = start_pos;
99821308Sache
99921308Sache  if (vi_redoing)
100021308Sache    {
100121308Sache      if (vi_insert_buffer && *vi_insert_buffer)
100221308Sache	rl_begin_undo_group ();
100321308Sache      rl_delete_text (rl_point, rl_mark);
100421308Sache      if (vi_insert_buffer && *vi_insert_buffer)
100521308Sache	{
100621308Sache	  rl_insert_text (vi_insert_buffer);
100721308Sache	  rl_end_undo_group ();
100821308Sache	}
100921308Sache    }
101021308Sache  else
101121308Sache    {
101221308Sache      rl_begin_undo_group ();		/* to make the `u' command work */
101321308Sache      rl_kill_text (rl_point, rl_mark);
101421308Sache      /* `C' does not save the text inserted for undoing or redoing. */
101521308Sache      if (_rl_uppercase_p (key) == 0)
101621308Sache        _rl_vi_doing_insert = 1;
101721308Sache      _rl_vi_set_last (key, count, rl_arg_sign);
101821308Sache      rl_vi_insertion_mode (1, key);
101921308Sache    }
102021308Sache
102121308Sache  return (0);
102221308Sache}
102321308Sache
102421308Sacheint
102521308Sacherl_vi_yank_to (count, key)
102621308Sache     int count, key;
102721308Sache{
102821308Sache  int c, save = rl_point;
102921308Sache
103021308Sache  if (_rl_uppercase_p (key))
103121308Sache    rl_stuff_char ('$');
103221308Sache
103321308Sache  if (rl_vi_domove (key, &c))
103421308Sache    {
103575406Sache      rl_ding ();
103621308Sache      return -1;
103721308Sache    }
103821308Sache
103921308Sache  /* These are the motion commands that do not require adjusting the
104021308Sache     mark. */
104121308Sache  if ((strchr (" l|h^0%bB", c) == 0) && (rl_mark < rl_end))
104221308Sache    rl_mark++;
104321308Sache
104421308Sache  rl_begin_undo_group ();
104521308Sache  rl_kill_text (rl_point, rl_mark);
104621308Sache  rl_end_undo_group ();
104721308Sache  rl_do_undo ();
104821308Sache  rl_point = save;
104921308Sache
105021308Sache  return (0);
105121308Sache}
105221308Sache
105321308Sacheint
105421308Sacherl_vi_delete (count, key)
105521308Sache     int count, key;
105621308Sache{
105721308Sache  int end;
105821308Sache
105921308Sache  if (rl_end == 0)
106021308Sache    {
106175406Sache      rl_ding ();
106221308Sache      return -1;
106321308Sache    }
106421308Sache
1065119610Sache  if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
1066119610Sache    end = _rl_find_next_mbchar (rl_line_buffer, rl_point, count, MB_FIND_NONZERO);
1067119610Sache  else
1068119610Sache    end = rl_point + count;
106921308Sache
107021308Sache  if (end >= rl_end)
107121308Sache    end = rl_end;
107221308Sache
107321308Sache  rl_kill_text (rl_point, end);
107421308Sache
107521308Sache  if (rl_point > 0 && rl_point == rl_end)
1076119610Sache    rl_backward_char (1, key);
107721308Sache  return (0);
107821308Sache}
107921308Sache
108021308Sacheint
108121308Sacherl_vi_back_to_indent (count, key)
108221308Sache     int count, key;
108321308Sache{
108421308Sache  rl_beg_of_line (1, key);
108521308Sache  while (rl_point < rl_end && whitespace (rl_line_buffer[rl_point]))
108621308Sache    rl_point++;
108721308Sache  return (0);
108821308Sache}
108921308Sache
109021308Sacheint
109121308Sacherl_vi_first_print (count, key)
109221308Sache     int count, key;
109321308Sache{
109421308Sache  return (rl_vi_back_to_indent (1, key));
109521308Sache}
109621308Sache
109721308Sacheint
109821308Sacherl_vi_char_search (count, key)
109921308Sache     int count, key;
110021308Sache{
1101119610Sache#if defined (HANDLE_MULTIBYTE)
1102119610Sache  static char *target;
1103119610Sache  static int mb_len;
1104119610Sache#else
110521308Sache  static char target;
1106119610Sache#endif
110721308Sache  static int orig_dir, dir;
110821308Sache
110921308Sache  if (key == ';' || key == ',')
111021308Sache    dir = key == ';' ? orig_dir : -orig_dir;
111121308Sache  else
111221308Sache    {
111321308Sache      if (vi_redoing)
1114119610Sache#if defined (HANDLE_MULTIBYTE)
1115119610Sache	target = _rl_vi_last_search_mbchar;
1116119610Sache#else
111721308Sache	target = _rl_vi_last_search_char;
1118119610Sache#endif
111921308Sache      else
112075406Sache	{
1121119610Sache#if defined (HANDLE_MULTIBYTE)
1122119610Sache	  mb_len = _rl_read_mbchar (_rl_vi_last_search_mbchar, MB_LEN_MAX);
1123119610Sache	  target = _rl_vi_last_search_mbchar;
1124119610Sache#else
112575406Sache	  RL_SETSTATE(RL_STATE_MOREINPUT);
112675406Sache	  _rl_vi_last_search_char = target = rl_read_key ();
112775406Sache	  RL_UNSETSTATE(RL_STATE_MOREINPUT);
1128119610Sache#endif
112975406Sache	}
113021308Sache
113121308Sache      switch (key)
113221308Sache        {
113321308Sache        case 't':
113421308Sache          orig_dir = dir = FTO;
113521308Sache          break;
113621308Sache
113721308Sache        case 'T':
113821308Sache          orig_dir = dir = BTO;
113921308Sache          break;
114021308Sache
114121308Sache        case 'f':
114221308Sache          orig_dir = dir = FFIND;
114321308Sache          break;
114421308Sache
114521308Sache        case 'F':
114621308Sache          orig_dir = dir = BFIND;
114721308Sache          break;
114821308Sache        }
114921308Sache    }
115021308Sache
1151119610Sache#if defined (HANDLE_MULTIBYTE)
1152119610Sache   return (_rl_char_search_internal (count, dir, target, mb_len));
1153119610Sache#else
115421308Sache  return (_rl_char_search_internal (count, dir, target));
1155119610Sache#endif
115621308Sache}
115721308Sache
115821308Sache/* Match brackets */
115921308Sacheint
116021308Sacherl_vi_match (ignore, key)
116121308Sache     int ignore, key;
116221308Sache{
1163119610Sache  int count = 1, brack, pos, tmp, pre;
116421308Sache
116521308Sache  pos = rl_point;
116621308Sache  if ((brack = rl_vi_bracktype (rl_line_buffer[rl_point])) == 0)
116721308Sache    {
1168119610Sache      if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
1169119610Sache	{
1170119610Sache	  while ((brack = rl_vi_bracktype (rl_line_buffer[rl_point])) == 0)
1171119610Sache	    {
1172119610Sache	      pre = rl_point;
1173119610Sache	      rl_forward_char (1, key);
1174119610Sache	      if (pre == rl_point)
1175119610Sache	        break;
1176119610Sache	    }
1177119610Sache	}
1178119610Sache      else
1179119610Sache	while ((brack = rl_vi_bracktype (rl_line_buffer[rl_point])) == 0 &&
1180119610Sache		rl_point < rl_end - 1)
1181119610Sache	  rl_forward_char (1, key);
118221308Sache
118321308Sache      if (brack <= 0)
118421308Sache	{
118521308Sache	  rl_point = pos;
118675406Sache	  rl_ding ();
118721308Sache	  return -1;
118821308Sache	}
118921308Sache    }
119021308Sache
119121308Sache  pos = rl_point;
119221308Sache
119321308Sache  if (brack < 0)
119421308Sache    {
119521308Sache      while (count)
119621308Sache	{
1197119610Sache	  tmp = pos;
1198119610Sache	  if (MB_CUR_MAX == 1 || rl_byte_oriented)
1199119610Sache	    pos--;
1200119610Sache	  else
120121308Sache	    {
1202119610Sache	      pos = _rl_find_prev_mbchar (rl_line_buffer, pos, MB_FIND_ANY);
1203119610Sache	      if (tmp == pos)
1204119610Sache	        pos--;
1205119610Sache	    }
1206119610Sache	  if (pos >= 0)
1207119610Sache	    {
120821308Sache	      int b = rl_vi_bracktype (rl_line_buffer[pos]);
120921308Sache	      if (b == -brack)
121021308Sache		count--;
121121308Sache	      else if (b == brack)
121221308Sache		count++;
121321308Sache	    }
121421308Sache	  else
121521308Sache	    {
121675406Sache	      rl_ding ();
121721308Sache	      return -1;
121821308Sache	    }
121921308Sache	}
122021308Sache    }
122121308Sache  else
122221308Sache    {			/* brack > 0 */
122321308Sache      while (count)
122421308Sache	{
1225119610Sache	  if (MB_CUR_MAX == 1 || rl_byte_oriented)
1226119610Sache	    pos++;
1227119610Sache	  else
1228119610Sache	    pos = _rl_find_next_mbchar (rl_line_buffer, pos, 1, MB_FIND_ANY);
1229119610Sache
1230119610Sache	  if (pos < rl_end)
123121308Sache	    {
123221308Sache	      int b = rl_vi_bracktype (rl_line_buffer[pos]);
123321308Sache	      if (b == -brack)
123421308Sache		count--;
123521308Sache	      else if (b == brack)
123621308Sache		count++;
123721308Sache	    }
123821308Sache	  else
123921308Sache	    {
124075406Sache	      rl_ding ();
124121308Sache	      return -1;
124221308Sache	    }
124321308Sache	}
124421308Sache    }
124521308Sache  rl_point = pos;
124621308Sache  return (0);
124721308Sache}
124821308Sache
124921308Sacheint
125021308Sacherl_vi_bracktype (c)
125121308Sache     int c;
125221308Sache{
125321308Sache  switch (c)
125421308Sache    {
125521308Sache    case '(': return  1;
125621308Sache    case ')': return -1;
125721308Sache    case '[': return  2;
125821308Sache    case ']': return -2;
125921308Sache    case '{': return  3;
126021308Sache    case '}': return -3;
126121308Sache    default:  return  0;
126221308Sache    }
126321308Sache}
126421308Sache
1265119610Sache/* XXX - think about reading an entire mbchar with _rl_read_mbchar and
1266119610Sache   inserting it in one bunch instead of the loop below (like in
1267119610Sache   rl_vi_char_search or _rl_vi_change_mbchar_case.  Set c to mbchar[0]
1268119610Sache   for test against 033 or ^C.  Make sure that _rl_read_mbchar does
1269119610Sache   this right. */
127021308Sacheint
127121308Sacherl_vi_change_char (count, key)
127221308Sache     int count, key;
127321308Sache{
127421308Sache  int c;
127521308Sache
127621308Sache  if (vi_redoing)
127721308Sache    c = _rl_vi_last_replacement;
127821308Sache  else
127975406Sache    {
128075406Sache      RL_SETSTATE(RL_STATE_MOREINPUT);
128175406Sache      _rl_vi_last_replacement = c = rl_read_key ();
128275406Sache      RL_UNSETSTATE(RL_STATE_MOREINPUT);
128375406Sache    }
128421308Sache
128521308Sache  if (c == '\033' || c == CTRL ('C'))
128621308Sache    return -1;
128721308Sache
128821308Sache  while (count-- && rl_point < rl_end)
128921308Sache    {
129021308Sache      rl_begin_undo_group ();
129121308Sache
129221308Sache      rl_delete (1, c);
1293119610Sache#if defined (HANDLE_MULTIBYTE)
1294119610Sache      if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
1295119610Sache	while (_rl_insert_char (1, c))
1296119610Sache	  {
1297119610Sache	    RL_SETSTATE (RL_STATE_MOREINPUT);
1298119610Sache	    c = rl_read_key ();
1299119610Sache	    RL_UNSETSTATE (RL_STATE_MOREINPUT);
1300119610Sache	  }
1301119610Sache      else
1302119610Sache#endif
1303119610Sache	_rl_insert_char (1, c);
130421308Sache      if (count == 0)
1305119610Sache	rl_backward_char (1, c);
130621308Sache
130721308Sache      rl_end_undo_group ();
130821308Sache    }
130921308Sache  return (0);
131021308Sache}
131121308Sache
131221308Sacheint
131321308Sacherl_vi_subst (count, key)
131421308Sache     int count, key;
131521308Sache{
1316119610Sache  /* If we are redoing, rl_vi_change_to will stuff the last motion char */
1317119610Sache  if (vi_redoing == 0)
1318119610Sache    rl_stuff_char ((key == 'S') ? 'c' : ' ');	/* `S' == `cc', `s' == `c ' */
131921308Sache
1320119610Sache  return (rl_vi_change_to (count, 'c'));
132121308Sache}
132221308Sache
132321308Sacheint
132421308Sacherl_vi_overstrike (count, key)
132521308Sache     int count, key;
132621308Sache{
132721308Sache  if (_rl_vi_doing_insert == 0)
132821308Sache    {
132921308Sache      _rl_vi_doing_insert = 1;
133021308Sache      rl_begin_undo_group ();
133121308Sache    }
133221308Sache
1333119610Sache  if (count > 0)
133421308Sache    {
1335119610Sache      _rl_overwrite_char (count, key);
1336119610Sache      vi_replace_count += count;
1337119610Sache    }
133821308Sache
133921308Sache  return (0);
134021308Sache}
134121308Sache
134221308Sacheint
134321308Sacherl_vi_overstrike_delete (count, key)
134421308Sache     int count, key;
134521308Sache{
134621308Sache  int i, s;
134721308Sache
134821308Sache  for (i = 0; i < count; i++)
134921308Sache    {
135021308Sache      if (vi_replace_count == 0)
135121308Sache	{
135275406Sache	  rl_ding ();
135321308Sache	  break;
135421308Sache	}
135521308Sache      s = rl_point;
135621308Sache
135721308Sache      if (rl_do_undo ())
135821308Sache	vi_replace_count--;
135921308Sache
136021308Sache      if (rl_point == s)
1361119610Sache	rl_backward_char (1, key);
136221308Sache    }
136321308Sache
136421308Sache  if (vi_replace_count == 0 && _rl_vi_doing_insert)
136521308Sache    {
136621308Sache      rl_end_undo_group ();
136721308Sache      rl_do_undo ();
136821308Sache      _rl_vi_doing_insert = 0;
136921308Sache    }
137021308Sache  return (0);
137121308Sache}
137221308Sache
137321308Sacheint
137421308Sacherl_vi_replace (count, key)
137521308Sache     int count, key;
137621308Sache{
137721308Sache  int i;
137821308Sache
137921308Sache  vi_replace_count = 0;
138021308Sache
138121308Sache  if (!vi_replace_map)
138221308Sache    {
138321308Sache      vi_replace_map = rl_make_bare_keymap ();
138421308Sache
138521308Sache      for (i = ' '; i < KEYMAP_SIZE; i++)
138621308Sache	vi_replace_map[i].function = rl_vi_overstrike;
138721308Sache
138821308Sache      vi_replace_map[RUBOUT].function = rl_vi_overstrike_delete;
138921308Sache      vi_replace_map[ESC].function = rl_vi_movement_mode;
139021308Sache      vi_replace_map[RETURN].function = rl_newline;
139121308Sache      vi_replace_map[NEWLINE].function = rl_newline;
139221308Sache
139321308Sache      /* If the normal vi insertion keymap has ^H bound to erase, do the
139421308Sache         same here.  Probably should remove the assignment to RUBOUT up
139521308Sache         there, but I don't think it will make a difference in real life. */
139621308Sache      if (vi_insertion_keymap[CTRL ('H')].type == ISFUNC &&
139721308Sache	  vi_insertion_keymap[CTRL ('H')].function == rl_rubout)
139821308Sache	vi_replace_map[CTRL ('H')].function = rl_vi_overstrike_delete;
139921308Sache
140021308Sache    }
140121308Sache  _rl_keymap = vi_replace_map;
140221308Sache  return (0);
140321308Sache}
140421308Sache
140521308Sache#if 0
140621308Sache/* Try to complete the word we are standing on or the word that ends with
140721308Sache   the previous character.  A space matches everything.  Word delimiters are
140821308Sache   space and ;. */
140921308Sacheint
141021308Sacherl_vi_possible_completions()
141121308Sache{
141221308Sache  int save_pos = rl_point;
141321308Sache
141421308Sache  if (rl_line_buffer[rl_point] != ' ' && rl_line_buffer[rl_point] != ';')
141521308Sache    {
141621308Sache      while (rl_point < rl_end && rl_line_buffer[rl_point] != ' ' &&
141721308Sache	     rl_line_buffer[rl_point] != ';')
141821308Sache	rl_point++;
141921308Sache    }
142021308Sache  else if (rl_line_buffer[rl_point - 1] == ';')
142121308Sache    {
142275406Sache      rl_ding ();
142321308Sache      return (0);
142421308Sache    }
142521308Sache
142621308Sache  rl_possible_completions ();
142721308Sache  rl_point = save_pos;
142821308Sache
142921308Sache  return (0);
143021308Sache}
143121308Sache#endif
143221308Sache
143321308Sache/* Functions to save and restore marks. */
143421308Sacheint
143521308Sacherl_vi_set_mark (count, key)
143621308Sache     int count, key;
143721308Sache{
143821308Sache  int ch;
143921308Sache
144075406Sache  RL_SETSTATE(RL_STATE_MOREINPUT);
144121308Sache  ch = rl_read_key ();
144275406Sache  RL_UNSETSTATE(RL_STATE_MOREINPUT);
144375406Sache
1444119610Sache  if (ch < 'a' || ch > 'z')
144521308Sache    {
144675406Sache      rl_ding ();
144721308Sache      return -1;
144821308Sache    }
144921308Sache  ch -= 'a';
145021308Sache  vi_mark_chars[ch] = rl_point;
145121308Sache  return 0;
145221308Sache}
145321308Sache
145421308Sacheint
145521308Sacherl_vi_goto_mark (count, key)
145621308Sache     int count, key;
145721308Sache{
145821308Sache  int ch;
145921308Sache
146075406Sache  RL_SETSTATE(RL_STATE_MOREINPUT);
146121308Sache  ch = rl_read_key ();
146275406Sache  RL_UNSETSTATE(RL_STATE_MOREINPUT);
146375406Sache
146421308Sache  if (ch == '`')
146521308Sache    {
146621308Sache      rl_point = rl_mark;
146721308Sache      return 0;
146821308Sache    }
1469119610Sache  else if (ch < 'a' || ch > 'z')
147021308Sache    {
147175406Sache      rl_ding ();
147221308Sache      return -1;
147321308Sache    }
147421308Sache
147521308Sache  ch -= 'a';
147621308Sache  if (vi_mark_chars[ch] == -1)
147721308Sache    {
147875406Sache      rl_ding ();
147921308Sache      return -1;
148021308Sache    }
148121308Sache  rl_point = vi_mark_chars[ch];
148221308Sache  return 0;
148321308Sache}
148421308Sache
148521308Sache#endif /* VI_MODE */
1486