vi_mode.c revision 125665
1125665Sache/* $FreeBSD: head/contrib/libreadline/vi_mode.c 125665 2004-02-10 20:17:58Z ache $ */
221308Sache/* vi_mode.c -- A vi emulation mode for Bash.
321308Sache   Derived from code written by Jeff Sparkes (jsparkes@bnr.ca).  */
421308Sache
521308Sache/* Copyright (C) 1987, 1989, 1992 Free Software Foundation, Inc.
621308Sache
721308Sache   This file is part of the GNU Readline Library, a library for
821308Sache   reading lines of text with interactive input and history editing.
921308Sache
1021308Sache   The GNU Readline Library is free software; you can redistribute it
1121308Sache   and/or modify it under the terms of the GNU General Public License
1258310Sache   as published by the Free Software Foundation; either version 2, or
1321308Sache   (at your option) any later version.
1421308Sache
1521308Sache   The GNU Readline Library is distributed in the hope that it will be
1621308Sache   useful, but WITHOUT ANY WARRANTY; without even the implied warranty
1721308Sache   of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1821308Sache   GNU General Public License for more details.
1921308Sache
2021308Sache   The GNU General Public License is often shipped with GNU software, and
2121308Sache   is generally kept in a file called COPYING or LICENSE.  If you do not
2221308Sache   have a copy of the license, write to the Free Software Foundation,
2358310Sache   59 Temple Place, Suite 330, Boston, MA 02111 USA. */
2421308Sache#define READLINE_LIBRARY
2521308Sache
2621308Sache/* **************************************************************** */
2721308Sache/*								    */
2821308Sache/*			VI Emulation Mode			    */
2921308Sache/*								    */
3021308Sache/* **************************************************************** */
3121308Sache#include "rlconf.h"
3221308Sache
3321308Sache#if defined (VI_MODE)
3421308Sache
3521308Sache#if defined (HAVE_CONFIG_H)
3621308Sache#  include <config.h>
3721308Sache#endif
3821308Sache
3921308Sache#include <sys/types.h>
4021308Sache
4121308Sache#if defined (HAVE_STDLIB_H)
4221308Sache#  include <stdlib.h>
4321308Sache#else
4421308Sache#  include "ansi_stdlib.h"
4521308Sache#endif /* HAVE_STDLIB_H */
4621308Sache
4721308Sache#if defined (HAVE_UNISTD_H)
4821308Sache#  include <unistd.h>
4921308Sache#endif
5021308Sache
5121308Sache#include <stdio.h>
5221308Sache
5321308Sache/* Some standard library routines. */
5421308Sache#include "rldefs.h"
55119610Sache#include "rlmbutil.h"
56119610Sache
5721308Sache#include "readline.h"
5821308Sache#include "history.h"
5921308Sache
6058310Sache#include "rlprivate.h"
6158310Sache#include "xmalloc.h"
6258310Sache
6321308Sache#ifndef member
6421308Sache#define member(c, s) ((c) ? (char *)strchr ((s), (c)) != (char *)NULL : 0)
6521308Sache#endif
6621308Sache
6721308Sache/* Non-zero means enter insertion mode. */
6821308Sachestatic int _rl_vi_doing_insert;
6921308Sache
7021308Sache/* Command keys which do movement for xxx_to commands. */
71119610Sachestatic const char *vi_motion = " hl^$0ftFT;,%wbeWBE|";
7221308Sache
7321308Sache/* Keymap used for vi replace characters.  Created dynamically since
7421308Sache   rarely used. */
7521308Sachestatic Keymap vi_replace_map;
7621308Sache
7721308Sache/* The number of characters inserted in the last replace operation. */
7821308Sachestatic int vi_replace_count;
7921308Sache
8021308Sache/* If non-zero, we have text inserted after a c[motion] command that put
8121308Sache   us implicitly into insert mode.  Some people want this text to be
8221308Sache   attached to the command so that it is `redoable' with `.'. */
8321308Sachestatic int vi_continued_command;
8421308Sachestatic char *vi_insert_buffer;
8521308Sachestatic int vi_insert_buffer_size;
8621308Sache
8721308Sachestatic int _rl_vi_last_command = 'i';	/* default `.' puts you in insert mode */
8821308Sachestatic int _rl_vi_last_repeat = 1;
8921308Sachestatic int _rl_vi_last_arg_sign = 1;
9021308Sachestatic int _rl_vi_last_motion;
91119610Sache#if defined (HANDLE_MULTIBYTE)
92119610Sachestatic char _rl_vi_last_search_mbchar[MB_LEN_MAX];
93119610Sache#else
9421308Sachestatic int _rl_vi_last_search_char;
95119610Sache#endif
9621308Sachestatic int _rl_vi_last_replacement;
9721308Sache
9821308Sachestatic int _rl_vi_last_key_before_insert;
9921308Sache
10021308Sachestatic int vi_redoing;
10121308Sache
10221308Sache/* Text modification commands.  These are the `redoable' commands. */
10375406Sachestatic const char *vi_textmod = "_*\\AaIiCcDdPpYyRrSsXx~";
10421308Sache
10521308Sache/* Arrays for the saved marks. */
106119610Sachestatic int vi_mark_chars['z' - 'a' + 1];
10721308Sache
108119610Sachestatic void _rl_vi_stuff_insert PARAMS((int));
109119610Sachestatic void _rl_vi_save_insert PARAMS((UNDO_LIST *));
110119610Sachestatic int rl_digit_loop1 PARAMS((void));
11121308Sache
11221308Sachevoid
11321308Sache_rl_vi_initialize_line ()
11421308Sache{
11521308Sache  register int i;
11621308Sache
11721308Sache  for (i = 0; i < sizeof (vi_mark_chars) / sizeof (int); i++)
11821308Sache    vi_mark_chars[i] = -1;
11921308Sache}
12021308Sache
12121308Sachevoid
12221308Sache_rl_vi_reset_last ()
12321308Sache{
12421308Sache  _rl_vi_last_command = 'i';
12521308Sache  _rl_vi_last_repeat = 1;
12621308Sache  _rl_vi_last_arg_sign = 1;
12721308Sache  _rl_vi_last_motion = 0;
12821308Sache}
12921308Sache
13021308Sachevoid
13121308Sache_rl_vi_set_last (key, repeat, sign)
13221308Sache     int key, repeat, sign;
13321308Sache{
13421308Sache  _rl_vi_last_command = key;
13521308Sache  _rl_vi_last_repeat = repeat;
13621308Sache  _rl_vi_last_arg_sign = sign;
13721308Sache}
13821308Sache
13921308Sache/* Is the command C a VI mode text modification command? */
14021308Sacheint
14121308Sache_rl_vi_textmod_command (c)
14221308Sache     int c;
14321308Sache{
14421308Sache  return (member (c, vi_textmod));
14521308Sache}
14621308Sache
14721308Sachestatic void
14821308Sache_rl_vi_stuff_insert (count)
14921308Sache     int count;
15021308Sache{
15121308Sache  rl_begin_undo_group ();
15221308Sache  while (count--)
15321308Sache    rl_insert_text (vi_insert_buffer);
15421308Sache  rl_end_undo_group ();
15521308Sache}
15621308Sache
15721308Sache/* Bound to `.'.  Called from command mode, so we know that we have to
15821308Sache   redo a text modification command.  The default for _rl_vi_last_command
15921308Sache   puts you back into insert mode. */
16021308Sacheint
16121308Sacherl_vi_redo (count, c)
16221308Sache     int count, c;
16321308Sache{
164119610Sache  int r;
165119610Sache
16621308Sache  if (!rl_explicit_arg)
16721308Sache    {
16821308Sache      rl_numeric_arg = _rl_vi_last_repeat;
16921308Sache      rl_arg_sign = _rl_vi_last_arg_sign;
17021308Sache    }
17121308Sache
172119610Sache  r = 0;
17321308Sache  vi_redoing = 1;
17421308Sache  /* If we're redoing an insert with `i', stuff in the inserted text
17521308Sache     and do not go into insertion mode. */
17621308Sache  if (_rl_vi_last_command == 'i' && vi_insert_buffer && *vi_insert_buffer)
17721308Sache    {
17821308Sache      _rl_vi_stuff_insert (count);
17921308Sache      /* And back up point over the last character inserted. */
18021308Sache      if (rl_point > 0)
18121308Sache	rl_point--;
18221308Sache    }
18321308Sache  else
184119610Sache    r = _rl_dispatch (_rl_vi_last_command, _rl_keymap);
18521308Sache  vi_redoing = 0;
18621308Sache
187119610Sache  return (r);
18821308Sache}
18921308Sache
19021308Sache/* A placeholder for further expansion. */
19121308Sacheint
19221308Sacherl_vi_undo (count, key)
19321308Sache     int count, key;
19421308Sache{
19521308Sache  return (rl_undo_command (count, key));
19621308Sache}
19721308Sache
19821308Sache/* Yank the nth arg from the previous line into this line at point. */
19921308Sacheint
20021308Sacherl_vi_yank_arg (count, key)
20121308Sache     int count, key;
20221308Sache{
20321308Sache  /* Readline thinks that the first word on a line is the 0th, while vi
20421308Sache     thinks the first word on a line is the 1st.  Compensate. */
20521308Sache  if (rl_explicit_arg)
20621308Sache    rl_yank_nth_arg (count - 1, 0);
20721308Sache  else
20821308Sache    rl_yank_nth_arg ('$', 0);
20921308Sache
21021308Sache  return (0);
21121308Sache}
21221308Sache
21321308Sache/* With an argument, move back that many history lines, else move to the
21421308Sache   beginning of history. */
21521308Sacheint
21621308Sacherl_vi_fetch_history (count, c)
21721308Sache     int count, c;
21821308Sache{
21921308Sache  int wanted;
22021308Sache
22121308Sache  /* Giving an argument of n means we want the nth command in the history
22221308Sache     file.  The command number is interpreted the same way that the bash
22321308Sache     `history' command does it -- that is, giving an argument count of 450
22421308Sache     to this command would get the command listed as number 450 in the
22521308Sache     output of `history'. */
22621308Sache  if (rl_explicit_arg)
22721308Sache    {
22821308Sache      wanted = history_base + where_history () - count;
22921308Sache      if (wanted <= 0)
23021308Sache        rl_beginning_of_history (0, 0);
23121308Sache      else
23221308Sache        rl_get_previous_history (wanted, c);
23321308Sache    }
23421308Sache  else
23521308Sache    rl_beginning_of_history (count, 0);
23621308Sache  return (0);
23721308Sache}
23821308Sache
23921308Sache/* Search again for the last thing searched for. */
24021308Sacheint
24121308Sacherl_vi_search_again (count, key)
24221308Sache     int count, key;
24321308Sache{
24421308Sache  switch (key)
24521308Sache    {
24621308Sache    case 'n':
24721308Sache      rl_noninc_reverse_search_again (count, key);
24821308Sache      break;
24921308Sache
25021308Sache    case 'N':
25121308Sache      rl_noninc_forward_search_again (count, key);
25221308Sache      break;
25321308Sache    }
25421308Sache  return (0);
25521308Sache}
25621308Sache
25721308Sache/* Do a vi style search. */
25821308Sacheint
25921308Sacherl_vi_search (count, key)
26021308Sache     int count, key;
26121308Sache{
26221308Sache  switch (key)
26321308Sache    {
26421308Sache    case '?':
26521308Sache      rl_noninc_forward_search (count, key);
26621308Sache      break;
26721308Sache
26821308Sache    case '/':
26921308Sache      rl_noninc_reverse_search (count, key);
27021308Sache      break;
27121308Sache
27221308Sache    default:
27375406Sache      rl_ding ();
27421308Sache      break;
27521308Sache    }
27621308Sache  return (0);
27721308Sache}
27821308Sache
27921308Sache/* Completion, from vi's point of view. */
28021308Sacheint
28121308Sacherl_vi_complete (ignore, key)
28221308Sache     int ignore, key;
28321308Sache{
28421308Sache  if ((rl_point < rl_end) && (!whitespace (rl_line_buffer[rl_point])))
28521308Sache    {
28621308Sache      if (!whitespace (rl_line_buffer[rl_point + 1]))
28721308Sache	rl_vi_end_word (1, 'E');
28821308Sache      rl_point++;
28921308Sache    }
29021308Sache
29121308Sache  if (key == '*')
29221308Sache    rl_complete_internal ('*');	/* Expansion and replacement. */
29321308Sache  else if (key == '=')
29421308Sache    rl_complete_internal ('?');	/* List possible completions. */
29521308Sache  else if (key == '\\')
29621308Sache    rl_complete_internal (TAB);	/* Standard Readline completion. */
29721308Sache  else
29821308Sache    rl_complete (0, key);
29921308Sache
30021308Sache  if (key == '*' || key == '\\')
30121308Sache    {
30221308Sache      _rl_vi_set_last (key, 1, rl_arg_sign);
30321308Sache      rl_vi_insertion_mode (1, key);
30421308Sache    }
30521308Sache  return (0);
30621308Sache}
30721308Sache
30821308Sache/* Tilde expansion for vi mode. */
30921308Sacheint
31021308Sacherl_vi_tilde_expand (ignore, key)
31121308Sache     int ignore, key;
31221308Sache{
31321308Sache  rl_tilde_expand (0, key);
31421308Sache  _rl_vi_set_last (key, 1, rl_arg_sign);	/* XXX */
31521308Sache  rl_vi_insertion_mode (1, key);
31621308Sache  return (0);
31721308Sache}
31821308Sache
31921308Sache/* Previous word in vi mode. */
32021308Sacheint
32121308Sacherl_vi_prev_word (count, key)
32221308Sache     int count, key;
32321308Sache{
32421308Sache  if (count < 0)
32521308Sache    return (rl_vi_next_word (-count, key));
32621308Sache
32721308Sache  if (rl_point == 0)
32821308Sache    {
32975406Sache      rl_ding ();
33021308Sache      return (0);
33121308Sache    }
33221308Sache
33321308Sache  if (_rl_uppercase_p (key))
33447558Sache    rl_vi_bWord (count, key);
33521308Sache  else
33647558Sache    rl_vi_bword (count, key);
33721308Sache
33821308Sache  return (0);
33921308Sache}
34021308Sache
34121308Sache/* Next word in vi mode. */
34221308Sacheint
34321308Sacherl_vi_next_word (count, key)
34421308Sache     int count, key;
34521308Sache{
34621308Sache  if (count < 0)
34721308Sache    return (rl_vi_prev_word (-count, key));
34821308Sache
34921308Sache  if (rl_point >= (rl_end - 1))
35021308Sache    {
35175406Sache      rl_ding ();
35221308Sache      return (0);
35321308Sache    }
35421308Sache
35521308Sache  if (_rl_uppercase_p (key))
35647558Sache    rl_vi_fWord (count, key);
35721308Sache  else
35847558Sache    rl_vi_fword (count, key);
35921308Sache  return (0);
36021308Sache}
36121308Sache
36221308Sache/* Move to the end of the ?next? word. */
36321308Sacheint
36421308Sacherl_vi_end_word (count, key)
36521308Sache     int count, key;
36621308Sache{
36721308Sache  if (count < 0)
36821308Sache    {
36975406Sache      rl_ding ();
37021308Sache      return -1;
37121308Sache    }
37221308Sache
37321308Sache  if (_rl_uppercase_p (key))
37447558Sache    rl_vi_eWord (count, key);
37521308Sache  else
37647558Sache    rl_vi_eword (count, key);
37721308Sache  return (0);
37821308Sache}
37921308Sache
38021308Sache/* Move forward a word the way that 'W' does. */
38121308Sacheint
38247558Sacherl_vi_fWord (count, ignore)
38347558Sache     int count, ignore;
38421308Sache{
38521308Sache  while (count-- && rl_point < (rl_end - 1))
38621308Sache    {
38721308Sache      /* Skip until whitespace. */
38821308Sache      while (!whitespace (rl_line_buffer[rl_point]) && rl_point < rl_end)
38921308Sache	rl_point++;
39021308Sache
39121308Sache      /* Now skip whitespace. */
39221308Sache      while (whitespace (rl_line_buffer[rl_point]) && rl_point < rl_end)
39321308Sache	rl_point++;
39421308Sache    }
39521308Sache  return (0);
39621308Sache}
39721308Sache
39821308Sacheint
39947558Sacherl_vi_bWord (count, ignore)
40047558Sache     int count, ignore;
40121308Sache{
40221308Sache  while (count-- && rl_point > 0)
40321308Sache    {
40421308Sache      /* If we are at the start of a word, move back to whitespace so
40521308Sache	 we will go back to the start of the previous word. */
40621308Sache      if (!whitespace (rl_line_buffer[rl_point]) &&
40721308Sache	  whitespace (rl_line_buffer[rl_point - 1]))
40821308Sache	rl_point--;
40921308Sache
41021308Sache      while (rl_point > 0 && whitespace (rl_line_buffer[rl_point]))
41121308Sache	rl_point--;
41221308Sache
41321308Sache      if (rl_point > 0)
41421308Sache	{
41521308Sache	  while (--rl_point >= 0 && !whitespace (rl_line_buffer[rl_point]));
41621308Sache	  rl_point++;
41721308Sache	}
41821308Sache    }
41921308Sache  return (0);
42021308Sache}
42121308Sache
42221308Sacheint
42347558Sacherl_vi_eWord (count, ignore)
42447558Sache     int count, ignore;
42521308Sache{
42621308Sache  while (count-- && rl_point < (rl_end - 1))
42721308Sache    {
42821308Sache      if (!whitespace (rl_line_buffer[rl_point]))
42921308Sache	rl_point++;
43021308Sache
43121308Sache      /* Move to the next non-whitespace character (to the start of the
43221308Sache	 next word). */
43321308Sache      while (++rl_point < rl_end && whitespace (rl_line_buffer[rl_point]));
43421308Sache
43521308Sache      if (rl_point && rl_point < rl_end)
43621308Sache	{
43721308Sache	  /* Skip whitespace. */
43821308Sache	  while (rl_point < rl_end && whitespace (rl_line_buffer[rl_point]))
43921308Sache	    rl_point++;
44021308Sache
44121308Sache	  /* Skip until whitespace. */
44221308Sache	  while (rl_point < rl_end && !whitespace (rl_line_buffer[rl_point]))
44321308Sache	    rl_point++;
44421308Sache
44521308Sache	  /* Move back to the last character of the word. */
44621308Sache	  rl_point--;
44721308Sache	}
44821308Sache    }
44921308Sache  return (0);
45021308Sache}
45121308Sache
45221308Sacheint
45347558Sacherl_vi_fword (count, ignore)
45447558Sache     int count, ignore;
45521308Sache{
45621308Sache  while (count-- && rl_point < (rl_end - 1))
45721308Sache    {
45821308Sache      /* Move to white space (really non-identifer). */
459119610Sache      if (_rl_isident (rl_line_buffer[rl_point]))
46021308Sache	{
461119610Sache	  while (_rl_isident (rl_line_buffer[rl_point]) && rl_point < rl_end)
46221308Sache	    rl_point++;
46321308Sache	}
46421308Sache      else /* if (!whitespace (rl_line_buffer[rl_point])) */
46521308Sache	{
466119610Sache	  while (!_rl_isident (rl_line_buffer[rl_point]) &&
46721308Sache		 !whitespace (rl_line_buffer[rl_point]) && rl_point < rl_end)
46821308Sache	    rl_point++;
46921308Sache	}
47021308Sache
47121308Sache      /* Move past whitespace. */
47221308Sache      while (whitespace (rl_line_buffer[rl_point]) && rl_point < rl_end)
47321308Sache	rl_point++;
47421308Sache    }
47521308Sache  return (0);
47621308Sache}
47721308Sache
47821308Sacheint
47947558Sacherl_vi_bword (count, ignore)
48047558Sache     int count, ignore;
48121308Sache{
48221308Sache  while (count-- && rl_point > 0)
48321308Sache    {
48421308Sache      int last_is_ident;
48521308Sache
48621308Sache      /* If we are at the start of a word, move back to whitespace
48721308Sache	 so we will go back to the start of the previous word. */
48821308Sache      if (!whitespace (rl_line_buffer[rl_point]) &&
48921308Sache	  whitespace (rl_line_buffer[rl_point - 1]))
49021308Sache	rl_point--;
49121308Sache
49221308Sache      /* If this character and the previous character are `opposite', move
49321308Sache	 back so we don't get messed up by the rl_point++ down there in
49421308Sache	 the while loop.  Without this code, words like `l;' screw up the
49521308Sache	 function. */
496119610Sache      last_is_ident = _rl_isident (rl_line_buffer[rl_point - 1]);
497119610Sache      if ((_rl_isident (rl_line_buffer[rl_point]) && !last_is_ident) ||
498119610Sache	  (!_rl_isident (rl_line_buffer[rl_point]) && last_is_ident))
49921308Sache	rl_point--;
50021308Sache
50121308Sache      while (rl_point > 0 && whitespace (rl_line_buffer[rl_point]))
50221308Sache	rl_point--;
50321308Sache
50421308Sache      if (rl_point > 0)
50521308Sache	{
506119610Sache	  if (_rl_isident (rl_line_buffer[rl_point]))
507119610Sache	    while (--rl_point >= 0 && _rl_isident (rl_line_buffer[rl_point]));
50821308Sache	  else
509119610Sache	    while (--rl_point >= 0 && !_rl_isident (rl_line_buffer[rl_point]) &&
51021308Sache		   !whitespace (rl_line_buffer[rl_point]));
51121308Sache	  rl_point++;
51221308Sache	}
51321308Sache    }
51421308Sache  return (0);
51521308Sache}
51621308Sache
51721308Sacheint
51847558Sacherl_vi_eword (count, ignore)
51947558Sache     int count, ignore;
52021308Sache{
52121308Sache  while (count-- && rl_point < rl_end - 1)
52221308Sache    {
52321308Sache      if (!whitespace (rl_line_buffer[rl_point]))
52421308Sache	rl_point++;
52521308Sache
52621308Sache      while (rl_point < rl_end && whitespace (rl_line_buffer[rl_point]))
52721308Sache	rl_point++;
52821308Sache
52921308Sache      if (rl_point < rl_end)
53021308Sache	{
531119610Sache	  if (_rl_isident (rl_line_buffer[rl_point]))
532119610Sache	    while (++rl_point < rl_end && _rl_isident (rl_line_buffer[rl_point]));
53321308Sache	  else
534119610Sache	    while (++rl_point < rl_end && !_rl_isident (rl_line_buffer[rl_point])
53521308Sache		   && !whitespace (rl_line_buffer[rl_point]));
53621308Sache	}
53721308Sache      rl_point--;
53821308Sache    }
53921308Sache  return (0);
54021308Sache}
54121308Sache
54221308Sacheint
54321308Sacherl_vi_insert_beg (count, key)
54421308Sache     int count, key;
54521308Sache{
54621308Sache  rl_beg_of_line (1, key);
54721308Sache  rl_vi_insertion_mode (1, key);
54821308Sache  return (0);
54921308Sache}
55021308Sache
55121308Sacheint
55221308Sacherl_vi_append_mode (count, key)
55321308Sache     int count, key;
55421308Sache{
55521308Sache  if (rl_point < rl_end)
556119610Sache    {
557119610Sache      if (MB_CUR_MAX == 1 || rl_byte_oriented)
558119610Sache	rl_point++;
559119610Sache      else
560119610Sache        {
561119610Sache          int point = rl_point;
562119610Sache          rl_forward_char (1, key);
563119610Sache          if (point == rl_point)
564119610Sache            rl_point = rl_end;
565119610Sache        }
566119610Sache    }
56721308Sache  rl_vi_insertion_mode (1, key);
56821308Sache  return (0);
56921308Sache}
57021308Sache
57121308Sacheint
57221308Sacherl_vi_append_eol (count, key)
57321308Sache     int count, key;
57421308Sache{
57521308Sache  rl_end_of_line (1, key);
57621308Sache  rl_vi_append_mode (1, key);
57721308Sache  return (0);
57821308Sache}
57921308Sache
58021308Sache/* What to do in the case of C-d. */
58121308Sacheint
58221308Sacherl_vi_eof_maybe (count, c)
58321308Sache     int count, c;
58421308Sache{
58521308Sache  return (rl_newline (1, '\n'));
58621308Sache}
58721308Sache
58821308Sache/* Insertion mode stuff. */
58921308Sache
59021308Sache/* Switching from one mode to the other really just involves
59121308Sache   switching keymaps. */
59221308Sacheint
59321308Sacherl_vi_insertion_mode (count, key)
59421308Sache     int count, key;
59521308Sache{
59621308Sache  _rl_keymap = vi_insertion_keymap;
59721308Sache  _rl_vi_last_key_before_insert = key;
59821308Sache  return (0);
59921308Sache}
60021308Sache
60121308Sachestatic void
60221308Sache_rl_vi_save_insert (up)
60321308Sache      UNDO_LIST *up;
60421308Sache{
60521308Sache  int len, start, end;
60621308Sache
60735486Sache  if (up == 0)
60835486Sache    {
60935486Sache      if (vi_insert_buffer_size >= 1)
61035486Sache	vi_insert_buffer[0] = '\0';
61135486Sache      return;
61235486Sache    }
61335486Sache
61421308Sache  start = up->start;
61521308Sache  end = up->end;
61621308Sache  len = end - start + 1;
61721308Sache  if (len >= vi_insert_buffer_size)
61821308Sache    {
61921308Sache      vi_insert_buffer_size += (len + 32) - (len % 32);
620119610Sache      vi_insert_buffer = (char *)xrealloc (vi_insert_buffer, vi_insert_buffer_size);
62121308Sache    }
62221308Sache  strncpy (vi_insert_buffer, rl_line_buffer + start, len - 1);
62321308Sache  vi_insert_buffer[len-1] = '\0';
62421308Sache}
62521308Sache
62621308Sachevoid
62721308Sache_rl_vi_done_inserting ()
62821308Sache{
62921308Sache  if (_rl_vi_doing_insert)
63021308Sache    {
631119610Sache      /* The `C', `s', and `S' commands set this. */
63221308Sache      rl_end_undo_group ();
63321308Sache      /* Now, the text between rl_undo_list->next->start and
63421308Sache	 rl_undo_list->next->end is what was inserted while in insert
63521308Sache	 mode.  It gets copied to VI_INSERT_BUFFER because it depends
63621308Sache	 on absolute indices into the line which may change (though they
63721308Sache	 probably will not). */
63821308Sache      _rl_vi_doing_insert = 0;
63921308Sache      _rl_vi_save_insert (rl_undo_list->next);
64021308Sache      vi_continued_command = 1;
64121308Sache    }
64221308Sache  else
64321308Sache    {
64421308Sache      if (_rl_vi_last_key_before_insert == 'i' && rl_undo_list)
64521308Sache        _rl_vi_save_insert (rl_undo_list);
64621308Sache      /* XXX - Other keys probably need to be checked. */
64721308Sache      else if (_rl_vi_last_key_before_insert == 'C')
64821308Sache	rl_end_undo_group ();
64921308Sache      while (_rl_undo_group_level > 0)
65021308Sache	rl_end_undo_group ();
65121308Sache      vi_continued_command = 0;
65221308Sache    }
65321308Sache}
65421308Sache
65521308Sacheint
65621308Sacherl_vi_movement_mode (count, key)
65721308Sache     int count, key;
65821308Sache{
65921308Sache  if (rl_point > 0)
660119610Sache    rl_backward_char (1, key);
66121308Sache
66221308Sache  _rl_keymap = vi_movement_keymap;
66321308Sache  _rl_vi_done_inserting ();
66421308Sache  return (0);
66521308Sache}
66621308Sache
66721308Sacheint
66821308Sacherl_vi_arg_digit (count, c)
66921308Sache     int count, c;
67021308Sache{
67121308Sache  if (c == '0' && rl_numeric_arg == 1 && !rl_explicit_arg)
67221308Sache    return (rl_beg_of_line (1, c));
67321308Sache  else
67421308Sache    return (rl_digit_argument (count, c));
67521308Sache}
67621308Sache
677119610Sache/* Change the case of the next COUNT characters. */
678119610Sache#if defined (HANDLE_MULTIBYTE)
679119610Sachestatic int
680119610Sache_rl_vi_change_mbchar_case (count)
681119610Sache     int count;
682119610Sache{
683119610Sache  wchar_t wc;
684125665Sache  char mb[MB_LEN_MAX+1];
685125665Sache  int mblen;
686119610Sache  mbstate_t ps;
687119610Sache
688119610Sache  memset (&ps, 0, sizeof (mbstate_t));
689119610Sache  if (_rl_adjust_point (rl_line_buffer, rl_point, &ps) > 0)
690119610Sache    count--;
691119610Sache  while (count-- && rl_point < rl_end)
692119610Sache    {
693119610Sache      mbrtowc (&wc, rl_line_buffer + rl_point, rl_end - rl_point, &ps);
694119610Sache      if (iswupper (wc))
695119610Sache	wc = towlower (wc);
696119610Sache      else if (iswlower (wc))
697119610Sache	wc = towupper (wc);
698119610Sache      else
699119610Sache	{
700119610Sache	  /* Just skip over chars neither upper nor lower case */
701119610Sache	  rl_forward_char (1, 0);
702119610Sache	  continue;
703119610Sache	}
704119610Sache
705119610Sache      /* Vi is kind of strange here. */
706119610Sache      if (wc)
707119610Sache	{
708125665Sache	  mblen = wctomb (mb, wc);
709125665Sache	  if (mblen >= 0)
710125665Sache	    mb[mblen] = '\0';
711119610Sache	  rl_begin_undo_group ();
712119610Sache	  rl_delete (1, 0);
713119610Sache	  rl_insert_text (mb);
714119610Sache	  rl_end_undo_group ();
715119610Sache	  rl_vi_check ();
716119610Sache	}
717119610Sache      else
718119610Sache        rl_forward_char (1, 0);
719119610Sache    }
720119610Sache
721119610Sache  return 0;
722119610Sache}
723119610Sache#endif
724119610Sache
72521308Sacheint
72621308Sacherl_vi_change_case (count, ignore)
72721308Sache     int count, ignore;
72821308Sache{
72921308Sache  char c = 0;
73021308Sache
73121308Sache  /* Don't try this on an empty line. */
73221308Sache  if (rl_point >= rl_end)
73321308Sache    return (0);
73421308Sache
735119610Sache#if defined (HANDLE_MULTIBYTE)
736119610Sache  if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
737119610Sache    return (_rl_vi_change_mbchar_case (count));
738119610Sache#endif
739119610Sache
74021308Sache  while (count-- && rl_point < rl_end)
74121308Sache    {
74221308Sache      if (_rl_uppercase_p (rl_line_buffer[rl_point]))
74321308Sache	c = _rl_to_lower (rl_line_buffer[rl_point]);
74421308Sache      else if (_rl_lowercase_p (rl_line_buffer[rl_point]))
74521308Sache	c = _rl_to_upper (rl_line_buffer[rl_point]);
74621308Sache      else
74721308Sache	{
74821308Sache	  /* Just skip over characters neither upper nor lower case. */
749119610Sache	  rl_forward_char (1, c);
75021308Sache	  continue;
75121308Sache	}
75221308Sache
75321308Sache      /* Vi is kind of strange here. */
75421308Sache      if (c)
75521308Sache	{
75621308Sache	  rl_begin_undo_group ();
75721308Sache	  rl_delete (1, c);
758119610Sache	  _rl_insert_char (1, c);
75921308Sache	  rl_end_undo_group ();
76021308Sache	  rl_vi_check ();
76121308Sache        }
76221308Sache      else
763119610Sache	rl_forward_char (1, c);
76421308Sache    }
76521308Sache  return (0);
76621308Sache}
76721308Sache
76821308Sacheint
76921308Sacherl_vi_put (count, key)
77021308Sache     int count, key;
77121308Sache{
77221308Sache  if (!_rl_uppercase_p (key) && (rl_point + 1 <= rl_end))
773119610Sache    rl_point = _rl_find_next_mbchar (rl_line_buffer, rl_point, 1, MB_FIND_NONZERO);
77421308Sache
77547558Sache  rl_yank (1, key);
776119610Sache  rl_backward_char (1, key);
77721308Sache  return (0);
77821308Sache}
77921308Sache
78021308Sacheint
78121308Sacherl_vi_check ()
78221308Sache{
78321308Sache  if (rl_point && rl_point == rl_end)
784119610Sache    {
785119610Sache      if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
786119610Sache	rl_point = _rl_find_prev_mbchar (rl_line_buffer, rl_point, MB_FIND_NONZERO);
787119610Sache      else
788119610Sache        rl_point--;
789119610Sache    }
79021308Sache  return (0);
79121308Sache}
79221308Sache
79321308Sacheint
79421308Sacherl_vi_column (count, key)
79521308Sache     int count, key;
79621308Sache{
79721308Sache  if (count > rl_end)
79821308Sache    rl_end_of_line (1, key);
79921308Sache  else
80021308Sache    rl_point = count - 1;
80121308Sache  return (0);
80221308Sache}
80321308Sache
80421308Sacheint
80521308Sacherl_vi_domove (key, nextkey)
80621308Sache     int key, *nextkey;
80721308Sache{
80821308Sache  int c, save;
80921308Sache  int old_end;
81021308Sache
81121308Sache  rl_mark = rl_point;
81275406Sache  RL_SETSTATE(RL_STATE_MOREINPUT);
81321308Sache  c = rl_read_key ();
81475406Sache  RL_UNSETSTATE(RL_STATE_MOREINPUT);
81521308Sache  *nextkey = c;
81621308Sache
81721308Sache  if (!member (c, vi_motion))
81821308Sache    {
81921308Sache      if (_rl_digit_p (c))
82021308Sache	{
82121308Sache	  save = rl_numeric_arg;
82221308Sache	  rl_numeric_arg = _rl_digit_value (c);
82321308Sache	  rl_digit_loop1 ();
82421308Sache	  rl_numeric_arg *= save;
82575406Sache	  RL_SETSTATE(RL_STATE_MOREINPUT);
82621308Sache	  c = rl_read_key ();	/* real command */
82775406Sache	  RL_UNSETSTATE(RL_STATE_MOREINPUT);
82821308Sache	  *nextkey = c;
82921308Sache	}
83021308Sache      else if (key == c && (key == 'd' || key == 'y' || key == 'c'))
83121308Sache	{
83221308Sache	  rl_mark = rl_end;
83321308Sache	  rl_beg_of_line (1, c);
83421308Sache	  _rl_vi_last_motion = c;
83521308Sache	  return (0);
83621308Sache	}
83721308Sache      else
83821308Sache	return (-1);
83921308Sache    }
84021308Sache
84121308Sache  _rl_vi_last_motion = c;
84221308Sache
84321308Sache  /* Append a blank character temporarily so that the motion routines
84421308Sache     work right at the end of the line. */
84521308Sache  old_end = rl_end;
84621308Sache  rl_line_buffer[rl_end++] = ' ';
84721308Sache  rl_line_buffer[rl_end] = '\0';
84821308Sache
84921308Sache  _rl_dispatch (c, _rl_keymap);
85021308Sache
85121308Sache  /* Remove the blank that we added. */
85221308Sache  rl_end = old_end;
85321308Sache  rl_line_buffer[rl_end] = '\0';
85421308Sache  if (rl_point > rl_end)
85521308Sache    rl_point = rl_end;
85621308Sache
85721308Sache  /* No change in position means the command failed. */
85821308Sache  if (rl_mark == rl_point)
85921308Sache    return (-1);
86021308Sache
86121308Sache  /* rl_vi_f[wW]ord () leaves the cursor on the first character of the next
86221308Sache     word.  If we are not at the end of the line, and we are on a
86321308Sache     non-whitespace character, move back one (presumably to whitespace). */
86421308Sache  if ((_rl_to_upper (c) == 'W') && rl_point < rl_end && rl_point > rl_mark &&
86521308Sache      !whitespace (rl_line_buffer[rl_point]))
86621308Sache    rl_point--;
86721308Sache
86821308Sache  /* If cw or cW, back up to the end of a word, so the behaviour of ce
86921308Sache     or cE is the actual result.  Brute-force, no subtlety. */
87021308Sache  if (key == 'c' && rl_point >= rl_mark && (_rl_to_upper (c) == 'W'))
87121308Sache    {
87221308Sache      /* Don't move farther back than where we started. */
87321308Sache      while (rl_point > rl_mark && whitespace (rl_line_buffer[rl_point]))
87421308Sache	rl_point--;
87521308Sache
87621308Sache      /* Posix.2 says that if cw or cW moves the cursor towards the end of
87721308Sache	 the line, the character under the cursor should be deleted. */
87821308Sache      if (rl_point == rl_mark)
87921308Sache        rl_point++;
88021308Sache      else
88121308Sache	{
88221308Sache	  /* Move past the end of the word so that the kill doesn't
88321308Sache	     remove the last letter of the previous word.  Only do this
88421308Sache	     if we are not at the end of the line. */
88521308Sache	  if (rl_point >= 0 && rl_point < (rl_end - 1) && !whitespace (rl_line_buffer[rl_point]))
88621308Sache	    rl_point++;
88721308Sache	}
88821308Sache    }
88921308Sache
89021308Sache  if (rl_mark < rl_point)
891119610Sache    SWAP (rl_point, rl_mark);
89221308Sache
89321308Sache  return (0);
89421308Sache}
89521308Sache
89621308Sache/* A simplified loop for vi. Don't dispatch key at end.
89775406Sache   Don't recognize minus sign?
89875406Sache   Should this do rl_save_prompt/rl_restore_prompt? */
89921308Sachestatic int
90021308Sacherl_digit_loop1 ()
90121308Sache{
90221308Sache  int key, c;
90321308Sache
90475406Sache  RL_SETSTATE(RL_STATE_NUMERICARG);
90521308Sache  while (1)
90621308Sache    {
90775406Sache      if (rl_numeric_arg > 1000000)
90875406Sache	{
90975406Sache	  rl_explicit_arg = rl_numeric_arg = 0;
91075406Sache	  rl_ding ();
91175406Sache	  rl_clear_message ();
91275406Sache	  RL_UNSETSTATE(RL_STATE_NUMERICARG);
91375406Sache	  return 1;
91475406Sache	}
915119610Sache      rl_message ("(arg: %d) ", rl_arg_sign * rl_numeric_arg);
91675406Sache      RL_SETSTATE(RL_STATE_MOREINPUT);
91721308Sache      key = c = rl_read_key ();
91875406Sache      RL_UNSETSTATE(RL_STATE_MOREINPUT);
91921308Sache
920119610Sache      if (c >= 0 && _rl_keymap[c].type == ISFUNC &&
92121308Sache	  _rl_keymap[c].function == rl_universal_argument)
92221308Sache	{
92321308Sache	  rl_numeric_arg *= 4;
92421308Sache	  continue;
92521308Sache	}
92621308Sache
92721308Sache      c = UNMETA (c);
92821308Sache      if (_rl_digit_p (c))
92921308Sache	{
93021308Sache	  if (rl_explicit_arg)
93121308Sache	    rl_numeric_arg = (rl_numeric_arg * 10) + _rl_digit_value (c);
93221308Sache	  else
93321308Sache	    rl_numeric_arg = _rl_digit_value (c);
93421308Sache	  rl_explicit_arg = 1;
93521308Sache	}
93621308Sache      else
93721308Sache	{
93821308Sache	  rl_clear_message ();
93921308Sache	  rl_stuff_char (key);
94021308Sache	  break;
94121308Sache	}
94221308Sache    }
94375406Sache
94475406Sache  RL_UNSETSTATE(RL_STATE_NUMERICARG);
94521308Sache  return (0);
94621308Sache}
94721308Sache
94821308Sacheint
94921308Sacherl_vi_delete_to (count, key)
95021308Sache     int count, key;
95121308Sache{
95221308Sache  int c;
95321308Sache
95421308Sache  if (_rl_uppercase_p (key))
95521308Sache    rl_stuff_char ('$');
95621308Sache  else if (vi_redoing)
95721308Sache    rl_stuff_char (_rl_vi_last_motion);
95821308Sache
95921308Sache  if (rl_vi_domove (key, &c))
96021308Sache    {
96175406Sache      rl_ding ();
96221308Sache      return -1;
96321308Sache    }
96421308Sache
96521308Sache  /* These are the motion commands that do not require adjusting the
96621308Sache     mark. */
96721308Sache  if ((strchr (" l|h^0bB", c) == 0) && (rl_mark < rl_end))
96821308Sache    rl_mark++;
96921308Sache
97021308Sache  rl_kill_text (rl_point, rl_mark);
97121308Sache  return (0);
97221308Sache}
97321308Sache
97421308Sacheint
97521308Sacherl_vi_change_to (count, key)
97621308Sache     int count, key;
97721308Sache{
97821308Sache  int c, start_pos;
97921308Sache
98021308Sache  if (_rl_uppercase_p (key))
98121308Sache    rl_stuff_char ('$');
98221308Sache  else if (vi_redoing)
98321308Sache    rl_stuff_char (_rl_vi_last_motion);
98421308Sache
98521308Sache  start_pos = rl_point;
98621308Sache
98721308Sache  if (rl_vi_domove (key, &c))
98821308Sache    {
98975406Sache      rl_ding ();
99021308Sache      return -1;
99121308Sache    }
99221308Sache
99321308Sache  /* These are the motion commands that do not require adjusting the
99421308Sache     mark.  c[wW] are handled by special-case code in rl_vi_domove(),
99521308Sache     and already leave the mark at the correct location. */
99621308Sache  if ((strchr (" l|hwW^0bB", c) == 0) && (rl_mark < rl_end))
99721308Sache    rl_mark++;
99821308Sache
99921308Sache  /* The cursor never moves with c[wW]. */
100021308Sache  if ((_rl_to_upper (c) == 'W') && rl_point < start_pos)
100121308Sache    rl_point = start_pos;
100221308Sache
100321308Sache  if (vi_redoing)
100421308Sache    {
100521308Sache      if (vi_insert_buffer && *vi_insert_buffer)
100621308Sache	rl_begin_undo_group ();
100721308Sache      rl_delete_text (rl_point, rl_mark);
100821308Sache      if (vi_insert_buffer && *vi_insert_buffer)
100921308Sache	{
101021308Sache	  rl_insert_text (vi_insert_buffer);
101121308Sache	  rl_end_undo_group ();
101221308Sache	}
101321308Sache    }
101421308Sache  else
101521308Sache    {
101621308Sache      rl_begin_undo_group ();		/* to make the `u' command work */
101721308Sache      rl_kill_text (rl_point, rl_mark);
101821308Sache      /* `C' does not save the text inserted for undoing or redoing. */
101921308Sache      if (_rl_uppercase_p (key) == 0)
102021308Sache        _rl_vi_doing_insert = 1;
102121308Sache      _rl_vi_set_last (key, count, rl_arg_sign);
102221308Sache      rl_vi_insertion_mode (1, key);
102321308Sache    }
102421308Sache
102521308Sache  return (0);
102621308Sache}
102721308Sache
102821308Sacheint
102921308Sacherl_vi_yank_to (count, key)
103021308Sache     int count, key;
103121308Sache{
103221308Sache  int c, save = rl_point;
103321308Sache
103421308Sache  if (_rl_uppercase_p (key))
103521308Sache    rl_stuff_char ('$');
103621308Sache
103721308Sache  if (rl_vi_domove (key, &c))
103821308Sache    {
103975406Sache      rl_ding ();
104021308Sache      return -1;
104121308Sache    }
104221308Sache
104321308Sache  /* These are the motion commands that do not require adjusting the
104421308Sache     mark. */
104521308Sache  if ((strchr (" l|h^0%bB", c) == 0) && (rl_mark < rl_end))
104621308Sache    rl_mark++;
104721308Sache
104821308Sache  rl_begin_undo_group ();
104921308Sache  rl_kill_text (rl_point, rl_mark);
105021308Sache  rl_end_undo_group ();
105121308Sache  rl_do_undo ();
105221308Sache  rl_point = save;
105321308Sache
105421308Sache  return (0);
105521308Sache}
105621308Sache
105721308Sacheint
105821308Sacherl_vi_delete (count, key)
105921308Sache     int count, key;
106021308Sache{
106121308Sache  int end;
106221308Sache
106321308Sache  if (rl_end == 0)
106421308Sache    {
106575406Sache      rl_ding ();
106621308Sache      return -1;
106721308Sache    }
106821308Sache
1069119610Sache  if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
1070119610Sache    end = _rl_find_next_mbchar (rl_line_buffer, rl_point, count, MB_FIND_NONZERO);
1071119610Sache  else
1072119610Sache    end = rl_point + count;
107321308Sache
107421308Sache  if (end >= rl_end)
107521308Sache    end = rl_end;
107621308Sache
107721308Sache  rl_kill_text (rl_point, end);
107821308Sache
107921308Sache  if (rl_point > 0 && rl_point == rl_end)
1080119610Sache    rl_backward_char (1, key);
108121308Sache  return (0);
108221308Sache}
108321308Sache
108421308Sacheint
108521308Sacherl_vi_back_to_indent (count, key)
108621308Sache     int count, key;
108721308Sache{
108821308Sache  rl_beg_of_line (1, key);
108921308Sache  while (rl_point < rl_end && whitespace (rl_line_buffer[rl_point]))
109021308Sache    rl_point++;
109121308Sache  return (0);
109221308Sache}
109321308Sache
109421308Sacheint
109521308Sacherl_vi_first_print (count, key)
109621308Sache     int count, key;
109721308Sache{
109821308Sache  return (rl_vi_back_to_indent (1, key));
109921308Sache}
110021308Sache
110121308Sacheint
110221308Sacherl_vi_char_search (count, key)
110321308Sache     int count, key;
110421308Sache{
1105119610Sache#if defined (HANDLE_MULTIBYTE)
1106119610Sache  static char *target;
1107119610Sache  static int mb_len;
1108119610Sache#else
110921308Sache  static char target;
1110119610Sache#endif
111121308Sache  static int orig_dir, dir;
111221308Sache
111321308Sache  if (key == ';' || key == ',')
111421308Sache    dir = key == ';' ? orig_dir : -orig_dir;
111521308Sache  else
111621308Sache    {
111721308Sache      if (vi_redoing)
1118119610Sache#if defined (HANDLE_MULTIBYTE)
1119119610Sache	target = _rl_vi_last_search_mbchar;
1120119610Sache#else
112121308Sache	target = _rl_vi_last_search_char;
1122119610Sache#endif
112321308Sache      else
112475406Sache	{
1125119610Sache#if defined (HANDLE_MULTIBYTE)
1126119610Sache	  mb_len = _rl_read_mbchar (_rl_vi_last_search_mbchar, MB_LEN_MAX);
1127119610Sache	  target = _rl_vi_last_search_mbchar;
1128119610Sache#else
112975406Sache	  RL_SETSTATE(RL_STATE_MOREINPUT);
113075406Sache	  _rl_vi_last_search_char = target = rl_read_key ();
113175406Sache	  RL_UNSETSTATE(RL_STATE_MOREINPUT);
1132119610Sache#endif
113375406Sache	}
113421308Sache
113521308Sache      switch (key)
113621308Sache        {
113721308Sache        case 't':
113821308Sache          orig_dir = dir = FTO;
113921308Sache          break;
114021308Sache
114121308Sache        case 'T':
114221308Sache          orig_dir = dir = BTO;
114321308Sache          break;
114421308Sache
114521308Sache        case 'f':
114621308Sache          orig_dir = dir = FFIND;
114721308Sache          break;
114821308Sache
114921308Sache        case 'F':
115021308Sache          orig_dir = dir = BFIND;
115121308Sache          break;
115221308Sache        }
115321308Sache    }
115421308Sache
1155119610Sache#if defined (HANDLE_MULTIBYTE)
1156119610Sache   return (_rl_char_search_internal (count, dir, target, mb_len));
1157119610Sache#else
115821308Sache  return (_rl_char_search_internal (count, dir, target));
1159119610Sache#endif
116021308Sache}
116121308Sache
116221308Sache/* Match brackets */
116321308Sacheint
116421308Sacherl_vi_match (ignore, key)
116521308Sache     int ignore, key;
116621308Sache{
1167119610Sache  int count = 1, brack, pos, tmp, pre;
116821308Sache
116921308Sache  pos = rl_point;
117021308Sache  if ((brack = rl_vi_bracktype (rl_line_buffer[rl_point])) == 0)
117121308Sache    {
1172119610Sache      if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
1173119610Sache	{
1174119610Sache	  while ((brack = rl_vi_bracktype (rl_line_buffer[rl_point])) == 0)
1175119610Sache	    {
1176119610Sache	      pre = rl_point;
1177119610Sache	      rl_forward_char (1, key);
1178119610Sache	      if (pre == rl_point)
1179119610Sache	        break;
1180119610Sache	    }
1181119610Sache	}
1182119610Sache      else
1183119610Sache	while ((brack = rl_vi_bracktype (rl_line_buffer[rl_point])) == 0 &&
1184119610Sache		rl_point < rl_end - 1)
1185119610Sache	  rl_forward_char (1, key);
118621308Sache
118721308Sache      if (brack <= 0)
118821308Sache	{
118921308Sache	  rl_point = pos;
119075406Sache	  rl_ding ();
119121308Sache	  return -1;
119221308Sache	}
119321308Sache    }
119421308Sache
119521308Sache  pos = rl_point;
119621308Sache
119721308Sache  if (brack < 0)
119821308Sache    {
119921308Sache      while (count)
120021308Sache	{
1201119610Sache	  tmp = pos;
1202119610Sache	  if (MB_CUR_MAX == 1 || rl_byte_oriented)
1203119610Sache	    pos--;
1204119610Sache	  else
120521308Sache	    {
1206119610Sache	      pos = _rl_find_prev_mbchar (rl_line_buffer, pos, MB_FIND_ANY);
1207119610Sache	      if (tmp == pos)
1208119610Sache	        pos--;
1209119610Sache	    }
1210119610Sache	  if (pos >= 0)
1211119610Sache	    {
121221308Sache	      int b = rl_vi_bracktype (rl_line_buffer[pos]);
121321308Sache	      if (b == -brack)
121421308Sache		count--;
121521308Sache	      else if (b == brack)
121621308Sache		count++;
121721308Sache	    }
121821308Sache	  else
121921308Sache	    {
122075406Sache	      rl_ding ();
122121308Sache	      return -1;
122221308Sache	    }
122321308Sache	}
122421308Sache    }
122521308Sache  else
122621308Sache    {			/* brack > 0 */
122721308Sache      while (count)
122821308Sache	{
1229119610Sache	  if (MB_CUR_MAX == 1 || rl_byte_oriented)
1230119610Sache	    pos++;
1231119610Sache	  else
1232119610Sache	    pos = _rl_find_next_mbchar (rl_line_buffer, pos, 1, MB_FIND_ANY);
1233119610Sache
1234119610Sache	  if (pos < rl_end)
123521308Sache	    {
123621308Sache	      int b = rl_vi_bracktype (rl_line_buffer[pos]);
123721308Sache	      if (b == -brack)
123821308Sache		count--;
123921308Sache	      else if (b == brack)
124021308Sache		count++;
124121308Sache	    }
124221308Sache	  else
124321308Sache	    {
124475406Sache	      rl_ding ();
124521308Sache	      return -1;
124621308Sache	    }
124721308Sache	}
124821308Sache    }
124921308Sache  rl_point = pos;
125021308Sache  return (0);
125121308Sache}
125221308Sache
125321308Sacheint
125421308Sacherl_vi_bracktype (c)
125521308Sache     int c;
125621308Sache{
125721308Sache  switch (c)
125821308Sache    {
125921308Sache    case '(': return  1;
126021308Sache    case ')': return -1;
126121308Sache    case '[': return  2;
126221308Sache    case ']': return -2;
126321308Sache    case '{': return  3;
126421308Sache    case '}': return -3;
126521308Sache    default:  return  0;
126621308Sache    }
126721308Sache}
126821308Sache
1269119610Sache/* XXX - think about reading an entire mbchar with _rl_read_mbchar and
1270119610Sache   inserting it in one bunch instead of the loop below (like in
1271119610Sache   rl_vi_char_search or _rl_vi_change_mbchar_case.  Set c to mbchar[0]
1272119610Sache   for test against 033 or ^C.  Make sure that _rl_read_mbchar does
1273119610Sache   this right. */
127421308Sacheint
127521308Sacherl_vi_change_char (count, key)
127621308Sache     int count, key;
127721308Sache{
127821308Sache  int c;
127921308Sache
128021308Sache  if (vi_redoing)
128121308Sache    c = _rl_vi_last_replacement;
128221308Sache  else
128375406Sache    {
128475406Sache      RL_SETSTATE(RL_STATE_MOREINPUT);
128575406Sache      _rl_vi_last_replacement = c = rl_read_key ();
128675406Sache      RL_UNSETSTATE(RL_STATE_MOREINPUT);
128775406Sache    }
128821308Sache
128921308Sache  if (c == '\033' || c == CTRL ('C'))
129021308Sache    return -1;
129121308Sache
129221308Sache  while (count-- && rl_point < rl_end)
129321308Sache    {
129421308Sache      rl_begin_undo_group ();
129521308Sache
129621308Sache      rl_delete (1, c);
1297119610Sache#if defined (HANDLE_MULTIBYTE)
1298119610Sache      if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
1299119610Sache	while (_rl_insert_char (1, c))
1300119610Sache	  {
1301119610Sache	    RL_SETSTATE (RL_STATE_MOREINPUT);
1302119610Sache	    c = rl_read_key ();
1303119610Sache	    RL_UNSETSTATE (RL_STATE_MOREINPUT);
1304119610Sache	  }
1305119610Sache      else
1306119610Sache#endif
1307119610Sache	_rl_insert_char (1, c);
130821308Sache      if (count == 0)
1309119610Sache	rl_backward_char (1, c);
131021308Sache
131121308Sache      rl_end_undo_group ();
131221308Sache    }
131321308Sache  return (0);
131421308Sache}
131521308Sache
131621308Sacheint
131721308Sacherl_vi_subst (count, key)
131821308Sache     int count, key;
131921308Sache{
1320119610Sache  /* If we are redoing, rl_vi_change_to will stuff the last motion char */
1321119610Sache  if (vi_redoing == 0)
1322119610Sache    rl_stuff_char ((key == 'S') ? 'c' : ' ');	/* `S' == `cc', `s' == `c ' */
132321308Sache
1324119610Sache  return (rl_vi_change_to (count, 'c'));
132521308Sache}
132621308Sache
132721308Sacheint
132821308Sacherl_vi_overstrike (count, key)
132921308Sache     int count, key;
133021308Sache{
133121308Sache  if (_rl_vi_doing_insert == 0)
133221308Sache    {
133321308Sache      _rl_vi_doing_insert = 1;
133421308Sache      rl_begin_undo_group ();
133521308Sache    }
133621308Sache
1337119610Sache  if (count > 0)
133821308Sache    {
1339119610Sache      _rl_overwrite_char (count, key);
1340119610Sache      vi_replace_count += count;
1341119610Sache    }
134221308Sache
134321308Sache  return (0);
134421308Sache}
134521308Sache
134621308Sacheint
134721308Sacherl_vi_overstrike_delete (count, key)
134821308Sache     int count, key;
134921308Sache{
135021308Sache  int i, s;
135121308Sache
135221308Sache  for (i = 0; i < count; i++)
135321308Sache    {
135421308Sache      if (vi_replace_count == 0)
135521308Sache	{
135675406Sache	  rl_ding ();
135721308Sache	  break;
135821308Sache	}
135921308Sache      s = rl_point;
136021308Sache
136121308Sache      if (rl_do_undo ())
136221308Sache	vi_replace_count--;
136321308Sache
136421308Sache      if (rl_point == s)
1365119610Sache	rl_backward_char (1, key);
136621308Sache    }
136721308Sache
136821308Sache  if (vi_replace_count == 0 && _rl_vi_doing_insert)
136921308Sache    {
137021308Sache      rl_end_undo_group ();
137121308Sache      rl_do_undo ();
137221308Sache      _rl_vi_doing_insert = 0;
137321308Sache    }
137421308Sache  return (0);
137521308Sache}
137621308Sache
137721308Sacheint
137821308Sacherl_vi_replace (count, key)
137921308Sache     int count, key;
138021308Sache{
138121308Sache  int i;
138221308Sache
138321308Sache  vi_replace_count = 0;
138421308Sache
138521308Sache  if (!vi_replace_map)
138621308Sache    {
138721308Sache      vi_replace_map = rl_make_bare_keymap ();
138821308Sache
138921308Sache      for (i = ' '; i < KEYMAP_SIZE; i++)
139021308Sache	vi_replace_map[i].function = rl_vi_overstrike;
139121308Sache
139221308Sache      vi_replace_map[RUBOUT].function = rl_vi_overstrike_delete;
139321308Sache      vi_replace_map[ESC].function = rl_vi_movement_mode;
139421308Sache      vi_replace_map[RETURN].function = rl_newline;
139521308Sache      vi_replace_map[NEWLINE].function = rl_newline;
139621308Sache
139721308Sache      /* If the normal vi insertion keymap has ^H bound to erase, do the
139821308Sache         same here.  Probably should remove the assignment to RUBOUT up
139921308Sache         there, but I don't think it will make a difference in real life. */
140021308Sache      if (vi_insertion_keymap[CTRL ('H')].type == ISFUNC &&
140121308Sache	  vi_insertion_keymap[CTRL ('H')].function == rl_rubout)
140221308Sache	vi_replace_map[CTRL ('H')].function = rl_vi_overstrike_delete;
140321308Sache
140421308Sache    }
140521308Sache  _rl_keymap = vi_replace_map;
140621308Sache  return (0);
140721308Sache}
140821308Sache
140921308Sache#if 0
141021308Sache/* Try to complete the word we are standing on or the word that ends with
141121308Sache   the previous character.  A space matches everything.  Word delimiters are
141221308Sache   space and ;. */
141321308Sacheint
141421308Sacherl_vi_possible_completions()
141521308Sache{
141621308Sache  int save_pos = rl_point;
141721308Sache
141821308Sache  if (rl_line_buffer[rl_point] != ' ' && rl_line_buffer[rl_point] != ';')
141921308Sache    {
142021308Sache      while (rl_point < rl_end && rl_line_buffer[rl_point] != ' ' &&
142121308Sache	     rl_line_buffer[rl_point] != ';')
142221308Sache	rl_point++;
142321308Sache    }
142421308Sache  else if (rl_line_buffer[rl_point - 1] == ';')
142521308Sache    {
142675406Sache      rl_ding ();
142721308Sache      return (0);
142821308Sache    }
142921308Sache
143021308Sache  rl_possible_completions ();
143121308Sache  rl_point = save_pos;
143221308Sache
143321308Sache  return (0);
143421308Sache}
143521308Sache#endif
143621308Sache
143721308Sache/* Functions to save and restore marks. */
143821308Sacheint
143921308Sacherl_vi_set_mark (count, key)
144021308Sache     int count, key;
144121308Sache{
144221308Sache  int ch;
144321308Sache
144475406Sache  RL_SETSTATE(RL_STATE_MOREINPUT);
144521308Sache  ch = rl_read_key ();
144675406Sache  RL_UNSETSTATE(RL_STATE_MOREINPUT);
144775406Sache
1448119610Sache  if (ch < 'a' || ch > 'z')
144921308Sache    {
145075406Sache      rl_ding ();
145121308Sache      return -1;
145221308Sache    }
145321308Sache  ch -= 'a';
145421308Sache  vi_mark_chars[ch] = rl_point;
145521308Sache  return 0;
145621308Sache}
145721308Sache
145821308Sacheint
145921308Sacherl_vi_goto_mark (count, key)
146021308Sache     int count, key;
146121308Sache{
146221308Sache  int ch;
146321308Sache
146475406Sache  RL_SETSTATE(RL_STATE_MOREINPUT);
146521308Sache  ch = rl_read_key ();
146675406Sache  RL_UNSETSTATE(RL_STATE_MOREINPUT);
146775406Sache
146821308Sache  if (ch == '`')
146921308Sache    {
147021308Sache      rl_point = rl_mark;
147121308Sache      return 0;
147221308Sache    }
1473119610Sache  else if (ch < 'a' || ch > 'z')
147421308Sache    {
147575406Sache      rl_ding ();
147621308Sache      return -1;
147721308Sache    }
147821308Sache
147921308Sache  ch -= 'a';
148021308Sache  if (vi_mark_chars[ch] == -1)
148121308Sache    {
148275406Sache      rl_ding ();
148321308Sache      return -1;
148421308Sache    }
148521308Sache  rl_point = vi_mark_chars[ch];
148621308Sache  return 0;
148721308Sache}
148821308Sache
148921308Sache#endif /* VI_MODE */
1490