vi_mode.c revision 75406
1193645Ssimon/* vi_mode.c -- A vi emulation mode for Bash.
2280297Sjkim   Derived from code written by Jeff Sparkes (jsparkes@bnr.ca).  */
3280297Sjkim
4280297Sjkim/* Copyright (C) 1987, 1989, 1992 Free Software Foundation, Inc.
5193645Ssimon
6193645Ssimon   This file is part of the GNU Readline Library, a library for
7340704Sjkim   reading lines of text with interactive input and history editing.
8193645Ssimon
9193645Ssimon   The GNU Readline Library is free software; you can redistribute it
10193645Ssimon   and/or modify it under the terms of the GNU General Public License
11193645Ssimon   as published by the Free Software Foundation; either version 2, or
12193645Ssimon   (at your option) any later version.
13193645Ssimon
14280297Sjkim   The GNU Readline Library is distributed in the hope that it will be
15193645Ssimon   useful, but WITHOUT ANY WARRANTY; without even the implied warranty
16193645Ssimon   of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17193645Ssimon   GNU General Public License for more details.
18193645Ssimon
19193645Ssimon   The GNU General Public License is often shipped with GNU software, and
20193645Ssimon   is generally kept in a file called COPYING or LICENSE.  If you do not
21193645Ssimon   have a copy of the license, write to the Free Software Foundation,
22193645Ssimon   59 Temple Place, Suite 330, Boston, MA 02111 USA. */
23193645Ssimon#define READLINE_LIBRARY
24193645Ssimon
25193645Ssimon/* **************************************************************** */
26193645Ssimon/*								    */
27193645Ssimon/*			VI Emulation Mode			    */
28193645Ssimon/*								    */
29193645Ssimon/* **************************************************************** */
30193645Ssimon#include "rlconf.h"
31193645Ssimon
32193645Ssimon#if defined (VI_MODE)
33193645Ssimon
34193645Ssimon#if defined (HAVE_CONFIG_H)
35193645Ssimon#  include <config.h>
36193645Ssimon#endif
37193645Ssimon
38193645Ssimon#include <sys/types.h>
39193645Ssimon
40193645Ssimon#if defined (HAVE_STDLIB_H)
41193645Ssimon#  include <stdlib.h>
42193645Ssimon#else
43193645Ssimon#  include "ansi_stdlib.h"
44193645Ssimon#endif /* HAVE_STDLIB_H */
45193645Ssimon
46193645Ssimon#if defined (HAVE_UNISTD_H)
47193645Ssimon#  include <unistd.h>
48193645Ssimon#endif
49193645Ssimon
50193645Ssimon#include <stdio.h>
51193645Ssimon
52193645Ssimon/* Some standard library routines. */
53193645Ssimon#include "rldefs.h"
54193645Ssimon#include "readline.h"
55193645Ssimon#include "history.h"
56193645Ssimon
57193645Ssimon#include "rlprivate.h"
58193645Ssimon#include "xmalloc.h"
59193645Ssimon
60193645Ssimon#ifndef _rl_digit_p
61193645Ssimon#define _rl_digit_p(c)  ((c) >= '0' && (c) <= '9')
62193645Ssimon#endif
63193645Ssimon
64193645Ssimon#ifndef _rl_digit_value
65280297Sjkim#define _rl_digit_value(c) ((c) - '0')
66280297Sjkim#endif
67280297Sjkim
68193645Ssimon#ifndef member
69193645Ssimon#define member(c, s) ((c) ? (char *)strchr ((s), (c)) != (char *)NULL : 0)
70193645Ssimon#endif
71280297Sjkim
72280297Sjkim#ifndef isident
73280297Sjkim#define isident(c) ((_rl_pure_alphabetic (c) || _rl_digit_p (c) || c == '_'))
74280297Sjkim#endif
75280297Sjkim
76280297Sjkim#ifndef exchange
77280297Sjkim#define exchange(x, y) do {int temp = x; x = y; y = temp;} while (0)
78280297Sjkim#endif
79280297Sjkim
80280297Sjkim/* Non-zero means enter insertion mode. */
81280297Sjkimstatic int _rl_vi_doing_insert;
82280297Sjkim
83280297Sjkim/* Command keys which do movement for xxx_to commands. */
84280297Sjkimstatic const char *vi_motion = " hl^$0ftFt;,%wbeWBE|";
85280297Sjkim
86280297Sjkim/* Keymap used for vi replace characters.  Created dynamically since
87280297Sjkim   rarely used. */
88280297Sjkimstatic Keymap vi_replace_map;
89280297Sjkim
90193645Ssimon/* The number of characters inserted in the last replace operation. */
91280297Sjkimstatic int vi_replace_count;
92280297Sjkim
93280297Sjkim/* If non-zero, we have text inserted after a c[motion] command that put
94280297Sjkim   us implicitly into insert mode.  Some people want this text to be
95193645Ssimon   attached to the command so that it is `redoable' with `.'. */
96193645Ssimonstatic int vi_continued_command;
97193645Ssimonstatic char *vi_insert_buffer;
98280297Sjkimstatic int vi_insert_buffer_size;
99280297Sjkim
100280297Sjkimstatic int _rl_vi_last_command = 'i';	/* default `.' puts you in insert mode */
101280297Sjkimstatic int _rl_vi_last_repeat = 1;
102280297Sjkimstatic int _rl_vi_last_arg_sign = 1;
103193645Ssimonstatic int _rl_vi_last_motion;
104280297Sjkimstatic int _rl_vi_last_search_char;
105193645Ssimonstatic int _rl_vi_last_replacement;
106280297Sjkim
107280297Sjkimstatic int _rl_vi_last_key_before_insert;
108280297Sjkim
109193645Ssimonstatic int vi_redoing;
110280297Sjkim
111280297Sjkim/* Text modification commands.  These are the `redoable' commands. */
112280297Sjkimstatic const char *vi_textmod = "_*\\AaIiCcDdPpYyRrSsXx~";
113193645Ssimon
114280297Sjkim/* Arrays for the saved marks. */
115280297Sjkimstatic int vi_mark_chars[27];
116193645Ssimon
117280297Sjkimstatic int rl_digit_loop1 __P((void));
118193645Ssimon
119280297Sjkimvoid
120193645Ssimon_rl_vi_initialize_line ()
121280297Sjkim{
122193645Ssimon  register int i;
123280297Sjkim
124280297Sjkim  for (i = 0; i < sizeof (vi_mark_chars) / sizeof (int); i++)
125193645Ssimon    vi_mark_chars[i] = -1;
126280297Sjkim}
127280297Sjkim
128193645Ssimonvoid
129280297Sjkim_rl_vi_reset_last ()
130280297Sjkim{
131193645Ssimon  _rl_vi_last_command = 'i';
132280297Sjkim  _rl_vi_last_repeat = 1;
133193645Ssimon  _rl_vi_last_arg_sign = 1;
134280297Sjkim  _rl_vi_last_motion = 0;
135280297Sjkim}
136193645Ssimon
137280297Sjkimvoid
138280297Sjkim_rl_vi_set_last (key, repeat, sign)
139193645Ssimon     int key, repeat, sign;
140280297Sjkim{
141280297Sjkim  _rl_vi_last_command = key;
142193645Ssimon  _rl_vi_last_repeat = repeat;
143280297Sjkim  _rl_vi_last_arg_sign = sign;
144280297Sjkim}
145193645Ssimon
146280297Sjkim/* Is the command C a VI mode text modification command? */
147280297Sjkimint
148193645Ssimon_rl_vi_textmod_command (c)
149280297Sjkim     int c;
150280297Sjkim{
151193645Ssimon  return (member (c, vi_textmod));
152280297Sjkim}
153193645Ssimon
154280297Sjkimstatic void
155280297Sjkim_rl_vi_stuff_insert (count)
156193645Ssimon     int count;
157280297Sjkim{
158280297Sjkim  rl_begin_undo_group ();
159193645Ssimon  while (count--)
160280297Sjkim    rl_insert_text (vi_insert_buffer);
161193645Ssimon  rl_end_undo_group ();
162280297Sjkim}
163280297Sjkim
164280297Sjkim/* Bound to `.'.  Called from command mode, so we know that we have to
165280297Sjkim   redo a text modification command.  The default for _rl_vi_last_command
166280297Sjkim   puts you back into insert mode. */
167280297Sjkimint
168280297Sjkimrl_vi_redo (count, c)
169280297Sjkim     int count, c;
170280297Sjkim{
171280297Sjkim  if (!rl_explicit_arg)
172280297Sjkim    {
173280297Sjkim      rl_numeric_arg = _rl_vi_last_repeat;
174280297Sjkim      rl_arg_sign = _rl_vi_last_arg_sign;
175280297Sjkim    }
176280297Sjkim
177280297Sjkim  vi_redoing = 1;
178280297Sjkim  /* If we're redoing an insert with `i', stuff in the inserted text
179280297Sjkim     and do not go into insertion mode. */
180280297Sjkim  if (_rl_vi_last_command == 'i' && vi_insert_buffer && *vi_insert_buffer)
181280297Sjkim    {
182193645Ssimon      _rl_vi_stuff_insert (count);
183280297Sjkim      /* And back up point over the last character inserted. */
184193645Ssimon      if (rl_point > 0)
185280297Sjkim	rl_point--;
186193645Ssimon    }
187280297Sjkim  else
188193645Ssimon    _rl_dispatch (_rl_vi_last_command, _rl_keymap);
189280297Sjkim  vi_redoing = 0;
190193645Ssimon
191280297Sjkim  return (0);
192280297Sjkim}
193193645Ssimon
194280297Sjkim/* A placeholder for further expansion. */
195280297Sjkimint
196280297Sjkimrl_vi_undo (count, key)
197193645Ssimon     int count, key;
198193645Ssimon{
199193645Ssimon  return (rl_undo_command (count, key));
200280297Sjkim}
201280297Sjkim
202280297Sjkim/* Yank the nth arg from the previous line into this line at point. */
203280297Sjkimint
204280297Sjkimrl_vi_yank_arg (count, key)
205280297Sjkim     int count, key;
206280297Sjkim{
207280297Sjkim  /* Readline thinks that the first word on a line is the 0th, while vi
208280297Sjkim     thinks the first word on a line is the 1st.  Compensate. */
209280297Sjkim  if (rl_explicit_arg)
210280297Sjkim    rl_yank_nth_arg (count - 1, 0);
211280297Sjkim  else
212280297Sjkim    rl_yank_nth_arg ('$', 0);
213280297Sjkim
214280297Sjkim  return (0);
215280297Sjkim}
216291719Sjkim
217193645Ssimon/* With an argument, move back that many history lines, else move to the
218280297Sjkim   beginning of history. */
219280297Sjkimint
220325337Sjkimrl_vi_fetch_history (count, c)
221325337Sjkim     int count, c;
222193645Ssimon{
223280297Sjkim  int wanted;
224280297Sjkim
225291719Sjkim  /* Giving an argument of n means we want the nth command in the history
226340704Sjkim     file.  The command number is interpreted the same way that the bash
227280297Sjkim     `history' command does it -- that is, giving an argument count of 450
228340704Sjkim     to this command would get the command listed as number 450 in the
229340704Sjkim     output of `history'. */
230280297Sjkim  if (rl_explicit_arg)
231280297Sjkim    {
232280297Sjkim      wanted = history_base + where_history () - count;
233193645Ssimon      if (wanted <= 0)
234280297Sjkim        rl_beginning_of_history (0, 0);
235193645Ssimon      else
236280297Sjkim        rl_get_previous_history (wanted, c);
237280297Sjkim    }
238193645Ssimon  else
239280297Sjkim    rl_beginning_of_history (count, 0);
240193645Ssimon  return (0);
241291719Sjkim}
242291719Sjkim
243291719Sjkim/* Search again for the last thing searched for. */
244280297Sjkimint
245193645Ssimonrl_vi_search_again (count, key)
246280297Sjkim     int count, key;
247280297Sjkim{
248280297Sjkim  switch (key)
249280297Sjkim    {
250280297Sjkim    case 'n':
251280297Sjkim      rl_noninc_reverse_search_again (count, key);
252193645Ssimon      break;
253193645Ssimon
254193645Ssimon    case 'N':
255280297Sjkim      rl_noninc_forward_search_again (count, key);
256280297Sjkim      break;
257280297Sjkim    }
258280297Sjkim  return (0);
259280297Sjkim}
260193645Ssimon
261280297Sjkim/* Do a vi style search. */
262325337Sjkimint
263280297Sjkimrl_vi_search (count, key)
264325337Sjkim     int count, key;
265280297Sjkim{
266325337Sjkim  switch (key)
267325337Sjkim    {
268193645Ssimon    case '?':
269280297Sjkim      rl_noninc_forward_search (count, key);
270280297Sjkim      break;
271280297Sjkim
272280297Sjkim    case '/':
273280297Sjkim      rl_noninc_reverse_search (count, key);
274280297Sjkim      break;
275193645Ssimon
276280297Sjkim    default:
277193645Ssimon      rl_ding ();
278280297Sjkim      break;
279280297Sjkim    }
280193645Ssimon  return (0);
281280297Sjkim}
282193645Ssimon
283280297Sjkim/* Completion, from vi's point of view. */
284int
285rl_vi_complete (ignore, key)
286     int ignore, key;
287{
288  if ((rl_point < rl_end) && (!whitespace (rl_line_buffer[rl_point])))
289    {
290      if (!whitespace (rl_line_buffer[rl_point + 1]))
291	rl_vi_end_word (1, 'E');
292      rl_point++;
293    }
294
295  if (key == '*')
296    rl_complete_internal ('*');	/* Expansion and replacement. */
297  else if (key == '=')
298    rl_complete_internal ('?');	/* List possible completions. */
299  else if (key == '\\')
300    rl_complete_internal (TAB);	/* Standard Readline completion. */
301  else
302    rl_complete (0, key);
303
304  if (key == '*' || key == '\\')
305    {
306      _rl_vi_set_last (key, 1, rl_arg_sign);
307      rl_vi_insertion_mode (1, key);
308    }
309  return (0);
310}
311
312/* Tilde expansion for vi mode. */
313int
314rl_vi_tilde_expand (ignore, key)
315     int ignore, key;
316{
317  rl_tilde_expand (0, key);
318  _rl_vi_set_last (key, 1, rl_arg_sign);	/* XXX */
319  rl_vi_insertion_mode (1, key);
320  return (0);
321}
322
323/* Previous word in vi mode. */
324int
325rl_vi_prev_word (count, key)
326     int count, key;
327{
328  if (count < 0)
329    return (rl_vi_next_word (-count, key));
330
331  if (rl_point == 0)
332    {
333      rl_ding ();
334      return (0);
335    }
336
337  if (_rl_uppercase_p (key))
338    rl_vi_bWord (count, key);
339  else
340    rl_vi_bword (count, key);
341
342  return (0);
343}
344
345/* Next word in vi mode. */
346int
347rl_vi_next_word (count, key)
348     int count, key;
349{
350  if (count < 0)
351    return (rl_vi_prev_word (-count, key));
352
353  if (rl_point >= (rl_end - 1))
354    {
355      rl_ding ();
356      return (0);
357    }
358
359  if (_rl_uppercase_p (key))
360    rl_vi_fWord (count, key);
361  else
362    rl_vi_fword (count, key);
363  return (0);
364}
365
366/* Move to the end of the ?next? word. */
367int
368rl_vi_end_word (count, key)
369     int count, key;
370{
371  if (count < 0)
372    {
373      rl_ding ();
374      return -1;
375    }
376
377  if (_rl_uppercase_p (key))
378    rl_vi_eWord (count, key);
379  else
380    rl_vi_eword (count, key);
381  return (0);
382}
383
384/* Move forward a word the way that 'W' does. */
385int
386rl_vi_fWord (count, ignore)
387     int count, ignore;
388{
389  while (count-- && rl_point < (rl_end - 1))
390    {
391      /* Skip until whitespace. */
392      while (!whitespace (rl_line_buffer[rl_point]) && rl_point < rl_end)
393	rl_point++;
394
395      /* Now skip whitespace. */
396      while (whitespace (rl_line_buffer[rl_point]) && rl_point < rl_end)
397	rl_point++;
398    }
399  return (0);
400}
401
402int
403rl_vi_bWord (count, ignore)
404     int count, ignore;
405{
406  while (count-- && rl_point > 0)
407    {
408      /* If we are at the start of a word, move back to whitespace so
409	 we will go back to the start of the previous word. */
410      if (!whitespace (rl_line_buffer[rl_point]) &&
411	  whitespace (rl_line_buffer[rl_point - 1]))
412	rl_point--;
413
414      while (rl_point > 0 && whitespace (rl_line_buffer[rl_point]))
415	rl_point--;
416
417      if (rl_point > 0)
418	{
419	  while (--rl_point >= 0 && !whitespace (rl_line_buffer[rl_point]));
420	  rl_point++;
421	}
422    }
423  return (0);
424}
425
426int
427rl_vi_eWord (count, ignore)
428     int count, ignore;
429{
430  while (count-- && rl_point < (rl_end - 1))
431    {
432      if (!whitespace (rl_line_buffer[rl_point]))
433	rl_point++;
434
435      /* Move to the next non-whitespace character (to the start of the
436	 next word). */
437      while (++rl_point < rl_end && whitespace (rl_line_buffer[rl_point]));
438
439      if (rl_point && rl_point < rl_end)
440	{
441	  /* Skip whitespace. */
442	  while (rl_point < rl_end && whitespace (rl_line_buffer[rl_point]))
443	    rl_point++;
444
445	  /* Skip until whitespace. */
446	  while (rl_point < rl_end && !whitespace (rl_line_buffer[rl_point]))
447	    rl_point++;
448
449	  /* Move back to the last character of the word. */
450	  rl_point--;
451	}
452    }
453  return (0);
454}
455
456int
457rl_vi_fword (count, ignore)
458     int count, ignore;
459{
460  while (count-- && rl_point < (rl_end - 1))
461    {
462      /* Move to white space (really non-identifer). */
463      if (isident (rl_line_buffer[rl_point]))
464	{
465	  while (isident (rl_line_buffer[rl_point]) && rl_point < rl_end)
466	    rl_point++;
467	}
468      else /* if (!whitespace (rl_line_buffer[rl_point])) */
469	{
470	  while (!isident (rl_line_buffer[rl_point]) &&
471		 !whitespace (rl_line_buffer[rl_point]) && rl_point < rl_end)
472	    rl_point++;
473	}
474
475      /* Move past whitespace. */
476      while (whitespace (rl_line_buffer[rl_point]) && rl_point < rl_end)
477	rl_point++;
478    }
479  return (0);
480}
481
482int
483rl_vi_bword (count, ignore)
484     int count, ignore;
485{
486  while (count-- && rl_point > 0)
487    {
488      int last_is_ident;
489
490      /* If we are at the start of a word, move back to whitespace
491	 so we will go back to the start of the previous word. */
492      if (!whitespace (rl_line_buffer[rl_point]) &&
493	  whitespace (rl_line_buffer[rl_point - 1]))
494	rl_point--;
495
496      /* If this character and the previous character are `opposite', move
497	 back so we don't get messed up by the rl_point++ down there in
498	 the while loop.  Without this code, words like `l;' screw up the
499	 function. */
500      last_is_ident = isident (rl_line_buffer[rl_point - 1]);
501      if ((isident (rl_line_buffer[rl_point]) && !last_is_ident) ||
502	  (!isident (rl_line_buffer[rl_point]) && last_is_ident))
503	rl_point--;
504
505      while (rl_point > 0 && whitespace (rl_line_buffer[rl_point]))
506	rl_point--;
507
508      if (rl_point > 0)
509	{
510	  if (isident (rl_line_buffer[rl_point]))
511	    while (--rl_point >= 0 && isident (rl_line_buffer[rl_point]));
512	  else
513	    while (--rl_point >= 0 && !isident (rl_line_buffer[rl_point]) &&
514		   !whitespace (rl_line_buffer[rl_point]));
515	  rl_point++;
516	}
517    }
518  return (0);
519}
520
521int
522rl_vi_eword (count, ignore)
523     int count, ignore;
524{
525  while (count-- && rl_point < rl_end - 1)
526    {
527      if (!whitespace (rl_line_buffer[rl_point]))
528	rl_point++;
529
530      while (rl_point < rl_end && whitespace (rl_line_buffer[rl_point]))
531	rl_point++;
532
533      if (rl_point < rl_end)
534	{
535	  if (isident (rl_line_buffer[rl_point]))
536	    while (++rl_point < rl_end && isident (rl_line_buffer[rl_point]));
537	  else
538	    while (++rl_point < rl_end && !isident (rl_line_buffer[rl_point])
539		   && !whitespace (rl_line_buffer[rl_point]));
540	}
541      rl_point--;
542    }
543  return (0);
544}
545
546int
547rl_vi_insert_beg (count, key)
548     int count, key;
549{
550  rl_beg_of_line (1, key);
551  rl_vi_insertion_mode (1, key);
552  return (0);
553}
554
555int
556rl_vi_append_mode (count, key)
557     int count, key;
558{
559  if (rl_point < rl_end)
560    rl_point++;
561  rl_vi_insertion_mode (1, key);
562  return (0);
563}
564
565int
566rl_vi_append_eol (count, key)
567     int count, key;
568{
569  rl_end_of_line (1, key);
570  rl_vi_append_mode (1, key);
571  return (0);
572}
573
574/* What to do in the case of C-d. */
575int
576rl_vi_eof_maybe (count, c)
577     int count, c;
578{
579  return (rl_newline (1, '\n'));
580}
581
582/* Insertion mode stuff. */
583
584/* Switching from one mode to the other really just involves
585   switching keymaps. */
586int
587rl_vi_insertion_mode (count, key)
588     int count, key;
589{
590  _rl_keymap = vi_insertion_keymap;
591  _rl_vi_last_key_before_insert = key;
592  return (0);
593}
594
595static void
596_rl_vi_save_insert (up)
597      UNDO_LIST *up;
598{
599  int len, start, end;
600
601  if (up == 0)
602    {
603      if (vi_insert_buffer_size >= 1)
604	vi_insert_buffer[0] = '\0';
605      return;
606    }
607
608  start = up->start;
609  end = up->end;
610  len = end - start + 1;
611  if (len >= vi_insert_buffer_size)
612    {
613      vi_insert_buffer_size += (len + 32) - (len % 32);
614      vi_insert_buffer = xrealloc (vi_insert_buffer, vi_insert_buffer_size);
615    }
616  strncpy (vi_insert_buffer, rl_line_buffer + start, len - 1);
617  vi_insert_buffer[len-1] = '\0';
618}
619
620void
621_rl_vi_done_inserting ()
622{
623  if (_rl_vi_doing_insert)
624    {
625      rl_end_undo_group ();
626      /* Now, the text between rl_undo_list->next->start and
627	 rl_undo_list->next->end is what was inserted while in insert
628	 mode.  It gets copied to VI_INSERT_BUFFER because it depends
629	 on absolute indices into the line which may change (though they
630	 probably will not). */
631      _rl_vi_doing_insert = 0;
632      _rl_vi_save_insert (rl_undo_list->next);
633      vi_continued_command = 1;
634    }
635  else
636    {
637      if (_rl_vi_last_key_before_insert == 'i' && rl_undo_list)
638        _rl_vi_save_insert (rl_undo_list);
639      /* XXX - Other keys probably need to be checked. */
640      else if (_rl_vi_last_key_before_insert == 'C')
641	rl_end_undo_group ();
642      while (_rl_undo_group_level > 0)
643	rl_end_undo_group ();
644      vi_continued_command = 0;
645    }
646}
647
648int
649rl_vi_movement_mode (count, key)
650     int count, key;
651{
652  if (rl_point > 0)
653    rl_backward (1, key);
654
655  _rl_keymap = vi_movement_keymap;
656  _rl_vi_done_inserting ();
657  return (0);
658}
659
660int
661rl_vi_arg_digit (count, c)
662     int count, c;
663{
664  if (c == '0' && rl_numeric_arg == 1 && !rl_explicit_arg)
665    return (rl_beg_of_line (1, c));
666  else
667    return (rl_digit_argument (count, c));
668}
669
670int
671rl_vi_change_case (count, ignore)
672     int count, ignore;
673{
674  char c = 0;
675
676  /* Don't try this on an empty line. */
677  if (rl_point >= rl_end)
678    return (0);
679
680  while (count-- && rl_point < rl_end)
681    {
682      if (_rl_uppercase_p (rl_line_buffer[rl_point]))
683	c = _rl_to_lower (rl_line_buffer[rl_point]);
684      else if (_rl_lowercase_p (rl_line_buffer[rl_point]))
685	c = _rl_to_upper (rl_line_buffer[rl_point]);
686      else
687	{
688	  /* Just skip over characters neither upper nor lower case. */
689	  rl_forward (1, c);
690	  continue;
691	}
692
693      /* Vi is kind of strange here. */
694      if (c)
695	{
696	  rl_begin_undo_group ();
697	  rl_delete (1, c);
698	  rl_insert (1, c);
699	  rl_end_undo_group ();
700	  rl_vi_check ();
701        }
702      else
703	rl_forward (1, c);
704    }
705  return (0);
706}
707
708int
709rl_vi_put (count, key)
710     int count, key;
711{
712  if (!_rl_uppercase_p (key) && (rl_point + 1 <= rl_end))
713    rl_point++;
714
715  rl_yank (1, key);
716  rl_backward (1, key);
717  return (0);
718}
719
720int
721rl_vi_check ()
722{
723  if (rl_point && rl_point == rl_end)
724    rl_point--;
725  return (0);
726}
727
728int
729rl_vi_column (count, key)
730     int count, key;
731{
732  if (count > rl_end)
733    rl_end_of_line (1, key);
734  else
735    rl_point = count - 1;
736  return (0);
737}
738
739int
740rl_vi_domove (key, nextkey)
741     int key, *nextkey;
742{
743  int c, save;
744  int old_end;
745
746  rl_mark = rl_point;
747  RL_SETSTATE(RL_STATE_MOREINPUT);
748  c = rl_read_key ();
749  RL_UNSETSTATE(RL_STATE_MOREINPUT);
750  *nextkey = c;
751
752  if (!member (c, vi_motion))
753    {
754      if (_rl_digit_p (c))
755	{
756	  save = rl_numeric_arg;
757	  rl_numeric_arg = _rl_digit_value (c);
758	  rl_digit_loop1 ();
759	  rl_numeric_arg *= save;
760	  RL_SETSTATE(RL_STATE_MOREINPUT);
761	  c = rl_read_key ();	/* real command */
762	  RL_UNSETSTATE(RL_STATE_MOREINPUT);
763	  *nextkey = c;
764	}
765      else if (key == c && (key == 'd' || key == 'y' || key == 'c'))
766	{
767	  rl_mark = rl_end;
768	  rl_beg_of_line (1, c);
769	  _rl_vi_last_motion = c;
770	  return (0);
771	}
772      else
773	return (-1);
774    }
775
776  _rl_vi_last_motion = c;
777
778  /* Append a blank character temporarily so that the motion routines
779     work right at the end of the line. */
780  old_end = rl_end;
781  rl_line_buffer[rl_end++] = ' ';
782  rl_line_buffer[rl_end] = '\0';
783
784  _rl_dispatch (c, _rl_keymap);
785
786  /* Remove the blank that we added. */
787  rl_end = old_end;
788  rl_line_buffer[rl_end] = '\0';
789  if (rl_point > rl_end)
790    rl_point = rl_end;
791
792  /* No change in position means the command failed. */
793  if (rl_mark == rl_point)
794    return (-1);
795
796  /* rl_vi_f[wW]ord () leaves the cursor on the first character of the next
797     word.  If we are not at the end of the line, and we are on a
798     non-whitespace character, move back one (presumably to whitespace). */
799  if ((_rl_to_upper (c) == 'W') && rl_point < rl_end && rl_point > rl_mark &&
800      !whitespace (rl_line_buffer[rl_point]))
801    rl_point--;
802
803  /* If cw or cW, back up to the end of a word, so the behaviour of ce
804     or cE is the actual result.  Brute-force, no subtlety. */
805  if (key == 'c' && rl_point >= rl_mark && (_rl_to_upper (c) == 'W'))
806    {
807      /* Don't move farther back than where we started. */
808      while (rl_point > rl_mark && whitespace (rl_line_buffer[rl_point]))
809	rl_point--;
810
811      /* Posix.2 says that if cw or cW moves the cursor towards the end of
812	 the line, the character under the cursor should be deleted. */
813      if (rl_point == rl_mark)
814        rl_point++;
815      else
816	{
817	  /* Move past the end of the word so that the kill doesn't
818	     remove the last letter of the previous word.  Only do this
819	     if we are not at the end of the line. */
820	  if (rl_point >= 0 && rl_point < (rl_end - 1) && !whitespace (rl_line_buffer[rl_point]))
821	    rl_point++;
822	}
823    }
824
825  if (rl_mark < rl_point)
826    exchange (rl_point, rl_mark);
827
828  return (0);
829}
830
831/* A simplified loop for vi. Don't dispatch key at end.
832   Don't recognize minus sign?
833   Should this do rl_save_prompt/rl_restore_prompt? */
834static int
835rl_digit_loop1 ()
836{
837  int key, c;
838
839  RL_SETSTATE(RL_STATE_NUMERICARG);
840  while (1)
841    {
842      if (rl_numeric_arg > 1000000)
843	{
844	  rl_explicit_arg = rl_numeric_arg = 0;
845	  rl_ding ();
846	  rl_clear_message ();
847	  RL_UNSETSTATE(RL_STATE_NUMERICARG);
848	  return 1;
849	}
850      rl_message ("(arg: %d) ", rl_arg_sign * rl_numeric_arg, 0);
851      RL_SETSTATE(RL_STATE_MOREINPUT);
852      key = c = rl_read_key ();
853      RL_UNSETSTATE(RL_STATE_MOREINPUT);
854
855      if (_rl_keymap[c].type == ISFUNC &&
856	  _rl_keymap[c].function == rl_universal_argument)
857	{
858	  rl_numeric_arg *= 4;
859	  continue;
860	}
861
862      c = UNMETA (c);
863      if (_rl_digit_p (c))
864	{
865	  if (rl_explicit_arg)
866	    rl_numeric_arg = (rl_numeric_arg * 10) + _rl_digit_value (c);
867	  else
868	    rl_numeric_arg = _rl_digit_value (c);
869	  rl_explicit_arg = 1;
870	}
871      else
872	{
873	  rl_clear_message ();
874	  rl_stuff_char (key);
875	  break;
876	}
877    }
878
879  RL_UNSETSTATE(RL_STATE_NUMERICARG);
880  return (0);
881}
882
883int
884rl_vi_delete_to (count, key)
885     int count, key;
886{
887  int c;
888
889  if (_rl_uppercase_p (key))
890    rl_stuff_char ('$');
891  else if (vi_redoing)
892    rl_stuff_char (_rl_vi_last_motion);
893
894  if (rl_vi_domove (key, &c))
895    {
896      rl_ding ();
897      return -1;
898    }
899
900  /* These are the motion commands that do not require adjusting the
901     mark. */
902  if ((strchr (" l|h^0bB", c) == 0) && (rl_mark < rl_end))
903    rl_mark++;
904
905  rl_kill_text (rl_point, rl_mark);
906  return (0);
907}
908
909int
910rl_vi_change_to (count, key)
911     int count, key;
912{
913  int c, start_pos;
914
915  if (_rl_uppercase_p (key))
916    rl_stuff_char ('$');
917  else if (vi_redoing)
918    rl_stuff_char (_rl_vi_last_motion);
919
920  start_pos = rl_point;
921
922  if (rl_vi_domove (key, &c))
923    {
924      rl_ding ();
925      return -1;
926    }
927
928  /* These are the motion commands that do not require adjusting the
929     mark.  c[wW] are handled by special-case code in rl_vi_domove(),
930     and already leave the mark at the correct location. */
931  if ((strchr (" l|hwW^0bB", c) == 0) && (rl_mark < rl_end))
932    rl_mark++;
933
934  /* The cursor never moves with c[wW]. */
935  if ((_rl_to_upper (c) == 'W') && rl_point < start_pos)
936    rl_point = start_pos;
937
938  if (vi_redoing)
939    {
940      if (vi_insert_buffer && *vi_insert_buffer)
941	rl_begin_undo_group ();
942      rl_delete_text (rl_point, rl_mark);
943      if (vi_insert_buffer && *vi_insert_buffer)
944	{
945	  rl_insert_text (vi_insert_buffer);
946	  rl_end_undo_group ();
947	}
948    }
949  else
950    {
951      rl_begin_undo_group ();		/* to make the `u' command work */
952      rl_kill_text (rl_point, rl_mark);
953      /* `C' does not save the text inserted for undoing or redoing. */
954      if (_rl_uppercase_p (key) == 0)
955        _rl_vi_doing_insert = 1;
956      _rl_vi_set_last (key, count, rl_arg_sign);
957      rl_vi_insertion_mode (1, key);
958    }
959
960  return (0);
961}
962
963int
964rl_vi_yank_to (count, key)
965     int count, key;
966{
967  int c, save = rl_point;
968
969  if (_rl_uppercase_p (key))
970    rl_stuff_char ('$');
971
972  if (rl_vi_domove (key, &c))
973    {
974      rl_ding ();
975      return -1;
976    }
977
978  /* These are the motion commands that do not require adjusting the
979     mark. */
980  if ((strchr (" l|h^0%bB", c) == 0) && (rl_mark < rl_end))
981    rl_mark++;
982
983  rl_begin_undo_group ();
984  rl_kill_text (rl_point, rl_mark);
985  rl_end_undo_group ();
986  rl_do_undo ();
987  rl_point = save;
988
989  return (0);
990}
991
992int
993rl_vi_delete (count, key)
994     int count, key;
995{
996  int end;
997
998  if (rl_end == 0)
999    {
1000      rl_ding ();
1001      return -1;
1002    }
1003
1004  end = rl_point + count;
1005
1006  if (end >= rl_end)
1007    end = rl_end;
1008
1009  rl_kill_text (rl_point, end);
1010
1011  if (rl_point > 0 && rl_point == rl_end)
1012    rl_backward (1, key);
1013  return (0);
1014}
1015
1016int
1017rl_vi_back_to_indent (count, key)
1018     int count, key;
1019{
1020  rl_beg_of_line (1, key);
1021  while (rl_point < rl_end && whitespace (rl_line_buffer[rl_point]))
1022    rl_point++;
1023  return (0);
1024}
1025
1026int
1027rl_vi_first_print (count, key)
1028     int count, key;
1029{
1030  return (rl_vi_back_to_indent (1, key));
1031}
1032
1033int
1034rl_vi_char_search (count, key)
1035     int count, key;
1036{
1037  static char target;
1038  static int orig_dir, dir;
1039
1040  if (key == ';' || key == ',')
1041    dir = key == ';' ? orig_dir : -orig_dir;
1042  else
1043    {
1044      if (vi_redoing)
1045	target = _rl_vi_last_search_char;
1046      else
1047	{
1048	  RL_SETSTATE(RL_STATE_MOREINPUT);
1049	  _rl_vi_last_search_char = target = rl_read_key ();
1050	  RL_UNSETSTATE(RL_STATE_MOREINPUT);
1051	}
1052
1053      switch (key)
1054        {
1055        case 't':
1056          orig_dir = dir = FTO;
1057          break;
1058
1059        case 'T':
1060          orig_dir = dir = BTO;
1061          break;
1062
1063        case 'f':
1064          orig_dir = dir = FFIND;
1065          break;
1066
1067        case 'F':
1068          orig_dir = dir = BFIND;
1069          break;
1070        }
1071    }
1072
1073  return (_rl_char_search_internal (count, dir, target));
1074}
1075
1076/* Match brackets */
1077int
1078rl_vi_match (ignore, key)
1079     int ignore, key;
1080{
1081  int count = 1, brack, pos;
1082
1083  pos = rl_point;
1084  if ((brack = rl_vi_bracktype (rl_line_buffer[rl_point])) == 0)
1085    {
1086      while ((brack = rl_vi_bracktype (rl_line_buffer[rl_point])) == 0 &&
1087	     rl_point < rl_end - 1)
1088	rl_forward (1, key);
1089
1090      if (brack <= 0)
1091	{
1092	  rl_point = pos;
1093	  rl_ding ();
1094	  return -1;
1095	}
1096    }
1097
1098  pos = rl_point;
1099
1100  if (brack < 0)
1101    {
1102      while (count)
1103	{
1104	  if (--pos >= 0)
1105	    {
1106	      int b = rl_vi_bracktype (rl_line_buffer[pos]);
1107	      if (b == -brack)
1108		count--;
1109	      else if (b == brack)
1110		count++;
1111	    }
1112	  else
1113	    {
1114	      rl_ding ();
1115	      return -1;
1116	    }
1117	}
1118    }
1119  else
1120    {			/* brack > 0 */
1121      while (count)
1122	{
1123	  if (++pos < rl_end)
1124	    {
1125	      int b = rl_vi_bracktype (rl_line_buffer[pos]);
1126	      if (b == -brack)
1127		count--;
1128	      else if (b == brack)
1129		count++;
1130	    }
1131	  else
1132	    {
1133	      rl_ding ();
1134	      return -1;
1135	    }
1136	}
1137    }
1138  rl_point = pos;
1139  return (0);
1140}
1141
1142int
1143rl_vi_bracktype (c)
1144     int c;
1145{
1146  switch (c)
1147    {
1148    case '(': return  1;
1149    case ')': return -1;
1150    case '[': return  2;
1151    case ']': return -2;
1152    case '{': return  3;
1153    case '}': return -3;
1154    default:  return  0;
1155    }
1156}
1157
1158int
1159rl_vi_change_char (count, key)
1160     int count, key;
1161{
1162  int c;
1163
1164  if (vi_redoing)
1165    c = _rl_vi_last_replacement;
1166  else
1167    {
1168      RL_SETSTATE(RL_STATE_MOREINPUT);
1169      _rl_vi_last_replacement = c = rl_read_key ();
1170      RL_UNSETSTATE(RL_STATE_MOREINPUT);
1171    }
1172
1173  if (c == '\033' || c == CTRL ('C'))
1174    return -1;
1175
1176  while (count-- && rl_point < rl_end)
1177    {
1178      rl_begin_undo_group ();
1179
1180      rl_delete (1, c);
1181      rl_insert (1, c);
1182      if (count == 0)
1183	rl_backward (1, c);
1184
1185      rl_end_undo_group ();
1186    }
1187  return (0);
1188}
1189
1190int
1191rl_vi_subst (count, key)
1192     int count, key;
1193{
1194  rl_begin_undo_group ();
1195
1196  if (_rl_uppercase_p (key))
1197    {
1198      rl_beg_of_line (1, key);
1199      rl_kill_line (1, key);
1200    }
1201  else
1202    rl_delete_text (rl_point, rl_point+count);
1203
1204  rl_end_undo_group ();
1205
1206  _rl_vi_set_last (key, count, rl_arg_sign);
1207
1208  if (vi_redoing)
1209    {
1210      int o = _rl_doing_an_undo;
1211
1212      _rl_doing_an_undo = 1;
1213      if (vi_insert_buffer && *vi_insert_buffer)
1214	rl_insert_text (vi_insert_buffer);
1215      _rl_doing_an_undo = o;
1216    }
1217  else
1218    {
1219      rl_begin_undo_group ();
1220      _rl_vi_doing_insert = 1;
1221      rl_vi_insertion_mode (1, key);
1222    }
1223
1224  return (0);
1225}
1226
1227int
1228rl_vi_overstrike (count, key)
1229     int count, key;
1230{
1231  int i;
1232
1233  if (_rl_vi_doing_insert == 0)
1234    {
1235      _rl_vi_doing_insert = 1;
1236      rl_begin_undo_group ();
1237    }
1238
1239  for (i = 0; i < count; i++)
1240    {
1241      vi_replace_count++;
1242      rl_begin_undo_group ();
1243
1244      if (rl_point < rl_end)
1245	{
1246	  rl_delete (1, key);
1247	  rl_insert (1, key);
1248	}
1249      else
1250	rl_insert (1, key);
1251
1252      rl_end_undo_group ();
1253    }
1254  return (0);
1255}
1256
1257int
1258rl_vi_overstrike_delete (count, key)
1259     int count, key;
1260{
1261  int i, s;
1262
1263  for (i = 0; i < count; i++)
1264    {
1265      if (vi_replace_count == 0)
1266	{
1267	  rl_ding ();
1268	  break;
1269	}
1270      s = rl_point;
1271
1272      if (rl_do_undo ())
1273	vi_replace_count--;
1274
1275      if (rl_point == s)
1276	rl_backward (1, key);
1277    }
1278
1279  if (vi_replace_count == 0 && _rl_vi_doing_insert)
1280    {
1281      rl_end_undo_group ();
1282      rl_do_undo ();
1283      _rl_vi_doing_insert = 0;
1284    }
1285  return (0);
1286}
1287
1288int
1289rl_vi_replace (count, key)
1290     int count, key;
1291{
1292  int i;
1293
1294  vi_replace_count = 0;
1295
1296  if (!vi_replace_map)
1297    {
1298      vi_replace_map = rl_make_bare_keymap ();
1299
1300      for (i = ' '; i < KEYMAP_SIZE; i++)
1301	vi_replace_map[i].function = rl_vi_overstrike;
1302
1303      vi_replace_map[RUBOUT].function = rl_vi_overstrike_delete;
1304      vi_replace_map[ESC].function = rl_vi_movement_mode;
1305      vi_replace_map[RETURN].function = rl_newline;
1306      vi_replace_map[NEWLINE].function = rl_newline;
1307
1308      /* If the normal vi insertion keymap has ^H bound to erase, do the
1309         same here.  Probably should remove the assignment to RUBOUT up
1310         there, but I don't think it will make a difference in real life. */
1311      if (vi_insertion_keymap[CTRL ('H')].type == ISFUNC &&
1312	  vi_insertion_keymap[CTRL ('H')].function == rl_rubout)
1313	vi_replace_map[CTRL ('H')].function = rl_vi_overstrike_delete;
1314
1315    }
1316  _rl_keymap = vi_replace_map;
1317  return (0);
1318}
1319
1320#if 0
1321/* Try to complete the word we are standing on or the word that ends with
1322   the previous character.  A space matches everything.  Word delimiters are
1323   space and ;. */
1324int
1325rl_vi_possible_completions()
1326{
1327  int save_pos = rl_point;
1328
1329  if (rl_line_buffer[rl_point] != ' ' && rl_line_buffer[rl_point] != ';')
1330    {
1331      while (rl_point < rl_end && rl_line_buffer[rl_point] != ' ' &&
1332	     rl_line_buffer[rl_point] != ';')
1333	rl_point++;
1334    }
1335  else if (rl_line_buffer[rl_point - 1] == ';')
1336    {
1337      rl_ding ();
1338      return (0);
1339    }
1340
1341  rl_possible_completions ();
1342  rl_point = save_pos;
1343
1344  return (0);
1345}
1346#endif
1347
1348/* Functions to save and restore marks. */
1349int
1350rl_vi_set_mark (count, key)
1351     int count, key;
1352{
1353  int ch;
1354
1355  RL_SETSTATE(RL_STATE_MOREINPUT);
1356  ch = rl_read_key ();
1357  RL_UNSETSTATE(RL_STATE_MOREINPUT);
1358
1359  if (_rl_lowercase_p (ch) == 0)
1360    {
1361      rl_ding ();
1362      return -1;
1363    }
1364  ch -= 'a';
1365  vi_mark_chars[ch] = rl_point;
1366  return 0;
1367}
1368
1369int
1370rl_vi_goto_mark (count, key)
1371     int count, key;
1372{
1373  int ch;
1374
1375  RL_SETSTATE(RL_STATE_MOREINPUT);
1376  ch = rl_read_key ();
1377  RL_UNSETSTATE(RL_STATE_MOREINPUT);
1378
1379  if (ch == '`')
1380    {
1381      rl_point = rl_mark;
1382      return 0;
1383    }
1384  else if (_rl_lowercase_p (ch) == 0)
1385    {
1386      rl_ding ();
1387      return -1;
1388    }
1389
1390  ch -= 'a';
1391  if (vi_mark_chars[ch] == -1)
1392    {
1393      rl_ding ();
1394      return -1;
1395    }
1396  rl_point = vi_mark_chars[ch];
1397  return 0;
1398}
1399
1400#endif /* VI_MODE */
1401