vi_mode.c revision 157188
1125665Sache/* $FreeBSD: head/contrib/libreadline/vi_mode.c 157188 2006-03-27 23:11:32Z ache $ */
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
113157188Sachestatic int _rl_vi_arg_dispatch PARAMS((int));
114119610Sachestatic int rl_digit_loop1 PARAMS((void));
11521308Sache
116157188Sachestatic int _rl_vi_set_mark PARAMS((void));
117157188Sachestatic int _rl_vi_goto_mark PARAMS((void));
118157188Sache
119157188Sachestatic int _rl_vi_callback_getchar PARAMS((char *, int));
120157188Sache
121157188Sache#if defined (READLINE_CALLBACKS)
122157188Sachestatic int _rl_vi_callback_set_mark PARAMS((_rl_callback_generic_arg *));
123157188Sachestatic int _rl_vi_callback_goto_mark PARAMS((_rl_callback_generic_arg *));
124157188Sachestatic int _rl_vi_callback_change_char PARAMS((_rl_callback_generic_arg *));
125157188Sachestatic int _rl_vi_callback_char_search PARAMS((_rl_callback_generic_arg *));
126157188Sache#endif
127157188Sache
12821308Sachevoid
12921308Sache_rl_vi_initialize_line ()
13021308Sache{
13121308Sache  register int i;
13221308Sache
13321308Sache  for (i = 0; i < sizeof (vi_mark_chars) / sizeof (int); i++)
13421308Sache    vi_mark_chars[i] = -1;
135157188Sache
136157188Sache  RL_UNSETSTATE(RL_STATE_VICMDONCE);
13721308Sache}
13821308Sache
13921308Sachevoid
14021308Sache_rl_vi_reset_last ()
14121308Sache{
14221308Sache  _rl_vi_last_command = 'i';
14321308Sache  _rl_vi_last_repeat = 1;
14421308Sache  _rl_vi_last_arg_sign = 1;
14521308Sache  _rl_vi_last_motion = 0;
14621308Sache}
14721308Sache
14821308Sachevoid
14921308Sache_rl_vi_set_last (key, repeat, sign)
15021308Sache     int key, repeat, sign;
15121308Sache{
15221308Sache  _rl_vi_last_command = key;
15321308Sache  _rl_vi_last_repeat = repeat;
15421308Sache  _rl_vi_last_arg_sign = sign;
15521308Sache}
15621308Sache
157136759Speter/* A convenience function that calls _rl_vi_set_last to save the last command
158136759Speter   information and enters insertion mode. */
159136759Spetervoid
160136759Speterrl_vi_start_inserting (key, repeat, sign)
161136759Speter     int key, repeat, sign;
162136759Speter{
163136759Speter  _rl_vi_set_last (key, repeat, sign);
164136759Speter  rl_vi_insertion_mode (1, key);
165136759Speter}
166136759Speter
16721308Sache/* Is the command C a VI mode text modification command? */
16821308Sacheint
16921308Sache_rl_vi_textmod_command (c)
17021308Sache     int c;
17121308Sache{
17221308Sache  return (member (c, vi_textmod));
17321308Sache}
17421308Sache
17521308Sachestatic void
17621308Sache_rl_vi_stuff_insert (count)
17721308Sache     int count;
17821308Sache{
17921308Sache  rl_begin_undo_group ();
18021308Sache  while (count--)
18121308Sache    rl_insert_text (vi_insert_buffer);
18221308Sache  rl_end_undo_group ();
18321308Sache}
18421308Sache
18521308Sache/* Bound to `.'.  Called from command mode, so we know that we have to
18621308Sache   redo a text modification command.  The default for _rl_vi_last_command
18721308Sache   puts you back into insert mode. */
18821308Sacheint
18921308Sacherl_vi_redo (count, c)
19021308Sache     int count, c;
19121308Sache{
192119610Sache  int r;
193119610Sache
19421308Sache  if (!rl_explicit_arg)
19521308Sache    {
19621308Sache      rl_numeric_arg = _rl_vi_last_repeat;
19721308Sache      rl_arg_sign = _rl_vi_last_arg_sign;
19821308Sache    }
19921308Sache
200119610Sache  r = 0;
20121308Sache  vi_redoing = 1;
20221308Sache  /* If we're redoing an insert with `i', stuff in the inserted text
20321308Sache     and do not go into insertion mode. */
20421308Sache  if (_rl_vi_last_command == 'i' && vi_insert_buffer && *vi_insert_buffer)
20521308Sache    {
20621308Sache      _rl_vi_stuff_insert (count);
20721308Sache      /* And back up point over the last character inserted. */
20821308Sache      if (rl_point > 0)
20921308Sache	rl_point--;
21021308Sache    }
21121308Sache  else
212119610Sache    r = _rl_dispatch (_rl_vi_last_command, _rl_keymap);
21321308Sache  vi_redoing = 0;
21421308Sache
215119610Sache  return (r);
21621308Sache}
21721308Sache
21821308Sache/* A placeholder for further expansion. */
21921308Sacheint
22021308Sacherl_vi_undo (count, key)
22121308Sache     int count, key;
22221308Sache{
22321308Sache  return (rl_undo_command (count, key));
22421308Sache}
22521308Sache
22621308Sache/* Yank the nth arg from the previous line into this line at point. */
22721308Sacheint
22821308Sacherl_vi_yank_arg (count, key)
22921308Sache     int count, key;
23021308Sache{
23121308Sache  /* Readline thinks that the first word on a line is the 0th, while vi
23221308Sache     thinks the first word on a line is the 1st.  Compensate. */
23321308Sache  if (rl_explicit_arg)
23421308Sache    rl_yank_nth_arg (count - 1, 0);
23521308Sache  else
23621308Sache    rl_yank_nth_arg ('$', 0);
23721308Sache
23821308Sache  return (0);
23921308Sache}
24021308Sache
24121308Sache/* With an argument, move back that many history lines, else move to the
24221308Sache   beginning of history. */
24321308Sacheint
24421308Sacherl_vi_fetch_history (count, c)
24521308Sache     int count, c;
24621308Sache{
24721308Sache  int wanted;
24821308Sache
24921308Sache  /* Giving an argument of n means we want the nth command in the history
25021308Sache     file.  The command number is interpreted the same way that the bash
25121308Sache     `history' command does it -- that is, giving an argument count of 450
25221308Sache     to this command would get the command listed as number 450 in the
25321308Sache     output of `history'. */
25421308Sache  if (rl_explicit_arg)
25521308Sache    {
25621308Sache      wanted = history_base + where_history () - count;
25721308Sache      if (wanted <= 0)
25821308Sache        rl_beginning_of_history (0, 0);
25921308Sache      else
26021308Sache        rl_get_previous_history (wanted, c);
26121308Sache    }
26221308Sache  else
26321308Sache    rl_beginning_of_history (count, 0);
26421308Sache  return (0);
26521308Sache}
26621308Sache
26721308Sache/* Search again for the last thing searched for. */
26821308Sacheint
26921308Sacherl_vi_search_again (count, key)
27021308Sache     int count, key;
27121308Sache{
27221308Sache  switch (key)
27321308Sache    {
27421308Sache    case 'n':
27521308Sache      rl_noninc_reverse_search_again (count, key);
27621308Sache      break;
27721308Sache
27821308Sache    case 'N':
27921308Sache      rl_noninc_forward_search_again (count, key);
28021308Sache      break;
28121308Sache    }
28221308Sache  return (0);
28321308Sache}
28421308Sache
28521308Sache/* Do a vi style search. */
28621308Sacheint
28721308Sacherl_vi_search (count, key)
28821308Sache     int count, key;
28921308Sache{
29021308Sache  switch (key)
29121308Sache    {
29221308Sache    case '?':
293136759Speter      _rl_free_saved_history_line ();
29421308Sache      rl_noninc_forward_search (count, key);
29521308Sache      break;
29621308Sache
29721308Sache    case '/':
298136759Speter      _rl_free_saved_history_line ();
29921308Sache      rl_noninc_reverse_search (count, key);
30021308Sache      break;
30121308Sache
30221308Sache    default:
30375406Sache      rl_ding ();
30421308Sache      break;
30521308Sache    }
30621308Sache  return (0);
30721308Sache}
30821308Sache
30921308Sache/* Completion, from vi's point of view. */
31021308Sacheint
31121308Sacherl_vi_complete (ignore, key)
31221308Sache     int ignore, key;
31321308Sache{
31421308Sache  if ((rl_point < rl_end) && (!whitespace (rl_line_buffer[rl_point])))
31521308Sache    {
31621308Sache      if (!whitespace (rl_line_buffer[rl_point + 1]))
31721308Sache	rl_vi_end_word (1, 'E');
31821308Sache      rl_point++;
31921308Sache    }
32021308Sache
32121308Sache  if (key == '*')
32221308Sache    rl_complete_internal ('*');	/* Expansion and replacement. */
32321308Sache  else if (key == '=')
32421308Sache    rl_complete_internal ('?');	/* List possible completions. */
32521308Sache  else if (key == '\\')
32621308Sache    rl_complete_internal (TAB);	/* Standard Readline completion. */
32721308Sache  else
32821308Sache    rl_complete (0, key);
32921308Sache
33021308Sache  if (key == '*' || key == '\\')
331136759Speter    rl_vi_start_inserting (key, 1, rl_arg_sign);
332136759Speter
33321308Sache  return (0);
33421308Sache}
33521308Sache
33621308Sache/* Tilde expansion for vi mode. */
33721308Sacheint
33821308Sacherl_vi_tilde_expand (ignore, key)
33921308Sache     int ignore, key;
34021308Sache{
34121308Sache  rl_tilde_expand (0, key);
342136759Speter  rl_vi_start_inserting (key, 1, rl_arg_sign);
34321308Sache  return (0);
34421308Sache}
34521308Sache
34621308Sache/* Previous word in vi mode. */
34721308Sacheint
34821308Sacherl_vi_prev_word (count, key)
34921308Sache     int count, key;
35021308Sache{
35121308Sache  if (count < 0)
35221308Sache    return (rl_vi_next_word (-count, key));
35321308Sache
35421308Sache  if (rl_point == 0)
35521308Sache    {
35675406Sache      rl_ding ();
35721308Sache      return (0);
35821308Sache    }
35921308Sache
36021308Sache  if (_rl_uppercase_p (key))
36147558Sache    rl_vi_bWord (count, key);
36221308Sache  else
36347558Sache    rl_vi_bword (count, key);
36421308Sache
36521308Sache  return (0);
36621308Sache}
36721308Sache
36821308Sache/* Next word in vi mode. */
36921308Sacheint
37021308Sacherl_vi_next_word (count, key)
37121308Sache     int count, key;
37221308Sache{
37321308Sache  if (count < 0)
37421308Sache    return (rl_vi_prev_word (-count, key));
37521308Sache
37621308Sache  if (rl_point >= (rl_end - 1))
37721308Sache    {
37875406Sache      rl_ding ();
37921308Sache      return (0);
38021308Sache    }
38121308Sache
38221308Sache  if (_rl_uppercase_p (key))
38347558Sache    rl_vi_fWord (count, key);
38421308Sache  else
38547558Sache    rl_vi_fword (count, key);
38621308Sache  return (0);
38721308Sache}
38821308Sache
38921308Sache/* Move to the end of the ?next? word. */
39021308Sacheint
39121308Sacherl_vi_end_word (count, key)
39221308Sache     int count, key;
39321308Sache{
39421308Sache  if (count < 0)
39521308Sache    {
39675406Sache      rl_ding ();
39721308Sache      return -1;
39821308Sache    }
39921308Sache
40021308Sache  if (_rl_uppercase_p (key))
40147558Sache    rl_vi_eWord (count, key);
40221308Sache  else
40347558Sache    rl_vi_eword (count, key);
40421308Sache  return (0);
40521308Sache}
40621308Sache
40721308Sache/* Move forward a word the way that 'W' does. */
40821308Sacheint
40947558Sacherl_vi_fWord (count, ignore)
41047558Sache     int count, ignore;
41121308Sache{
41221308Sache  while (count-- && rl_point < (rl_end - 1))
41321308Sache    {
41421308Sache      /* Skip until whitespace. */
41521308Sache      while (!whitespace (rl_line_buffer[rl_point]) && rl_point < rl_end)
41621308Sache	rl_point++;
41721308Sache
41821308Sache      /* Now skip whitespace. */
41921308Sache      while (whitespace (rl_line_buffer[rl_point]) && rl_point < rl_end)
42021308Sache	rl_point++;
42121308Sache    }
42221308Sache  return (0);
42321308Sache}
42421308Sache
42521308Sacheint
42647558Sacherl_vi_bWord (count, ignore)
42747558Sache     int count, ignore;
42821308Sache{
42921308Sache  while (count-- && rl_point > 0)
43021308Sache    {
43121308Sache      /* If we are at the start of a word, move back to whitespace so
43221308Sache	 we will go back to the start of the previous word. */
43321308Sache      if (!whitespace (rl_line_buffer[rl_point]) &&
43421308Sache	  whitespace (rl_line_buffer[rl_point - 1]))
43521308Sache	rl_point--;
43621308Sache
43721308Sache      while (rl_point > 0 && whitespace (rl_line_buffer[rl_point]))
43821308Sache	rl_point--;
43921308Sache
44021308Sache      if (rl_point > 0)
44121308Sache	{
44221308Sache	  while (--rl_point >= 0 && !whitespace (rl_line_buffer[rl_point]));
44321308Sache	  rl_point++;
44421308Sache	}
44521308Sache    }
44621308Sache  return (0);
44721308Sache}
44821308Sache
44921308Sacheint
45047558Sacherl_vi_eWord (count, ignore)
45147558Sache     int count, ignore;
45221308Sache{
45321308Sache  while (count-- && rl_point < (rl_end - 1))
45421308Sache    {
45521308Sache      if (!whitespace (rl_line_buffer[rl_point]))
45621308Sache	rl_point++;
45721308Sache
45821308Sache      /* Move to the next non-whitespace character (to the start of the
45921308Sache	 next word). */
460136759Speter      while (rl_point < rl_end && whitespace (rl_line_buffer[rl_point]))
461136759Speter	rl_point++;
46221308Sache
46321308Sache      if (rl_point && rl_point < rl_end)
46421308Sache	{
46521308Sache	  /* Skip whitespace. */
46621308Sache	  while (rl_point < rl_end && whitespace (rl_line_buffer[rl_point]))
46721308Sache	    rl_point++;
46821308Sache
46921308Sache	  /* Skip until whitespace. */
47021308Sache	  while (rl_point < rl_end && !whitespace (rl_line_buffer[rl_point]))
47121308Sache	    rl_point++;
47221308Sache
47321308Sache	  /* Move back to the last character of the word. */
47421308Sache	  rl_point--;
47521308Sache	}
47621308Sache    }
47721308Sache  return (0);
47821308Sache}
47921308Sache
48021308Sacheint
48147558Sacherl_vi_fword (count, ignore)
48247558Sache     int count, ignore;
48321308Sache{
48421308Sache  while (count-- && rl_point < (rl_end - 1))
48521308Sache    {
48621308Sache      /* Move to white space (really non-identifer). */
487119610Sache      if (_rl_isident (rl_line_buffer[rl_point]))
48821308Sache	{
489119610Sache	  while (_rl_isident (rl_line_buffer[rl_point]) && rl_point < rl_end)
49021308Sache	    rl_point++;
49121308Sache	}
49221308Sache      else /* if (!whitespace (rl_line_buffer[rl_point])) */
49321308Sache	{
494119610Sache	  while (!_rl_isident (rl_line_buffer[rl_point]) &&
49521308Sache		 !whitespace (rl_line_buffer[rl_point]) && rl_point < rl_end)
49621308Sache	    rl_point++;
49721308Sache	}
49821308Sache
49921308Sache      /* Move past whitespace. */
50021308Sache      while (whitespace (rl_line_buffer[rl_point]) && rl_point < rl_end)
50121308Sache	rl_point++;
50221308Sache    }
50321308Sache  return (0);
50421308Sache}
50521308Sache
50621308Sacheint
50747558Sacherl_vi_bword (count, ignore)
50847558Sache     int count, ignore;
50921308Sache{
51021308Sache  while (count-- && rl_point > 0)
51121308Sache    {
51221308Sache      int last_is_ident;
51321308Sache
51421308Sache      /* If we are at the start of a word, move back to whitespace
51521308Sache	 so we will go back to the start of the previous word. */
51621308Sache      if (!whitespace (rl_line_buffer[rl_point]) &&
51721308Sache	  whitespace (rl_line_buffer[rl_point - 1]))
51821308Sache	rl_point--;
51921308Sache
52021308Sache      /* If this character and the previous character are `opposite', move
52121308Sache	 back so we don't get messed up by the rl_point++ down there in
52221308Sache	 the while loop.  Without this code, words like `l;' screw up the
52321308Sache	 function. */
524119610Sache      last_is_ident = _rl_isident (rl_line_buffer[rl_point - 1]);
525119610Sache      if ((_rl_isident (rl_line_buffer[rl_point]) && !last_is_ident) ||
526119610Sache	  (!_rl_isident (rl_line_buffer[rl_point]) && last_is_ident))
52721308Sache	rl_point--;
52821308Sache
52921308Sache      while (rl_point > 0 && whitespace (rl_line_buffer[rl_point]))
53021308Sache	rl_point--;
53121308Sache
53221308Sache      if (rl_point > 0)
53321308Sache	{
534119610Sache	  if (_rl_isident (rl_line_buffer[rl_point]))
535119610Sache	    while (--rl_point >= 0 && _rl_isident (rl_line_buffer[rl_point]));
53621308Sache	  else
537119610Sache	    while (--rl_point >= 0 && !_rl_isident (rl_line_buffer[rl_point]) &&
53821308Sache		   !whitespace (rl_line_buffer[rl_point]));
53921308Sache	  rl_point++;
54021308Sache	}
54121308Sache    }
54221308Sache  return (0);
54321308Sache}
54421308Sache
54521308Sacheint
54647558Sacherl_vi_eword (count, ignore)
54747558Sache     int count, ignore;
54821308Sache{
54921308Sache  while (count-- && rl_point < rl_end - 1)
55021308Sache    {
55121308Sache      if (!whitespace (rl_line_buffer[rl_point]))
55221308Sache	rl_point++;
55321308Sache
55421308Sache      while (rl_point < rl_end && whitespace (rl_line_buffer[rl_point]))
55521308Sache	rl_point++;
55621308Sache
55721308Sache      if (rl_point < rl_end)
55821308Sache	{
559119610Sache	  if (_rl_isident (rl_line_buffer[rl_point]))
560119610Sache	    while (++rl_point < rl_end && _rl_isident (rl_line_buffer[rl_point]));
56121308Sache	  else
562119610Sache	    while (++rl_point < rl_end && !_rl_isident (rl_line_buffer[rl_point])
56321308Sache		   && !whitespace (rl_line_buffer[rl_point]));
56421308Sache	}
56521308Sache      rl_point--;
56621308Sache    }
56721308Sache  return (0);
56821308Sache}
56921308Sache
57021308Sacheint
57121308Sacherl_vi_insert_beg (count, key)
57221308Sache     int count, key;
57321308Sache{
57421308Sache  rl_beg_of_line (1, key);
57521308Sache  rl_vi_insertion_mode (1, key);
57621308Sache  return (0);
57721308Sache}
57821308Sache
57921308Sacheint
58021308Sacherl_vi_append_mode (count, key)
58121308Sache     int count, key;
58221308Sache{
58321308Sache  if (rl_point < rl_end)
584119610Sache    {
585119610Sache      if (MB_CUR_MAX == 1 || rl_byte_oriented)
586119610Sache	rl_point++;
587119610Sache      else
588119610Sache        {
589119610Sache          int point = rl_point;
590119610Sache          rl_forward_char (1, key);
591119610Sache          if (point == rl_point)
592119610Sache            rl_point = rl_end;
593119610Sache        }
594119610Sache    }
59521308Sache  rl_vi_insertion_mode (1, key);
59621308Sache  return (0);
59721308Sache}
59821308Sache
59921308Sacheint
60021308Sacherl_vi_append_eol (count, key)
60121308Sache     int count, key;
60221308Sache{
60321308Sache  rl_end_of_line (1, key);
60421308Sache  rl_vi_append_mode (1, key);
60521308Sache  return (0);
60621308Sache}
60721308Sache
60821308Sache/* What to do in the case of C-d. */
60921308Sacheint
61021308Sacherl_vi_eof_maybe (count, c)
61121308Sache     int count, c;
61221308Sache{
61321308Sache  return (rl_newline (1, '\n'));
61421308Sache}
61521308Sache
61621308Sache/* Insertion mode stuff. */
61721308Sache
61821308Sache/* Switching from one mode to the other really just involves
61921308Sache   switching keymaps. */
62021308Sacheint
62121308Sacherl_vi_insertion_mode (count, key)
62221308Sache     int count, key;
62321308Sache{
62421308Sache  _rl_keymap = vi_insertion_keymap;
62521308Sache  _rl_vi_last_key_before_insert = key;
62621308Sache  return (0);
62721308Sache}
62821308Sache
62921308Sachestatic void
63021308Sache_rl_vi_save_insert (up)
63121308Sache      UNDO_LIST *up;
63221308Sache{
63321308Sache  int len, start, end;
63421308Sache
63535486Sache  if (up == 0)
63635486Sache    {
63735486Sache      if (vi_insert_buffer_size >= 1)
63835486Sache	vi_insert_buffer[0] = '\0';
63935486Sache      return;
64035486Sache    }
64135486Sache
64221308Sache  start = up->start;
64321308Sache  end = up->end;
64421308Sache  len = end - start + 1;
64521308Sache  if (len >= vi_insert_buffer_size)
64621308Sache    {
64721308Sache      vi_insert_buffer_size += (len + 32) - (len % 32);
648119610Sache      vi_insert_buffer = (char *)xrealloc (vi_insert_buffer, vi_insert_buffer_size);
64921308Sache    }
65021308Sache  strncpy (vi_insert_buffer, rl_line_buffer + start, len - 1);
65121308Sache  vi_insert_buffer[len-1] = '\0';
65221308Sache}
65321308Sache
65421308Sachevoid
65521308Sache_rl_vi_done_inserting ()
65621308Sache{
65721308Sache  if (_rl_vi_doing_insert)
65821308Sache    {
659119610Sache      /* The `C', `s', and `S' commands set this. */
66021308Sache      rl_end_undo_group ();
66121308Sache      /* Now, the text between rl_undo_list->next->start and
66221308Sache	 rl_undo_list->next->end is what was inserted while in insert
66321308Sache	 mode.  It gets copied to VI_INSERT_BUFFER because it depends
66421308Sache	 on absolute indices into the line which may change (though they
66521308Sache	 probably will not). */
66621308Sache      _rl_vi_doing_insert = 0;
66721308Sache      _rl_vi_save_insert (rl_undo_list->next);
66821308Sache      vi_continued_command = 1;
66921308Sache    }
67021308Sache  else
67121308Sache    {
672136759Speter      if ((_rl_vi_last_key_before_insert == 'i' || _rl_vi_last_key_before_insert == 'a') && rl_undo_list)
67321308Sache        _rl_vi_save_insert (rl_undo_list);
67421308Sache      /* XXX - Other keys probably need to be checked. */
67521308Sache      else if (_rl_vi_last_key_before_insert == 'C')
67621308Sache	rl_end_undo_group ();
67721308Sache      while (_rl_undo_group_level > 0)
67821308Sache	rl_end_undo_group ();
67921308Sache      vi_continued_command = 0;
68021308Sache    }
68121308Sache}
68221308Sache
68321308Sacheint
68421308Sacherl_vi_movement_mode (count, key)
68521308Sache     int count, key;
68621308Sache{
68721308Sache  if (rl_point > 0)
688119610Sache    rl_backward_char (1, key);
68921308Sache
69021308Sache  _rl_keymap = vi_movement_keymap;
69121308Sache  _rl_vi_done_inserting ();
692157188Sache
693157188Sache  /* This is how POSIX.2 says `U' should behave -- everything up until the
694157188Sache     first time you go into command mode should not be undone. */
695157188Sache  if (RL_ISSTATE (RL_STATE_VICMDONCE) == 0)
696157188Sache    rl_free_undo_list ();
697157188Sache
698157188Sache  RL_SETSTATE (RL_STATE_VICMDONCE);
69921308Sache  return (0);
70021308Sache}
70121308Sache
70221308Sacheint
70321308Sacherl_vi_arg_digit (count, c)
70421308Sache     int count, c;
70521308Sache{
70621308Sache  if (c == '0' && rl_numeric_arg == 1 && !rl_explicit_arg)
70721308Sache    return (rl_beg_of_line (1, c));
70821308Sache  else
70921308Sache    return (rl_digit_argument (count, c));
71021308Sache}
71121308Sache
712119610Sache/* Change the case of the next COUNT characters. */
713119610Sache#if defined (HANDLE_MULTIBYTE)
714119610Sachestatic int
715119610Sache_rl_vi_change_mbchar_case (count)
716119610Sache     int count;
717119610Sache{
718119610Sache  wchar_t wc;
719125665Sache  char mb[MB_LEN_MAX+1];
720136759Speter  int mblen, p;
721119610Sache  mbstate_t ps;
722119610Sache
723119610Sache  memset (&ps, 0, sizeof (mbstate_t));
724119610Sache  if (_rl_adjust_point (rl_line_buffer, rl_point, &ps) > 0)
725119610Sache    count--;
726119610Sache  while (count-- && rl_point < rl_end)
727119610Sache    {
728119610Sache      mbrtowc (&wc, rl_line_buffer + rl_point, rl_end - rl_point, &ps);
729119610Sache      if (iswupper (wc))
730119610Sache	wc = towlower (wc);
731119610Sache      else if (iswlower (wc))
732119610Sache	wc = towupper (wc);
733119610Sache      else
734119610Sache	{
735119610Sache	  /* Just skip over chars neither upper nor lower case */
736119610Sache	  rl_forward_char (1, 0);
737119610Sache	  continue;
738119610Sache	}
739119610Sache
740119610Sache      /* Vi is kind of strange here. */
741119610Sache      if (wc)
742119610Sache	{
743136759Speter	  p = rl_point;
744136759Speter	  mblen = wcrtomb (mb, wc, &ps);
745125665Sache	  if (mblen >= 0)
746125665Sache	    mb[mblen] = '\0';
747119610Sache	  rl_begin_undo_group ();
748136759Speter	  rl_vi_delete (1, 0);
749136759Speter	  if (rl_point < p)	/* Did we retreat at EOL? */
750136759Speter	    rl_point++;	/* XXX - should we advance more than 1 for mbchar? */
751119610Sache	  rl_insert_text (mb);
752119610Sache	  rl_end_undo_group ();
753119610Sache	  rl_vi_check ();
754119610Sache	}
755119610Sache      else
756119610Sache        rl_forward_char (1, 0);
757119610Sache    }
758119610Sache
759119610Sache  return 0;
760119610Sache}
761119610Sache#endif
762119610Sache
76321308Sacheint
76421308Sacherl_vi_change_case (count, ignore)
76521308Sache     int count, ignore;
76621308Sache{
767136759Speter  int c, p;
76821308Sache
76921308Sache  /* Don't try this on an empty line. */
77021308Sache  if (rl_point >= rl_end)
77121308Sache    return (0);
77221308Sache
773136759Speter  c = 0;
774119610Sache#if defined (HANDLE_MULTIBYTE)
775119610Sache  if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
776119610Sache    return (_rl_vi_change_mbchar_case (count));
777119610Sache#endif
778119610Sache
77921308Sache  while (count-- && rl_point < rl_end)
78021308Sache    {
78121308Sache      if (_rl_uppercase_p (rl_line_buffer[rl_point]))
78221308Sache	c = _rl_to_lower (rl_line_buffer[rl_point]);
78321308Sache      else if (_rl_lowercase_p (rl_line_buffer[rl_point]))
78421308Sache	c = _rl_to_upper (rl_line_buffer[rl_point]);
78521308Sache      else
78621308Sache	{
78721308Sache	  /* Just skip over characters neither upper nor lower case. */
788119610Sache	  rl_forward_char (1, c);
78921308Sache	  continue;
79021308Sache	}
79121308Sache
79221308Sache      /* Vi is kind of strange here. */
79321308Sache      if (c)
79421308Sache	{
795136759Speter	  p = rl_point;
79621308Sache	  rl_begin_undo_group ();
797136759Speter	  rl_vi_delete (1, c);
798136759Speter	  if (rl_point < p)	/* Did we retreat at EOL? */
799136759Speter	    rl_point++;
800119610Sache	  _rl_insert_char (1, c);
80121308Sache	  rl_end_undo_group ();
80221308Sache	  rl_vi_check ();
80321308Sache        }
80421308Sache      else
805119610Sache	rl_forward_char (1, c);
80621308Sache    }
80721308Sache  return (0);
80821308Sache}
80921308Sache
81021308Sacheint
81121308Sacherl_vi_put (count, key)
81221308Sache     int count, key;
81321308Sache{
81421308Sache  if (!_rl_uppercase_p (key) && (rl_point + 1 <= rl_end))
815119610Sache    rl_point = _rl_find_next_mbchar (rl_line_buffer, rl_point, 1, MB_FIND_NONZERO);
81621308Sache
817136759Speter  while (count--)
818136759Speter    rl_yank (1, key);
819136759Speter
820119610Sache  rl_backward_char (1, key);
82121308Sache  return (0);
82221308Sache}
82321308Sache
82421308Sacheint
82521308Sacherl_vi_check ()
82621308Sache{
82721308Sache  if (rl_point && rl_point == rl_end)
828119610Sache    {
829119610Sache      if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
830119610Sache	rl_point = _rl_find_prev_mbchar (rl_line_buffer, rl_point, MB_FIND_NONZERO);
831119610Sache      else
832119610Sache        rl_point--;
833119610Sache    }
83421308Sache  return (0);
83521308Sache}
83621308Sache
83721308Sacheint
83821308Sacherl_vi_column (count, key)
83921308Sache     int count, key;
84021308Sache{
84121308Sache  if (count > rl_end)
84221308Sache    rl_end_of_line (1, key);
84321308Sache  else
84421308Sache    rl_point = count - 1;
84521308Sache  return (0);
84621308Sache}
84721308Sache
84821308Sacheint
84921308Sacherl_vi_domove (key, nextkey)
85021308Sache     int key, *nextkey;
85121308Sache{
85221308Sache  int c, save;
85321308Sache  int old_end;
85421308Sache
85521308Sache  rl_mark = rl_point;
85675406Sache  RL_SETSTATE(RL_STATE_MOREINPUT);
85721308Sache  c = rl_read_key ();
85875406Sache  RL_UNSETSTATE(RL_STATE_MOREINPUT);
85921308Sache  *nextkey = c;
86021308Sache
86121308Sache  if (!member (c, vi_motion))
86221308Sache    {
86321308Sache      if (_rl_digit_p (c))
86421308Sache	{
86521308Sache	  save = rl_numeric_arg;
86621308Sache	  rl_numeric_arg = _rl_digit_value (c);
867136759Speter	  rl_explicit_arg = 1;
868157188Sache	  RL_SETSTATE (RL_STATE_NUMERICARG|RL_STATE_VIMOTION);
86921308Sache	  rl_digit_loop1 ();
870157188Sache	  RL_UNSETSTATE (RL_STATE_VIMOTION);
87121308Sache	  rl_numeric_arg *= save;
87275406Sache	  RL_SETSTATE(RL_STATE_MOREINPUT);
87321308Sache	  c = rl_read_key ();	/* real command */
87475406Sache	  RL_UNSETSTATE(RL_STATE_MOREINPUT);
87521308Sache	  *nextkey = c;
87621308Sache	}
87721308Sache      else if (key == c && (key == 'd' || key == 'y' || key == 'c'))
87821308Sache	{
87921308Sache	  rl_mark = rl_end;
88021308Sache	  rl_beg_of_line (1, c);
88121308Sache	  _rl_vi_last_motion = c;
88221308Sache	  return (0);
88321308Sache	}
88421308Sache      else
88521308Sache	return (-1);
88621308Sache    }
88721308Sache
88821308Sache  _rl_vi_last_motion = c;
88921308Sache
89021308Sache  /* Append a blank character temporarily so that the motion routines
89121308Sache     work right at the end of the line. */
89221308Sache  old_end = rl_end;
89321308Sache  rl_line_buffer[rl_end++] = ' ';
89421308Sache  rl_line_buffer[rl_end] = '\0';
89521308Sache
89621308Sache  _rl_dispatch (c, _rl_keymap);
89721308Sache
89821308Sache  /* Remove the blank that we added. */
89921308Sache  rl_end = old_end;
90021308Sache  rl_line_buffer[rl_end] = '\0';
90121308Sache  if (rl_point > rl_end)
90221308Sache    rl_point = rl_end;
90321308Sache
90421308Sache  /* No change in position means the command failed. */
90521308Sache  if (rl_mark == rl_point)
90621308Sache    return (-1);
90721308Sache
90821308Sache  /* rl_vi_f[wW]ord () leaves the cursor on the first character of the next
90921308Sache     word.  If we are not at the end of the line, and we are on a
91021308Sache     non-whitespace character, move back one (presumably to whitespace). */
91121308Sache  if ((_rl_to_upper (c) == 'W') && rl_point < rl_end && rl_point > rl_mark &&
91221308Sache      !whitespace (rl_line_buffer[rl_point]))
91321308Sache    rl_point--;
91421308Sache
91521308Sache  /* If cw or cW, back up to the end of a word, so the behaviour of ce
91621308Sache     or cE is the actual result.  Brute-force, no subtlety. */
91721308Sache  if (key == 'c' && rl_point >= rl_mark && (_rl_to_upper (c) == 'W'))
91821308Sache    {
91921308Sache      /* Don't move farther back than where we started. */
92021308Sache      while (rl_point > rl_mark && whitespace (rl_line_buffer[rl_point]))
92121308Sache	rl_point--;
92221308Sache
92321308Sache      /* Posix.2 says that if cw or cW moves the cursor towards the end of
92421308Sache	 the line, the character under the cursor should be deleted. */
92521308Sache      if (rl_point == rl_mark)
92621308Sache        rl_point++;
92721308Sache      else
92821308Sache	{
92921308Sache	  /* Move past the end of the word so that the kill doesn't
93021308Sache	     remove the last letter of the previous word.  Only do this
93121308Sache	     if we are not at the end of the line. */
93221308Sache	  if (rl_point >= 0 && rl_point < (rl_end - 1) && !whitespace (rl_line_buffer[rl_point]))
93321308Sache	    rl_point++;
93421308Sache	}
93521308Sache    }
93621308Sache
93721308Sache  if (rl_mark < rl_point)
938119610Sache    SWAP (rl_point, rl_mark);
93921308Sache
94021308Sache  return (0);
94121308Sache}
94221308Sache
943157188Sache/* Process C as part of the current numeric argument.  Return -1 if the
944157188Sache   argument should be aborted, 0 if we should not read any more chars, and
945157188Sache   1 if we should continue to read chars. */
946157188Sachestatic int
947157188Sache_rl_vi_arg_dispatch (c)
948157188Sache     int c;
949157188Sache{
950157188Sache  int key;
951157188Sache
952157188Sache  key = c;
953157188Sache  if (c >= 0 && _rl_keymap[c].type == ISFUNC && _rl_keymap[c].function == rl_universal_argument)
954157188Sache    {
955157188Sache      rl_numeric_arg *= 4;
956157188Sache      return 1;
957157188Sache    }
958157188Sache
959157188Sache  c = UNMETA (c);
960157188Sache
961157188Sache  if (_rl_digit_p (c))
962157188Sache    {
963157188Sache      if (rl_explicit_arg)
964157188Sache	rl_numeric_arg = (rl_numeric_arg * 10) + _rl_digit_value (c);
965157188Sache      else
966157188Sache	rl_numeric_arg = _rl_digit_value (c);
967157188Sache      rl_explicit_arg = 1;
968157188Sache      return 1;
969157188Sache    }
970157188Sache  else
971157188Sache    {
972157188Sache      rl_clear_message ();
973157188Sache      rl_stuff_char (key);
974157188Sache      return 0;
975157188Sache    }
976157188Sache}
977157188Sache
97821308Sache/* A simplified loop for vi. Don't dispatch key at end.
97975406Sache   Don't recognize minus sign?
98075406Sache   Should this do rl_save_prompt/rl_restore_prompt? */
98121308Sachestatic int
98221308Sacherl_digit_loop1 ()
98321308Sache{
984157188Sache  int c, r;
98521308Sache
98621308Sache  while (1)
98721308Sache    {
988157188Sache      if (_rl_arg_overflow ())
989157188Sache	return 1;
99021308Sache
991157188Sache      c = _rl_arg_getchar ();
99221308Sache
993157188Sache      r = _rl_vi_arg_dispatch (c);
994157188Sache      if (r <= 0)
995157188Sache	break;
99621308Sache    }
99775406Sache
99875406Sache  RL_UNSETSTATE(RL_STATE_NUMERICARG);
99921308Sache  return (0);
100021308Sache}
100121308Sache
100221308Sacheint
100321308Sacherl_vi_delete_to (count, key)
100421308Sache     int count, key;
100521308Sache{
100621308Sache  int c;
100721308Sache
100821308Sache  if (_rl_uppercase_p (key))
100921308Sache    rl_stuff_char ('$');
101021308Sache  else if (vi_redoing)
101121308Sache    rl_stuff_char (_rl_vi_last_motion);
101221308Sache
101321308Sache  if (rl_vi_domove (key, &c))
101421308Sache    {
101575406Sache      rl_ding ();
101621308Sache      return -1;
101721308Sache    }
101821308Sache
101921308Sache  /* These are the motion commands that do not require adjusting the
102021308Sache     mark. */
102121308Sache  if ((strchr (" l|h^0bB", c) == 0) && (rl_mark < rl_end))
102221308Sache    rl_mark++;
102321308Sache
102421308Sache  rl_kill_text (rl_point, rl_mark);
102521308Sache  return (0);
102621308Sache}
102721308Sache
102821308Sacheint
102921308Sacherl_vi_change_to (count, key)
103021308Sache     int count, key;
103121308Sache{
103221308Sache  int c, start_pos;
103321308Sache
103421308Sache  if (_rl_uppercase_p (key))
103521308Sache    rl_stuff_char ('$');
103621308Sache  else if (vi_redoing)
103721308Sache    rl_stuff_char (_rl_vi_last_motion);
103821308Sache
103921308Sache  start_pos = rl_point;
104021308Sache
104121308Sache  if (rl_vi_domove (key, &c))
104221308Sache    {
104375406Sache      rl_ding ();
104421308Sache      return -1;
104521308Sache    }
104621308Sache
104721308Sache  /* These are the motion commands that do not require adjusting the
104821308Sache     mark.  c[wW] are handled by special-case code in rl_vi_domove(),
104921308Sache     and already leave the mark at the correct location. */
105021308Sache  if ((strchr (" l|hwW^0bB", c) == 0) && (rl_mark < rl_end))
105121308Sache    rl_mark++;
105221308Sache
105321308Sache  /* The cursor never moves with c[wW]. */
105421308Sache  if ((_rl_to_upper (c) == 'W') && rl_point < start_pos)
105521308Sache    rl_point = start_pos;
105621308Sache
105721308Sache  if (vi_redoing)
105821308Sache    {
105921308Sache      if (vi_insert_buffer && *vi_insert_buffer)
106021308Sache	rl_begin_undo_group ();
106121308Sache      rl_delete_text (rl_point, rl_mark);
106221308Sache      if (vi_insert_buffer && *vi_insert_buffer)
106321308Sache	{
106421308Sache	  rl_insert_text (vi_insert_buffer);
106521308Sache	  rl_end_undo_group ();
106621308Sache	}
106721308Sache    }
106821308Sache  else
106921308Sache    {
107021308Sache      rl_begin_undo_group ();		/* to make the `u' command work */
107121308Sache      rl_kill_text (rl_point, rl_mark);
107221308Sache      /* `C' does not save the text inserted for undoing or redoing. */
107321308Sache      if (_rl_uppercase_p (key) == 0)
107421308Sache        _rl_vi_doing_insert = 1;
1075136759Speter      rl_vi_start_inserting (key, rl_numeric_arg, rl_arg_sign);
107621308Sache    }
107721308Sache
107821308Sache  return (0);
107921308Sache}
108021308Sache
108121308Sacheint
108221308Sacherl_vi_yank_to (count, key)
108321308Sache     int count, key;
108421308Sache{
1085157188Sache  int c, save;
108621308Sache
1087157188Sache  save = rl_point;
108821308Sache  if (_rl_uppercase_p (key))
108921308Sache    rl_stuff_char ('$');
109021308Sache
109121308Sache  if (rl_vi_domove (key, &c))
109221308Sache    {
109375406Sache      rl_ding ();
109421308Sache      return -1;
109521308Sache    }
109621308Sache
109721308Sache  /* These are the motion commands that do not require adjusting the
109821308Sache     mark. */
109921308Sache  if ((strchr (" l|h^0%bB", c) == 0) && (rl_mark < rl_end))
110021308Sache    rl_mark++;
110121308Sache
110221308Sache  rl_begin_undo_group ();
110321308Sache  rl_kill_text (rl_point, rl_mark);
110421308Sache  rl_end_undo_group ();
110521308Sache  rl_do_undo ();
110621308Sache  rl_point = save;
110721308Sache
110821308Sache  return (0);
110921308Sache}
111021308Sache
111121308Sacheint
1112157188Sacherl_vi_rubout (count, key)
1113157188Sache     int count, key;
1114157188Sache{
1115157188Sache  int p, opoint;
1116157188Sache
1117157188Sache  if (count < 0)
1118157188Sache    return (rl_vi_delete (-count, key));
1119157188Sache
1120157188Sache  if (rl_point == 0)
1121157188Sache    {
1122157188Sache      rl_ding ();
1123157188Sache      return -1;
1124157188Sache    }
1125157188Sache
1126157188Sache  opoint = rl_point;
1127157188Sache  if (count > 1 && MB_CUR_MAX > 1 && rl_byte_oriented == 0)
1128157188Sache    rl_backward_char (count, key);
1129157188Sache  else if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
1130157188Sache    rl_point = _rl_find_prev_mbchar (rl_line_buffer, rl_point, MB_FIND_NONZERO);
1131157188Sache  else
1132157188Sache    rl_point -= count;
1133157188Sache
1134157188Sache  if (rl_point < 0)
1135157188Sache    rl_point = 0;
1136157188Sache
1137157188Sache  rl_kill_text (rl_point, opoint);
1138157188Sache
1139157188Sache  return (0);
1140157188Sache}
1141157188Sache
1142157188Sacheint
114321308Sacherl_vi_delete (count, key)
114421308Sache     int count, key;
114521308Sache{
114621308Sache  int end;
114721308Sache
1148157188Sache  if (count < 0)
1149157188Sache    return (rl_vi_rubout (-count, key));
1150157188Sache
115121308Sache  if (rl_end == 0)
115221308Sache    {
115375406Sache      rl_ding ();
115421308Sache      return -1;
115521308Sache    }
115621308Sache
1157119610Sache  if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
1158119610Sache    end = _rl_find_next_mbchar (rl_line_buffer, rl_point, count, MB_FIND_NONZERO);
1159119610Sache  else
1160119610Sache    end = rl_point + count;
116121308Sache
116221308Sache  if (end >= rl_end)
116321308Sache    end = rl_end;
116421308Sache
116521308Sache  rl_kill_text (rl_point, end);
116621308Sache
116721308Sache  if (rl_point > 0 && rl_point == rl_end)
1168119610Sache    rl_backward_char (1, key);
1169157188Sache
117021308Sache  return (0);
117121308Sache}
117221308Sache
117321308Sacheint
117421308Sacherl_vi_back_to_indent (count, key)
117521308Sache     int count, key;
117621308Sache{
117721308Sache  rl_beg_of_line (1, key);
117821308Sache  while (rl_point < rl_end && whitespace (rl_line_buffer[rl_point]))
117921308Sache    rl_point++;
118021308Sache  return (0);
118121308Sache}
118221308Sache
118321308Sacheint
118421308Sacherl_vi_first_print (count, key)
118521308Sache     int count, key;
118621308Sache{
118721308Sache  return (rl_vi_back_to_indent (1, key));
118821308Sache}
118921308Sache
1190157188Sachestatic int _rl_cs_dir, _rl_cs_orig_dir;
1191157188Sache
1192157188Sache#if defined (READLINE_CALLBACKS)
1193157188Sachestatic int
1194157188Sache_rl_vi_callback_char_search (data)
1195157188Sache     _rl_callback_generic_arg *data;
1196157188Sache{
1197157188Sache#if defined (HANDLE_MULTIBYTE)
1198157188Sache  _rl_vi_last_search_mblen = _rl_read_mbchar (_rl_vi_last_search_mbchar, MB_LEN_MAX);
1199157188Sache#else
1200157188Sache  RL_SETSTATE(RL_STATE_MOREINPUT);
1201157188Sache  _rl_vi_last_search_char = rl_read_key ();
1202157188Sache  RL_UNSETSTATE(RL_STATE_MOREINPUT);
1203157188Sache#endif
1204157188Sache
1205157188Sache  _rl_callback_func = 0;
1206157188Sache  _rl_want_redisplay = 1;
1207157188Sache
1208157188Sache#if defined (HANDLE_MULTIBYTE)
1209157188Sache  return (_rl_char_search_internal (data->count, _rl_cs_dir, _rl_vi_last_search_mbchar, _rl_vi_last_search_mblen));
1210157188Sache#else
1211157188Sache  return (_rl_char_search_internal (data->count, _rl_cs_dir, _rl_vi_last_search_char));
1212157188Sache#endif
1213157188Sache}
1214157188Sache#endif
1215157188Sache
121621308Sacheint
121721308Sacherl_vi_char_search (count, key)
121821308Sache     int count, key;
121921308Sache{
1220119610Sache#if defined (HANDLE_MULTIBYTE)
1221119610Sache  static char *target;
1222157188Sache  static int tlen;
1223119610Sache#else
122421308Sache  static char target;
1225119610Sache#endif
122621308Sache
122721308Sache  if (key == ';' || key == ',')
1228157188Sache    _rl_cs_dir = (key == ';') ? _rl_cs_orig_dir : -_rl_cs_orig_dir;
122921308Sache  else
123021308Sache    {
123121308Sache      switch (key)
123221308Sache        {
123321308Sache        case 't':
1234157188Sache          _rl_cs_orig_dir = _rl_cs_dir = FTO;
123521308Sache          break;
123621308Sache
123721308Sache        case 'T':
1238157188Sache          _rl_cs_orig_dir = _rl_cs_dir = BTO;
123921308Sache          break;
124021308Sache
124121308Sache        case 'f':
1242157188Sache          _rl_cs_orig_dir = _rl_cs_dir = FFIND;
124321308Sache          break;
124421308Sache
124521308Sache        case 'F':
1246157188Sache          _rl_cs_orig_dir = _rl_cs_dir = BFIND;
124721308Sache          break;
124821308Sache        }
1249157188Sache
1250157188Sache      if (vi_redoing)
1251157188Sache	{
1252157188Sache	  /* set target and tlen below */
1253157188Sache	}
1254157188Sache#if defined (READLINE_CALLBACKS)
1255157188Sache      else if (RL_ISSTATE (RL_STATE_CALLBACK))
1256157188Sache        {
1257157188Sache          _rl_callback_data = _rl_callback_data_alloc (count);
1258157188Sache          _rl_callback_data->i1 = _rl_cs_dir;
1259157188Sache          _rl_callback_func = _rl_vi_callback_char_search;
1260157188Sache          return (0);
1261157188Sache        }
1262157188Sache#endif
1263157188Sache      else
1264157188Sache	{
1265157188Sache#if defined (HANDLE_MULTIBYTE)
1266157188Sache	  _rl_vi_last_search_mblen = _rl_read_mbchar (_rl_vi_last_search_mbchar, MB_LEN_MAX);
1267157188Sache#else
1268157188Sache	  RL_SETSTATE(RL_STATE_MOREINPUT);
1269157188Sache	  _rl_vi_last_search_char = rl_read_key ();
1270157188Sache	  RL_UNSETSTATE(RL_STATE_MOREINPUT);
1271157188Sache#endif
1272157188Sache	}
127321308Sache    }
127421308Sache
1275119610Sache#if defined (HANDLE_MULTIBYTE)
1276157188Sache  target = _rl_vi_last_search_mbchar;
1277157188Sache  tlen = _rl_vi_last_search_mblen;
1278119610Sache#else
1279157188Sache  target = _rl_vi_last_search_char;
1280119610Sache#endif
1281157188Sache
1282157188Sache#if defined (HANDLE_MULTIBYTE)
1283157188Sache  return (_rl_char_search_internal (count, _rl_cs_dir, target, tlen));
1284157188Sache#else
1285157188Sache  return (_rl_char_search_internal (count, _rl_cs_dir, target));
1286157188Sache#endif
128721308Sache}
128821308Sache
128921308Sache/* Match brackets */
129021308Sacheint
129121308Sacherl_vi_match (ignore, key)
129221308Sache     int ignore, key;
129321308Sache{
1294119610Sache  int count = 1, brack, pos, tmp, pre;
129521308Sache
129621308Sache  pos = rl_point;
129721308Sache  if ((brack = rl_vi_bracktype (rl_line_buffer[rl_point])) == 0)
129821308Sache    {
1299119610Sache      if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
1300119610Sache	{
1301119610Sache	  while ((brack = rl_vi_bracktype (rl_line_buffer[rl_point])) == 0)
1302119610Sache	    {
1303119610Sache	      pre = rl_point;
1304119610Sache	      rl_forward_char (1, key);
1305119610Sache	      if (pre == rl_point)
1306119610Sache	        break;
1307119610Sache	    }
1308119610Sache	}
1309119610Sache      else
1310119610Sache	while ((brack = rl_vi_bracktype (rl_line_buffer[rl_point])) == 0 &&
1311119610Sache		rl_point < rl_end - 1)
1312119610Sache	  rl_forward_char (1, key);
131321308Sache
131421308Sache      if (brack <= 0)
131521308Sache	{
131621308Sache	  rl_point = pos;
131775406Sache	  rl_ding ();
131821308Sache	  return -1;
131921308Sache	}
132021308Sache    }
132121308Sache
132221308Sache  pos = rl_point;
132321308Sache
132421308Sache  if (brack < 0)
132521308Sache    {
132621308Sache      while (count)
132721308Sache	{
1328119610Sache	  tmp = pos;
1329119610Sache	  if (MB_CUR_MAX == 1 || rl_byte_oriented)
1330119610Sache	    pos--;
1331119610Sache	  else
133221308Sache	    {
1333119610Sache	      pos = _rl_find_prev_mbchar (rl_line_buffer, pos, MB_FIND_ANY);
1334119610Sache	      if (tmp == pos)
1335119610Sache	        pos--;
1336119610Sache	    }
1337119610Sache	  if (pos >= 0)
1338119610Sache	    {
133921308Sache	      int b = rl_vi_bracktype (rl_line_buffer[pos]);
134021308Sache	      if (b == -brack)
134121308Sache		count--;
134221308Sache	      else if (b == brack)
134321308Sache		count++;
134421308Sache	    }
134521308Sache	  else
134621308Sache	    {
134775406Sache	      rl_ding ();
134821308Sache	      return -1;
134921308Sache	    }
135021308Sache	}
135121308Sache    }
135221308Sache  else
135321308Sache    {			/* brack > 0 */
135421308Sache      while (count)
135521308Sache	{
1356119610Sache	  if (MB_CUR_MAX == 1 || rl_byte_oriented)
1357119610Sache	    pos++;
1358119610Sache	  else
1359119610Sache	    pos = _rl_find_next_mbchar (rl_line_buffer, pos, 1, MB_FIND_ANY);
1360119610Sache
1361119610Sache	  if (pos < rl_end)
136221308Sache	    {
136321308Sache	      int b = rl_vi_bracktype (rl_line_buffer[pos]);
136421308Sache	      if (b == -brack)
136521308Sache		count--;
136621308Sache	      else if (b == brack)
136721308Sache		count++;
136821308Sache	    }
136921308Sache	  else
137021308Sache	    {
137175406Sache	      rl_ding ();
137221308Sache	      return -1;
137321308Sache	    }
137421308Sache	}
137521308Sache    }
137621308Sache  rl_point = pos;
137721308Sache  return (0);
137821308Sache}
137921308Sache
138021308Sacheint
138121308Sacherl_vi_bracktype (c)
138221308Sache     int c;
138321308Sache{
138421308Sache  switch (c)
138521308Sache    {
138621308Sache    case '(': return  1;
138721308Sache    case ')': return -1;
138821308Sache    case '[': return  2;
138921308Sache    case ']': return -2;
139021308Sache    case '{': return  3;
139121308Sache    case '}': return -3;
139221308Sache    default:  return  0;
139321308Sache    }
139421308Sache}
139521308Sache
1396157188Sachestatic int
1397157188Sache_rl_vi_change_char (count, c, mb)
1398157188Sache     int count, c;
1399157188Sache     char *mb;
140021308Sache{
1401157188Sache  int p;
140221308Sache
140321308Sache  if (c == '\033' || c == CTRL ('C'))
140421308Sache    return -1;
140521308Sache
1406136759Speter  rl_begin_undo_group ();
140721308Sache  while (count-- && rl_point < rl_end)
140821308Sache    {
1409136759Speter      p = rl_point;
1410136759Speter      rl_vi_delete (1, c);
1411157188Sache      if (rl_point < p)		/* Did we retreat at EOL? */
1412157188Sache	rl_point++;
1413119610Sache#if defined (HANDLE_MULTIBYTE)
1414119610Sache      if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
1415157188Sache	rl_insert_text (mb);
1416119610Sache      else
1417119610Sache#endif
1418157188Sache	_rl_insert_char (1, c);
1419136759Speter    }
1420157188Sache
1421157188Sache  /* The cursor shall be left on the last character changed. */
1422157188Sache  rl_backward_char (1, c);
1423157188Sache
1424136759Speter  rl_end_undo_group ();
142521308Sache
142621308Sache  return (0);
142721308Sache}
142821308Sache
1429157188Sachestatic int
1430157188Sache_rl_vi_callback_getchar (mb, mblen)
1431157188Sache     char *mb;
1432157188Sache     int mblen;
1433157188Sache{
1434157188Sache  int c;
1435157188Sache
1436157188Sache  RL_SETSTATE(RL_STATE_MOREINPUT);
1437157188Sache  c = rl_read_key ();
1438157188Sache  RL_UNSETSTATE(RL_STATE_MOREINPUT);
1439157188Sache
1440157188Sache#if defined (HANDLE_MULTIBYTE)
1441157188Sache  if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
1442157188Sache    c = _rl_read_mbstring (c, mb, mblen);
1443157188Sache#endif
1444157188Sache
1445157188Sache  return c;
1446157188Sache}
1447157188Sache
1448157188Sache#if defined (READLINE_CALLBACKS)
1449157188Sachestatic int
1450157188Sache_rl_vi_callback_change_char (data)
1451157188Sache     _rl_callback_generic_arg *data;
1452157188Sache{
1453157188Sache  int c;
1454157188Sache  char mb[MB_LEN_MAX];
1455157188Sache
1456157188Sache  _rl_vi_last_replacement = c = _rl_vi_callback_getchar (mb, MB_LEN_MAX);
1457157188Sache
1458157188Sache  _rl_callback_func = 0;
1459157188Sache  _rl_want_redisplay = 1;
1460157188Sache
1461157188Sache  return (_rl_vi_change_char (data->count, c, mb));
1462157188Sache}
1463157188Sache#endif
1464157188Sache
146521308Sacheint
1466157188Sacherl_vi_change_char (count, key)
1467157188Sache     int count, key;
1468157188Sache{
1469157188Sache  int c;
1470157188Sache  char mb[MB_LEN_MAX];
1471157188Sache
1472157188Sache  if (vi_redoing)
1473157188Sache    {
1474157188Sache      c = _rl_vi_last_replacement;
1475157188Sache      mb[0] = c;
1476157188Sache      mb[1] = '\0';
1477157188Sache    }
1478157188Sache#if defined (READLINE_CALLBACKS)
1479157188Sache  else if (RL_ISSTATE (RL_STATE_CALLBACK))
1480157188Sache    {
1481157188Sache      _rl_callback_data = _rl_callback_data_alloc (count);
1482157188Sache      _rl_callback_func = _rl_vi_callback_change_char;
1483157188Sache      return (0);
1484157188Sache    }
1485157188Sache#endif
1486157188Sache  else
1487157188Sache    _rl_vi_last_replacement = c = _rl_vi_callback_getchar (mb, MB_LEN_MAX);
1488157188Sache
1489157188Sache  return (_rl_vi_change_char (count, c, mb));
1490157188Sache}
1491157188Sache
1492157188Sacheint
149321308Sacherl_vi_subst (count, key)
149421308Sache     int count, key;
149521308Sache{
1496119610Sache  /* If we are redoing, rl_vi_change_to will stuff the last motion char */
1497119610Sache  if (vi_redoing == 0)
1498136759Speter    rl_stuff_char ((key == 'S') ? 'c' : 'l');	/* `S' == `cc', `s' == `cl' */
149921308Sache
1500119610Sache  return (rl_vi_change_to (count, 'c'));
150121308Sache}
150221308Sache
150321308Sacheint
150421308Sacherl_vi_overstrike (count, key)
150521308Sache     int count, key;
150621308Sache{
150721308Sache  if (_rl_vi_doing_insert == 0)
150821308Sache    {
150921308Sache      _rl_vi_doing_insert = 1;
151021308Sache      rl_begin_undo_group ();
151121308Sache    }
151221308Sache
1513119610Sache  if (count > 0)
151421308Sache    {
1515119610Sache      _rl_overwrite_char (count, key);
1516119610Sache      vi_replace_count += count;
1517119610Sache    }
151821308Sache
151921308Sache  return (0);
152021308Sache}
152121308Sache
152221308Sacheint
152321308Sacherl_vi_overstrike_delete (count, key)
152421308Sache     int count, key;
152521308Sache{
152621308Sache  int i, s;
152721308Sache
152821308Sache  for (i = 0; i < count; i++)
152921308Sache    {
153021308Sache      if (vi_replace_count == 0)
153121308Sache	{
153275406Sache	  rl_ding ();
153321308Sache	  break;
153421308Sache	}
153521308Sache      s = rl_point;
153621308Sache
153721308Sache      if (rl_do_undo ())
153821308Sache	vi_replace_count--;
153921308Sache
154021308Sache      if (rl_point == s)
1541119610Sache	rl_backward_char (1, key);
154221308Sache    }
154321308Sache
154421308Sache  if (vi_replace_count == 0 && _rl_vi_doing_insert)
154521308Sache    {
154621308Sache      rl_end_undo_group ();
154721308Sache      rl_do_undo ();
154821308Sache      _rl_vi_doing_insert = 0;
154921308Sache    }
155021308Sache  return (0);
155121308Sache}
155221308Sache
155321308Sacheint
155421308Sacherl_vi_replace (count, key)
155521308Sache     int count, key;
155621308Sache{
155721308Sache  int i;
155821308Sache
155921308Sache  vi_replace_count = 0;
156021308Sache
156121308Sache  if (!vi_replace_map)
156221308Sache    {
156321308Sache      vi_replace_map = rl_make_bare_keymap ();
156421308Sache
156521308Sache      for (i = ' '; i < KEYMAP_SIZE; i++)
156621308Sache	vi_replace_map[i].function = rl_vi_overstrike;
156721308Sache
156821308Sache      vi_replace_map[RUBOUT].function = rl_vi_overstrike_delete;
156921308Sache      vi_replace_map[ESC].function = rl_vi_movement_mode;
157021308Sache      vi_replace_map[RETURN].function = rl_newline;
157121308Sache      vi_replace_map[NEWLINE].function = rl_newline;
157221308Sache
157321308Sache      /* If the normal vi insertion keymap has ^H bound to erase, do the
157421308Sache         same here.  Probably should remove the assignment to RUBOUT up
157521308Sache         there, but I don't think it will make a difference in real life. */
157621308Sache      if (vi_insertion_keymap[CTRL ('H')].type == ISFUNC &&
157721308Sache	  vi_insertion_keymap[CTRL ('H')].function == rl_rubout)
157821308Sache	vi_replace_map[CTRL ('H')].function = rl_vi_overstrike_delete;
157921308Sache
158021308Sache    }
158121308Sache  _rl_keymap = vi_replace_map;
158221308Sache  return (0);
158321308Sache}
158421308Sache
158521308Sache#if 0
158621308Sache/* Try to complete the word we are standing on or the word that ends with
158721308Sache   the previous character.  A space matches everything.  Word delimiters are
158821308Sache   space and ;. */
158921308Sacheint
159021308Sacherl_vi_possible_completions()
159121308Sache{
159221308Sache  int save_pos = rl_point;
159321308Sache
159421308Sache  if (rl_line_buffer[rl_point] != ' ' && rl_line_buffer[rl_point] != ';')
159521308Sache    {
159621308Sache      while (rl_point < rl_end && rl_line_buffer[rl_point] != ' ' &&
159721308Sache	     rl_line_buffer[rl_point] != ';')
159821308Sache	rl_point++;
159921308Sache    }
160021308Sache  else if (rl_line_buffer[rl_point - 1] == ';')
160121308Sache    {
160275406Sache      rl_ding ();
160321308Sache      return (0);
160421308Sache    }
160521308Sache
160621308Sache  rl_possible_completions ();
160721308Sache  rl_point = save_pos;
160821308Sache
160921308Sache  return (0);
161021308Sache}
161121308Sache#endif
161221308Sache
161321308Sache/* Functions to save and restore marks. */
1614157188Sachestatic int
1615157188Sache_rl_vi_set_mark ()
161621308Sache{
161721308Sache  int ch;
161821308Sache
161975406Sache  RL_SETSTATE(RL_STATE_MOREINPUT);
162021308Sache  ch = rl_read_key ();
162175406Sache  RL_UNSETSTATE(RL_STATE_MOREINPUT);
162275406Sache
1623119610Sache  if (ch < 'a' || ch > 'z')
162421308Sache    {
162575406Sache      rl_ding ();
162621308Sache      return -1;
162721308Sache    }
162821308Sache  ch -= 'a';
162921308Sache  vi_mark_chars[ch] = rl_point;
163021308Sache  return 0;
163121308Sache}
163221308Sache
1633157188Sache#if defined (READLINE_CALLBACKS)
1634157188Sachestatic int
1635157188Sache_rl_vi_callback_set_mark (data)
1636157188Sache     _rl_callback_generic_arg *data;
1637157188Sache{
1638157188Sache  _rl_callback_func = 0;
1639157188Sache  _rl_want_redisplay = 1;
1640157188Sache
1641157188Sache  return (_rl_vi_set_mark ());
1642157188Sache}
1643157188Sache#endif
1644157188Sache
164521308Sacheint
1646157188Sacherl_vi_set_mark (count, key)
164721308Sache     int count, key;
164821308Sache{
1649157188Sache#if defined (READLINE_CALLBACKS)
1650157188Sache  if (RL_ISSTATE (RL_STATE_CALLBACK))
1651157188Sache    {
1652157188Sache      _rl_callback_data = 0;
1653157188Sache      _rl_callback_func = _rl_vi_callback_set_mark;
1654157188Sache      return (0);
1655157188Sache    }
1656157188Sache#endif
1657157188Sache
1658157188Sache  return (_rl_vi_set_mark ());
1659157188Sache}
1660157188Sache
1661157188Sachestatic int
1662157188Sache_rl_vi_goto_mark ()
1663157188Sache{
166421308Sache  int ch;
166521308Sache
166675406Sache  RL_SETSTATE(RL_STATE_MOREINPUT);
166721308Sache  ch = rl_read_key ();
166875406Sache  RL_UNSETSTATE(RL_STATE_MOREINPUT);
166975406Sache
167021308Sache  if (ch == '`')
167121308Sache    {
167221308Sache      rl_point = rl_mark;
167321308Sache      return 0;
167421308Sache    }
1675119610Sache  else if (ch < 'a' || ch > 'z')
167621308Sache    {
167775406Sache      rl_ding ();
167821308Sache      return -1;
167921308Sache    }
168021308Sache
168121308Sache  ch -= 'a';
168221308Sache  if (vi_mark_chars[ch] == -1)
168321308Sache    {
168475406Sache      rl_ding ();
168521308Sache      return -1;
168621308Sache    }
168721308Sache  rl_point = vi_mark_chars[ch];
168821308Sache  return 0;
168921308Sache}
169021308Sache
1691157188Sache#if defined (READLINE_CALLBACKS)
1692157188Sachestatic int
1693157188Sache_rl_vi_callback_goto_mark (data)
1694157188Sache     _rl_callback_generic_arg *data;
1695157188Sache{
1696157188Sache  _rl_callback_func = 0;
1697157188Sache  _rl_want_redisplay = 1;
1698157188Sache
1699157188Sache  return (_rl_vi_goto_mark ());
1700157188Sache}
1701157188Sache#endif
1702157188Sache
1703157188Sacheint
1704157188Sacherl_vi_goto_mark (count, key)
1705157188Sache     int count, key;
1706157188Sache{
1707157188Sache#if defined (READLINE_CALLBACKS)
1708157188Sache  if (RL_ISSTATE (RL_STATE_CALLBACK))
1709157188Sache    {
1710157188Sache      _rl_callback_data = 0;
1711157188Sache      _rl_callback_func = _rl_vi_callback_goto_mark;
1712157188Sache      return (0);
1713157188Sache    }
1714157188Sache#endif
1715157188Sache
1716157188Sache  return (_rl_vi_goto_mark ());
1717157188Sache}
171821308Sache#endif /* VI_MODE */
1719