vi_mode.c revision 136759
1125665Sache/* $FreeBSD: head/contrib/libreadline/vi_mode.c 136759 2004-10-21 20:10:14Z peter $ */
2136759Speter
321308Sache/* vi_mode.c -- A vi emulation mode for Bash.
421308Sache   Derived from code written by Jeff Sparkes (jsparkes@bnr.ca).  */
521308Sache
6136759Speter/* Copyright (C) 1987-2004 Free Software Foundation, Inc.
721308Sache
821308Sache   This file is part of the GNU Readline Library, a library for
921308Sache   reading lines of text with interactive input and history editing.
1021308Sache
1121308Sache   The GNU Readline Library is free software; you can redistribute it
1221308Sache   and/or modify it under the terms of the GNU General Public License
1358310Sache   as published by the Free Software Foundation; either version 2, or
1421308Sache   (at your option) any later version.
1521308Sache
1621308Sache   The GNU Readline Library is distributed in the hope that it will be
1721308Sache   useful, but WITHOUT ANY WARRANTY; without even the implied warranty
1821308Sache   of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1921308Sache   GNU General Public License for more details.
2021308Sache
2121308Sache   The GNU General Public License is often shipped with GNU software, and
2221308Sache   is generally kept in a file called COPYING or LICENSE.  If you do not
2321308Sache   have a copy of the license, write to the Free Software Foundation,
2458310Sache   59 Temple Place, Suite 330, Boston, MA 02111 USA. */
2521308Sache#define READLINE_LIBRARY
2621308Sache
2721308Sache/* **************************************************************** */
2821308Sache/*								    */
2921308Sache/*			VI Emulation Mode			    */
3021308Sache/*								    */
3121308Sache/* **************************************************************** */
3221308Sache#include "rlconf.h"
3321308Sache
3421308Sache#if defined (VI_MODE)
3521308Sache
3621308Sache#if defined (HAVE_CONFIG_H)
3721308Sache#  include <config.h>
3821308Sache#endif
3921308Sache
4021308Sache#include <sys/types.h>
4121308Sache
4221308Sache#if defined (HAVE_STDLIB_H)
4321308Sache#  include <stdlib.h>
4421308Sache#else
4521308Sache#  include "ansi_stdlib.h"
4621308Sache#endif /* HAVE_STDLIB_H */
4721308Sache
4821308Sache#if defined (HAVE_UNISTD_H)
4921308Sache#  include <unistd.h>
5021308Sache#endif
5121308Sache
5221308Sache#include <stdio.h>
5321308Sache
5421308Sache/* Some standard library routines. */
5521308Sache#include "rldefs.h"
56119610Sache#include "rlmbutil.h"
57119610Sache
5821308Sache#include "readline.h"
5921308Sache#include "history.h"
6021308Sache
6158310Sache#include "rlprivate.h"
6258310Sache#include "xmalloc.h"
6358310Sache
6421308Sache#ifndef member
6521308Sache#define member(c, s) ((c) ? (char *)strchr ((s), (c)) != (char *)NULL : 0)
6621308Sache#endif
6721308Sache
68136759Speterint _rl_vi_last_command = 'i';	/* default `.' puts you in insert mode */
69136759Speter
7021308Sache/* Non-zero means enter insertion mode. */
7121308Sachestatic int _rl_vi_doing_insert;
7221308Sache
7321308Sache/* Command keys which do movement for xxx_to commands. */
74119610Sachestatic const char *vi_motion = " hl^$0ftFT;,%wbeWBE|";
7521308Sache
7621308Sache/* Keymap used for vi replace characters.  Created dynamically since
7721308Sache   rarely used. */
7821308Sachestatic Keymap vi_replace_map;
7921308Sache
8021308Sache/* The number of characters inserted in the last replace operation. */
8121308Sachestatic int vi_replace_count;
8221308Sache
8321308Sache/* If non-zero, we have text inserted after a c[motion] command that put
8421308Sache   us implicitly into insert mode.  Some people want this text to be
8521308Sache   attached to the command so that it is `redoable' with `.'. */
8621308Sachestatic int vi_continued_command;
8721308Sachestatic char *vi_insert_buffer;
8821308Sachestatic int vi_insert_buffer_size;
8921308Sache
9021308Sachestatic int _rl_vi_last_repeat = 1;
9121308Sachestatic int _rl_vi_last_arg_sign = 1;
9221308Sachestatic int _rl_vi_last_motion;
93119610Sache#if defined (HANDLE_MULTIBYTE)
94119610Sachestatic char _rl_vi_last_search_mbchar[MB_LEN_MAX];
95119610Sache#else
9621308Sachestatic int _rl_vi_last_search_char;
97119610Sache#endif
9821308Sachestatic int _rl_vi_last_replacement;
9921308Sache
10021308Sachestatic int _rl_vi_last_key_before_insert;
10121308Sache
10221308Sachestatic int vi_redoing;
10321308Sache
10421308Sache/* Text modification commands.  These are the `redoable' commands. */
10575406Sachestatic const char *vi_textmod = "_*\\AaIiCcDdPpYyRrSsXx~";
10621308Sache
10721308Sache/* Arrays for the saved marks. */
108119610Sachestatic int vi_mark_chars['z' - 'a' + 1];
10921308Sache
110119610Sachestatic void _rl_vi_stuff_insert PARAMS((int));
111119610Sachestatic void _rl_vi_save_insert PARAMS((UNDO_LIST *));
112119610Sachestatic int rl_digit_loop1 PARAMS((void));
11321308Sache
11421308Sachevoid
11521308Sache_rl_vi_initialize_line ()
11621308Sache{
11721308Sache  register int i;
11821308Sache
11921308Sache  for (i = 0; i < sizeof (vi_mark_chars) / sizeof (int); i++)
12021308Sache    vi_mark_chars[i] = -1;
12121308Sache}
12221308Sache
12321308Sachevoid
12421308Sache_rl_vi_reset_last ()
12521308Sache{
12621308Sache  _rl_vi_last_command = 'i';
12721308Sache  _rl_vi_last_repeat = 1;
12821308Sache  _rl_vi_last_arg_sign = 1;
12921308Sache  _rl_vi_last_motion = 0;
13021308Sache}
13121308Sache
13221308Sachevoid
13321308Sache_rl_vi_set_last (key, repeat, sign)
13421308Sache     int key, repeat, sign;
13521308Sache{
13621308Sache  _rl_vi_last_command = key;
13721308Sache  _rl_vi_last_repeat = repeat;
13821308Sache  _rl_vi_last_arg_sign = sign;
13921308Sache}
14021308Sache
141136759Speter/* A convenience function that calls _rl_vi_set_last to save the last command
142136759Speter   information and enters insertion mode. */
143136759Spetervoid
144136759Speterrl_vi_start_inserting (key, repeat, sign)
145136759Speter     int key, repeat, sign;
146136759Speter{
147136759Speter  _rl_vi_set_last (key, repeat, sign);
148136759Speter  rl_vi_insertion_mode (1, key);
149136759Speter}
150136759Speter
15121308Sache/* Is the command C a VI mode text modification command? */
15221308Sacheint
15321308Sache_rl_vi_textmod_command (c)
15421308Sache     int c;
15521308Sache{
15621308Sache  return (member (c, vi_textmod));
15721308Sache}
15821308Sache
15921308Sachestatic void
16021308Sache_rl_vi_stuff_insert (count)
16121308Sache     int count;
16221308Sache{
16321308Sache  rl_begin_undo_group ();
16421308Sache  while (count--)
16521308Sache    rl_insert_text (vi_insert_buffer);
16621308Sache  rl_end_undo_group ();
16721308Sache}
16821308Sache
16921308Sache/* Bound to `.'.  Called from command mode, so we know that we have to
17021308Sache   redo a text modification command.  The default for _rl_vi_last_command
17121308Sache   puts you back into insert mode. */
17221308Sacheint
17321308Sacherl_vi_redo (count, c)
17421308Sache     int count, c;
17521308Sache{
176119610Sache  int r;
177119610Sache
17821308Sache  if (!rl_explicit_arg)
17921308Sache    {
18021308Sache      rl_numeric_arg = _rl_vi_last_repeat;
18121308Sache      rl_arg_sign = _rl_vi_last_arg_sign;
18221308Sache    }
18321308Sache
184119610Sache  r = 0;
18521308Sache  vi_redoing = 1;
18621308Sache  /* If we're redoing an insert with `i', stuff in the inserted text
18721308Sache     and do not go into insertion mode. */
18821308Sache  if (_rl_vi_last_command == 'i' && vi_insert_buffer && *vi_insert_buffer)
18921308Sache    {
19021308Sache      _rl_vi_stuff_insert (count);
19121308Sache      /* And back up point over the last character inserted. */
19221308Sache      if (rl_point > 0)
19321308Sache	rl_point--;
19421308Sache    }
19521308Sache  else
196119610Sache    r = _rl_dispatch (_rl_vi_last_command, _rl_keymap);
19721308Sache  vi_redoing = 0;
19821308Sache
199119610Sache  return (r);
20021308Sache}
20121308Sache
20221308Sache/* A placeholder for further expansion. */
20321308Sacheint
20421308Sacherl_vi_undo (count, key)
20521308Sache     int count, key;
20621308Sache{
20721308Sache  return (rl_undo_command (count, key));
20821308Sache}
20921308Sache
21021308Sache/* Yank the nth arg from the previous line into this line at point. */
21121308Sacheint
21221308Sacherl_vi_yank_arg (count, key)
21321308Sache     int count, key;
21421308Sache{
21521308Sache  /* Readline thinks that the first word on a line is the 0th, while vi
21621308Sache     thinks the first word on a line is the 1st.  Compensate. */
21721308Sache  if (rl_explicit_arg)
21821308Sache    rl_yank_nth_arg (count - 1, 0);
21921308Sache  else
22021308Sache    rl_yank_nth_arg ('$', 0);
22121308Sache
22221308Sache  return (0);
22321308Sache}
22421308Sache
22521308Sache/* With an argument, move back that many history lines, else move to the
22621308Sache   beginning of history. */
22721308Sacheint
22821308Sacherl_vi_fetch_history (count, c)
22921308Sache     int count, c;
23021308Sache{
23121308Sache  int wanted;
23221308Sache
23321308Sache  /* Giving an argument of n means we want the nth command in the history
23421308Sache     file.  The command number is interpreted the same way that the bash
23521308Sache     `history' command does it -- that is, giving an argument count of 450
23621308Sache     to this command would get the command listed as number 450 in the
23721308Sache     output of `history'. */
23821308Sache  if (rl_explicit_arg)
23921308Sache    {
24021308Sache      wanted = history_base + where_history () - count;
24121308Sache      if (wanted <= 0)
24221308Sache        rl_beginning_of_history (0, 0);
24321308Sache      else
24421308Sache        rl_get_previous_history (wanted, c);
24521308Sache    }
24621308Sache  else
24721308Sache    rl_beginning_of_history (count, 0);
24821308Sache  return (0);
24921308Sache}
25021308Sache
25121308Sache/* Search again for the last thing searched for. */
25221308Sacheint
25321308Sacherl_vi_search_again (count, key)
25421308Sache     int count, key;
25521308Sache{
25621308Sache  switch (key)
25721308Sache    {
25821308Sache    case 'n':
25921308Sache      rl_noninc_reverse_search_again (count, key);
26021308Sache      break;
26121308Sache
26221308Sache    case 'N':
26321308Sache      rl_noninc_forward_search_again (count, key);
26421308Sache      break;
26521308Sache    }
26621308Sache  return (0);
26721308Sache}
26821308Sache
26921308Sache/* Do a vi style search. */
27021308Sacheint
27121308Sacherl_vi_search (count, key)
27221308Sache     int count, key;
27321308Sache{
27421308Sache  switch (key)
27521308Sache    {
27621308Sache    case '?':
277136759Speter      _rl_free_saved_history_line ();
27821308Sache      rl_noninc_forward_search (count, key);
27921308Sache      break;
28021308Sache
28121308Sache    case '/':
282136759Speter      _rl_free_saved_history_line ();
28321308Sache      rl_noninc_reverse_search (count, key);
28421308Sache      break;
28521308Sache
28621308Sache    default:
28775406Sache      rl_ding ();
28821308Sache      break;
28921308Sache    }
29021308Sache  return (0);
29121308Sache}
29221308Sache
29321308Sache/* Completion, from vi's point of view. */
29421308Sacheint
29521308Sacherl_vi_complete (ignore, key)
29621308Sache     int ignore, key;
29721308Sache{
29821308Sache  if ((rl_point < rl_end) && (!whitespace (rl_line_buffer[rl_point])))
29921308Sache    {
30021308Sache      if (!whitespace (rl_line_buffer[rl_point + 1]))
30121308Sache	rl_vi_end_word (1, 'E');
30221308Sache      rl_point++;
30321308Sache    }
30421308Sache
30521308Sache  if (key == '*')
30621308Sache    rl_complete_internal ('*');	/* Expansion and replacement. */
30721308Sache  else if (key == '=')
30821308Sache    rl_complete_internal ('?');	/* List possible completions. */
30921308Sache  else if (key == '\\')
31021308Sache    rl_complete_internal (TAB);	/* Standard Readline completion. */
31121308Sache  else
31221308Sache    rl_complete (0, key);
31321308Sache
31421308Sache  if (key == '*' || key == '\\')
315136759Speter    rl_vi_start_inserting (key, 1, rl_arg_sign);
316136759Speter
31721308Sache  return (0);
31821308Sache}
31921308Sache
32021308Sache/* Tilde expansion for vi mode. */
32121308Sacheint
32221308Sacherl_vi_tilde_expand (ignore, key)
32321308Sache     int ignore, key;
32421308Sache{
32521308Sache  rl_tilde_expand (0, key);
326136759Speter  rl_vi_start_inserting (key, 1, rl_arg_sign);
32721308Sache  return (0);
32821308Sache}
32921308Sache
33021308Sache/* Previous word in vi mode. */
33121308Sacheint
33221308Sacherl_vi_prev_word (count, key)
33321308Sache     int count, key;
33421308Sache{
33521308Sache  if (count < 0)
33621308Sache    return (rl_vi_next_word (-count, key));
33721308Sache
33821308Sache  if (rl_point == 0)
33921308Sache    {
34075406Sache      rl_ding ();
34121308Sache      return (0);
34221308Sache    }
34321308Sache
34421308Sache  if (_rl_uppercase_p (key))
34547558Sache    rl_vi_bWord (count, key);
34621308Sache  else
34747558Sache    rl_vi_bword (count, key);
34821308Sache
34921308Sache  return (0);
35021308Sache}
35121308Sache
35221308Sache/* Next word in vi mode. */
35321308Sacheint
35421308Sacherl_vi_next_word (count, key)
35521308Sache     int count, key;
35621308Sache{
35721308Sache  if (count < 0)
35821308Sache    return (rl_vi_prev_word (-count, key));
35921308Sache
36021308Sache  if (rl_point >= (rl_end - 1))
36121308Sache    {
36275406Sache      rl_ding ();
36321308Sache      return (0);
36421308Sache    }
36521308Sache
36621308Sache  if (_rl_uppercase_p (key))
36747558Sache    rl_vi_fWord (count, key);
36821308Sache  else
36947558Sache    rl_vi_fword (count, key);
37021308Sache  return (0);
37121308Sache}
37221308Sache
37321308Sache/* Move to the end of the ?next? word. */
37421308Sacheint
37521308Sacherl_vi_end_word (count, key)
37621308Sache     int count, key;
37721308Sache{
37821308Sache  if (count < 0)
37921308Sache    {
38075406Sache      rl_ding ();
38121308Sache      return -1;
38221308Sache    }
38321308Sache
38421308Sache  if (_rl_uppercase_p (key))
38547558Sache    rl_vi_eWord (count, key);
38621308Sache  else
38747558Sache    rl_vi_eword (count, key);
38821308Sache  return (0);
38921308Sache}
39021308Sache
39121308Sache/* Move forward a word the way that 'W' does. */
39221308Sacheint
39347558Sacherl_vi_fWord (count, ignore)
39447558Sache     int count, ignore;
39521308Sache{
39621308Sache  while (count-- && rl_point < (rl_end - 1))
39721308Sache    {
39821308Sache      /* Skip until whitespace. */
39921308Sache      while (!whitespace (rl_line_buffer[rl_point]) && rl_point < rl_end)
40021308Sache	rl_point++;
40121308Sache
40221308Sache      /* Now skip whitespace. */
40321308Sache      while (whitespace (rl_line_buffer[rl_point]) && rl_point < rl_end)
40421308Sache	rl_point++;
40521308Sache    }
40621308Sache  return (0);
40721308Sache}
40821308Sache
40921308Sacheint
41047558Sacherl_vi_bWord (count, ignore)
41147558Sache     int count, ignore;
41221308Sache{
41321308Sache  while (count-- && rl_point > 0)
41421308Sache    {
41521308Sache      /* If we are at the start of a word, move back to whitespace so
41621308Sache	 we will go back to the start of the previous word. */
41721308Sache      if (!whitespace (rl_line_buffer[rl_point]) &&
41821308Sache	  whitespace (rl_line_buffer[rl_point - 1]))
41921308Sache	rl_point--;
42021308Sache
42121308Sache      while (rl_point > 0 && whitespace (rl_line_buffer[rl_point]))
42221308Sache	rl_point--;
42321308Sache
42421308Sache      if (rl_point > 0)
42521308Sache	{
42621308Sache	  while (--rl_point >= 0 && !whitespace (rl_line_buffer[rl_point]));
42721308Sache	  rl_point++;
42821308Sache	}
42921308Sache    }
43021308Sache  return (0);
43121308Sache}
43221308Sache
43321308Sacheint
43447558Sacherl_vi_eWord (count, ignore)
43547558Sache     int count, ignore;
43621308Sache{
43721308Sache  while (count-- && rl_point < (rl_end - 1))
43821308Sache    {
43921308Sache      if (!whitespace (rl_line_buffer[rl_point]))
44021308Sache	rl_point++;
44121308Sache
44221308Sache      /* Move to the next non-whitespace character (to the start of the
44321308Sache	 next word). */
444136759Speter      while (rl_point < rl_end && whitespace (rl_line_buffer[rl_point]))
445136759Speter	rl_point++;
44621308Sache
44721308Sache      if (rl_point && rl_point < rl_end)
44821308Sache	{
44921308Sache	  /* Skip whitespace. */
45021308Sache	  while (rl_point < rl_end && whitespace (rl_line_buffer[rl_point]))
45121308Sache	    rl_point++;
45221308Sache
45321308Sache	  /* Skip until whitespace. */
45421308Sache	  while (rl_point < rl_end && !whitespace (rl_line_buffer[rl_point]))
45521308Sache	    rl_point++;
45621308Sache
45721308Sache	  /* Move back to the last character of the word. */
45821308Sache	  rl_point--;
45921308Sache	}
46021308Sache    }
46121308Sache  return (0);
46221308Sache}
46321308Sache
46421308Sacheint
46547558Sacherl_vi_fword (count, ignore)
46647558Sache     int count, ignore;
46721308Sache{
46821308Sache  while (count-- && rl_point < (rl_end - 1))
46921308Sache    {
47021308Sache      /* Move to white space (really non-identifer). */
471119610Sache      if (_rl_isident (rl_line_buffer[rl_point]))
47221308Sache	{
473119610Sache	  while (_rl_isident (rl_line_buffer[rl_point]) && rl_point < rl_end)
47421308Sache	    rl_point++;
47521308Sache	}
47621308Sache      else /* if (!whitespace (rl_line_buffer[rl_point])) */
47721308Sache	{
478119610Sache	  while (!_rl_isident (rl_line_buffer[rl_point]) &&
47921308Sache		 !whitespace (rl_line_buffer[rl_point]) && rl_point < rl_end)
48021308Sache	    rl_point++;
48121308Sache	}
48221308Sache
48321308Sache      /* Move past whitespace. */
48421308Sache      while (whitespace (rl_line_buffer[rl_point]) && rl_point < rl_end)
48521308Sache	rl_point++;
48621308Sache    }
48721308Sache  return (0);
48821308Sache}
48921308Sache
49021308Sacheint
49147558Sacherl_vi_bword (count, ignore)
49247558Sache     int count, ignore;
49321308Sache{
49421308Sache  while (count-- && rl_point > 0)
49521308Sache    {
49621308Sache      int last_is_ident;
49721308Sache
49821308Sache      /* If we are at the start of a word, move back to whitespace
49921308Sache	 so we will go back to the start of the previous word. */
50021308Sache      if (!whitespace (rl_line_buffer[rl_point]) &&
50121308Sache	  whitespace (rl_line_buffer[rl_point - 1]))
50221308Sache	rl_point--;
50321308Sache
50421308Sache      /* If this character and the previous character are `opposite', move
50521308Sache	 back so we don't get messed up by the rl_point++ down there in
50621308Sache	 the while loop.  Without this code, words like `l;' screw up the
50721308Sache	 function. */
508119610Sache      last_is_ident = _rl_isident (rl_line_buffer[rl_point - 1]);
509119610Sache      if ((_rl_isident (rl_line_buffer[rl_point]) && !last_is_ident) ||
510119610Sache	  (!_rl_isident (rl_line_buffer[rl_point]) && last_is_ident))
51121308Sache	rl_point--;
51221308Sache
51321308Sache      while (rl_point > 0 && whitespace (rl_line_buffer[rl_point]))
51421308Sache	rl_point--;
51521308Sache
51621308Sache      if (rl_point > 0)
51721308Sache	{
518119610Sache	  if (_rl_isident (rl_line_buffer[rl_point]))
519119610Sache	    while (--rl_point >= 0 && _rl_isident (rl_line_buffer[rl_point]));
52021308Sache	  else
521119610Sache	    while (--rl_point >= 0 && !_rl_isident (rl_line_buffer[rl_point]) &&
52221308Sache		   !whitespace (rl_line_buffer[rl_point]));
52321308Sache	  rl_point++;
52421308Sache	}
52521308Sache    }
52621308Sache  return (0);
52721308Sache}
52821308Sache
52921308Sacheint
53047558Sacherl_vi_eword (count, ignore)
53147558Sache     int count, ignore;
53221308Sache{
53321308Sache  while (count-- && rl_point < rl_end - 1)
53421308Sache    {
53521308Sache      if (!whitespace (rl_line_buffer[rl_point]))
53621308Sache	rl_point++;
53721308Sache
53821308Sache      while (rl_point < rl_end && whitespace (rl_line_buffer[rl_point]))
53921308Sache	rl_point++;
54021308Sache
54121308Sache      if (rl_point < rl_end)
54221308Sache	{
543119610Sache	  if (_rl_isident (rl_line_buffer[rl_point]))
544119610Sache	    while (++rl_point < rl_end && _rl_isident (rl_line_buffer[rl_point]));
54521308Sache	  else
546119610Sache	    while (++rl_point < rl_end && !_rl_isident (rl_line_buffer[rl_point])
54721308Sache		   && !whitespace (rl_line_buffer[rl_point]));
54821308Sache	}
54921308Sache      rl_point--;
55021308Sache    }
55121308Sache  return (0);
55221308Sache}
55321308Sache
55421308Sacheint
55521308Sacherl_vi_insert_beg (count, key)
55621308Sache     int count, key;
55721308Sache{
55821308Sache  rl_beg_of_line (1, key);
55921308Sache  rl_vi_insertion_mode (1, key);
56021308Sache  return (0);
56121308Sache}
56221308Sache
56321308Sacheint
56421308Sacherl_vi_append_mode (count, key)
56521308Sache     int count, key;
56621308Sache{
56721308Sache  if (rl_point < rl_end)
568119610Sache    {
569119610Sache      if (MB_CUR_MAX == 1 || rl_byte_oriented)
570119610Sache	rl_point++;
571119610Sache      else
572119610Sache        {
573119610Sache          int point = rl_point;
574119610Sache          rl_forward_char (1, key);
575119610Sache          if (point == rl_point)
576119610Sache            rl_point = rl_end;
577119610Sache        }
578119610Sache    }
57921308Sache  rl_vi_insertion_mode (1, key);
58021308Sache  return (0);
58121308Sache}
58221308Sache
58321308Sacheint
58421308Sacherl_vi_append_eol (count, key)
58521308Sache     int count, key;
58621308Sache{
58721308Sache  rl_end_of_line (1, key);
58821308Sache  rl_vi_append_mode (1, key);
58921308Sache  return (0);
59021308Sache}
59121308Sache
59221308Sache/* What to do in the case of C-d. */
59321308Sacheint
59421308Sacherl_vi_eof_maybe (count, c)
59521308Sache     int count, c;
59621308Sache{
59721308Sache  return (rl_newline (1, '\n'));
59821308Sache}
59921308Sache
60021308Sache/* Insertion mode stuff. */
60121308Sache
60221308Sache/* Switching from one mode to the other really just involves
60321308Sache   switching keymaps. */
60421308Sacheint
60521308Sacherl_vi_insertion_mode (count, key)
60621308Sache     int count, key;
60721308Sache{
60821308Sache  _rl_keymap = vi_insertion_keymap;
60921308Sache  _rl_vi_last_key_before_insert = key;
61021308Sache  return (0);
61121308Sache}
61221308Sache
61321308Sachestatic void
61421308Sache_rl_vi_save_insert (up)
61521308Sache      UNDO_LIST *up;
61621308Sache{
61721308Sache  int len, start, end;
61821308Sache
61935486Sache  if (up == 0)
62035486Sache    {
62135486Sache      if (vi_insert_buffer_size >= 1)
62235486Sache	vi_insert_buffer[0] = '\0';
62335486Sache      return;
62435486Sache    }
62535486Sache
62621308Sache  start = up->start;
62721308Sache  end = up->end;
62821308Sache  len = end - start + 1;
62921308Sache  if (len >= vi_insert_buffer_size)
63021308Sache    {
63121308Sache      vi_insert_buffer_size += (len + 32) - (len % 32);
632119610Sache      vi_insert_buffer = (char *)xrealloc (vi_insert_buffer, vi_insert_buffer_size);
63321308Sache    }
63421308Sache  strncpy (vi_insert_buffer, rl_line_buffer + start, len - 1);
63521308Sache  vi_insert_buffer[len-1] = '\0';
63621308Sache}
63721308Sache
63821308Sachevoid
63921308Sache_rl_vi_done_inserting ()
64021308Sache{
64121308Sache  if (_rl_vi_doing_insert)
64221308Sache    {
643119610Sache      /* The `C', `s', and `S' commands set this. */
64421308Sache      rl_end_undo_group ();
64521308Sache      /* Now, the text between rl_undo_list->next->start and
64621308Sache	 rl_undo_list->next->end is what was inserted while in insert
64721308Sache	 mode.  It gets copied to VI_INSERT_BUFFER because it depends
64821308Sache	 on absolute indices into the line which may change (though they
64921308Sache	 probably will not). */
65021308Sache      _rl_vi_doing_insert = 0;
65121308Sache      _rl_vi_save_insert (rl_undo_list->next);
65221308Sache      vi_continued_command = 1;
65321308Sache    }
65421308Sache  else
65521308Sache    {
656136759Speter      if ((_rl_vi_last_key_before_insert == 'i' || _rl_vi_last_key_before_insert == 'a') && rl_undo_list)
65721308Sache        _rl_vi_save_insert (rl_undo_list);
65821308Sache      /* XXX - Other keys probably need to be checked. */
65921308Sache      else if (_rl_vi_last_key_before_insert == 'C')
66021308Sache	rl_end_undo_group ();
66121308Sache      while (_rl_undo_group_level > 0)
66221308Sache	rl_end_undo_group ();
66321308Sache      vi_continued_command = 0;
66421308Sache    }
66521308Sache}
66621308Sache
66721308Sacheint
66821308Sacherl_vi_movement_mode (count, key)
66921308Sache     int count, key;
67021308Sache{
67121308Sache  if (rl_point > 0)
672119610Sache    rl_backward_char (1, key);
67321308Sache
67421308Sache  _rl_keymap = vi_movement_keymap;
67521308Sache  _rl_vi_done_inserting ();
67621308Sache  return (0);
67721308Sache}
67821308Sache
67921308Sacheint
68021308Sacherl_vi_arg_digit (count, c)
68121308Sache     int count, c;
68221308Sache{
68321308Sache  if (c == '0' && rl_numeric_arg == 1 && !rl_explicit_arg)
68421308Sache    return (rl_beg_of_line (1, c));
68521308Sache  else
68621308Sache    return (rl_digit_argument (count, c));
68721308Sache}
68821308Sache
689119610Sache/* Change the case of the next COUNT characters. */
690119610Sache#if defined (HANDLE_MULTIBYTE)
691119610Sachestatic int
692119610Sache_rl_vi_change_mbchar_case (count)
693119610Sache     int count;
694119610Sache{
695119610Sache  wchar_t wc;
696125665Sache  char mb[MB_LEN_MAX+1];
697136759Speter  int mblen, p;
698119610Sache  mbstate_t ps;
699119610Sache
700119610Sache  memset (&ps, 0, sizeof (mbstate_t));
701119610Sache  if (_rl_adjust_point (rl_line_buffer, rl_point, &ps) > 0)
702119610Sache    count--;
703119610Sache  while (count-- && rl_point < rl_end)
704119610Sache    {
705119610Sache      mbrtowc (&wc, rl_line_buffer + rl_point, rl_end - rl_point, &ps);
706119610Sache      if (iswupper (wc))
707119610Sache	wc = towlower (wc);
708119610Sache      else if (iswlower (wc))
709119610Sache	wc = towupper (wc);
710119610Sache      else
711119610Sache	{
712119610Sache	  /* Just skip over chars neither upper nor lower case */
713119610Sache	  rl_forward_char (1, 0);
714119610Sache	  continue;
715119610Sache	}
716119610Sache
717119610Sache      /* Vi is kind of strange here. */
718119610Sache      if (wc)
719119610Sache	{
720136759Speter	  p = rl_point;
721136759Speter	  mblen = wcrtomb (mb, wc, &ps);
722125665Sache	  if (mblen >= 0)
723125665Sache	    mb[mblen] = '\0';
724119610Sache	  rl_begin_undo_group ();
725136759Speter	  rl_vi_delete (1, 0);
726136759Speter	  if (rl_point < p)	/* Did we retreat at EOL? */
727136759Speter	    rl_point++;	/* XXX - should we advance more than 1 for mbchar? */
728119610Sache	  rl_insert_text (mb);
729119610Sache	  rl_end_undo_group ();
730119610Sache	  rl_vi_check ();
731119610Sache	}
732119610Sache      else
733119610Sache        rl_forward_char (1, 0);
734119610Sache    }
735119610Sache
736119610Sache  return 0;
737119610Sache}
738119610Sache#endif
739119610Sache
74021308Sacheint
74121308Sacherl_vi_change_case (count, ignore)
74221308Sache     int count, ignore;
74321308Sache{
744136759Speter  int c, p;
74521308Sache
74621308Sache  /* Don't try this on an empty line. */
74721308Sache  if (rl_point >= rl_end)
74821308Sache    return (0);
74921308Sache
750136759Speter  c = 0;
751119610Sache#if defined (HANDLE_MULTIBYTE)
752119610Sache  if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
753119610Sache    return (_rl_vi_change_mbchar_case (count));
754119610Sache#endif
755119610Sache
75621308Sache  while (count-- && rl_point < rl_end)
75721308Sache    {
75821308Sache      if (_rl_uppercase_p (rl_line_buffer[rl_point]))
75921308Sache	c = _rl_to_lower (rl_line_buffer[rl_point]);
76021308Sache      else if (_rl_lowercase_p (rl_line_buffer[rl_point]))
76121308Sache	c = _rl_to_upper (rl_line_buffer[rl_point]);
76221308Sache      else
76321308Sache	{
76421308Sache	  /* Just skip over characters neither upper nor lower case. */
765119610Sache	  rl_forward_char (1, c);
76621308Sache	  continue;
76721308Sache	}
76821308Sache
76921308Sache      /* Vi is kind of strange here. */
77021308Sache      if (c)
77121308Sache	{
772136759Speter	  p = rl_point;
77321308Sache	  rl_begin_undo_group ();
774136759Speter	  rl_vi_delete (1, c);
775136759Speter	  if (rl_point < p)	/* Did we retreat at EOL? */
776136759Speter	    rl_point++;
777119610Sache	  _rl_insert_char (1, c);
77821308Sache	  rl_end_undo_group ();
77921308Sache	  rl_vi_check ();
78021308Sache        }
78121308Sache      else
782119610Sache	rl_forward_char (1, c);
78321308Sache    }
78421308Sache  return (0);
78521308Sache}
78621308Sache
78721308Sacheint
78821308Sacherl_vi_put (count, key)
78921308Sache     int count, key;
79021308Sache{
79121308Sache  if (!_rl_uppercase_p (key) && (rl_point + 1 <= rl_end))
792119610Sache    rl_point = _rl_find_next_mbchar (rl_line_buffer, rl_point, 1, MB_FIND_NONZERO);
79321308Sache
794136759Speter  while (count--)
795136759Speter    rl_yank (1, key);
796136759Speter
797119610Sache  rl_backward_char (1, key);
79821308Sache  return (0);
79921308Sache}
80021308Sache
80121308Sacheint
80221308Sacherl_vi_check ()
80321308Sache{
80421308Sache  if (rl_point && rl_point == rl_end)
805119610Sache    {
806119610Sache      if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
807119610Sache	rl_point = _rl_find_prev_mbchar (rl_line_buffer, rl_point, MB_FIND_NONZERO);
808119610Sache      else
809119610Sache        rl_point--;
810119610Sache    }
81121308Sache  return (0);
81221308Sache}
81321308Sache
81421308Sacheint
81521308Sacherl_vi_column (count, key)
81621308Sache     int count, key;
81721308Sache{
81821308Sache  if (count > rl_end)
81921308Sache    rl_end_of_line (1, key);
82021308Sache  else
82121308Sache    rl_point = count - 1;
82221308Sache  return (0);
82321308Sache}
82421308Sache
82521308Sacheint
82621308Sacherl_vi_domove (key, nextkey)
82721308Sache     int key, *nextkey;
82821308Sache{
82921308Sache  int c, save;
83021308Sache  int old_end;
83121308Sache
83221308Sache  rl_mark = rl_point;
83375406Sache  RL_SETSTATE(RL_STATE_MOREINPUT);
83421308Sache  c = rl_read_key ();
83575406Sache  RL_UNSETSTATE(RL_STATE_MOREINPUT);
83621308Sache  *nextkey = c;
83721308Sache
83821308Sache  if (!member (c, vi_motion))
83921308Sache    {
84021308Sache      if (_rl_digit_p (c))
84121308Sache	{
84221308Sache	  save = rl_numeric_arg;
84321308Sache	  rl_numeric_arg = _rl_digit_value (c);
844136759Speter	  rl_explicit_arg = 1;
84521308Sache	  rl_digit_loop1 ();
84621308Sache	  rl_numeric_arg *= save;
84775406Sache	  RL_SETSTATE(RL_STATE_MOREINPUT);
84821308Sache	  c = rl_read_key ();	/* real command */
84975406Sache	  RL_UNSETSTATE(RL_STATE_MOREINPUT);
85021308Sache	  *nextkey = c;
85121308Sache	}
85221308Sache      else if (key == c && (key == 'd' || key == 'y' || key == 'c'))
85321308Sache	{
85421308Sache	  rl_mark = rl_end;
85521308Sache	  rl_beg_of_line (1, c);
85621308Sache	  _rl_vi_last_motion = c;
85721308Sache	  return (0);
85821308Sache	}
85921308Sache      else
86021308Sache	return (-1);
86121308Sache    }
86221308Sache
86321308Sache  _rl_vi_last_motion = c;
86421308Sache
86521308Sache  /* Append a blank character temporarily so that the motion routines
86621308Sache     work right at the end of the line. */
86721308Sache  old_end = rl_end;
86821308Sache  rl_line_buffer[rl_end++] = ' ';
86921308Sache  rl_line_buffer[rl_end] = '\0';
87021308Sache
87121308Sache  _rl_dispatch (c, _rl_keymap);
87221308Sache
87321308Sache  /* Remove the blank that we added. */
87421308Sache  rl_end = old_end;
87521308Sache  rl_line_buffer[rl_end] = '\0';
87621308Sache  if (rl_point > rl_end)
87721308Sache    rl_point = rl_end;
87821308Sache
87921308Sache  /* No change in position means the command failed. */
88021308Sache  if (rl_mark == rl_point)
88121308Sache    return (-1);
88221308Sache
88321308Sache  /* rl_vi_f[wW]ord () leaves the cursor on the first character of the next
88421308Sache     word.  If we are not at the end of the line, and we are on a
88521308Sache     non-whitespace character, move back one (presumably to whitespace). */
88621308Sache  if ((_rl_to_upper (c) == 'W') && rl_point < rl_end && rl_point > rl_mark &&
88721308Sache      !whitespace (rl_line_buffer[rl_point]))
88821308Sache    rl_point--;
88921308Sache
89021308Sache  /* If cw or cW, back up to the end of a word, so the behaviour of ce
89121308Sache     or cE is the actual result.  Brute-force, no subtlety. */
89221308Sache  if (key == 'c' && rl_point >= rl_mark && (_rl_to_upper (c) == 'W'))
89321308Sache    {
89421308Sache      /* Don't move farther back than where we started. */
89521308Sache      while (rl_point > rl_mark && whitespace (rl_line_buffer[rl_point]))
89621308Sache	rl_point--;
89721308Sache
89821308Sache      /* Posix.2 says that if cw or cW moves the cursor towards the end of
89921308Sache	 the line, the character under the cursor should be deleted. */
90021308Sache      if (rl_point == rl_mark)
90121308Sache        rl_point++;
90221308Sache      else
90321308Sache	{
90421308Sache	  /* Move past the end of the word so that the kill doesn't
90521308Sache	     remove the last letter of the previous word.  Only do this
90621308Sache	     if we are not at the end of the line. */
90721308Sache	  if (rl_point >= 0 && rl_point < (rl_end - 1) && !whitespace (rl_line_buffer[rl_point]))
90821308Sache	    rl_point++;
90921308Sache	}
91021308Sache    }
91121308Sache
91221308Sache  if (rl_mark < rl_point)
913119610Sache    SWAP (rl_point, rl_mark);
91421308Sache
91521308Sache  return (0);
91621308Sache}
91721308Sache
91821308Sache/* A simplified loop for vi. Don't dispatch key at end.
91975406Sache   Don't recognize minus sign?
92075406Sache   Should this do rl_save_prompt/rl_restore_prompt? */
92121308Sachestatic int
92221308Sacherl_digit_loop1 ()
92321308Sache{
92421308Sache  int key, c;
92521308Sache
92675406Sache  RL_SETSTATE(RL_STATE_NUMERICARG);
92721308Sache  while (1)
92821308Sache    {
92975406Sache      if (rl_numeric_arg > 1000000)
93075406Sache	{
93175406Sache	  rl_explicit_arg = rl_numeric_arg = 0;
93275406Sache	  rl_ding ();
93375406Sache	  rl_clear_message ();
93475406Sache	  RL_UNSETSTATE(RL_STATE_NUMERICARG);
93575406Sache	  return 1;
93675406Sache	}
937119610Sache      rl_message ("(arg: %d) ", rl_arg_sign * rl_numeric_arg);
93875406Sache      RL_SETSTATE(RL_STATE_MOREINPUT);
93921308Sache      key = c = rl_read_key ();
94075406Sache      RL_UNSETSTATE(RL_STATE_MOREINPUT);
94121308Sache
942119610Sache      if (c >= 0 && _rl_keymap[c].type == ISFUNC &&
94321308Sache	  _rl_keymap[c].function == rl_universal_argument)
94421308Sache	{
94521308Sache	  rl_numeric_arg *= 4;
94621308Sache	  continue;
94721308Sache	}
94821308Sache
94921308Sache      c = UNMETA (c);
95021308Sache      if (_rl_digit_p (c))
95121308Sache	{
95221308Sache	  if (rl_explicit_arg)
95321308Sache	    rl_numeric_arg = (rl_numeric_arg * 10) + _rl_digit_value (c);
95421308Sache	  else
95521308Sache	    rl_numeric_arg = _rl_digit_value (c);
95621308Sache	  rl_explicit_arg = 1;
95721308Sache	}
95821308Sache      else
95921308Sache	{
96021308Sache	  rl_clear_message ();
96121308Sache	  rl_stuff_char (key);
96221308Sache	  break;
96321308Sache	}
96421308Sache    }
96575406Sache
96675406Sache  RL_UNSETSTATE(RL_STATE_NUMERICARG);
96721308Sache  return (0);
96821308Sache}
96921308Sache
97021308Sacheint
97121308Sacherl_vi_delete_to (count, key)
97221308Sache     int count, key;
97321308Sache{
97421308Sache  int c;
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  if (rl_vi_domove (key, &c))
98221308Sache    {
98375406Sache      rl_ding ();
98421308Sache      return -1;
98521308Sache    }
98621308Sache
98721308Sache  /* These are the motion commands that do not require adjusting the
98821308Sache     mark. */
98921308Sache  if ((strchr (" l|h^0bB", c) == 0) && (rl_mark < rl_end))
99021308Sache    rl_mark++;
99121308Sache
99221308Sache  rl_kill_text (rl_point, rl_mark);
99321308Sache  return (0);
99421308Sache}
99521308Sache
99621308Sacheint
99721308Sacherl_vi_change_to (count, key)
99821308Sache     int count, key;
99921308Sache{
100021308Sache  int c, start_pos;
100121308Sache
100221308Sache  if (_rl_uppercase_p (key))
100321308Sache    rl_stuff_char ('$');
100421308Sache  else if (vi_redoing)
100521308Sache    rl_stuff_char (_rl_vi_last_motion);
100621308Sache
100721308Sache  start_pos = rl_point;
100821308Sache
100921308Sache  if (rl_vi_domove (key, &c))
101021308Sache    {
101175406Sache      rl_ding ();
101221308Sache      return -1;
101321308Sache    }
101421308Sache
101521308Sache  /* These are the motion commands that do not require adjusting the
101621308Sache     mark.  c[wW] are handled by special-case code in rl_vi_domove(),
101721308Sache     and already leave the mark at the correct location. */
101821308Sache  if ((strchr (" l|hwW^0bB", c) == 0) && (rl_mark < rl_end))
101921308Sache    rl_mark++;
102021308Sache
102121308Sache  /* The cursor never moves with c[wW]. */
102221308Sache  if ((_rl_to_upper (c) == 'W') && rl_point < start_pos)
102321308Sache    rl_point = start_pos;
102421308Sache
102521308Sache  if (vi_redoing)
102621308Sache    {
102721308Sache      if (vi_insert_buffer && *vi_insert_buffer)
102821308Sache	rl_begin_undo_group ();
102921308Sache      rl_delete_text (rl_point, rl_mark);
103021308Sache      if (vi_insert_buffer && *vi_insert_buffer)
103121308Sache	{
103221308Sache	  rl_insert_text (vi_insert_buffer);
103321308Sache	  rl_end_undo_group ();
103421308Sache	}
103521308Sache    }
103621308Sache  else
103721308Sache    {
103821308Sache      rl_begin_undo_group ();		/* to make the `u' command work */
103921308Sache      rl_kill_text (rl_point, rl_mark);
104021308Sache      /* `C' does not save the text inserted for undoing or redoing. */
104121308Sache      if (_rl_uppercase_p (key) == 0)
104221308Sache        _rl_vi_doing_insert = 1;
1043136759Speter      rl_vi_start_inserting (key, rl_numeric_arg, rl_arg_sign);
104421308Sache    }
104521308Sache
104621308Sache  return (0);
104721308Sache}
104821308Sache
104921308Sacheint
105021308Sacherl_vi_yank_to (count, key)
105121308Sache     int count, key;
105221308Sache{
105321308Sache  int c, save = rl_point;
105421308Sache
105521308Sache  if (_rl_uppercase_p (key))
105621308Sache    rl_stuff_char ('$');
105721308Sache
105821308Sache  if (rl_vi_domove (key, &c))
105921308Sache    {
106075406Sache      rl_ding ();
106121308Sache      return -1;
106221308Sache    }
106321308Sache
106421308Sache  /* These are the motion commands that do not require adjusting the
106521308Sache     mark. */
106621308Sache  if ((strchr (" l|h^0%bB", c) == 0) && (rl_mark < rl_end))
106721308Sache    rl_mark++;
106821308Sache
106921308Sache  rl_begin_undo_group ();
107021308Sache  rl_kill_text (rl_point, rl_mark);
107121308Sache  rl_end_undo_group ();
107221308Sache  rl_do_undo ();
107321308Sache  rl_point = save;
107421308Sache
107521308Sache  return (0);
107621308Sache}
107721308Sache
107821308Sacheint
107921308Sacherl_vi_delete (count, key)
108021308Sache     int count, key;
108121308Sache{
108221308Sache  int end;
108321308Sache
108421308Sache  if (rl_end == 0)
108521308Sache    {
108675406Sache      rl_ding ();
108721308Sache      return -1;
108821308Sache    }
108921308Sache
1090119610Sache  if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
1091119610Sache    end = _rl_find_next_mbchar (rl_line_buffer, rl_point, count, MB_FIND_NONZERO);
1092119610Sache  else
1093119610Sache    end = rl_point + count;
109421308Sache
109521308Sache  if (end >= rl_end)
109621308Sache    end = rl_end;
109721308Sache
109821308Sache  rl_kill_text (rl_point, end);
109921308Sache
110021308Sache  if (rl_point > 0 && rl_point == rl_end)
1101119610Sache    rl_backward_char (1, key);
110221308Sache  return (0);
110321308Sache}
110421308Sache
110521308Sacheint
110621308Sacherl_vi_back_to_indent (count, key)
110721308Sache     int count, key;
110821308Sache{
110921308Sache  rl_beg_of_line (1, key);
111021308Sache  while (rl_point < rl_end && whitespace (rl_line_buffer[rl_point]))
111121308Sache    rl_point++;
111221308Sache  return (0);
111321308Sache}
111421308Sache
111521308Sacheint
111621308Sacherl_vi_first_print (count, key)
111721308Sache     int count, key;
111821308Sache{
111921308Sache  return (rl_vi_back_to_indent (1, key));
112021308Sache}
112121308Sache
112221308Sacheint
112321308Sacherl_vi_char_search (count, key)
112421308Sache     int count, key;
112521308Sache{
1126119610Sache#if defined (HANDLE_MULTIBYTE)
1127119610Sache  static char *target;
1128119610Sache  static int mb_len;
1129119610Sache#else
113021308Sache  static char target;
1131119610Sache#endif
113221308Sache  static int orig_dir, dir;
113321308Sache
113421308Sache  if (key == ';' || key == ',')
113521308Sache    dir = key == ';' ? orig_dir : -orig_dir;
113621308Sache  else
113721308Sache    {
113821308Sache      if (vi_redoing)
1139119610Sache#if defined (HANDLE_MULTIBYTE)
1140119610Sache	target = _rl_vi_last_search_mbchar;
1141119610Sache#else
114221308Sache	target = _rl_vi_last_search_char;
1143119610Sache#endif
114421308Sache      else
114575406Sache	{
1146119610Sache#if defined (HANDLE_MULTIBYTE)
1147119610Sache	  mb_len = _rl_read_mbchar (_rl_vi_last_search_mbchar, MB_LEN_MAX);
1148119610Sache	  target = _rl_vi_last_search_mbchar;
1149119610Sache#else
115075406Sache	  RL_SETSTATE(RL_STATE_MOREINPUT);
115175406Sache	  _rl_vi_last_search_char = target = rl_read_key ();
115275406Sache	  RL_UNSETSTATE(RL_STATE_MOREINPUT);
1153119610Sache#endif
115475406Sache	}
115521308Sache
115621308Sache      switch (key)
115721308Sache        {
115821308Sache        case 't':
115921308Sache          orig_dir = dir = FTO;
116021308Sache          break;
116121308Sache
116221308Sache        case 'T':
116321308Sache          orig_dir = dir = BTO;
116421308Sache          break;
116521308Sache
116621308Sache        case 'f':
116721308Sache          orig_dir = dir = FFIND;
116821308Sache          break;
116921308Sache
117021308Sache        case 'F':
117121308Sache          orig_dir = dir = BFIND;
117221308Sache          break;
117321308Sache        }
117421308Sache    }
117521308Sache
1176119610Sache#if defined (HANDLE_MULTIBYTE)
1177119610Sache   return (_rl_char_search_internal (count, dir, target, mb_len));
1178119610Sache#else
117921308Sache  return (_rl_char_search_internal (count, dir, target));
1180119610Sache#endif
118121308Sache}
118221308Sache
118321308Sache/* Match brackets */
118421308Sacheint
118521308Sacherl_vi_match (ignore, key)
118621308Sache     int ignore, key;
118721308Sache{
1188119610Sache  int count = 1, brack, pos, tmp, pre;
118921308Sache
119021308Sache  pos = rl_point;
119121308Sache  if ((brack = rl_vi_bracktype (rl_line_buffer[rl_point])) == 0)
119221308Sache    {
1193119610Sache      if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
1194119610Sache	{
1195119610Sache	  while ((brack = rl_vi_bracktype (rl_line_buffer[rl_point])) == 0)
1196119610Sache	    {
1197119610Sache	      pre = rl_point;
1198119610Sache	      rl_forward_char (1, key);
1199119610Sache	      if (pre == rl_point)
1200119610Sache	        break;
1201119610Sache	    }
1202119610Sache	}
1203119610Sache      else
1204119610Sache	while ((brack = rl_vi_bracktype (rl_line_buffer[rl_point])) == 0 &&
1205119610Sache		rl_point < rl_end - 1)
1206119610Sache	  rl_forward_char (1, key);
120721308Sache
120821308Sache      if (brack <= 0)
120921308Sache	{
121021308Sache	  rl_point = pos;
121175406Sache	  rl_ding ();
121221308Sache	  return -1;
121321308Sache	}
121421308Sache    }
121521308Sache
121621308Sache  pos = rl_point;
121721308Sache
121821308Sache  if (brack < 0)
121921308Sache    {
122021308Sache      while (count)
122121308Sache	{
1222119610Sache	  tmp = pos;
1223119610Sache	  if (MB_CUR_MAX == 1 || rl_byte_oriented)
1224119610Sache	    pos--;
1225119610Sache	  else
122621308Sache	    {
1227119610Sache	      pos = _rl_find_prev_mbchar (rl_line_buffer, pos, MB_FIND_ANY);
1228119610Sache	      if (tmp == pos)
1229119610Sache	        pos--;
1230119610Sache	    }
1231119610Sache	  if (pos >= 0)
1232119610Sache	    {
123321308Sache	      int b = rl_vi_bracktype (rl_line_buffer[pos]);
123421308Sache	      if (b == -brack)
123521308Sache		count--;
123621308Sache	      else if (b == brack)
123721308Sache		count++;
123821308Sache	    }
123921308Sache	  else
124021308Sache	    {
124175406Sache	      rl_ding ();
124221308Sache	      return -1;
124321308Sache	    }
124421308Sache	}
124521308Sache    }
124621308Sache  else
124721308Sache    {			/* brack > 0 */
124821308Sache      while (count)
124921308Sache	{
1250119610Sache	  if (MB_CUR_MAX == 1 || rl_byte_oriented)
1251119610Sache	    pos++;
1252119610Sache	  else
1253119610Sache	    pos = _rl_find_next_mbchar (rl_line_buffer, pos, 1, MB_FIND_ANY);
1254119610Sache
1255119610Sache	  if (pos < rl_end)
125621308Sache	    {
125721308Sache	      int b = rl_vi_bracktype (rl_line_buffer[pos]);
125821308Sache	      if (b == -brack)
125921308Sache		count--;
126021308Sache	      else if (b == brack)
126121308Sache		count++;
126221308Sache	    }
126321308Sache	  else
126421308Sache	    {
126575406Sache	      rl_ding ();
126621308Sache	      return -1;
126721308Sache	    }
126821308Sache	}
126921308Sache    }
127021308Sache  rl_point = pos;
127121308Sache  return (0);
127221308Sache}
127321308Sache
127421308Sacheint
127521308Sacherl_vi_bracktype (c)
127621308Sache     int c;
127721308Sache{
127821308Sache  switch (c)
127921308Sache    {
128021308Sache    case '(': return  1;
128121308Sache    case ')': return -1;
128221308Sache    case '[': return  2;
128321308Sache    case ']': return -2;
128421308Sache    case '{': return  3;
128521308Sache    case '}': return -3;
128621308Sache    default:  return  0;
128721308Sache    }
128821308Sache}
128921308Sache
1290119610Sache/* XXX - think about reading an entire mbchar with _rl_read_mbchar and
1291119610Sache   inserting it in one bunch instead of the loop below (like in
1292136759Speter   rl_vi_char_search or _rl_vi_change_mbchar_case).  Set c to mbchar[0]
1293119610Sache   for test against 033 or ^C.  Make sure that _rl_read_mbchar does
1294119610Sache   this right. */
129521308Sacheint
129621308Sacherl_vi_change_char (count, key)
129721308Sache     int count, key;
129821308Sache{
1299136759Speter  int c, p;
130021308Sache
130121308Sache  if (vi_redoing)
130221308Sache    c = _rl_vi_last_replacement;
130321308Sache  else
130475406Sache    {
130575406Sache      RL_SETSTATE(RL_STATE_MOREINPUT);
130675406Sache      _rl_vi_last_replacement = c = rl_read_key ();
130775406Sache      RL_UNSETSTATE(RL_STATE_MOREINPUT);
130875406Sache    }
130921308Sache
131021308Sache  if (c == '\033' || c == CTRL ('C'))
131121308Sache    return -1;
131221308Sache
1313136759Speter  rl_begin_undo_group ();
131421308Sache  while (count-- && rl_point < rl_end)
131521308Sache    {
1316136759Speter      p = rl_point;
1317136759Speter      rl_vi_delete (1, c);
1318119610Sache#if defined (HANDLE_MULTIBYTE)
1319119610Sache      if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
1320136759Speter	{
1321136759Speter	  if (rl_point < p)		/* Did we retreat at EOL? */
1322136759Speter	    rl_point++;
1323136759Speter	  while (_rl_insert_char (1, c))
1324136759Speter	    {
1325136759Speter	      RL_SETSTATE (RL_STATE_MOREINPUT);
1326136759Speter	      c = rl_read_key ();
1327136759Speter	      RL_UNSETSTATE (RL_STATE_MOREINPUT);
1328136759Speter	    }
1329136759Speter	}
1330119610Sache      else
1331119610Sache#endif
1332136759Speter	{
1333136759Speter	  if (rl_point < p)		/* Did we retreat at EOL? */
1334136759Speter	    rl_point++;
1335136759Speter	  _rl_insert_char (1, c);
1336136759Speter	}
1337136759Speter    }
1338136759Speter  rl_end_undo_group ();
133921308Sache
134021308Sache  return (0);
134121308Sache}
134221308Sache
134321308Sacheint
134421308Sacherl_vi_subst (count, key)
134521308Sache     int count, key;
134621308Sache{
1347119610Sache  /* If we are redoing, rl_vi_change_to will stuff the last motion char */
1348119610Sache  if (vi_redoing == 0)
1349136759Speter    rl_stuff_char ((key == 'S') ? 'c' : 'l');	/* `S' == `cc', `s' == `cl' */
135021308Sache
1351119610Sache  return (rl_vi_change_to (count, 'c'));
135221308Sache}
135321308Sache
135421308Sacheint
135521308Sacherl_vi_overstrike (count, key)
135621308Sache     int count, key;
135721308Sache{
135821308Sache  if (_rl_vi_doing_insert == 0)
135921308Sache    {
136021308Sache      _rl_vi_doing_insert = 1;
136121308Sache      rl_begin_undo_group ();
136221308Sache    }
136321308Sache
1364119610Sache  if (count > 0)
136521308Sache    {
1366119610Sache      _rl_overwrite_char (count, key);
1367119610Sache      vi_replace_count += count;
1368119610Sache    }
136921308Sache
137021308Sache  return (0);
137121308Sache}
137221308Sache
137321308Sacheint
137421308Sacherl_vi_overstrike_delete (count, key)
137521308Sache     int count, key;
137621308Sache{
137721308Sache  int i, s;
137821308Sache
137921308Sache  for (i = 0; i < count; i++)
138021308Sache    {
138121308Sache      if (vi_replace_count == 0)
138221308Sache	{
138375406Sache	  rl_ding ();
138421308Sache	  break;
138521308Sache	}
138621308Sache      s = rl_point;
138721308Sache
138821308Sache      if (rl_do_undo ())
138921308Sache	vi_replace_count--;
139021308Sache
139121308Sache      if (rl_point == s)
1392119610Sache	rl_backward_char (1, key);
139321308Sache    }
139421308Sache
139521308Sache  if (vi_replace_count == 0 && _rl_vi_doing_insert)
139621308Sache    {
139721308Sache      rl_end_undo_group ();
139821308Sache      rl_do_undo ();
139921308Sache      _rl_vi_doing_insert = 0;
140021308Sache    }
140121308Sache  return (0);
140221308Sache}
140321308Sache
140421308Sacheint
140521308Sacherl_vi_replace (count, key)
140621308Sache     int count, key;
140721308Sache{
140821308Sache  int i;
140921308Sache
141021308Sache  vi_replace_count = 0;
141121308Sache
141221308Sache  if (!vi_replace_map)
141321308Sache    {
141421308Sache      vi_replace_map = rl_make_bare_keymap ();
141521308Sache
141621308Sache      for (i = ' '; i < KEYMAP_SIZE; i++)
141721308Sache	vi_replace_map[i].function = rl_vi_overstrike;
141821308Sache
141921308Sache      vi_replace_map[RUBOUT].function = rl_vi_overstrike_delete;
142021308Sache      vi_replace_map[ESC].function = rl_vi_movement_mode;
142121308Sache      vi_replace_map[RETURN].function = rl_newline;
142221308Sache      vi_replace_map[NEWLINE].function = rl_newline;
142321308Sache
142421308Sache      /* If the normal vi insertion keymap has ^H bound to erase, do the
142521308Sache         same here.  Probably should remove the assignment to RUBOUT up
142621308Sache         there, but I don't think it will make a difference in real life. */
142721308Sache      if (vi_insertion_keymap[CTRL ('H')].type == ISFUNC &&
142821308Sache	  vi_insertion_keymap[CTRL ('H')].function == rl_rubout)
142921308Sache	vi_replace_map[CTRL ('H')].function = rl_vi_overstrike_delete;
143021308Sache
143121308Sache    }
143221308Sache  _rl_keymap = vi_replace_map;
143321308Sache  return (0);
143421308Sache}
143521308Sache
143621308Sache#if 0
143721308Sache/* Try to complete the word we are standing on or the word that ends with
143821308Sache   the previous character.  A space matches everything.  Word delimiters are
143921308Sache   space and ;. */
144021308Sacheint
144121308Sacherl_vi_possible_completions()
144221308Sache{
144321308Sache  int save_pos = rl_point;
144421308Sache
144521308Sache  if (rl_line_buffer[rl_point] != ' ' && rl_line_buffer[rl_point] != ';')
144621308Sache    {
144721308Sache      while (rl_point < rl_end && rl_line_buffer[rl_point] != ' ' &&
144821308Sache	     rl_line_buffer[rl_point] != ';')
144921308Sache	rl_point++;
145021308Sache    }
145121308Sache  else if (rl_line_buffer[rl_point - 1] == ';')
145221308Sache    {
145375406Sache      rl_ding ();
145421308Sache      return (0);
145521308Sache    }
145621308Sache
145721308Sache  rl_possible_completions ();
145821308Sache  rl_point = save_pos;
145921308Sache
146021308Sache  return (0);
146121308Sache}
146221308Sache#endif
146321308Sache
146421308Sache/* Functions to save and restore marks. */
146521308Sacheint
146621308Sacherl_vi_set_mark (count, key)
146721308Sache     int count, key;
146821308Sache{
146921308Sache  int ch;
147021308Sache
147175406Sache  RL_SETSTATE(RL_STATE_MOREINPUT);
147221308Sache  ch = rl_read_key ();
147375406Sache  RL_UNSETSTATE(RL_STATE_MOREINPUT);
147475406Sache
1475119610Sache  if (ch < 'a' || ch > 'z')
147621308Sache    {
147775406Sache      rl_ding ();
147821308Sache      return -1;
147921308Sache    }
148021308Sache  ch -= 'a';
148121308Sache  vi_mark_chars[ch] = rl_point;
148221308Sache  return 0;
148321308Sache}
148421308Sache
148521308Sacheint
148621308Sacherl_vi_goto_mark (count, key)
148721308Sache     int count, key;
148821308Sache{
148921308Sache  int ch;
149021308Sache
149175406Sache  RL_SETSTATE(RL_STATE_MOREINPUT);
149221308Sache  ch = rl_read_key ();
149375406Sache  RL_UNSETSTATE(RL_STATE_MOREINPUT);
149475406Sache
149521308Sache  if (ch == '`')
149621308Sache    {
149721308Sache      rl_point = rl_mark;
149821308Sache      return 0;
149921308Sache    }
1500119610Sache  else if (ch < 'a' || ch > 'z')
150121308Sache    {
150275406Sache      rl_ding ();
150321308Sache      return -1;
150421308Sache    }
150521308Sache
150621308Sache  ch -= 'a';
150721308Sache  if (vi_mark_chars[ch] == -1)
150821308Sache    {
150975406Sache      rl_ding ();
151021308Sache      return -1;
151121308Sache    }
151221308Sache  rl_point = vi_mark_chars[ch];
151321308Sache  return 0;
151421308Sache}
151521308Sache
151621308Sache#endif /* VI_MODE */
1517