1125665Sache/* $FreeBSD$ */
221308Sache/* vi_mode.c -- A vi emulation mode for Bash.
321308Sache   Derived from code written by Jeff Sparkes (jsparkes@bnr.ca).  */
421308Sache
5157188Sache/* Copyright (C) 1987-2005 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
67136759Speterint _rl_vi_last_command = 'i';	/* default `.' puts you in insert mode */
68136759Speter
6921308Sache/* Non-zero means enter insertion mode. */
7021308Sachestatic int _rl_vi_doing_insert;
7121308Sache
7221308Sache/* Command keys which do movement for xxx_to commands. */
73119610Sachestatic const char *vi_motion = " hl^$0ftFT;,%wbeWBE|";
7421308Sache
7521308Sache/* Keymap used for vi replace characters.  Created dynamically since
7621308Sache   rarely used. */
7721308Sachestatic Keymap vi_replace_map;
7821308Sache
7921308Sache/* The number of characters inserted in the last replace operation. */
8021308Sachestatic int vi_replace_count;
8121308Sache
8221308Sache/* If non-zero, we have text inserted after a c[motion] command that put
8321308Sache   us implicitly into insert mode.  Some people want this text to be
8421308Sache   attached to the command so that it is `redoable' with `.'. */
8521308Sachestatic int vi_continued_command;
8621308Sachestatic char *vi_insert_buffer;
8721308Sachestatic int vi_insert_buffer_size;
8821308Sache
8921308Sachestatic int _rl_vi_last_repeat = 1;
9021308Sachestatic int _rl_vi_last_arg_sign = 1;
9121308Sachestatic int _rl_vi_last_motion;
92119610Sache#if defined (HANDLE_MULTIBYTE)
93119610Sachestatic char _rl_vi_last_search_mbchar[MB_LEN_MAX];
94157188Sachestatic int _rl_vi_last_search_mblen;
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 *));
112157188Sache
113165675Sachestatic void _rl_vi_backup PARAMS((void));
114165675Sache
115157188Sachestatic int _rl_vi_arg_dispatch PARAMS((int));
116119610Sachestatic int rl_digit_loop1 PARAMS((void));
11721308Sache
118157188Sachestatic int _rl_vi_set_mark PARAMS((void));
119157188Sachestatic int _rl_vi_goto_mark PARAMS((void));
120157188Sache
121165675Sachestatic void _rl_vi_append_forward PARAMS((int));
122165675Sache
123157188Sachestatic int _rl_vi_callback_getchar PARAMS((char *, int));
124157188Sache
125157188Sache#if defined (READLINE_CALLBACKS)
126157188Sachestatic int _rl_vi_callback_set_mark PARAMS((_rl_callback_generic_arg *));
127157188Sachestatic int _rl_vi_callback_goto_mark PARAMS((_rl_callback_generic_arg *));
128157188Sachestatic int _rl_vi_callback_change_char PARAMS((_rl_callback_generic_arg *));
129157188Sachestatic int _rl_vi_callback_char_search PARAMS((_rl_callback_generic_arg *));
130157188Sache#endif
131157188Sache
13221308Sachevoid
13321308Sache_rl_vi_initialize_line ()
13421308Sache{
13521308Sache  register int i;
13621308Sache
13721308Sache  for (i = 0; i < sizeof (vi_mark_chars) / sizeof (int); i++)
13821308Sache    vi_mark_chars[i] = -1;
139157188Sache
140157188Sache  RL_UNSETSTATE(RL_STATE_VICMDONCE);
14121308Sache}
14221308Sache
14321308Sachevoid
14421308Sache_rl_vi_reset_last ()
14521308Sache{
14621308Sache  _rl_vi_last_command = 'i';
14721308Sache  _rl_vi_last_repeat = 1;
14821308Sache  _rl_vi_last_arg_sign = 1;
14921308Sache  _rl_vi_last_motion = 0;
15021308Sache}
15121308Sache
15221308Sachevoid
15321308Sache_rl_vi_set_last (key, repeat, sign)
15421308Sache     int key, repeat, sign;
15521308Sache{
15621308Sache  _rl_vi_last_command = key;
15721308Sache  _rl_vi_last_repeat = repeat;
15821308Sache  _rl_vi_last_arg_sign = sign;
15921308Sache}
16021308Sache
161136759Speter/* A convenience function that calls _rl_vi_set_last to save the last command
162136759Speter   information and enters insertion mode. */
163136759Spetervoid
164136759Speterrl_vi_start_inserting (key, repeat, sign)
165136759Speter     int key, repeat, sign;
166136759Speter{
167136759Speter  _rl_vi_set_last (key, repeat, sign);
168136759Speter  rl_vi_insertion_mode (1, key);
169136759Speter}
170136759Speter
17121308Sache/* Is the command C a VI mode text modification command? */
17221308Sacheint
17321308Sache_rl_vi_textmod_command (c)
17421308Sache     int c;
17521308Sache{
17621308Sache  return (member (c, vi_textmod));
17721308Sache}
17821308Sache
17921308Sachestatic void
18021308Sache_rl_vi_stuff_insert (count)
18121308Sache     int count;
18221308Sache{
18321308Sache  rl_begin_undo_group ();
18421308Sache  while (count--)
18521308Sache    rl_insert_text (vi_insert_buffer);
18621308Sache  rl_end_undo_group ();
18721308Sache}
18821308Sache
18921308Sache/* Bound to `.'.  Called from command mode, so we know that we have to
19021308Sache   redo a text modification command.  The default for _rl_vi_last_command
19121308Sache   puts you back into insert mode. */
19221308Sacheint
19321308Sacherl_vi_redo (count, c)
19421308Sache     int count, c;
19521308Sache{
196119610Sache  int r;
197119610Sache
19821308Sache  if (!rl_explicit_arg)
19921308Sache    {
20021308Sache      rl_numeric_arg = _rl_vi_last_repeat;
20121308Sache      rl_arg_sign = _rl_vi_last_arg_sign;
20221308Sache    }
20321308Sache
204119610Sache  r = 0;
20521308Sache  vi_redoing = 1;
20621308Sache  /* If we're redoing an insert with `i', stuff in the inserted text
20721308Sache     and do not go into insertion mode. */
20821308Sache  if (_rl_vi_last_command == 'i' && vi_insert_buffer && *vi_insert_buffer)
20921308Sache    {
21021308Sache      _rl_vi_stuff_insert (count);
21121308Sache      /* And back up point over the last character inserted. */
21221308Sache      if (rl_point > 0)
213165675Sache	_rl_vi_backup ();
21421308Sache    }
215165675Sache  /* Ditto for redoing an insert with `a', but move forward a character first
216165675Sache     like the `a' command does. */
217165675Sache  else if (_rl_vi_last_command == 'a' && vi_insert_buffer && *vi_insert_buffer)
218165675Sache    {
219165675Sache      _rl_vi_append_forward ('a');
220165675Sache      _rl_vi_stuff_insert (count);
221165675Sache      if (rl_point > 0)
222165675Sache	_rl_vi_backup ();
223165675Sache    }
22421308Sache  else
225119610Sache    r = _rl_dispatch (_rl_vi_last_command, _rl_keymap);
22621308Sache  vi_redoing = 0;
22721308Sache
228119610Sache  return (r);
22921308Sache}
23021308Sache
23121308Sache/* A placeholder for further expansion. */
23221308Sacheint
23321308Sacherl_vi_undo (count, key)
23421308Sache     int count, key;
23521308Sache{
23621308Sache  return (rl_undo_command (count, key));
23721308Sache}
23821308Sache
23921308Sache/* Yank the nth arg from the previous line into this line at point. */
24021308Sacheint
24121308Sacherl_vi_yank_arg (count, key)
24221308Sache     int count, key;
24321308Sache{
24421308Sache  /* Readline thinks that the first word on a line is the 0th, while vi
24521308Sache     thinks the first word on a line is the 1st.  Compensate. */
24621308Sache  if (rl_explicit_arg)
24721308Sache    rl_yank_nth_arg (count - 1, 0);
24821308Sache  else
24921308Sache    rl_yank_nth_arg ('$', 0);
25021308Sache
25121308Sache  return (0);
25221308Sache}
25321308Sache
25421308Sache/* With an argument, move back that many history lines, else move to the
25521308Sache   beginning of history. */
25621308Sacheint
25721308Sacherl_vi_fetch_history (count, c)
25821308Sache     int count, c;
25921308Sache{
26021308Sache  int wanted;
26121308Sache
26221308Sache  /* Giving an argument of n means we want the nth command in the history
26321308Sache     file.  The command number is interpreted the same way that the bash
26421308Sache     `history' command does it -- that is, giving an argument count of 450
26521308Sache     to this command would get the command listed as number 450 in the
26621308Sache     output of `history'. */
26721308Sache  if (rl_explicit_arg)
26821308Sache    {
26921308Sache      wanted = history_base + where_history () - count;
27021308Sache      if (wanted <= 0)
27121308Sache        rl_beginning_of_history (0, 0);
27221308Sache      else
27321308Sache        rl_get_previous_history (wanted, c);
27421308Sache    }
27521308Sache  else
27621308Sache    rl_beginning_of_history (count, 0);
27721308Sache  return (0);
27821308Sache}
27921308Sache
28021308Sache/* Search again for the last thing searched for. */
28121308Sacheint
28221308Sacherl_vi_search_again (count, key)
28321308Sache     int count, key;
28421308Sache{
28521308Sache  switch (key)
28621308Sache    {
28721308Sache    case 'n':
28821308Sache      rl_noninc_reverse_search_again (count, key);
28921308Sache      break;
29021308Sache
29121308Sache    case 'N':
29221308Sache      rl_noninc_forward_search_again (count, key);
29321308Sache      break;
29421308Sache    }
29521308Sache  return (0);
29621308Sache}
29721308Sache
29821308Sache/* Do a vi style search. */
29921308Sacheint
30021308Sacherl_vi_search (count, key)
30121308Sache     int count, key;
30221308Sache{
30321308Sache  switch (key)
30421308Sache    {
30521308Sache    case '?':
306136759Speter      _rl_free_saved_history_line ();
30721308Sache      rl_noninc_forward_search (count, key);
30821308Sache      break;
30921308Sache
31021308Sache    case '/':
311136759Speter      _rl_free_saved_history_line ();
31221308Sache      rl_noninc_reverse_search (count, key);
31321308Sache      break;
31421308Sache
31521308Sache    default:
31675406Sache      rl_ding ();
31721308Sache      break;
31821308Sache    }
31921308Sache  return (0);
32021308Sache}
32121308Sache
32221308Sache/* Completion, from vi's point of view. */
32321308Sacheint
32421308Sacherl_vi_complete (ignore, key)
32521308Sache     int ignore, key;
32621308Sache{
32721308Sache  if ((rl_point < rl_end) && (!whitespace (rl_line_buffer[rl_point])))
32821308Sache    {
32921308Sache      if (!whitespace (rl_line_buffer[rl_point + 1]))
33021308Sache	rl_vi_end_word (1, 'E');
33121308Sache      rl_point++;
33221308Sache    }
33321308Sache
33421308Sache  if (key == '*')
33521308Sache    rl_complete_internal ('*');	/* Expansion and replacement. */
33621308Sache  else if (key == '=')
33721308Sache    rl_complete_internal ('?');	/* List possible completions. */
33821308Sache  else if (key == '\\')
33921308Sache    rl_complete_internal (TAB);	/* Standard Readline completion. */
34021308Sache  else
34121308Sache    rl_complete (0, key);
34221308Sache
34321308Sache  if (key == '*' || key == '\\')
344136759Speter    rl_vi_start_inserting (key, 1, rl_arg_sign);
345136759Speter
34621308Sache  return (0);
34721308Sache}
34821308Sache
34921308Sache/* Tilde expansion for vi mode. */
35021308Sacheint
35121308Sacherl_vi_tilde_expand (ignore, key)
35221308Sache     int ignore, key;
35321308Sache{
35421308Sache  rl_tilde_expand (0, key);
355136759Speter  rl_vi_start_inserting (key, 1, rl_arg_sign);
35621308Sache  return (0);
35721308Sache}
35821308Sache
35921308Sache/* Previous word in vi mode. */
36021308Sacheint
36121308Sacherl_vi_prev_word (count, key)
36221308Sache     int count, key;
36321308Sache{
36421308Sache  if (count < 0)
36521308Sache    return (rl_vi_next_word (-count, key));
36621308Sache
36721308Sache  if (rl_point == 0)
36821308Sache    {
36975406Sache      rl_ding ();
37021308Sache      return (0);
37121308Sache    }
37221308Sache
37321308Sache  if (_rl_uppercase_p (key))
37447558Sache    rl_vi_bWord (count, key);
37521308Sache  else
37647558Sache    rl_vi_bword (count, key);
37721308Sache
37821308Sache  return (0);
37921308Sache}
38021308Sache
38121308Sache/* Next word in vi mode. */
38221308Sacheint
38321308Sacherl_vi_next_word (count, key)
38421308Sache     int count, key;
38521308Sache{
38621308Sache  if (count < 0)
38721308Sache    return (rl_vi_prev_word (-count, key));
38821308Sache
38921308Sache  if (rl_point >= (rl_end - 1))
39021308Sache    {
39175406Sache      rl_ding ();
39221308Sache      return (0);
39321308Sache    }
39421308Sache
39521308Sache  if (_rl_uppercase_p (key))
39647558Sache    rl_vi_fWord (count, key);
39721308Sache  else
39847558Sache    rl_vi_fword (count, key);
39921308Sache  return (0);
40021308Sache}
40121308Sache
40221308Sache/* Move to the end of the ?next? word. */
40321308Sacheint
40421308Sacherl_vi_end_word (count, key)
40521308Sache     int count, key;
40621308Sache{
40721308Sache  if (count < 0)
40821308Sache    {
40975406Sache      rl_ding ();
41021308Sache      return -1;
41121308Sache    }
41221308Sache
41321308Sache  if (_rl_uppercase_p (key))
41447558Sache    rl_vi_eWord (count, key);
41521308Sache  else
41647558Sache    rl_vi_eword (count, key);
41721308Sache  return (0);
41821308Sache}
41921308Sache
42021308Sache/* Move forward a word the way that 'W' does. */
42121308Sacheint
42247558Sacherl_vi_fWord (count, ignore)
42347558Sache     int count, ignore;
42421308Sache{
42521308Sache  while (count-- && rl_point < (rl_end - 1))
42621308Sache    {
42721308Sache      /* Skip until whitespace. */
42821308Sache      while (!whitespace (rl_line_buffer[rl_point]) && rl_point < rl_end)
42921308Sache	rl_point++;
43021308Sache
43121308Sache      /* Now skip whitespace. */
43221308Sache      while (whitespace (rl_line_buffer[rl_point]) && rl_point < rl_end)
43321308Sache	rl_point++;
43421308Sache    }
43521308Sache  return (0);
43621308Sache}
43721308Sache
43821308Sacheint
43947558Sacherl_vi_bWord (count, ignore)
44047558Sache     int count, ignore;
44121308Sache{
44221308Sache  while (count-- && rl_point > 0)
44321308Sache    {
44421308Sache      /* If we are at the start of a word, move back to whitespace so
44521308Sache	 we will go back to the start of the previous word. */
44621308Sache      if (!whitespace (rl_line_buffer[rl_point]) &&
44721308Sache	  whitespace (rl_line_buffer[rl_point - 1]))
44821308Sache	rl_point--;
44921308Sache
45021308Sache      while (rl_point > 0 && whitespace (rl_line_buffer[rl_point]))
45121308Sache	rl_point--;
45221308Sache
45321308Sache      if (rl_point > 0)
45421308Sache	{
45521308Sache	  while (--rl_point >= 0 && !whitespace (rl_line_buffer[rl_point]));
45621308Sache	  rl_point++;
45721308Sache	}
45821308Sache    }
45921308Sache  return (0);
46021308Sache}
46121308Sache
46221308Sacheint
46347558Sacherl_vi_eWord (count, ignore)
46447558Sache     int count, ignore;
46521308Sache{
46621308Sache  while (count-- && rl_point < (rl_end - 1))
46721308Sache    {
46821308Sache      if (!whitespace (rl_line_buffer[rl_point]))
46921308Sache	rl_point++;
47021308Sache
47121308Sache      /* Move to the next non-whitespace character (to the start of the
47221308Sache	 next word). */
473136759Speter      while (rl_point < rl_end && whitespace (rl_line_buffer[rl_point]))
474136759Speter	rl_point++;
47521308Sache
47621308Sache      if (rl_point && rl_point < rl_end)
47721308Sache	{
47821308Sache	  /* Skip whitespace. */
47921308Sache	  while (rl_point < rl_end && whitespace (rl_line_buffer[rl_point]))
48021308Sache	    rl_point++;
48121308Sache
48221308Sache	  /* Skip until whitespace. */
48321308Sache	  while (rl_point < rl_end && !whitespace (rl_line_buffer[rl_point]))
48421308Sache	    rl_point++;
48521308Sache
48621308Sache	  /* Move back to the last character of the word. */
48721308Sache	  rl_point--;
48821308Sache	}
48921308Sache    }
49021308Sache  return (0);
49121308Sache}
49221308Sache
49321308Sacheint
49447558Sacherl_vi_fword (count, ignore)
49547558Sache     int count, ignore;
49621308Sache{
49721308Sache  while (count-- && rl_point < (rl_end - 1))
49821308Sache    {
49921308Sache      /* Move to white space (really non-identifer). */
500119610Sache      if (_rl_isident (rl_line_buffer[rl_point]))
50121308Sache	{
502119610Sache	  while (_rl_isident (rl_line_buffer[rl_point]) && rl_point < rl_end)
50321308Sache	    rl_point++;
50421308Sache	}
50521308Sache      else /* if (!whitespace (rl_line_buffer[rl_point])) */
50621308Sache	{
507119610Sache	  while (!_rl_isident (rl_line_buffer[rl_point]) &&
50821308Sache		 !whitespace (rl_line_buffer[rl_point]) && rl_point < rl_end)
50921308Sache	    rl_point++;
51021308Sache	}
51121308Sache
51221308Sache      /* Move past whitespace. */
51321308Sache      while (whitespace (rl_line_buffer[rl_point]) && rl_point < rl_end)
51421308Sache	rl_point++;
51521308Sache    }
51621308Sache  return (0);
51721308Sache}
51821308Sache
51921308Sacheint
52047558Sacherl_vi_bword (count, ignore)
52147558Sache     int count, ignore;
52221308Sache{
52321308Sache  while (count-- && rl_point > 0)
52421308Sache    {
52521308Sache      int last_is_ident;
52621308Sache
52721308Sache      /* If we are at the start of a word, move back to whitespace
52821308Sache	 so we will go back to the start of the previous word. */
52921308Sache      if (!whitespace (rl_line_buffer[rl_point]) &&
53021308Sache	  whitespace (rl_line_buffer[rl_point - 1]))
53121308Sache	rl_point--;
53221308Sache
53321308Sache      /* If this character and the previous character are `opposite', move
53421308Sache	 back so we don't get messed up by the rl_point++ down there in
53521308Sache	 the while loop.  Without this code, words like `l;' screw up the
53621308Sache	 function. */
537119610Sache      last_is_ident = _rl_isident (rl_line_buffer[rl_point - 1]);
538119610Sache      if ((_rl_isident (rl_line_buffer[rl_point]) && !last_is_ident) ||
539119610Sache	  (!_rl_isident (rl_line_buffer[rl_point]) && last_is_ident))
54021308Sache	rl_point--;
54121308Sache
54221308Sache      while (rl_point > 0 && whitespace (rl_line_buffer[rl_point]))
54321308Sache	rl_point--;
54421308Sache
54521308Sache      if (rl_point > 0)
54621308Sache	{
547119610Sache	  if (_rl_isident (rl_line_buffer[rl_point]))
548119610Sache	    while (--rl_point >= 0 && _rl_isident (rl_line_buffer[rl_point]));
54921308Sache	  else
550119610Sache	    while (--rl_point >= 0 && !_rl_isident (rl_line_buffer[rl_point]) &&
55121308Sache		   !whitespace (rl_line_buffer[rl_point]));
55221308Sache	  rl_point++;
55321308Sache	}
55421308Sache    }
55521308Sache  return (0);
55621308Sache}
55721308Sache
55821308Sacheint
55947558Sacherl_vi_eword (count, ignore)
56047558Sache     int count, ignore;
56121308Sache{
56221308Sache  while (count-- && rl_point < rl_end - 1)
56321308Sache    {
56421308Sache      if (!whitespace (rl_line_buffer[rl_point]))
56521308Sache	rl_point++;
56621308Sache
56721308Sache      while (rl_point < rl_end && whitespace (rl_line_buffer[rl_point]))
56821308Sache	rl_point++;
56921308Sache
57021308Sache      if (rl_point < rl_end)
57121308Sache	{
572119610Sache	  if (_rl_isident (rl_line_buffer[rl_point]))
573119610Sache	    while (++rl_point < rl_end && _rl_isident (rl_line_buffer[rl_point]));
57421308Sache	  else
575119610Sache	    while (++rl_point < rl_end && !_rl_isident (rl_line_buffer[rl_point])
57621308Sache		   && !whitespace (rl_line_buffer[rl_point]));
57721308Sache	}
57821308Sache      rl_point--;
57921308Sache    }
58021308Sache  return (0);
58121308Sache}
58221308Sache
58321308Sacheint
58421308Sacherl_vi_insert_beg (count, key)
58521308Sache     int count, key;
58621308Sache{
58721308Sache  rl_beg_of_line (1, key);
58821308Sache  rl_vi_insertion_mode (1, key);
58921308Sache  return (0);
59021308Sache}
59121308Sache
592165675Sachestatic void
593165675Sache_rl_vi_append_forward (key)
594165675Sache     int key;
59521308Sache{
596165675Sache  int point;
597165675Sache
59821308Sache  if (rl_point < rl_end)
599119610Sache    {
600119610Sache      if (MB_CUR_MAX == 1 || rl_byte_oriented)
601119610Sache	rl_point++;
602119610Sache      else
603119610Sache        {
604165675Sache          point = rl_point;
605119610Sache          rl_forward_char (1, key);
606119610Sache          if (point == rl_point)
607119610Sache            rl_point = rl_end;
608119610Sache        }
609119610Sache    }
610165675Sache}
611165675Sache
612165675Sacheint
613165675Sacherl_vi_append_mode (count, key)
614165675Sache     int count, key;
615165675Sache{
616165675Sache  _rl_vi_append_forward (key);
617165675Sache  rl_vi_start_inserting (key, 1, rl_arg_sign);
61821308Sache  return (0);
61921308Sache}
62021308Sache
62121308Sacheint
62221308Sacherl_vi_append_eol (count, key)
62321308Sache     int count, key;
62421308Sache{
62521308Sache  rl_end_of_line (1, key);
62621308Sache  rl_vi_append_mode (1, key);
62721308Sache  return (0);
62821308Sache}
62921308Sache
63021308Sache/* What to do in the case of C-d. */
63121308Sacheint
63221308Sacherl_vi_eof_maybe (count, c)
63321308Sache     int count, c;
63421308Sache{
63521308Sache  return (rl_newline (1, '\n'));
63621308Sache}
63721308Sache
63821308Sache/* Insertion mode stuff. */
63921308Sache
64021308Sache/* Switching from one mode to the other really just involves
64121308Sache   switching keymaps. */
64221308Sacheint
64321308Sacherl_vi_insertion_mode (count, key)
64421308Sache     int count, key;
64521308Sache{
64621308Sache  _rl_keymap = vi_insertion_keymap;
64721308Sache  _rl_vi_last_key_before_insert = key;
64821308Sache  return (0);
64921308Sache}
65021308Sache
65121308Sachestatic void
65221308Sache_rl_vi_save_insert (up)
65321308Sache      UNDO_LIST *up;
65421308Sache{
65521308Sache  int len, start, end;
65621308Sache
657165675Sache  if (up == 0 || up->what != UNDO_INSERT)
65835486Sache    {
65935486Sache      if (vi_insert_buffer_size >= 1)
66035486Sache	vi_insert_buffer[0] = '\0';
66135486Sache      return;
66235486Sache    }
66335486Sache
66421308Sache  start = up->start;
66521308Sache  end = up->end;
66621308Sache  len = end - start + 1;
66721308Sache  if (len >= vi_insert_buffer_size)
66821308Sache    {
66921308Sache      vi_insert_buffer_size += (len + 32) - (len % 32);
670119610Sache      vi_insert_buffer = (char *)xrealloc (vi_insert_buffer, vi_insert_buffer_size);
67121308Sache    }
67221308Sache  strncpy (vi_insert_buffer, rl_line_buffer + start, len - 1);
67321308Sache  vi_insert_buffer[len-1] = '\0';
67421308Sache}
67521308Sache
67621308Sachevoid
67721308Sache_rl_vi_done_inserting ()
67821308Sache{
67921308Sache  if (_rl_vi_doing_insert)
68021308Sache    {
681119610Sache      /* The `C', `s', and `S' commands set this. */
68221308Sache      rl_end_undo_group ();
68321308Sache      /* Now, the text between rl_undo_list->next->start and
68421308Sache	 rl_undo_list->next->end is what was inserted while in insert
68521308Sache	 mode.  It gets copied to VI_INSERT_BUFFER because it depends
68621308Sache	 on absolute indices into the line which may change (though they
68721308Sache	 probably will not). */
68821308Sache      _rl_vi_doing_insert = 0;
68921308Sache      _rl_vi_save_insert (rl_undo_list->next);
69021308Sache      vi_continued_command = 1;
69121308Sache    }
69221308Sache  else
69321308Sache    {
694136759Speter      if ((_rl_vi_last_key_before_insert == 'i' || _rl_vi_last_key_before_insert == 'a') && rl_undo_list)
69521308Sache        _rl_vi_save_insert (rl_undo_list);
69621308Sache      /* XXX - Other keys probably need to be checked. */
69721308Sache      else if (_rl_vi_last_key_before_insert == 'C')
69821308Sache	rl_end_undo_group ();
69921308Sache      while (_rl_undo_group_level > 0)
70021308Sache	rl_end_undo_group ();
70121308Sache      vi_continued_command = 0;
70221308Sache    }
70321308Sache}
70421308Sache
70521308Sacheint
70621308Sacherl_vi_movement_mode (count, key)
70721308Sache     int count, key;
70821308Sache{
70921308Sache  if (rl_point > 0)
710119610Sache    rl_backward_char (1, key);
71121308Sache
71221308Sache  _rl_keymap = vi_movement_keymap;
71321308Sache  _rl_vi_done_inserting ();
714157188Sache
715157188Sache  /* This is how POSIX.2 says `U' should behave -- everything up until the
716157188Sache     first time you go into command mode should not be undone. */
717157188Sache  if (RL_ISSTATE (RL_STATE_VICMDONCE) == 0)
718157188Sache    rl_free_undo_list ();
719157188Sache
720157188Sache  RL_SETSTATE (RL_STATE_VICMDONCE);
72121308Sache  return (0);
72221308Sache}
72321308Sache
72421308Sacheint
72521308Sacherl_vi_arg_digit (count, c)
72621308Sache     int count, c;
72721308Sache{
72821308Sache  if (c == '0' && rl_numeric_arg == 1 && !rl_explicit_arg)
72921308Sache    return (rl_beg_of_line (1, c));
73021308Sache  else
73121308Sache    return (rl_digit_argument (count, c));
73221308Sache}
73321308Sache
734119610Sache/* Change the case of the next COUNT characters. */
735119610Sache#if defined (HANDLE_MULTIBYTE)
736119610Sachestatic int
737119610Sache_rl_vi_change_mbchar_case (count)
738119610Sache     int count;
739119610Sache{
740119610Sache  wchar_t wc;
741125665Sache  char mb[MB_LEN_MAX+1];
742165675Sache  int mlen, p;
743119610Sache  mbstate_t ps;
744119610Sache
745119610Sache  memset (&ps, 0, sizeof (mbstate_t));
746119610Sache  if (_rl_adjust_point (rl_line_buffer, rl_point, &ps) > 0)
747119610Sache    count--;
748119610Sache  while (count-- && rl_point < rl_end)
749119610Sache    {
750119610Sache      mbrtowc (&wc, rl_line_buffer + rl_point, rl_end - rl_point, &ps);
751119610Sache      if (iswupper (wc))
752119610Sache	wc = towlower (wc);
753119610Sache      else if (iswlower (wc))
754119610Sache	wc = towupper (wc);
755119610Sache      else
756119610Sache	{
757119610Sache	  /* Just skip over chars neither upper nor lower case */
758119610Sache	  rl_forward_char (1, 0);
759119610Sache	  continue;
760119610Sache	}
761119610Sache
762119610Sache      /* Vi is kind of strange here. */
763119610Sache      if (wc)
764119610Sache	{
765136759Speter	  p = rl_point;
766165675Sache	  mlen = wcrtomb (mb, wc, &ps);
767165675Sache	  if (mlen >= 0)
768165675Sache	    mb[mlen] = '\0';
769119610Sache	  rl_begin_undo_group ();
770136759Speter	  rl_vi_delete (1, 0);
771136759Speter	  if (rl_point < p)	/* Did we retreat at EOL? */
772136759Speter	    rl_point++;	/* XXX - should we advance more than 1 for mbchar? */
773119610Sache	  rl_insert_text (mb);
774119610Sache	  rl_end_undo_group ();
775119610Sache	  rl_vi_check ();
776119610Sache	}
777119610Sache      else
778119610Sache        rl_forward_char (1, 0);
779119610Sache    }
780119610Sache
781119610Sache  return 0;
782119610Sache}
783119610Sache#endif
784119610Sache
78521308Sacheint
78621308Sacherl_vi_change_case (count, ignore)
78721308Sache     int count, ignore;
78821308Sache{
789136759Speter  int c, p;
79021308Sache
79121308Sache  /* Don't try this on an empty line. */
79221308Sache  if (rl_point >= rl_end)
79321308Sache    return (0);
79421308Sache
795136759Speter  c = 0;
796119610Sache#if defined (HANDLE_MULTIBYTE)
797119610Sache  if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
798119610Sache    return (_rl_vi_change_mbchar_case (count));
799119610Sache#endif
800119610Sache
80121308Sache  while (count-- && rl_point < rl_end)
80221308Sache    {
80321308Sache      if (_rl_uppercase_p (rl_line_buffer[rl_point]))
80421308Sache	c = _rl_to_lower (rl_line_buffer[rl_point]);
80521308Sache      else if (_rl_lowercase_p (rl_line_buffer[rl_point]))
80621308Sache	c = _rl_to_upper (rl_line_buffer[rl_point]);
80721308Sache      else
80821308Sache	{
80921308Sache	  /* Just skip over characters neither upper nor lower case. */
810119610Sache	  rl_forward_char (1, c);
81121308Sache	  continue;
81221308Sache	}
81321308Sache
81421308Sache      /* Vi is kind of strange here. */
81521308Sache      if (c)
81621308Sache	{
817136759Speter	  p = rl_point;
81821308Sache	  rl_begin_undo_group ();
819136759Speter	  rl_vi_delete (1, c);
820136759Speter	  if (rl_point < p)	/* Did we retreat at EOL? */
821136759Speter	    rl_point++;
822119610Sache	  _rl_insert_char (1, c);
82321308Sache	  rl_end_undo_group ();
82421308Sache	  rl_vi_check ();
82521308Sache        }
82621308Sache      else
827119610Sache	rl_forward_char (1, c);
82821308Sache    }
82921308Sache  return (0);
83021308Sache}
83121308Sache
83221308Sacheint
83321308Sacherl_vi_put (count, key)
83421308Sache     int count, key;
83521308Sache{
83621308Sache  if (!_rl_uppercase_p (key) && (rl_point + 1 <= rl_end))
837119610Sache    rl_point = _rl_find_next_mbchar (rl_line_buffer, rl_point, 1, MB_FIND_NONZERO);
83821308Sache
839136759Speter  while (count--)
840136759Speter    rl_yank (1, key);
841136759Speter
842119610Sache  rl_backward_char (1, key);
84321308Sache  return (0);
84421308Sache}
84521308Sache
846165675Sachestatic void
847165675Sache_rl_vi_backup ()
848165675Sache{
849165675Sache  if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
850165675Sache    rl_point = _rl_find_prev_mbchar (rl_line_buffer, rl_point, MB_FIND_NONZERO);
851165675Sache  else
852165675Sache    rl_point--;
853165675Sache}
854165675Sache
85521308Sacheint
85621308Sacherl_vi_check ()
85721308Sache{
85821308Sache  if (rl_point && rl_point == rl_end)
859119610Sache    {
860119610Sache      if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
861119610Sache	rl_point = _rl_find_prev_mbchar (rl_line_buffer, rl_point, MB_FIND_NONZERO);
862119610Sache      else
863119610Sache        rl_point--;
864119610Sache    }
86521308Sache  return (0);
86621308Sache}
86721308Sache
86821308Sacheint
86921308Sacherl_vi_column (count, key)
87021308Sache     int count, key;
87121308Sache{
87221308Sache  if (count > rl_end)
87321308Sache    rl_end_of_line (1, key);
87421308Sache  else
87521308Sache    rl_point = count - 1;
87621308Sache  return (0);
87721308Sache}
87821308Sache
87921308Sacheint
88021308Sacherl_vi_domove (key, nextkey)
88121308Sache     int key, *nextkey;
88221308Sache{
88321308Sache  int c, save;
88421308Sache  int old_end;
88521308Sache
88621308Sache  rl_mark = rl_point;
88775406Sache  RL_SETSTATE(RL_STATE_MOREINPUT);
88821308Sache  c = rl_read_key ();
88975406Sache  RL_UNSETSTATE(RL_STATE_MOREINPUT);
890173406Sache
891173406Sache  if (c < 0)
892173406Sache    {
893173406Sache      *nextkey = 0;
894173406Sache      return -1;
895173406Sache    }
896173406Sache
89721308Sache  *nextkey = c;
89821308Sache
89921308Sache  if (!member (c, vi_motion))
90021308Sache    {
90121308Sache      if (_rl_digit_p (c))
90221308Sache	{
90321308Sache	  save = rl_numeric_arg;
90421308Sache	  rl_numeric_arg = _rl_digit_value (c);
905136759Speter	  rl_explicit_arg = 1;
906157188Sache	  RL_SETSTATE (RL_STATE_NUMERICARG|RL_STATE_VIMOTION);
90721308Sache	  rl_digit_loop1 ();
908157188Sache	  RL_UNSETSTATE (RL_STATE_VIMOTION);
90921308Sache	  rl_numeric_arg *= save;
91075406Sache	  RL_SETSTATE(RL_STATE_MOREINPUT);
91121308Sache	  c = rl_read_key ();	/* real command */
91275406Sache	  RL_UNSETSTATE(RL_STATE_MOREINPUT);
913173406Sache	  if (c < 0)
914173406Sache	    {
915173406Sache	      *nextkey = 0;
916173406Sache	      return -1;
917173406Sache	    }
91821308Sache	  *nextkey = c;
91921308Sache	}
92021308Sache      else if (key == c && (key == 'd' || key == 'y' || key == 'c'))
92121308Sache	{
92221308Sache	  rl_mark = rl_end;
92321308Sache	  rl_beg_of_line (1, c);
92421308Sache	  _rl_vi_last_motion = c;
92521308Sache	  return (0);
92621308Sache	}
92721308Sache      else
92821308Sache	return (-1);
92921308Sache    }
93021308Sache
93121308Sache  _rl_vi_last_motion = c;
93221308Sache
93321308Sache  /* Append a blank character temporarily so that the motion routines
93421308Sache     work right at the end of the line. */
93521308Sache  old_end = rl_end;
93621308Sache  rl_line_buffer[rl_end++] = ' ';
93721308Sache  rl_line_buffer[rl_end] = '\0';
93821308Sache
93921308Sache  _rl_dispatch (c, _rl_keymap);
94021308Sache
94121308Sache  /* Remove the blank that we added. */
94221308Sache  rl_end = old_end;
94321308Sache  rl_line_buffer[rl_end] = '\0';
94421308Sache  if (rl_point > rl_end)
94521308Sache    rl_point = rl_end;
94621308Sache
94721308Sache  /* No change in position means the command failed. */
94821308Sache  if (rl_mark == rl_point)
94921308Sache    return (-1);
95021308Sache
95121308Sache  /* rl_vi_f[wW]ord () leaves the cursor on the first character of the next
95221308Sache     word.  If we are not at the end of the line, and we are on a
95321308Sache     non-whitespace character, move back one (presumably to whitespace). */
95421308Sache  if ((_rl_to_upper (c) == 'W') && rl_point < rl_end && rl_point > rl_mark &&
95521308Sache      !whitespace (rl_line_buffer[rl_point]))
95621308Sache    rl_point--;
95721308Sache
95821308Sache  /* If cw or cW, back up to the end of a word, so the behaviour of ce
95921308Sache     or cE is the actual result.  Brute-force, no subtlety. */
96021308Sache  if (key == 'c' && rl_point >= rl_mark && (_rl_to_upper (c) == 'W'))
96121308Sache    {
96221308Sache      /* Don't move farther back than where we started. */
96321308Sache      while (rl_point > rl_mark && whitespace (rl_line_buffer[rl_point]))
96421308Sache	rl_point--;
96521308Sache
96621308Sache      /* Posix.2 says that if cw or cW moves the cursor towards the end of
96721308Sache	 the line, the character under the cursor should be deleted. */
96821308Sache      if (rl_point == rl_mark)
96921308Sache        rl_point++;
97021308Sache      else
97121308Sache	{
97221308Sache	  /* Move past the end of the word so that the kill doesn't
97321308Sache	     remove the last letter of the previous word.  Only do this
97421308Sache	     if we are not at the end of the line. */
97521308Sache	  if (rl_point >= 0 && rl_point < (rl_end - 1) && !whitespace (rl_line_buffer[rl_point]))
97621308Sache	    rl_point++;
97721308Sache	}
97821308Sache    }
97921308Sache
98021308Sache  if (rl_mark < rl_point)
981119610Sache    SWAP (rl_point, rl_mark);
98221308Sache
98321308Sache  return (0);
98421308Sache}
98521308Sache
986157188Sache/* Process C as part of the current numeric argument.  Return -1 if the
987157188Sache   argument should be aborted, 0 if we should not read any more chars, and
988157188Sache   1 if we should continue to read chars. */
989157188Sachestatic int
990157188Sache_rl_vi_arg_dispatch (c)
991157188Sache     int c;
992157188Sache{
993157188Sache  int key;
994157188Sache
995157188Sache  key = c;
996157188Sache  if (c >= 0 && _rl_keymap[c].type == ISFUNC && _rl_keymap[c].function == rl_universal_argument)
997157188Sache    {
998157188Sache      rl_numeric_arg *= 4;
999157188Sache      return 1;
1000157188Sache    }
1001157188Sache
1002157188Sache  c = UNMETA (c);
1003157188Sache
1004157188Sache  if (_rl_digit_p (c))
1005157188Sache    {
1006157188Sache      if (rl_explicit_arg)
1007157188Sache	rl_numeric_arg = (rl_numeric_arg * 10) + _rl_digit_value (c);
1008157188Sache      else
1009157188Sache	rl_numeric_arg = _rl_digit_value (c);
1010157188Sache      rl_explicit_arg = 1;
1011157188Sache      return 1;
1012157188Sache    }
1013157188Sache  else
1014157188Sache    {
1015157188Sache      rl_clear_message ();
1016157188Sache      rl_stuff_char (key);
1017157188Sache      return 0;
1018157188Sache    }
1019157188Sache}
1020157188Sache
102121308Sache/* A simplified loop for vi. Don't dispatch key at end.
102275406Sache   Don't recognize minus sign?
102375406Sache   Should this do rl_save_prompt/rl_restore_prompt? */
102421308Sachestatic int
102521308Sacherl_digit_loop1 ()
102621308Sache{
1027157188Sache  int c, r;
102821308Sache
102921308Sache  while (1)
103021308Sache    {
1031157188Sache      if (_rl_arg_overflow ())
1032157188Sache	return 1;
103321308Sache
1034157188Sache      c = _rl_arg_getchar ();
103521308Sache
1036157188Sache      r = _rl_vi_arg_dispatch (c);
1037157188Sache      if (r <= 0)
1038157188Sache	break;
103921308Sache    }
104075406Sache
104175406Sache  RL_UNSETSTATE(RL_STATE_NUMERICARG);
104221308Sache  return (0);
104321308Sache}
104421308Sache
104521308Sacheint
104621308Sacherl_vi_delete_to (count, key)
104721308Sache     int count, key;
104821308Sache{
104921308Sache  int c;
105021308Sache
105121308Sache  if (_rl_uppercase_p (key))
105221308Sache    rl_stuff_char ('$');
105321308Sache  else if (vi_redoing)
105421308Sache    rl_stuff_char (_rl_vi_last_motion);
105521308Sache
105621308Sache  if (rl_vi_domove (key, &c))
105721308Sache    {
105875406Sache      rl_ding ();
105921308Sache      return -1;
106021308Sache    }
106121308Sache
106221308Sache  /* These are the motion commands that do not require adjusting the
106321308Sache     mark. */
106421308Sache  if ((strchr (" l|h^0bB", c) == 0) && (rl_mark < rl_end))
106521308Sache    rl_mark++;
106621308Sache
106721308Sache  rl_kill_text (rl_point, rl_mark);
106821308Sache  return (0);
106921308Sache}
107021308Sache
107121308Sacheint
107221308Sacherl_vi_change_to (count, key)
107321308Sache     int count, key;
107421308Sache{
107521308Sache  int c, start_pos;
107621308Sache
107721308Sache  if (_rl_uppercase_p (key))
107821308Sache    rl_stuff_char ('$');
107921308Sache  else if (vi_redoing)
108021308Sache    rl_stuff_char (_rl_vi_last_motion);
108121308Sache
108221308Sache  start_pos = rl_point;
108321308Sache
108421308Sache  if (rl_vi_domove (key, &c))
108521308Sache    {
108675406Sache      rl_ding ();
108721308Sache      return -1;
108821308Sache    }
108921308Sache
109021308Sache  /* These are the motion commands that do not require adjusting the
109121308Sache     mark.  c[wW] are handled by special-case code in rl_vi_domove(),
109221308Sache     and already leave the mark at the correct location. */
109321308Sache  if ((strchr (" l|hwW^0bB", c) == 0) && (rl_mark < rl_end))
109421308Sache    rl_mark++;
109521308Sache
109621308Sache  /* The cursor never moves with c[wW]. */
109721308Sache  if ((_rl_to_upper (c) == 'W') && rl_point < start_pos)
109821308Sache    rl_point = start_pos;
109921308Sache
110021308Sache  if (vi_redoing)
110121308Sache    {
110221308Sache      if (vi_insert_buffer && *vi_insert_buffer)
110321308Sache	rl_begin_undo_group ();
110421308Sache      rl_delete_text (rl_point, rl_mark);
110521308Sache      if (vi_insert_buffer && *vi_insert_buffer)
110621308Sache	{
110721308Sache	  rl_insert_text (vi_insert_buffer);
110821308Sache	  rl_end_undo_group ();
110921308Sache	}
111021308Sache    }
111121308Sache  else
111221308Sache    {
111321308Sache      rl_begin_undo_group ();		/* to make the `u' command work */
111421308Sache      rl_kill_text (rl_point, rl_mark);
111521308Sache      /* `C' does not save the text inserted for undoing or redoing. */
111621308Sache      if (_rl_uppercase_p (key) == 0)
111721308Sache        _rl_vi_doing_insert = 1;
1118136759Speter      rl_vi_start_inserting (key, rl_numeric_arg, rl_arg_sign);
111921308Sache    }
112021308Sache
112121308Sache  return (0);
112221308Sache}
112321308Sache
112421308Sacheint
112521308Sacherl_vi_yank_to (count, key)
112621308Sache     int count, key;
112721308Sache{
1128157188Sache  int c, save;
112921308Sache
1130157188Sache  save = rl_point;
113121308Sache  if (_rl_uppercase_p (key))
113221308Sache    rl_stuff_char ('$');
113321308Sache
113421308Sache  if (rl_vi_domove (key, &c))
113521308Sache    {
113675406Sache      rl_ding ();
113721308Sache      return -1;
113821308Sache    }
113921308Sache
114021308Sache  /* These are the motion commands that do not require adjusting the
114121308Sache     mark. */
114221308Sache  if ((strchr (" l|h^0%bB", c) == 0) && (rl_mark < rl_end))
114321308Sache    rl_mark++;
114421308Sache
114521308Sache  rl_begin_undo_group ();
114621308Sache  rl_kill_text (rl_point, rl_mark);
114721308Sache  rl_end_undo_group ();
114821308Sache  rl_do_undo ();
114921308Sache  rl_point = save;
115021308Sache
115121308Sache  return (0);
115221308Sache}
115321308Sache
115421308Sacheint
1155157188Sacherl_vi_rubout (count, key)
1156157188Sache     int count, key;
1157157188Sache{
1158165675Sache  int opoint;
1159157188Sache
1160157188Sache  if (count < 0)
1161157188Sache    return (rl_vi_delete (-count, key));
1162157188Sache
1163157188Sache  if (rl_point == 0)
1164157188Sache    {
1165157188Sache      rl_ding ();
1166157188Sache      return -1;
1167157188Sache    }
1168157188Sache
1169157188Sache  opoint = rl_point;
1170157188Sache  if (count > 1 && MB_CUR_MAX > 1 && rl_byte_oriented == 0)
1171157188Sache    rl_backward_char (count, key);
1172157188Sache  else if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
1173157188Sache    rl_point = _rl_find_prev_mbchar (rl_line_buffer, rl_point, MB_FIND_NONZERO);
1174157188Sache  else
1175157188Sache    rl_point -= count;
1176157188Sache
1177157188Sache  if (rl_point < 0)
1178157188Sache    rl_point = 0;
1179157188Sache
1180157188Sache  rl_kill_text (rl_point, opoint);
1181157188Sache
1182157188Sache  return (0);
1183157188Sache}
1184157188Sache
1185157188Sacheint
118621308Sacherl_vi_delete (count, key)
118721308Sache     int count, key;
118821308Sache{
118921308Sache  int end;
119021308Sache
1191157188Sache  if (count < 0)
1192157188Sache    return (rl_vi_rubout (-count, key));
1193157188Sache
119421308Sache  if (rl_end == 0)
119521308Sache    {
119675406Sache      rl_ding ();
119721308Sache      return -1;
119821308Sache    }
119921308Sache
1200119610Sache  if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
1201119610Sache    end = _rl_find_next_mbchar (rl_line_buffer, rl_point, count, MB_FIND_NONZERO);
1202119610Sache  else
1203119610Sache    end = rl_point + count;
120421308Sache
120521308Sache  if (end >= rl_end)
120621308Sache    end = rl_end;
120721308Sache
120821308Sache  rl_kill_text (rl_point, end);
120921308Sache
121021308Sache  if (rl_point > 0 && rl_point == rl_end)
1211119610Sache    rl_backward_char (1, key);
1212157188Sache
121321308Sache  return (0);
121421308Sache}
121521308Sache
121621308Sacheint
121721308Sacherl_vi_back_to_indent (count, key)
121821308Sache     int count, key;
121921308Sache{
122021308Sache  rl_beg_of_line (1, key);
122121308Sache  while (rl_point < rl_end && whitespace (rl_line_buffer[rl_point]))
122221308Sache    rl_point++;
122321308Sache  return (0);
122421308Sache}
122521308Sache
122621308Sacheint
122721308Sacherl_vi_first_print (count, key)
122821308Sache     int count, key;
122921308Sache{
123021308Sache  return (rl_vi_back_to_indent (1, key));
123121308Sache}
123221308Sache
1233157188Sachestatic int _rl_cs_dir, _rl_cs_orig_dir;
1234157188Sache
1235157188Sache#if defined (READLINE_CALLBACKS)
1236157188Sachestatic int
1237157188Sache_rl_vi_callback_char_search (data)
1238157188Sache     _rl_callback_generic_arg *data;
1239157188Sache{
1240173406Sache  int c;
1241157188Sache#if defined (HANDLE_MULTIBYTE)
1242173406Sache  c = _rl_vi_last_search_mblen = _rl_read_mbchar (_rl_vi_last_search_mbchar, MB_LEN_MAX);
1243157188Sache#else
1244157188Sache  RL_SETSTATE(RL_STATE_MOREINPUT);
1245173406Sache  c = rl_read_key ();
1246157188Sache  RL_UNSETSTATE(RL_STATE_MOREINPUT);
1247157188Sache#endif
1248157188Sache
1249173406Sache  if (c <= 0)
1250173406Sache    return -1;
1251173406Sache
1252173406Sache#if !defined (HANDLE_MULTIBYTE)
1253173406Sache  _rl_vi_last_search_char = c;
1254173406Sache#endif
1255173406Sache
1256157188Sache  _rl_callback_func = 0;
1257157188Sache  _rl_want_redisplay = 1;
1258157188Sache
1259157188Sache#if defined (HANDLE_MULTIBYTE)
1260157188Sache  return (_rl_char_search_internal (data->count, _rl_cs_dir, _rl_vi_last_search_mbchar, _rl_vi_last_search_mblen));
1261157188Sache#else
1262157188Sache  return (_rl_char_search_internal (data->count, _rl_cs_dir, _rl_vi_last_search_char));
1263157188Sache#endif
1264157188Sache}
1265157188Sache#endif
1266157188Sache
126721308Sacheint
126821308Sacherl_vi_char_search (count, key)
126921308Sache     int count, key;
127021308Sache{
1271173406Sache  int c;
1272119610Sache#if defined (HANDLE_MULTIBYTE)
1273119610Sache  static char *target;
1274157188Sache  static int tlen;
1275119610Sache#else
127621308Sache  static char target;
1277119610Sache#endif
127821308Sache
127921308Sache  if (key == ';' || key == ',')
1280157188Sache    _rl_cs_dir = (key == ';') ? _rl_cs_orig_dir : -_rl_cs_orig_dir;
128121308Sache  else
128221308Sache    {
128321308Sache      switch (key)
128421308Sache        {
128521308Sache        case 't':
1286157188Sache          _rl_cs_orig_dir = _rl_cs_dir = FTO;
128721308Sache          break;
128821308Sache
128921308Sache        case 'T':
1290157188Sache          _rl_cs_orig_dir = _rl_cs_dir = BTO;
129121308Sache          break;
129221308Sache
129321308Sache        case 'f':
1294157188Sache          _rl_cs_orig_dir = _rl_cs_dir = FFIND;
129521308Sache          break;
129621308Sache
129721308Sache        case 'F':
1298157188Sache          _rl_cs_orig_dir = _rl_cs_dir = BFIND;
129921308Sache          break;
130021308Sache        }
1301157188Sache
1302157188Sache      if (vi_redoing)
1303157188Sache	{
1304157188Sache	  /* set target and tlen below */
1305157188Sache	}
1306157188Sache#if defined (READLINE_CALLBACKS)
1307157188Sache      else if (RL_ISSTATE (RL_STATE_CALLBACK))
1308157188Sache        {
1309157188Sache          _rl_callback_data = _rl_callback_data_alloc (count);
1310157188Sache          _rl_callback_data->i1 = _rl_cs_dir;
1311157188Sache          _rl_callback_func = _rl_vi_callback_char_search;
1312157188Sache          return (0);
1313157188Sache        }
1314157188Sache#endif
1315157188Sache      else
1316157188Sache	{
1317157188Sache#if defined (HANDLE_MULTIBYTE)
1318173406Sache	  c = _rl_read_mbchar (_rl_vi_last_search_mbchar, MB_LEN_MAX);
1319173406Sache	  if (c <= 0)
1320173406Sache	    return -1;
1321173406Sache	  _rl_vi_last_search_mblen = c;
1322157188Sache#else
1323157188Sache	  RL_SETSTATE(RL_STATE_MOREINPUT);
1324173406Sache	  c = rl_read_key ();
1325157188Sache	  RL_UNSETSTATE(RL_STATE_MOREINPUT);
1326173406Sache	  if (c < 0)
1327173406Sache	    return -1;
1328173406Sache	  _rl_vi_last_search_char = c;
1329157188Sache#endif
1330157188Sache	}
133121308Sache    }
133221308Sache
1333119610Sache#if defined (HANDLE_MULTIBYTE)
1334157188Sache  target = _rl_vi_last_search_mbchar;
1335157188Sache  tlen = _rl_vi_last_search_mblen;
1336119610Sache#else
1337157188Sache  target = _rl_vi_last_search_char;
1338119610Sache#endif
1339157188Sache
1340157188Sache#if defined (HANDLE_MULTIBYTE)
1341157188Sache  return (_rl_char_search_internal (count, _rl_cs_dir, target, tlen));
1342157188Sache#else
1343157188Sache  return (_rl_char_search_internal (count, _rl_cs_dir, target));
1344157188Sache#endif
134521308Sache}
134621308Sache
134721308Sache/* Match brackets */
134821308Sacheint
134921308Sacherl_vi_match (ignore, key)
135021308Sache     int ignore, key;
135121308Sache{
1352119610Sache  int count = 1, brack, pos, tmp, pre;
135321308Sache
135421308Sache  pos = rl_point;
135521308Sache  if ((brack = rl_vi_bracktype (rl_line_buffer[rl_point])) == 0)
135621308Sache    {
1357119610Sache      if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
1358119610Sache	{
1359119610Sache	  while ((brack = rl_vi_bracktype (rl_line_buffer[rl_point])) == 0)
1360119610Sache	    {
1361119610Sache	      pre = rl_point;
1362119610Sache	      rl_forward_char (1, key);
1363119610Sache	      if (pre == rl_point)
1364119610Sache	        break;
1365119610Sache	    }
1366119610Sache	}
1367119610Sache      else
1368119610Sache	while ((brack = rl_vi_bracktype (rl_line_buffer[rl_point])) == 0 &&
1369119610Sache		rl_point < rl_end - 1)
1370119610Sache	  rl_forward_char (1, key);
137121308Sache
137221308Sache      if (brack <= 0)
137321308Sache	{
137421308Sache	  rl_point = pos;
137575406Sache	  rl_ding ();
137621308Sache	  return -1;
137721308Sache	}
137821308Sache    }
137921308Sache
138021308Sache  pos = rl_point;
138121308Sache
138221308Sache  if (brack < 0)
138321308Sache    {
138421308Sache      while (count)
138521308Sache	{
1386119610Sache	  tmp = pos;
1387119610Sache	  if (MB_CUR_MAX == 1 || rl_byte_oriented)
1388119610Sache	    pos--;
1389119610Sache	  else
139021308Sache	    {
1391119610Sache	      pos = _rl_find_prev_mbchar (rl_line_buffer, pos, MB_FIND_ANY);
1392119610Sache	      if (tmp == pos)
1393119610Sache	        pos--;
1394119610Sache	    }
1395119610Sache	  if (pos >= 0)
1396119610Sache	    {
139721308Sache	      int b = rl_vi_bracktype (rl_line_buffer[pos]);
139821308Sache	      if (b == -brack)
139921308Sache		count--;
140021308Sache	      else if (b == brack)
140121308Sache		count++;
140221308Sache	    }
140321308Sache	  else
140421308Sache	    {
140575406Sache	      rl_ding ();
140621308Sache	      return -1;
140721308Sache	    }
140821308Sache	}
140921308Sache    }
141021308Sache  else
141121308Sache    {			/* brack > 0 */
141221308Sache      while (count)
141321308Sache	{
1414119610Sache	  if (MB_CUR_MAX == 1 || rl_byte_oriented)
1415119610Sache	    pos++;
1416119610Sache	  else
1417119610Sache	    pos = _rl_find_next_mbchar (rl_line_buffer, pos, 1, MB_FIND_ANY);
1418119610Sache
1419119610Sache	  if (pos < rl_end)
142021308Sache	    {
142121308Sache	      int b = rl_vi_bracktype (rl_line_buffer[pos]);
142221308Sache	      if (b == -brack)
142321308Sache		count--;
142421308Sache	      else if (b == brack)
142521308Sache		count++;
142621308Sache	    }
142721308Sache	  else
142821308Sache	    {
142975406Sache	      rl_ding ();
143021308Sache	      return -1;
143121308Sache	    }
143221308Sache	}
143321308Sache    }
143421308Sache  rl_point = pos;
143521308Sache  return (0);
143621308Sache}
143721308Sache
143821308Sacheint
143921308Sacherl_vi_bracktype (c)
144021308Sache     int c;
144121308Sache{
144221308Sache  switch (c)
144321308Sache    {
144421308Sache    case '(': return  1;
144521308Sache    case ')': return -1;
144621308Sache    case '[': return  2;
144721308Sache    case ']': return -2;
144821308Sache    case '{': return  3;
144921308Sache    case '}': return -3;
145021308Sache    default:  return  0;
145121308Sache    }
145221308Sache}
145321308Sache
1454157188Sachestatic int
1455157188Sache_rl_vi_change_char (count, c, mb)
1456157188Sache     int count, c;
1457157188Sache     char *mb;
145821308Sache{
1459157188Sache  int p;
146021308Sache
146121308Sache  if (c == '\033' || c == CTRL ('C'))
146221308Sache    return -1;
146321308Sache
1464136759Speter  rl_begin_undo_group ();
146521308Sache  while (count-- && rl_point < rl_end)
146621308Sache    {
1467136759Speter      p = rl_point;
1468136759Speter      rl_vi_delete (1, c);
1469157188Sache      if (rl_point < p)		/* Did we retreat at EOL? */
1470157188Sache	rl_point++;
1471119610Sache#if defined (HANDLE_MULTIBYTE)
1472119610Sache      if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
1473157188Sache	rl_insert_text (mb);
1474119610Sache      else
1475119610Sache#endif
1476157188Sache	_rl_insert_char (1, c);
1477136759Speter    }
1478157188Sache
1479157188Sache  /* The cursor shall be left on the last character changed. */
1480157188Sache  rl_backward_char (1, c);
1481157188Sache
1482136759Speter  rl_end_undo_group ();
148321308Sache
148421308Sache  return (0);
148521308Sache}
148621308Sache
1487157188Sachestatic int
1488165675Sache_rl_vi_callback_getchar (mb, mlen)
1489157188Sache     char *mb;
1490165675Sache     int mlen;
1491157188Sache{
1492157188Sache  int c;
1493157188Sache
1494157188Sache  RL_SETSTATE(RL_STATE_MOREINPUT);
1495157188Sache  c = rl_read_key ();
1496157188Sache  RL_UNSETSTATE(RL_STATE_MOREINPUT);
1497157188Sache
1498173406Sache  if (c < 0)
1499173406Sache    return -1;
1500173406Sache
1501157188Sache#if defined (HANDLE_MULTIBYTE)
1502157188Sache  if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
1503165675Sache    c = _rl_read_mbstring (c, mb, mlen);
1504157188Sache#endif
1505157188Sache
1506157188Sache  return c;
1507157188Sache}
1508157188Sache
1509157188Sache#if defined (READLINE_CALLBACKS)
1510157188Sachestatic int
1511157188Sache_rl_vi_callback_change_char (data)
1512157188Sache     _rl_callback_generic_arg *data;
1513157188Sache{
1514157188Sache  int c;
1515157188Sache  char mb[MB_LEN_MAX];
1516157188Sache
1517157188Sache  _rl_vi_last_replacement = c = _rl_vi_callback_getchar (mb, MB_LEN_MAX);
1518157188Sache
1519173406Sache  if (c < 0)
1520173406Sache    return -1;
1521173406Sache
1522157188Sache  _rl_callback_func = 0;
1523157188Sache  _rl_want_redisplay = 1;
1524157188Sache
1525157188Sache  return (_rl_vi_change_char (data->count, c, mb));
1526157188Sache}
1527157188Sache#endif
1528157188Sache
152921308Sacheint
1530157188Sacherl_vi_change_char (count, key)
1531157188Sache     int count, key;
1532157188Sache{
1533157188Sache  int c;
1534157188Sache  char mb[MB_LEN_MAX];
1535157188Sache
1536157188Sache  if (vi_redoing)
1537157188Sache    {
1538157188Sache      c = _rl_vi_last_replacement;
1539157188Sache      mb[0] = c;
1540157188Sache      mb[1] = '\0';
1541157188Sache    }
1542157188Sache#if defined (READLINE_CALLBACKS)
1543157188Sache  else if (RL_ISSTATE (RL_STATE_CALLBACK))
1544157188Sache    {
1545157188Sache      _rl_callback_data = _rl_callback_data_alloc (count);
1546157188Sache      _rl_callback_func = _rl_vi_callback_change_char;
1547157188Sache      return (0);
1548157188Sache    }
1549157188Sache#endif
1550157188Sache  else
1551157188Sache    _rl_vi_last_replacement = c = _rl_vi_callback_getchar (mb, MB_LEN_MAX);
1552157188Sache
1553173406Sache  if (c < 0)
1554173406Sache    return -1;
1555173406Sache
1556157188Sache  return (_rl_vi_change_char (count, c, mb));
1557157188Sache}
1558157188Sache
1559157188Sacheint
156021308Sacherl_vi_subst (count, key)
156121308Sache     int count, key;
156221308Sache{
1563119610Sache  /* If we are redoing, rl_vi_change_to will stuff the last motion char */
1564119610Sache  if (vi_redoing == 0)
1565136759Speter    rl_stuff_char ((key == 'S') ? 'c' : 'l');	/* `S' == `cc', `s' == `cl' */
156621308Sache
1567119610Sache  return (rl_vi_change_to (count, 'c'));
156821308Sache}
156921308Sache
157021308Sacheint
157121308Sacherl_vi_overstrike (count, key)
157221308Sache     int count, key;
157321308Sache{
157421308Sache  if (_rl_vi_doing_insert == 0)
157521308Sache    {
157621308Sache      _rl_vi_doing_insert = 1;
157721308Sache      rl_begin_undo_group ();
157821308Sache    }
157921308Sache
1580119610Sache  if (count > 0)
158121308Sache    {
1582119610Sache      _rl_overwrite_char (count, key);
1583119610Sache      vi_replace_count += count;
1584119610Sache    }
158521308Sache
158621308Sache  return (0);
158721308Sache}
158821308Sache
158921308Sacheint
159021308Sacherl_vi_overstrike_delete (count, key)
159121308Sache     int count, key;
159221308Sache{
159321308Sache  int i, s;
159421308Sache
159521308Sache  for (i = 0; i < count; i++)
159621308Sache    {
159721308Sache      if (vi_replace_count == 0)
159821308Sache	{
159975406Sache	  rl_ding ();
160021308Sache	  break;
160121308Sache	}
160221308Sache      s = rl_point;
160321308Sache
160421308Sache      if (rl_do_undo ())
160521308Sache	vi_replace_count--;
160621308Sache
160721308Sache      if (rl_point == s)
1608119610Sache	rl_backward_char (1, key);
160921308Sache    }
161021308Sache
161121308Sache  if (vi_replace_count == 0 && _rl_vi_doing_insert)
161221308Sache    {
161321308Sache      rl_end_undo_group ();
161421308Sache      rl_do_undo ();
161521308Sache      _rl_vi_doing_insert = 0;
161621308Sache    }
161721308Sache  return (0);
161821308Sache}
161921308Sache
162021308Sacheint
162121308Sacherl_vi_replace (count, key)
162221308Sache     int count, key;
162321308Sache{
162421308Sache  int i;
162521308Sache
162621308Sache  vi_replace_count = 0;
162721308Sache
162821308Sache  if (!vi_replace_map)
162921308Sache    {
163021308Sache      vi_replace_map = rl_make_bare_keymap ();
163121308Sache
163221308Sache      for (i = ' '; i < KEYMAP_SIZE; i++)
163321308Sache	vi_replace_map[i].function = rl_vi_overstrike;
163421308Sache
163521308Sache      vi_replace_map[RUBOUT].function = rl_vi_overstrike_delete;
163621308Sache      vi_replace_map[ESC].function = rl_vi_movement_mode;
163721308Sache      vi_replace_map[RETURN].function = rl_newline;
163821308Sache      vi_replace_map[NEWLINE].function = rl_newline;
163921308Sache
164021308Sache      /* If the normal vi insertion keymap has ^H bound to erase, do the
164121308Sache         same here.  Probably should remove the assignment to RUBOUT up
164221308Sache         there, but I don't think it will make a difference in real life. */
164321308Sache      if (vi_insertion_keymap[CTRL ('H')].type == ISFUNC &&
164421308Sache	  vi_insertion_keymap[CTRL ('H')].function == rl_rubout)
164521308Sache	vi_replace_map[CTRL ('H')].function = rl_vi_overstrike_delete;
164621308Sache
164721308Sache    }
164821308Sache  _rl_keymap = vi_replace_map;
164921308Sache  return (0);
165021308Sache}
165121308Sache
165221308Sache#if 0
165321308Sache/* Try to complete the word we are standing on or the word that ends with
165421308Sache   the previous character.  A space matches everything.  Word delimiters are
165521308Sache   space and ;. */
165621308Sacheint
165721308Sacherl_vi_possible_completions()
165821308Sache{
165921308Sache  int save_pos = rl_point;
166021308Sache
166121308Sache  if (rl_line_buffer[rl_point] != ' ' && rl_line_buffer[rl_point] != ';')
166221308Sache    {
166321308Sache      while (rl_point < rl_end && rl_line_buffer[rl_point] != ' ' &&
166421308Sache	     rl_line_buffer[rl_point] != ';')
166521308Sache	rl_point++;
166621308Sache    }
166721308Sache  else if (rl_line_buffer[rl_point - 1] == ';')
166821308Sache    {
166975406Sache      rl_ding ();
167021308Sache      return (0);
167121308Sache    }
167221308Sache
167321308Sache  rl_possible_completions ();
167421308Sache  rl_point = save_pos;
167521308Sache
167621308Sache  return (0);
167721308Sache}
167821308Sache#endif
167921308Sache
168021308Sache/* Functions to save and restore marks. */
1681157188Sachestatic int
1682157188Sache_rl_vi_set_mark ()
168321308Sache{
168421308Sache  int ch;
168521308Sache
168675406Sache  RL_SETSTATE(RL_STATE_MOREINPUT);
168721308Sache  ch = rl_read_key ();
168875406Sache  RL_UNSETSTATE(RL_STATE_MOREINPUT);
168975406Sache
1690173406Sache  if (ch < 0 || ch < 'a' || ch > 'z')	/* make test against 0 explicit */
169121308Sache    {
169275406Sache      rl_ding ();
169321308Sache      return -1;
169421308Sache    }
169521308Sache  ch -= 'a';
169621308Sache  vi_mark_chars[ch] = rl_point;
169721308Sache  return 0;
169821308Sache}
169921308Sache
1700157188Sache#if defined (READLINE_CALLBACKS)
1701157188Sachestatic int
1702157188Sache_rl_vi_callback_set_mark (data)
1703157188Sache     _rl_callback_generic_arg *data;
1704157188Sache{
1705157188Sache  _rl_callback_func = 0;
1706157188Sache  _rl_want_redisplay = 1;
1707157188Sache
1708157188Sache  return (_rl_vi_set_mark ());
1709157188Sache}
1710157188Sache#endif
1711157188Sache
171221308Sacheint
1713157188Sacherl_vi_set_mark (count, key)
171421308Sache     int count, key;
171521308Sache{
1716157188Sache#if defined (READLINE_CALLBACKS)
1717157188Sache  if (RL_ISSTATE (RL_STATE_CALLBACK))
1718157188Sache    {
1719157188Sache      _rl_callback_data = 0;
1720157188Sache      _rl_callback_func = _rl_vi_callback_set_mark;
1721157188Sache      return (0);
1722157188Sache    }
1723157188Sache#endif
1724157188Sache
1725157188Sache  return (_rl_vi_set_mark ());
1726157188Sache}
1727157188Sache
1728157188Sachestatic int
1729157188Sache_rl_vi_goto_mark ()
1730157188Sache{
173121308Sache  int ch;
173221308Sache
173375406Sache  RL_SETSTATE(RL_STATE_MOREINPUT);
173421308Sache  ch = rl_read_key ();
173575406Sache  RL_UNSETSTATE(RL_STATE_MOREINPUT);
173675406Sache
173721308Sache  if (ch == '`')
173821308Sache    {
173921308Sache      rl_point = rl_mark;
174021308Sache      return 0;
174121308Sache    }
1742173406Sache  else if (ch < 0 || ch < 'a' || ch > 'z')	/* make test against 0 explicit */
174321308Sache    {
174475406Sache      rl_ding ();
174521308Sache      return -1;
174621308Sache    }
174721308Sache
174821308Sache  ch -= 'a';
174921308Sache  if (vi_mark_chars[ch] == -1)
175021308Sache    {
175175406Sache      rl_ding ();
175221308Sache      return -1;
175321308Sache    }
175421308Sache  rl_point = vi_mark_chars[ch];
175521308Sache  return 0;
175621308Sache}
175721308Sache
1758157188Sache#if defined (READLINE_CALLBACKS)
1759157188Sachestatic int
1760157188Sache_rl_vi_callback_goto_mark (data)
1761157188Sache     _rl_callback_generic_arg *data;
1762157188Sache{
1763157188Sache  _rl_callback_func = 0;
1764157188Sache  _rl_want_redisplay = 1;
1765157188Sache
1766157188Sache  return (_rl_vi_goto_mark ());
1767157188Sache}
1768157188Sache#endif
1769157188Sache
1770157188Sacheint
1771157188Sacherl_vi_goto_mark (count, key)
1772157188Sache     int count, key;
1773157188Sache{
1774157188Sache#if defined (READLINE_CALLBACKS)
1775157188Sache  if (RL_ISSTATE (RL_STATE_CALLBACK))
1776157188Sache    {
1777157188Sache      _rl_callback_data = 0;
1778157188Sache      _rl_callback_func = _rl_vi_callback_goto_mark;
1779157188Sache      return (0);
1780157188Sache    }
1781157188Sache#endif
1782157188Sache
1783157188Sache  return (_rl_vi_goto_mark ());
1784157188Sache}
178521308Sache#endif /* VI_MODE */
1786