Release 2.33.1
[external/binutils.git] / readline / vi_mode.c
1 /* vi_mode.c -- A vi emulation mode for Bash.
2    Derived from code written by Jeff Sparkes (jsparkes@bnr.ca).  */
3
4 /* Copyright (C) 1987-2018 Free Software Foundation, Inc.
5
6    This file is part of the GNU Readline Library (Readline), a library
7    for reading lines of text with interactive input and history editing.      
8
9    Readline is free software: you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation, either version 3 of the License, or
12    (at your option) any later version.
13
14    Readline is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18
19    You should have received a copy of the GNU General Public License
20    along with Readline.  If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 #define READLINE_LIBRARY
24
25 /* **************************************************************** */
26 /*                                                                  */
27 /*                      VI Emulation Mode                           */
28 /*                                                                  */
29 /* **************************************************************** */
30 #include "rlconf.h"
31
32 #if defined (VI_MODE)
33
34 #if defined (HAVE_CONFIG_H)
35 #  include <config.h>
36 #endif
37
38 #include <sys/types.h>
39
40 #if defined (HAVE_STDLIB_H)
41 #  include <stdlib.h>
42 #else
43 #  include "ansi_stdlib.h"
44 #endif /* HAVE_STDLIB_H */
45
46 #if defined (HAVE_UNISTD_H)
47 #  include <unistd.h>
48 #endif
49
50 #include <stdio.h>
51
52 /* Some standard library routines. */
53 #include "rldefs.h"
54 #include "rlmbutil.h"
55
56 #include "readline.h"
57 #include "history.h"
58
59 #include "rlprivate.h"
60 #include "xmalloc.h"
61
62 #ifndef member
63 #define member(c, s) ((c) ? (char *)strchr ((s), (c)) != (char *)NULL : 0)
64 #endif
65
66 /* Increment START to the next character in RL_LINE_BUFFER, handling multibyte chars */
67 #if defined (HANDLE_MULTIBYTE)
68 #define INCREMENT_POS(start) \
69   do { \
70         if (MB_CUR_MAX == 1 || rl_byte_oriented) \
71           start++; \
72         else \
73           start = _rl_find_next_mbchar (rl_line_buffer, start, 1, MB_FIND_ANY); \
74   } while (0)
75 #else /* !HANDLE_MULTIBYTE */
76 #define INCREMENT_POS(start)    (start)++
77 #endif /* !HANDLE_MULTIBYTE */
78
79 /* This is global so other parts of the code can check whether the last
80    command was a text modification command. */
81 int _rl_vi_last_command = 'i';  /* default `.' puts you in insert mode */
82
83 _rl_vimotion_cxt *_rl_vimvcxt = 0;
84
85 /* Non-zero indicates we are redoing a vi-mode command with `.' */
86 int _rl_vi_redoing;
87
88 /* Non-zero means enter insertion mode. */
89 static int _rl_vi_doing_insert;
90
91 /* Command keys which do movement for xxx_to commands. */
92 static const char * const vi_motion = " hl^$0ftFT;,%wbeWBE|`";
93
94 /* Keymap used for vi replace characters.  Created dynamically since
95    rarely used. */
96 static Keymap vi_replace_map;
97
98 /* The number of characters inserted in the last replace operation. */
99 static int vi_replace_count;
100
101 /* If non-zero, we have text inserted after a c[motion] command that put
102    us implicitly into insert mode.  Some people want this text to be
103    attached to the command so that it is `redoable' with `.'. */
104 static int vi_continued_command;
105 static char *vi_insert_buffer;
106 static int vi_insert_buffer_size;
107
108 static int _rl_vi_last_repeat = 1;
109 static int _rl_vi_last_arg_sign = 1;
110 static int _rl_vi_last_motion;
111 #if defined (HANDLE_MULTIBYTE)
112 static char _rl_vi_last_search_mbchar[MB_LEN_MAX];
113 static int _rl_vi_last_search_mblen;
114 #else
115 static int _rl_vi_last_search_char;
116 #endif
117 static char _rl_vi_last_replacement[MB_LEN_MAX+1];      /* reserve for trailing NULL */
118
119 static int _rl_vi_last_key_before_insert;
120
121 /* Text modification commands.  These are the `redoable' commands. */
122 static const char * const vi_textmod = "_*\\AaIiCcDdPpYyRrSsXx~";
123
124 /* Arrays for the saved marks. */
125 static int vi_mark_chars['z' - 'a' + 1];
126
127 static void _rl_vi_replace_insert PARAMS((int));
128 static void _rl_vi_save_replace PARAMS((void));
129 static void _rl_vi_stuff_insert PARAMS((int));
130 static void _rl_vi_save_insert PARAMS((UNDO_LIST *));
131
132 static void vi_save_insert_buffer PARAMS ((int, int));
133
134 static inline void _rl_vi_backup PARAMS((void));
135
136 static int _rl_vi_arg_dispatch PARAMS((int));
137 static int rl_digit_loop1 PARAMS((void));
138
139 static int _rl_vi_set_mark PARAMS((void));
140 static int _rl_vi_goto_mark PARAMS((void));
141
142 static inline int _rl_vi_advance_point PARAMS((void));
143 static inline int _rl_vi_backup_point PARAMS((void));
144
145 static void _rl_vi_append_forward PARAMS((int));
146
147 static int _rl_vi_callback_getchar PARAMS((char *, int));
148
149 #if defined (READLINE_CALLBACKS)
150 static int _rl_vi_callback_set_mark PARAMS((_rl_callback_generic_arg *));
151 static int _rl_vi_callback_goto_mark PARAMS((_rl_callback_generic_arg *));
152 static int _rl_vi_callback_change_char PARAMS((_rl_callback_generic_arg *));
153 static int _rl_vi_callback_char_search PARAMS((_rl_callback_generic_arg *));
154 #endif
155
156 static int rl_domove_read_callback PARAMS((_rl_vimotion_cxt *));
157 static int rl_domove_motion_callback PARAMS((_rl_vimotion_cxt *));
158 static int rl_vi_domove_getchar PARAMS((_rl_vimotion_cxt *));
159
160 static int vi_change_dispatch PARAMS((_rl_vimotion_cxt *));
161 static int vi_delete_dispatch PARAMS((_rl_vimotion_cxt *));
162 static int vi_yank_dispatch PARAMS((_rl_vimotion_cxt *));
163
164 static int vidomove_dispatch PARAMS((_rl_vimotion_cxt *));
165
166 void
167 _rl_vi_initialize_line (void)
168 {
169   register int i, n;
170
171   n = sizeof (vi_mark_chars) / sizeof (vi_mark_chars[0]);
172   for (i = 0; i < n; i++)
173     vi_mark_chars[i] = -1;
174
175   RL_UNSETSTATE(RL_STATE_VICMDONCE);
176 }
177
178 void
179 _rl_vi_reset_last (void)
180 {
181   _rl_vi_last_command = 'i';
182   _rl_vi_last_repeat = 1;
183   _rl_vi_last_arg_sign = 1;
184   _rl_vi_last_motion = 0;
185 }
186
187 void
188 _rl_vi_set_last (int key, int repeat, int sign)
189 {
190   _rl_vi_last_command = key;
191   _rl_vi_last_repeat = repeat;
192   _rl_vi_last_arg_sign = sign;
193 }
194
195 /* A convenience function that calls _rl_vi_set_last to save the last command
196    information and enters insertion mode. */
197 void
198 rl_vi_start_inserting (int key, int repeat, int sign)
199 {
200   _rl_vi_set_last (key, repeat, sign);
201   rl_begin_undo_group ();               /* ensure inserts aren't concatenated */
202   rl_vi_insertion_mode (1, key);
203 }
204
205 /* Is the command C a VI mode text modification command? */
206 int
207 _rl_vi_textmod_command (int c)
208 {
209   return (member (c, vi_textmod));
210 }
211
212 int
213 _rl_vi_motion_command (int c)
214 {
215   return (member (c, vi_motion));
216 }
217
218 static void
219 _rl_vi_replace_insert (int count)
220 {
221   int nchars;
222
223   nchars = strlen (vi_insert_buffer);
224
225   rl_begin_undo_group ();
226   while (count--)
227     /* nchars-1 to compensate for _rl_replace_text using `end+1' in call
228        to rl_delete_text */
229     _rl_replace_text (vi_insert_buffer, rl_point, rl_point+nchars-1);
230   rl_end_undo_group ();
231 }
232
233 static void
234 _rl_vi_stuff_insert (int count)
235 {
236   rl_begin_undo_group ();
237   while (count--)
238     rl_insert_text (vi_insert_buffer);
239   rl_end_undo_group ();
240 }
241
242 /* Bound to `.'.  Called from command mode, so we know that we have to
243    redo a text modification command.  The default for _rl_vi_last_command
244    puts you back into insert mode. */
245 int
246 rl_vi_redo (int count, int c)
247 {
248   int r;
249
250   if (rl_explicit_arg == 0)
251     {
252       rl_numeric_arg = _rl_vi_last_repeat;
253       rl_arg_sign = _rl_vi_last_arg_sign;
254     }
255
256   r = 0;
257   _rl_vi_redoing = 1;
258   /* If we're redoing an insert with `i', stuff in the inserted text
259      and do not go into insertion mode. */
260   if (_rl_vi_last_command == 'i' && vi_insert_buffer && *vi_insert_buffer)
261     {
262       _rl_vi_stuff_insert (count);
263       /* And back up point over the last character inserted. */
264       if (rl_point > 0)
265         _rl_vi_backup ();
266     }
267   else if (_rl_vi_last_command == 'R' && vi_insert_buffer && *vi_insert_buffer)
268     {
269       _rl_vi_replace_insert (count);
270       /* And back up point over the last character inserted. */
271       if (rl_point > 0)
272         _rl_vi_backup ();
273     }
274   /* Ditto for redoing an insert with `I', but move to the beginning of the
275      line like the `I' command does. */
276   else if (_rl_vi_last_command == 'I' && vi_insert_buffer && *vi_insert_buffer)
277     {
278       rl_beg_of_line (1, 'I');
279       _rl_vi_stuff_insert (count);
280       if (rl_point > 0)
281         _rl_vi_backup ();
282     }
283   /* Ditto for redoing an insert with `a', but move forward a character first
284      like the `a' command does. */
285   else if (_rl_vi_last_command == 'a' && vi_insert_buffer && *vi_insert_buffer)
286     {
287       _rl_vi_append_forward ('a');
288       _rl_vi_stuff_insert (count);
289       if (rl_point > 0)
290         _rl_vi_backup ();
291     }
292   /* Ditto for redoing an insert with `A', but move to the end of the line
293      like the `A' command does. */
294   else if (_rl_vi_last_command == 'A' && vi_insert_buffer && *vi_insert_buffer)
295     {
296       rl_end_of_line (1, 'A');
297       _rl_vi_stuff_insert (count);
298       if (rl_point > 0)
299         _rl_vi_backup ();
300     }
301   else
302     r = _rl_dispatch (_rl_vi_last_command, _rl_keymap);
303
304   _rl_vi_redoing = 0;
305
306   return (r);
307 }
308
309 /* A placeholder for further expansion. */
310 int
311 rl_vi_undo (int count, int key)
312 {
313   return (rl_undo_command (count, key));
314 }
315     
316 /* Yank the nth arg from the previous line into this line at point. */
317 int
318 rl_vi_yank_arg (int count, int key)
319 {
320   /* Readline thinks that the first word on a line is the 0th, while vi
321      thinks the first word on a line is the 1st.  Compensate. */
322   if (rl_explicit_arg)
323     rl_yank_nth_arg (count - 1, 0);
324   else
325     rl_yank_nth_arg ('$', 0);
326
327   return (0);
328 }
329
330 /* With an argument, move back that many history lines, else move to the
331    beginning of history. */
332 int
333 rl_vi_fetch_history (int count, int c)
334 {
335   int wanted;
336
337   /* Giving an argument of n means we want the nth command in the history
338      file.  The command number is interpreted the same way that the bash
339      `history' command does it -- that is, giving an argument count of 450
340      to this command would get the command listed as number 450 in the
341      output of `history'. */
342   if (rl_explicit_arg)
343     {
344       wanted = history_base + where_history () - count;
345       if (wanted <= 0)
346         rl_beginning_of_history (0, 0);
347       else
348         rl_get_previous_history (wanted, c);
349     }
350   else
351     rl_beginning_of_history (count, 0);
352   return (0);
353 }
354
355 /* Search again for the last thing searched for. */
356 int
357 rl_vi_search_again (int count, int key)
358 {
359   switch (key)
360     {
361     case 'n':
362       rl_noninc_reverse_search_again (count, key);
363       break;
364
365     case 'N':
366       rl_noninc_forward_search_again (count, key);
367       break;
368     }
369   return (0);
370 }
371
372 /* Do a vi style search. */
373 int
374 rl_vi_search (int count, int key)
375 {
376   switch (key)
377     {
378     case '?':
379       _rl_free_saved_history_line ();
380       rl_noninc_forward_search (count, key);
381       break;
382
383     case '/':
384       _rl_free_saved_history_line ();
385       rl_noninc_reverse_search (count, key);
386       break;
387
388     default:
389       rl_ding ();
390       break;
391     }
392   return (0);
393 }
394
395 /* Completion, from vi's point of view. */
396 int
397 rl_vi_complete (int ignore, int key)
398 {
399   if ((rl_point < rl_end) && (!whitespace (rl_line_buffer[rl_point])))
400     {
401       if (!whitespace (rl_line_buffer[rl_point + 1]))
402         rl_vi_end_word (1, 'E');
403       _rl_vi_advance_point ();
404     }
405
406   if (key == '*')
407     rl_complete_internal ('*'); /* Expansion and replacement. */
408   else if (key == '=')
409     rl_complete_internal ('?'); /* List possible completions. */
410   else if (key == '\\')
411     rl_complete_internal (TAB); /* Standard Readline completion. */
412   else
413     rl_complete (0, key);
414
415   if (key == '*' || key == '\\')
416     rl_vi_start_inserting (key, 1, rl_arg_sign);
417
418   return (0);
419 }
420
421 /* Tilde expansion for vi mode. */
422 int
423 rl_vi_tilde_expand (int ignore, int key)
424 {
425   rl_tilde_expand (0, key);
426   rl_vi_start_inserting (key, 1, rl_arg_sign);
427   return (0);
428 }
429
430 /* Previous word in vi mode. */
431 int
432 rl_vi_prev_word (int count, int key)
433 {
434   if (count < 0)
435     return (rl_vi_next_word (-count, key));
436
437   if (rl_point == 0)
438     {
439       rl_ding ();
440       return (0);
441     }
442
443   if (_rl_uppercase_p (key))
444     rl_vi_bWord (count, key);
445   else
446     rl_vi_bword (count, key);
447
448   return (0);
449 }
450
451 /* Next word in vi mode. */
452 int
453 rl_vi_next_word (int count, int key)
454 {
455   if (count < 0)
456     return (rl_vi_prev_word (-count, key));
457
458   if (rl_point >= (rl_end - 1))
459     {
460       rl_ding ();
461       return (0);
462     }
463
464   if (_rl_uppercase_p (key))
465     rl_vi_fWord (count, key);
466   else
467     rl_vi_fword (count, key);
468   return (0);
469 }
470
471 static inline int
472 _rl_vi_advance_point (void)
473 {
474   int point;
475
476   point = rl_point;
477   if (rl_point < rl_end)
478 #if defined (HANDLE_MULTIBYTE)
479     {
480       if (MB_CUR_MAX == 1 || rl_byte_oriented)
481         rl_point++;
482       else
483         {
484           point = rl_point;
485           rl_point = _rl_forward_char_internal (1);
486           if (point == rl_point || rl_point > rl_end)
487             rl_point = rl_end;
488         }
489     }
490 #else
491     rl_point++;
492 #endif
493
494   return point;
495 }
496
497 /* Move the cursor back one character. */
498 static inline void
499 _rl_vi_backup (void)
500 {
501   if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
502     rl_point = _rl_find_prev_mbchar (rl_line_buffer, rl_point, MB_FIND_NONZERO);
503   else
504     rl_point--;
505 }
506
507 /* Move the point back one character, returning the starting value and not
508    doing anything at the beginning of the line */
509 static inline int
510 _rl_vi_backup_point (void)
511 {
512   int point;
513
514   point = rl_point;
515   if (rl_point > 0)
516 #if defined (HANDLE_MULTIBYTE)
517     {
518       if (MB_CUR_MAX == 1 || rl_byte_oriented)
519         rl_point--;
520       else
521         {
522           point = rl_point;
523           rl_point = _rl_backward_char_internal (1);
524           if (rl_point < 0)
525             rl_point = 0;               /* XXX - not really necessary */
526         }
527     }
528 #else
529     rl_point--;
530 #endif
531   return point;
532 }
533
534 /* Move to the end of the ?next? word. */
535 int
536 rl_vi_end_word (int count, int key)
537 {
538   if (count < 0)
539     {
540       rl_ding ();
541       return 1;
542     }
543
544   if (_rl_uppercase_p (key))
545     rl_vi_eWord (count, key);
546   else
547     rl_vi_eword (count, key);
548   return (0);
549 }
550
551 /* Move forward a word the way that 'W' does. */
552 int
553 rl_vi_fWord (int count, int ignore)
554 {
555   while (count-- && rl_point < (rl_end - 1))
556     {
557       /* Skip until whitespace. */
558       while (!whitespace (rl_line_buffer[rl_point]) && rl_point < rl_end)
559         _rl_vi_advance_point ();
560
561       /* Now skip whitespace. */
562       while (whitespace (rl_line_buffer[rl_point]) && rl_point < rl_end)
563         _rl_vi_advance_point ();
564     }
565   return (0);
566 }
567
568 int
569 rl_vi_bWord (int count, int ignore)
570 {
571   while (count-- && rl_point > 0)
572     {
573       /* If we are at the start of a word, move back to whitespace so
574          we will go back to the start of the previous word. */
575       if (!whitespace (rl_line_buffer[rl_point]) &&
576           whitespace (rl_line_buffer[rl_point - 1]))
577         rl_point--;
578
579       while (rl_point > 0 && whitespace (rl_line_buffer[rl_point]))
580         _rl_vi_backup_point ();
581
582       if (rl_point > 0)
583         {
584           do
585             _rl_vi_backup_point ();
586           while (rl_point > 0 && !whitespace (rl_line_buffer[rl_point]));
587           if (rl_point > 0)     /* hit whitespace */
588             rl_point++;         
589
590           if (rl_point < 0)
591             rl_point = 0;
592         }
593     }
594   return (0);
595 }
596
597 int
598 rl_vi_eWord (int count, int ignore)
599 {
600   int opoint;
601
602   while (count-- && rl_point < (rl_end - 1))
603     {
604       if (whitespace (rl_line_buffer[rl_point]) == 0)
605         _rl_vi_advance_point ();
606
607       /* Move to the next non-whitespace character (to the start of the
608          next word). */
609       while (rl_point < rl_end && whitespace (rl_line_buffer[rl_point]))
610         _rl_vi_advance_point ();
611
612       if (rl_point && rl_point < rl_end)
613         {
614           opoint = rl_point;
615
616           /* Skip whitespace. */
617           while (rl_point < rl_end && whitespace (rl_line_buffer[rl_point]))
618             opoint = _rl_vi_advance_point ();   /* XXX - why? */
619
620           /* Skip until whitespace. */
621           while (rl_point < rl_end && !whitespace (rl_line_buffer[rl_point]))
622             opoint = _rl_vi_advance_point ();
623
624           /* Move back to the last character of the word. */
625           rl_point = opoint;
626         }
627     }
628   return (0);
629 }
630
631 int
632 rl_vi_fword (int count, int ignore)
633 {
634   int opoint;
635
636   while (count-- && rl_point < (rl_end - 1))
637     {
638       /* Move to white space (really non-identifer). */
639       if (_rl_isident (rl_line_buffer[rl_point]))
640         {
641           while (_rl_isident (rl_line_buffer[rl_point]) && rl_point < rl_end)
642             _rl_vi_advance_point ();
643         }
644       else /* if (!whitespace (rl_line_buffer[rl_point])) */
645         {
646           while (!_rl_isident (rl_line_buffer[rl_point]) &&
647                  !whitespace (rl_line_buffer[rl_point]) && rl_point < rl_end)
648             _rl_vi_advance_point ();
649         }
650
651       opoint = rl_point;
652
653       /* Move past whitespace. */
654       while (whitespace (rl_line_buffer[rl_point]) && rl_point < rl_end)
655         opoint = _rl_vi_advance_point ();
656     }
657   return (0);
658 }
659
660 int
661 rl_vi_bword (int count, int ignore)
662 {
663   int opoint;
664
665   while (count-- && rl_point > 0)
666     {
667       int prev_is_ident, cur_is_ident;
668
669       /* If we are at the start of a word, move back to whitespace
670          so we will go back to the start of the previous word. */
671       if (!whitespace (rl_line_buffer[rl_point]) &&
672           whitespace (rl_line_buffer[rl_point - 1]))
673         if (--rl_point == 0)
674           break;
675
676       /* If this character and the previous character are `opposite', move
677          back so we don't get messed up by the rl_point++ down there in
678          the while loop.  Without this code, words like `l;' screw up the
679          function. */
680       cur_is_ident = _rl_isident (rl_line_buffer[rl_point]);
681       opoint = _rl_vi_backup_point ();
682       prev_is_ident = _rl_isident (rl_line_buffer[rl_point]);
683       if ((cur_is_ident && !prev_is_ident) || (!cur_is_ident && prev_is_ident))
684         ;       /* leave point alone, we backed it up one character */
685       else
686         rl_point = opoint;
687
688       while (rl_point > 0 && whitespace (rl_line_buffer[rl_point]))
689         _rl_vi_backup_point ();
690
691       if (rl_point > 0)
692         {
693           opoint = rl_point;
694           if (_rl_isident (rl_line_buffer[rl_point]))
695             do
696               opoint = _rl_vi_backup_point ();
697             while (rl_point > 0 && _rl_isident (rl_line_buffer[rl_point]));
698           else
699             do
700               opoint = _rl_vi_backup_point ();
701             while (rl_point > 0 && !_rl_isident (rl_line_buffer[rl_point]) &&
702                    !whitespace (rl_line_buffer[rl_point]));
703
704           if (rl_point > 0)
705             rl_point = opoint;
706
707           if (rl_point < 0)
708             rl_point = 0;
709         }
710     }
711   return (0);
712 }
713
714 int
715 rl_vi_eword (int count, int ignore)
716 {
717   int opoint;
718
719   while (count-- && rl_point < (rl_end - 1))
720     {
721       if (whitespace (rl_line_buffer[rl_point]) == 0)
722         _rl_vi_advance_point ();
723
724       while (rl_point < rl_end && whitespace (rl_line_buffer[rl_point]))
725         _rl_vi_advance_point ();
726
727       opoint = rl_point;
728       if (rl_point < rl_end)
729         {
730           if (_rl_isident (rl_line_buffer[rl_point]))
731             do
732               {
733                 opoint = _rl_vi_advance_point ();
734               }
735             while (rl_point < rl_end && _rl_isident (rl_line_buffer[rl_point]));
736           else
737             do
738               {
739                 opoint = _rl_vi_advance_point ();
740               }
741             while (rl_point < rl_end && !_rl_isident (rl_line_buffer[rl_point])
742                    && !whitespace (rl_line_buffer[rl_point]));
743         }
744       rl_point = opoint;
745     }
746   return (0);
747 }
748
749 int
750 rl_vi_insert_beg (int count, int key)
751 {
752   rl_beg_of_line (1, key);
753   rl_vi_insert_mode (1, key);
754   return (0);
755 }
756
757 static void
758 _rl_vi_append_forward (int key)
759 {
760   _rl_vi_advance_point ();
761 }
762
763 int
764 rl_vi_append_mode (int count, int key)
765 {
766   _rl_vi_append_forward (key);
767   rl_vi_start_inserting (key, 1, rl_arg_sign);
768   return (0);
769 }
770
771 int
772 rl_vi_append_eol (int count, int key)
773 {
774   rl_end_of_line (1, key);
775   rl_vi_append_mode (1, key);
776   return (0);
777 }
778
779 /* What to do in the case of C-d. */
780 int
781 rl_vi_eof_maybe (int count, int c)
782 {
783   return (rl_newline (1, '\n'));
784 }
785
786 /* Insertion mode stuff. */
787
788 /* Switching from one mode to the other really just involves
789    switching keymaps. */
790 int
791 rl_vi_insertion_mode (int count, int key)
792 {
793   _rl_keymap = vi_insertion_keymap;
794   _rl_vi_last_key_before_insert = key;
795   if (_rl_show_mode_in_prompt)
796     _rl_reset_prompt ();
797   return (0);
798 }
799
800 int
801 rl_vi_insert_mode (int count, int key)
802 {
803   rl_vi_start_inserting (key, 1, rl_arg_sign);
804   return (0);
805 }
806
807 static void
808 vi_save_insert_buffer (int start, int len)
809 {
810   /* Same code as _rl_vi_save_insert below */
811   if (len >= vi_insert_buffer_size)
812     {
813       vi_insert_buffer_size += (len + 32) - (len % 32);
814       vi_insert_buffer = (char *)xrealloc (vi_insert_buffer, vi_insert_buffer_size);
815     }
816   strncpy (vi_insert_buffer, rl_line_buffer + start, len - 1);
817   vi_insert_buffer[len-1] = '\0';
818 }
819
820 static void
821 _rl_vi_save_replace (void)
822 {
823   int len, start, end;
824   UNDO_LIST *up;
825
826   up = rl_undo_list;
827   if (up == 0 || up->what != UNDO_END || vi_replace_count <= 0)
828     {
829       if (vi_insert_buffer_size >= 1)
830         vi_insert_buffer[0] = '\0';
831       return;
832     }
833   /* Let's try it the quick and easy way for now.  This should essentially
834      accommodate every UNDO_INSERT and save the inserted text to
835      vi_insert_buffer */
836   end = rl_point;
837   start = end - vi_replace_count + 1;
838   len = vi_replace_count + 1;
839
840   vi_save_insert_buffer (start, len);  
841 }
842
843 static void
844 _rl_vi_save_insert (UNDO_LIST *up)
845 {
846   int len, start, end;
847
848   if (up == 0 || up->what != UNDO_INSERT)
849     {
850       if (vi_insert_buffer_size >= 1)
851         vi_insert_buffer[0] = '\0';
852       return;
853     }
854
855   start = up->start;
856   end = up->end;
857   len = end - start + 1;
858
859   vi_save_insert_buffer (start, len);
860 }
861     
862 void
863 _rl_vi_done_inserting (void)
864 {
865   if (_rl_vi_doing_insert)
866     {
867       /* The `C', `s', and `S' commands set this. */
868       rl_end_undo_group ();
869       /* Now, the text between rl_undo_list->next->start and
870          rl_undo_list->next->end is what was inserted while in insert
871          mode.  It gets copied to VI_INSERT_BUFFER because it depends
872          on absolute indices into the line which may change (though they
873          probably will not). */
874       _rl_vi_doing_insert = 0;
875       if (_rl_vi_last_key_before_insert == 'R')
876         _rl_vi_save_replace ();         /* Half the battle */
877       else
878         _rl_vi_save_insert (rl_undo_list->next);
879       vi_continued_command = 1;
880     }
881   else
882     {
883       if (rl_undo_list && (_rl_vi_last_key_before_insert == 'i' ||
884                            _rl_vi_last_key_before_insert == 'a' ||
885                            _rl_vi_last_key_before_insert == 'I' ||
886                            _rl_vi_last_key_before_insert == 'A'))
887         _rl_vi_save_insert (rl_undo_list);
888       /* XXX - Other keys probably need to be checked. */
889       else if (_rl_vi_last_key_before_insert == 'C')
890         rl_end_undo_group ();
891       while (_rl_undo_group_level > 0)
892         rl_end_undo_group ();
893       vi_continued_command = 0;
894     }
895 }
896
897 int
898 rl_vi_movement_mode (int count, int key)
899 {
900   if (rl_point > 0)
901     rl_backward_char (1, key);
902
903   _rl_keymap = vi_movement_keymap;
904   _rl_vi_done_inserting ();
905
906   /* This is how POSIX.2 says `U' should behave -- everything up until the
907      first time you go into command mode should not be undone. */
908   if (RL_ISSTATE (RL_STATE_VICMDONCE) == 0)
909     rl_free_undo_list ();
910
911   if (_rl_show_mode_in_prompt)
912     _rl_reset_prompt ();
913
914   RL_SETSTATE (RL_STATE_VICMDONCE);
915   return (0);
916 }
917
918 int
919 rl_vi_arg_digit (int count, int c)
920 {
921   if (c == '0' && rl_numeric_arg == 1 && !rl_explicit_arg)
922     return (rl_beg_of_line (1, c));
923   else
924     return (rl_digit_argument (count, c));
925 }
926
927 /* Change the case of the next COUNT characters. */
928 #if defined (HANDLE_MULTIBYTE)
929 static int
930 _rl_vi_change_mbchar_case (int count)
931 {
932   wchar_t wc;
933   char mb[MB_LEN_MAX+1];
934   int mlen, p;
935   size_t m;
936   mbstate_t ps;
937
938   memset (&ps, 0, sizeof (mbstate_t));
939   if (_rl_adjust_point (rl_line_buffer, rl_point, &ps) > 0)
940     count--;
941   while (count-- && rl_point < rl_end)
942     {
943       m = mbrtowc (&wc, rl_line_buffer + rl_point, rl_end - rl_point, &ps);
944       if (MB_INVALIDCH (m))
945         wc = (wchar_t)rl_line_buffer[rl_point];
946       else if (MB_NULLWCH (m))
947         wc = L'\0';
948       if (iswupper (wc))
949         wc = towlower (wc);
950       else if (iswlower (wc))
951         wc = towupper (wc);
952       else
953         {
954           /* Just skip over chars neither upper nor lower case */
955           rl_forward_char (1, 0);
956           continue;
957         }
958
959       /* Vi is kind of strange here. */
960       if (wc)
961         {
962           p = rl_point;
963           mlen = wcrtomb (mb, wc, &ps);
964           if (mlen >= 0)
965             mb[mlen] = '\0';
966           rl_begin_undo_group ();
967           rl_vi_delete (1, 0);
968           if (rl_point < p)     /* Did we retreat at EOL? */
969             _rl_vi_advance_point ();
970           rl_insert_text (mb);
971           rl_end_undo_group ();
972           rl_vi_check ();
973         }
974       else
975         rl_forward_char (1, 0);
976     }
977
978   return 0;
979 }
980 #endif
981
982 int
983 rl_vi_change_case (int count, int ignore)
984 {
985   int c, p;
986
987   /* Don't try this on an empty line. */
988   if (rl_point >= rl_end)
989     return (0);
990
991   c = 0;
992 #if defined (HANDLE_MULTIBYTE)
993   if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
994     return (_rl_vi_change_mbchar_case (count));
995 #endif
996
997   while (count-- && rl_point < rl_end)
998     {
999       if (_rl_uppercase_p (rl_line_buffer[rl_point]))
1000         c = _rl_to_lower (rl_line_buffer[rl_point]);
1001       else if (_rl_lowercase_p (rl_line_buffer[rl_point]))
1002         c = _rl_to_upper (rl_line_buffer[rl_point]);
1003       else
1004         {
1005           /* Just skip over characters neither upper nor lower case. */
1006           rl_forward_char (1, c);
1007           continue;
1008         }
1009
1010       /* Vi is kind of strange here. */
1011       if (c)
1012         {
1013           p = rl_point;
1014           rl_begin_undo_group ();
1015           rl_vi_delete (1, c);
1016           if (rl_point < p)     /* Did we retreat at EOL? */
1017             rl_point++;
1018           _rl_insert_char (1, c);
1019           rl_end_undo_group ();
1020           rl_vi_check ();
1021         }
1022       else
1023         rl_forward_char (1, c);
1024     }
1025   return (0);
1026 }
1027
1028 int
1029 rl_vi_put (int count, int key)
1030 {
1031   if (!_rl_uppercase_p (key) && (rl_point + 1 <= rl_end))
1032     rl_point = _rl_find_next_mbchar (rl_line_buffer, rl_point, 1, MB_FIND_NONZERO);
1033
1034   while (count--)
1035     rl_yank (1, key);
1036
1037   rl_backward_char (1, key);
1038   return (0);
1039 }
1040
1041 /* Move the cursor back one character if you're at the end of the line */
1042 int
1043 rl_vi_check (void)
1044 {
1045   if (rl_point && rl_point == rl_end)
1046     _rl_vi_backup ();
1047   return (0);
1048 }
1049
1050 /* Move to the character position specified by COUNT */
1051 int
1052 rl_vi_column (int count, int key)
1053 {
1054   if (count > rl_end)
1055     rl_end_of_line (1, key);
1056   else
1057     {
1058       rl_point = 0;
1059       rl_point = _rl_forward_char_internal (count - 1);
1060     }
1061   return (0);
1062 }
1063
1064 /* Process C as part of the current numeric argument.  Return -1 if the
1065    argument should be aborted, 0 if we should not read any more chars, and
1066    1 if we should continue to read chars. */
1067 static int
1068 _rl_vi_arg_dispatch (int c)
1069 {
1070   int key;
1071
1072   key = c;
1073   if (c >= 0 && _rl_keymap[c].type == ISFUNC && _rl_keymap[c].function == rl_universal_argument)
1074     {
1075       rl_numeric_arg *= 4;
1076       return 1;
1077     }
1078
1079   c = UNMETA (c);
1080
1081   if (_rl_digit_p (c))
1082     {
1083       if (rl_explicit_arg)
1084         rl_numeric_arg = (rl_numeric_arg * 10) + _rl_digit_value (c);
1085       else
1086         rl_numeric_arg = _rl_digit_value (c);
1087       rl_explicit_arg = 1;
1088       return 1;         /* keep going */
1089     }
1090   else
1091     {
1092       rl_clear_message ();
1093       rl_stuff_char (key);
1094       return 0;         /* done */
1095     }
1096 }
1097
1098 /* A simplified loop for vi. Don't dispatch key at end.
1099    Don't recognize minus sign?
1100    Should this do rl_save_prompt/rl_restore_prompt? */
1101 static int
1102 rl_digit_loop1 (void)
1103 {
1104   int c, r;
1105
1106   while (1)
1107     {
1108       if (_rl_arg_overflow ())
1109         return 1;
1110
1111       c = _rl_arg_getchar ();
1112
1113       r = _rl_vi_arg_dispatch (c);
1114       if (r <= 0)
1115         break;
1116     }
1117
1118   RL_UNSETSTATE(RL_STATE_NUMERICARG);
1119   return (0);
1120 }
1121
1122 /* This set of functions is basically to handle the commands that take a
1123    motion argument while in callback mode: read the command, read the motion
1124    command modifier, find the extent of the text to affect, and dispatch the
1125    command for execution. */
1126 static void
1127 _rl_mvcxt_init (_rl_vimotion_cxt *m, int op, int key)
1128 {
1129   m->op = op;
1130   m->state = m->flags = 0;
1131   m->ncxt = 0;
1132   m->numeric_arg = -1;
1133   m->start = rl_point;
1134   m->end = rl_end;
1135   m->key = key;
1136   m->motion = -1;
1137 }
1138
1139 static _rl_vimotion_cxt *
1140 _rl_mvcxt_alloc (int op, int key)
1141 {
1142   _rl_vimotion_cxt *m;
1143
1144   m = xmalloc (sizeof (_rl_vimotion_cxt));
1145   _rl_mvcxt_init (m, op, key);
1146   return m;
1147 }
1148
1149 static void
1150 _rl_mvcxt_dispose (_rl_vimotion_cxt *m)
1151 {
1152   xfree (m);
1153 }
1154
1155 static int
1156 rl_domove_motion_callback (_rl_vimotion_cxt *m)
1157 {
1158   int c;
1159
1160   _rl_vi_last_motion = c = m->motion;
1161
1162   /* Append a blank character temporarily so that the motion routines
1163      work right at the end of the line.  Original value of rl_end is saved
1164      as m->end. */
1165   rl_line_buffer[rl_end++] = ' ';
1166   rl_line_buffer[rl_end] = '\0';
1167
1168   _rl_dispatch (c, _rl_keymap);
1169
1170 #if defined (READLINE_CALLBACKS)
1171   if (RL_ISSTATE (RL_STATE_CALLBACK))
1172     {
1173       /* Messy case where char search can be vi motion command; see rest of
1174          details in callback.c.  vi_char_search and callback_char_search just
1175          set and unset the CHARSEARCH state.  This is where any vi motion
1176          command that needs to set its own state should be handled, with any
1177          corresponding code to manage that state in callback.c */
1178       if (RL_ISSTATE (RL_STATE_CHARSEARCH))
1179         return 0;
1180       else
1181         return (_rl_vi_domove_motion_cleanup (c, m));
1182     }
1183 #endif
1184
1185   return (_rl_vi_domove_motion_cleanup (c, m));
1186 }
1187
1188 int
1189 _rl_vi_domove_motion_cleanup (int c, _rl_vimotion_cxt *m)
1190 {
1191   int r;
1192
1193   /* Remove the blank that we added in rl_domove_motion_callback. */
1194   rl_end = m->end;
1195   rl_line_buffer[rl_end] = '\0';
1196   if (rl_point > rl_end)
1197     rl_point = rl_end;
1198
1199   /* No change in position means the command failed. */
1200   if (rl_mark == rl_point)
1201     {
1202       RL_UNSETSTATE (RL_STATE_VIMOTION);
1203       return (-1);
1204     }
1205
1206   /* rl_vi_f[wW]ord () leaves the cursor on the first character of the next
1207      word.  If we are not at the end of the line, and we are on a
1208      non-whitespace character, move back one (presumably to whitespace). */
1209   if ((_rl_to_upper (c) == 'W') && rl_point < rl_end && rl_point > rl_mark &&
1210       !whitespace (rl_line_buffer[rl_point]))
1211     rl_point--;         /* XXX */
1212
1213   /* If cw or cW, back up to the end of a word, so the behaviour of ce
1214      or cE is the actual result.  Brute-force, no subtlety. */
1215   if (m->key == 'c' && rl_point >= rl_mark && (_rl_to_upper (c) == 'W'))
1216     {
1217       /* Don't move farther back than where we started. */
1218       while (rl_point > rl_mark && whitespace (rl_line_buffer[rl_point]))
1219         rl_point--;
1220
1221       /* Posix.2 says that if cw or cW moves the cursor towards the end of
1222          the line, the character under the cursor should be deleted. */
1223       if (rl_point == rl_mark)
1224         _rl_vi_advance_point ();
1225       else
1226         {
1227           /* Move past the end of the word so that the kill doesn't
1228              remove the last letter of the previous word.  Only do this
1229              if we are not at the end of the line. */
1230           if (rl_point >= 0 && rl_point < (rl_end - 1) && !whitespace (rl_line_buffer[rl_point]))
1231             _rl_vi_advance_point ();
1232         }
1233     }
1234
1235   if (rl_mark < rl_point)
1236     SWAP (rl_point, rl_mark);
1237
1238 #if defined (READLINE_CALLBACKS)
1239   if (RL_ISSTATE (RL_STATE_CALLBACK))
1240     (*rl_redisplay_function)();         /* make sure motion is displayed */
1241 #endif
1242
1243   r = vidomove_dispatch (m);
1244
1245   return (r);
1246 }
1247
1248 #define RL_VIMOVENUMARG()       (RL_ISSTATE (RL_STATE_VIMOTION) && RL_ISSTATE (RL_STATE_NUMERICARG))
1249
1250 static int
1251 rl_domove_read_callback (_rl_vimotion_cxt *m)
1252 {
1253   int c, save;
1254
1255   c = m->motion;
1256
1257   if (member (c, vi_motion))
1258     {
1259 #if defined (READLINE_CALLBACKS)
1260       /* If we just read a vi-mode motion command numeric argument, turn off
1261          the `reading numeric arg' state */
1262       if (RL_ISSTATE (RL_STATE_CALLBACK) && RL_VIMOVENUMARG())
1263         RL_UNSETSTATE (RL_STATE_NUMERICARG);
1264 #endif
1265       /* Should do everything, including turning off RL_STATE_VIMOTION */
1266       return (rl_domove_motion_callback (m));
1267     }
1268   else if (m->key == c && (m->key == 'd' || m->key == 'y' || m->key == 'c'))
1269     {
1270       rl_mark = rl_end;
1271       rl_beg_of_line (1, c);
1272       _rl_vi_last_motion = c;
1273       RL_UNSETSTATE (RL_STATE_VIMOTION);
1274       return (vidomove_dispatch (m));
1275     }
1276 #if defined (READLINE_CALLBACKS)
1277   /* XXX - these need to handle rl_universal_argument bindings */
1278   /* Reading vi motion char continuing numeric argument */
1279   else if (_rl_digit_p (c) && RL_ISSTATE (RL_STATE_CALLBACK) && RL_VIMOVENUMARG())
1280     {
1281       return (_rl_vi_arg_dispatch (c));
1282     }
1283   /* Readine vi motion char starting numeric argument */
1284   else if (_rl_digit_p (c) && RL_ISSTATE (RL_STATE_CALLBACK) && RL_ISSTATE (RL_STATE_VIMOTION) && (RL_ISSTATE (RL_STATE_NUMERICARG) == 0))
1285     {
1286       RL_SETSTATE (RL_STATE_NUMERICARG);
1287       return (_rl_vi_arg_dispatch (c));
1288     }
1289 #endif
1290   else if (_rl_digit_p (c))
1291     {
1292       /* This code path taken when not in callback mode */
1293       save = rl_numeric_arg;
1294       rl_numeric_arg = _rl_digit_value (c);
1295       rl_explicit_arg = 1;
1296       RL_SETSTATE (RL_STATE_NUMERICARG);
1297       rl_digit_loop1 ();
1298       rl_numeric_arg *= save;
1299       c = rl_vi_domove_getchar (m);
1300       if (c < 0)
1301         {
1302           m->motion = 0;
1303           return -1;
1304         }
1305       m->motion = c;
1306       return (rl_domove_motion_callback (m));
1307     }
1308   else
1309     {
1310       RL_UNSETSTATE (RL_STATE_VIMOTION);
1311       RL_UNSETSTATE (RL_STATE_NUMERICARG);
1312       return (1);
1313     }
1314 }
1315
1316 static int
1317 rl_vi_domove_getchar (_rl_vimotion_cxt *m)
1318 {
1319   int c;
1320
1321   RL_SETSTATE(RL_STATE_MOREINPUT);
1322   c = rl_read_key ();
1323   RL_UNSETSTATE(RL_STATE_MOREINPUT);
1324
1325   return c;
1326 }
1327
1328 #if defined (READLINE_CALLBACKS)
1329 int
1330 _rl_vi_domove_callback (_rl_vimotion_cxt *m)
1331 {
1332   int c, r;
1333
1334   m->motion = c = rl_vi_domove_getchar (m);
1335   if (c < 0)
1336     return 1;           /* EOF */
1337   r = rl_domove_read_callback (m);
1338
1339   return ((r == 0) ? r : 1);    /* normalize return values */
1340 }
1341 #endif
1342
1343 /* This code path is taken when not in callback mode. */
1344 int
1345 rl_vi_domove (int x, int *ignore)
1346 {
1347   int r;
1348   _rl_vimotion_cxt *m;
1349
1350   m = _rl_vimvcxt;
1351   *ignore = m->motion = rl_vi_domove_getchar (m);
1352
1353   if (m->motion < 0)
1354     {
1355       m->motion = 0;
1356       return -1;
1357     }
1358
1359   return (rl_domove_read_callback (m));
1360 }
1361
1362 static int
1363 vi_delete_dispatch (_rl_vimotion_cxt *m)
1364 {
1365   /* These are the motion commands that do not require adjusting the
1366      mark. */
1367   if (((strchr (" l|h^0bBFT`", m->motion) == 0) && (rl_point >= m->start)) &&
1368       (rl_mark < rl_end))
1369     INCREMENT_POS (rl_mark);
1370
1371   rl_kill_text (rl_point, rl_mark);
1372   return (0);
1373 }
1374
1375 int
1376 rl_vi_delete_to (int count, int key)
1377 {
1378   int c, r;
1379
1380   _rl_vimvcxt = _rl_mvcxt_alloc (VIM_DELETE, key);
1381   _rl_vimvcxt->start = rl_point;
1382
1383   rl_mark = rl_point;
1384   if (_rl_uppercase_p (key))
1385     {
1386       _rl_vimvcxt->motion = '$';
1387       r = rl_domove_motion_callback (_rl_vimvcxt);
1388     }
1389   else if (_rl_vi_redoing && _rl_vi_last_motion != 'd') /* `dd' is special */
1390     {
1391       _rl_vimvcxt->motion = _rl_vi_last_motion;
1392       r = rl_domove_motion_callback (_rl_vimvcxt);
1393     }
1394   else if (_rl_vi_redoing)              /* handle redoing `dd' here */
1395     {
1396       _rl_vimvcxt->motion = _rl_vi_last_motion;
1397       rl_mark = rl_end;
1398       rl_beg_of_line (1, key);
1399       RL_UNSETSTATE (RL_STATE_VIMOTION);
1400       r = vidomove_dispatch (_rl_vimvcxt);
1401     }
1402 #if defined (READLINE_CALLBACKS)
1403   else if (RL_ISSTATE (RL_STATE_CALLBACK))
1404     {
1405       RL_SETSTATE (RL_STATE_VIMOTION);
1406       return (0);
1407     }
1408 #endif
1409   else
1410     r = rl_vi_domove (key, &c);
1411
1412   if (r < 0)
1413     {
1414       rl_ding ();
1415       r = -1;
1416     }
1417
1418   _rl_mvcxt_dispose (_rl_vimvcxt);
1419   _rl_vimvcxt = 0;
1420
1421   return r;
1422 }
1423
1424 static int
1425 vi_change_dispatch (_rl_vimotion_cxt *m)
1426 {
1427   /* These are the motion commands that do not require adjusting the
1428      mark.  c[wW] are handled by special-case code in rl_vi_domove(),
1429      and already leave the mark at the correct location. */
1430   if (((strchr (" l|hwW^0bBFT`", m->motion) == 0) && (rl_point >= m->start)) &&
1431       (rl_mark < rl_end))
1432     INCREMENT_POS (rl_mark);
1433
1434   /* The cursor never moves with c[wW]. */
1435   if ((_rl_to_upper (m->motion) == 'W') && rl_point < m->start)
1436     rl_point = m->start;
1437
1438   if (_rl_vi_redoing)
1439     {
1440       if (vi_insert_buffer && *vi_insert_buffer)
1441         rl_begin_undo_group ();
1442       rl_delete_text (rl_point, rl_mark);
1443       if (vi_insert_buffer && *vi_insert_buffer)
1444         {
1445           rl_insert_text (vi_insert_buffer);
1446           rl_end_undo_group ();
1447         }
1448     }
1449   else
1450     {
1451       rl_begin_undo_group ();           /* to make the `u' command work */
1452       rl_kill_text (rl_point, rl_mark);
1453       /* `C' does not save the text inserted for undoing or redoing. */
1454       if (_rl_uppercase_p (m->key) == 0)
1455         _rl_vi_doing_insert = 1;
1456       /* XXX -- TODO -- use m->numericarg? */
1457       rl_vi_start_inserting (m->key, rl_numeric_arg, rl_arg_sign);
1458     }
1459
1460   return (0);
1461 }
1462
1463 int
1464 rl_vi_change_to (int count, int key)
1465 {
1466   int c, r;
1467
1468   _rl_vimvcxt = _rl_mvcxt_alloc (VIM_CHANGE, key);
1469   _rl_vimvcxt->start = rl_point;
1470
1471   rl_mark = rl_point;
1472   if (_rl_uppercase_p (key))
1473     {
1474       _rl_vimvcxt->motion = '$';
1475       r = rl_domove_motion_callback (_rl_vimvcxt);
1476     }
1477   else if (_rl_vi_redoing && _rl_vi_last_motion != 'c') /* `cc' is special */
1478     {
1479       _rl_vimvcxt->motion = _rl_vi_last_motion;
1480       r = rl_domove_motion_callback (_rl_vimvcxt);
1481     }
1482   else if (_rl_vi_redoing)              /* handle redoing `cc' here */
1483     {
1484       _rl_vimvcxt->motion = _rl_vi_last_motion;
1485       rl_mark = rl_end;
1486       rl_beg_of_line (1, key);
1487       RL_UNSETSTATE (RL_STATE_VIMOTION);
1488       r = vidomove_dispatch (_rl_vimvcxt);
1489     }
1490 #if defined (READLINE_CALLBACKS)
1491   else if (RL_ISSTATE (RL_STATE_CALLBACK))
1492     {
1493       RL_SETSTATE (RL_STATE_VIMOTION);
1494       return (0);
1495     }
1496 #endif
1497   else
1498     r = rl_vi_domove (key, &c);
1499
1500   if (r < 0)
1501     {
1502       rl_ding ();
1503       r = -1;   /* normalize return value */
1504     }
1505
1506   _rl_mvcxt_dispose (_rl_vimvcxt);
1507   _rl_vimvcxt = 0;
1508
1509   return r;
1510 }
1511
1512 static int
1513 vi_yank_dispatch (_rl_vimotion_cxt *m)
1514 {
1515   /* These are the motion commands that do not require adjusting the
1516      mark. */
1517   if (((strchr (" l|h^0%bBFT`", m->motion) == 0) && (rl_point >= m->start)) &&
1518       (rl_mark < rl_end))
1519     INCREMENT_POS (rl_mark);
1520
1521   rl_begin_undo_group ();
1522   rl_kill_text (rl_point, rl_mark);
1523   rl_end_undo_group ();
1524   rl_do_undo ();
1525   rl_point = m->start;
1526
1527   return (0);
1528 }
1529
1530 int
1531 rl_vi_yank_to (int count, int key)
1532 {
1533   int c, r;
1534
1535   _rl_vimvcxt = _rl_mvcxt_alloc (VIM_YANK, key);
1536   _rl_vimvcxt->start = rl_point;
1537
1538   rl_mark = rl_point;
1539   if (_rl_uppercase_p (key))
1540     {
1541       _rl_vimvcxt->motion = '$';
1542       r = rl_domove_motion_callback (_rl_vimvcxt);
1543     }
1544   else if (_rl_vi_redoing && _rl_vi_last_motion != 'y') /* `yy' is special */
1545     {
1546       _rl_vimvcxt->motion = _rl_vi_last_motion;
1547       r = rl_domove_motion_callback (_rl_vimvcxt);
1548     }
1549   else if (_rl_vi_redoing)                      /* handle redoing `yy' here */
1550     {
1551       _rl_vimvcxt->motion = _rl_vi_last_motion;
1552       rl_mark = rl_end;
1553       rl_beg_of_line (1, key);
1554       RL_UNSETSTATE (RL_STATE_VIMOTION);
1555       r = vidomove_dispatch (_rl_vimvcxt);
1556     }
1557 #if defined (READLINE_CALLBACKS)
1558   else if (RL_ISSTATE (RL_STATE_CALLBACK))
1559     {
1560       RL_SETSTATE (RL_STATE_VIMOTION);
1561       return (0);
1562     }
1563 #endif
1564   else
1565     r = rl_vi_domove (key, &c);
1566
1567   if (r < 0)
1568     {
1569       rl_ding ();
1570       r = -1;
1571     }
1572
1573   _rl_mvcxt_dispose (_rl_vimvcxt);
1574   _rl_vimvcxt = 0;
1575
1576   return r;
1577 }
1578
1579 static int
1580 vidomove_dispatch (_rl_vimotion_cxt *m)
1581 {
1582   int r;
1583
1584   switch (m->op)
1585     {
1586     case VIM_DELETE:
1587       r = vi_delete_dispatch (m);
1588       break;
1589     case VIM_CHANGE:
1590       r = vi_change_dispatch (m);
1591       break;
1592     case VIM_YANK:
1593       r = vi_yank_dispatch (m);
1594       break;
1595     default:
1596       _rl_errmsg ("vidomove_dispatch: unknown operator %d", m->op);
1597       r = 1;
1598       break;
1599     }
1600
1601   RL_UNSETSTATE (RL_STATE_VIMOTION);
1602   return r;
1603 }
1604
1605 int
1606 rl_vi_rubout (int count, int key)
1607 {
1608   int opoint;
1609
1610   if (count < 0)
1611     return (rl_vi_delete (-count, key));
1612
1613   if (rl_point == 0)
1614     {
1615       rl_ding ();
1616       return 1;
1617     }
1618
1619   opoint = rl_point;
1620   if (count > 1 && MB_CUR_MAX > 1 && rl_byte_oriented == 0)
1621     rl_backward_char (count, key);
1622   else if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
1623     rl_point = _rl_find_prev_mbchar (rl_line_buffer, rl_point, MB_FIND_NONZERO);
1624   else
1625     rl_point -= count;
1626
1627   if (rl_point < 0)
1628     rl_point = 0;
1629
1630   rl_kill_text (rl_point, opoint);
1631   
1632   return (0);
1633 }
1634
1635 int
1636 rl_vi_delete (int count, int key)
1637 {
1638   int end;
1639
1640   if (count < 0)
1641     return (rl_vi_rubout (-count, key));
1642
1643   if (rl_end == 0)
1644     {
1645       rl_ding ();
1646       return 1;
1647     }
1648
1649   if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
1650     end = _rl_find_next_mbchar (rl_line_buffer, rl_point, count, MB_FIND_NONZERO);
1651   else
1652     end = rl_point + count;
1653
1654   if (end >= rl_end)
1655     end = rl_end;
1656
1657   rl_kill_text (rl_point, end);
1658   
1659   if (rl_point > 0 && rl_point == rl_end)
1660     rl_backward_char (1, key);
1661
1662   return (0);
1663 }
1664
1665 /* This does what Posix specifies vi-mode C-w to do: using whitespace and
1666    punctuation characters as the word boundaries. */
1667
1668 #define vi_unix_word_boundary(c)        (whitespace(c) || ispunct(c))
1669
1670 int
1671 rl_vi_unix_word_rubout (int count, int key)
1672 {
1673   int orig_point;
1674
1675   if (rl_point == 0)
1676     rl_ding ();
1677   else
1678     {
1679       orig_point = rl_point;
1680       if (count <= 0)
1681         count = 1;
1682
1683       while (count--)
1684         {
1685           /* This isn't quite what ksh93 does but it seems to match what the
1686              Posix description of sh specifies, with a few accommodations
1687              for sequences of whitespace characters between words and at
1688              the end of the line. */
1689
1690           /* Skip over whitespace at the end of the line as a special case */
1691           if (rl_point > 0 && (rl_line_buffer[rl_point] == 0) &&
1692                 whitespace (rl_line_buffer[rl_point - 1]))
1693             while (--rl_point > 0 && whitespace (rl_line_buffer[rl_point]))
1694               ;
1695
1696           /* If we're at the start of a word, move back to word boundary so we
1697              move back to the `preceding' word */
1698           if (rl_point > 0 && (vi_unix_word_boundary (rl_line_buffer[rl_point]) == 0) &&
1699                 vi_unix_word_boundary (rl_line_buffer[rl_point - 1]))
1700             rl_point--;
1701
1702           /* If we are at a word boundary (whitespace/punct), move backward
1703              past a sequence of word boundary characters.  If we are at the
1704              end of a word (non-word boundary), move back to a word boundary */
1705           if (rl_point > 0 && vi_unix_word_boundary (rl_line_buffer[rl_point]))
1706             while (rl_point && vi_unix_word_boundary (rl_line_buffer[rl_point - 1]))
1707               rl_point--;
1708           else if (rl_point > 0 && vi_unix_word_boundary (rl_line_buffer[rl_point]) == 0)
1709             while (rl_point > 0 && (vi_unix_word_boundary (rl_line_buffer[rl_point - 1]) == 0))
1710               _rl_vi_backup_point ();
1711         }
1712
1713       rl_kill_text (orig_point, rl_point);
1714     }
1715
1716   return 0;
1717 }
1718
1719
1720 int
1721 rl_vi_back_to_indent (int count, int key)
1722 {
1723   rl_beg_of_line (1, key);
1724   while (rl_point < rl_end && whitespace (rl_line_buffer[rl_point]))
1725     rl_point++;
1726   return (0);
1727 }
1728
1729 int
1730 rl_vi_first_print (int count, int key)
1731 {
1732   return (rl_vi_back_to_indent (1, key));
1733 }
1734
1735 static int _rl_cs_dir, _rl_cs_orig_dir;
1736
1737 #if defined (READLINE_CALLBACKS)
1738 static int
1739 _rl_vi_callback_char_search (_rl_callback_generic_arg *data)
1740 {
1741   int c;
1742 #if defined (HANDLE_MULTIBYTE)
1743   c = _rl_vi_last_search_mblen = _rl_read_mbchar (_rl_vi_last_search_mbchar, MB_LEN_MAX);
1744 #else
1745   RL_SETSTATE(RL_STATE_MOREINPUT);
1746   c = rl_read_key ();
1747   RL_UNSETSTATE(RL_STATE_MOREINPUT);
1748 #endif
1749
1750   if (c <= 0)
1751     {
1752       RL_UNSETSTATE (RL_STATE_CHARSEARCH);
1753       return -1;
1754     }
1755
1756 #if !defined (HANDLE_MULTIBYTE)
1757   _rl_vi_last_search_char = c;
1758 #endif
1759
1760   _rl_callback_func = 0;
1761   _rl_want_redisplay = 1;
1762   RL_UNSETSTATE (RL_STATE_CHARSEARCH);
1763
1764 #if defined (HANDLE_MULTIBYTE)
1765   return (_rl_char_search_internal (data->count, _rl_cs_dir, _rl_vi_last_search_mbchar, _rl_vi_last_search_mblen));
1766 #else
1767   return (_rl_char_search_internal (data->count, _rl_cs_dir, _rl_vi_last_search_char));
1768 #endif  
1769 }
1770 #endif
1771
1772 int
1773 rl_vi_char_search (int count, int key)
1774 {
1775   int c;
1776 #if defined (HANDLE_MULTIBYTE)
1777   static char *target;
1778   static int tlen;
1779 #else
1780   static char target;
1781 #endif
1782
1783   if (key == ';' || key == ',')
1784     {
1785       if (_rl_cs_orig_dir == 0)
1786         return 1;
1787 #if defined (HANDLE_MULTIBYTE)
1788       if (_rl_vi_last_search_mblen == 0)
1789         return 1;
1790 #else
1791       if (_rl_vi_last_search_char == 0)
1792         return 1;
1793 #endif
1794       _rl_cs_dir = (key == ';') ? _rl_cs_orig_dir : -_rl_cs_orig_dir;
1795     }
1796   else
1797     {
1798       switch (key)
1799         {
1800         case 't':
1801           _rl_cs_orig_dir = _rl_cs_dir = FTO;
1802           break;
1803
1804         case 'T':
1805           _rl_cs_orig_dir = _rl_cs_dir = BTO;
1806           break;
1807
1808         case 'f':
1809           _rl_cs_orig_dir = _rl_cs_dir = FFIND;
1810           break;
1811
1812         case 'F':
1813           _rl_cs_orig_dir = _rl_cs_dir = BFIND;
1814           break;
1815         }
1816
1817       if (_rl_vi_redoing)
1818         {
1819           /* set target and tlen below */
1820         }
1821 #if defined (READLINE_CALLBACKS)
1822       else if (RL_ISSTATE (RL_STATE_CALLBACK))
1823         {
1824           _rl_callback_data = _rl_callback_data_alloc (count);
1825           _rl_callback_data->i1 = _rl_cs_dir;
1826           _rl_callback_data->i2 = key;
1827           _rl_callback_func = _rl_vi_callback_char_search;
1828           RL_SETSTATE (RL_STATE_CHARSEARCH);
1829           return (0);
1830         }
1831 #endif
1832       else
1833         {
1834 #if defined (HANDLE_MULTIBYTE)
1835           c = _rl_read_mbchar (_rl_vi_last_search_mbchar, MB_LEN_MAX);
1836           if (c <= 0)
1837             return -1;
1838           _rl_vi_last_search_mblen = c;
1839 #else
1840           RL_SETSTATE(RL_STATE_MOREINPUT);
1841           c = rl_read_key ();
1842           RL_UNSETSTATE(RL_STATE_MOREINPUT);
1843           if (c < 0)
1844             return -1;
1845           _rl_vi_last_search_char = c;
1846 #endif
1847         }
1848     }
1849
1850 #if defined (HANDLE_MULTIBYTE)
1851   target = _rl_vi_last_search_mbchar;
1852   tlen = _rl_vi_last_search_mblen;
1853 #else
1854   target = _rl_vi_last_search_char;
1855 #endif
1856
1857 #if defined (HANDLE_MULTIBYTE)
1858   return (_rl_char_search_internal (count, _rl_cs_dir, target, tlen));
1859 #else
1860   return (_rl_char_search_internal (count, _rl_cs_dir, target));
1861 #endif
1862 }
1863
1864 /* Match brackets */
1865 int
1866 rl_vi_match (int ignore, int key)
1867 {
1868   int count = 1, brack, pos, tmp, pre;
1869
1870   pos = rl_point;
1871   if ((brack = rl_vi_bracktype (rl_line_buffer[rl_point])) == 0)
1872     {
1873       if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
1874         {
1875           while ((brack = rl_vi_bracktype (rl_line_buffer[rl_point])) == 0)
1876             {
1877               pre = rl_point;
1878               rl_forward_char (1, key);
1879               if (pre == rl_point)
1880                 break;
1881             }
1882         }
1883       else
1884         while ((brack = rl_vi_bracktype (rl_line_buffer[rl_point])) == 0 &&
1885                 rl_point < rl_end - 1)
1886           rl_forward_char (1, key);
1887
1888       if (brack <= 0)
1889         {
1890           rl_point = pos;
1891           rl_ding ();
1892           return 1;
1893         }
1894     }
1895
1896   pos = rl_point;
1897
1898   if (brack < 0)
1899     {
1900       while (count)
1901         {
1902           tmp = pos;
1903           if (MB_CUR_MAX == 1 || rl_byte_oriented)
1904             pos--;
1905           else
1906             {
1907               pos = _rl_find_prev_mbchar (rl_line_buffer, pos, MB_FIND_ANY);
1908               if (tmp == pos)
1909                 pos--;
1910             }
1911           if (pos >= 0)
1912             {
1913               int b = rl_vi_bracktype (rl_line_buffer[pos]);
1914               if (b == -brack)
1915                 count--;
1916               else if (b == brack)
1917                 count++;
1918             }
1919           else
1920             {
1921               rl_ding ();
1922               return 1;
1923             }
1924         }
1925     }
1926   else
1927     {                   /* brack > 0 */
1928       while (count)
1929         {
1930           if (MB_CUR_MAX == 1 || rl_byte_oriented)
1931             pos++;
1932           else
1933             pos = _rl_find_next_mbchar (rl_line_buffer, pos, 1, MB_FIND_ANY);
1934
1935           if (pos < rl_end)
1936             {
1937               int b = rl_vi_bracktype (rl_line_buffer[pos]);
1938               if (b == -brack)
1939                 count--;
1940               else if (b == brack)
1941                 count++;
1942             }
1943           else
1944             {
1945               rl_ding ();
1946               return 1;
1947             }
1948         }
1949     }
1950   rl_point = pos;
1951   return (0);
1952 }
1953
1954 int
1955 rl_vi_bracktype (int c)
1956 {
1957   switch (c)
1958     {
1959     case '(': return  1;
1960     case ')': return -1;
1961     case '[': return  2;
1962     case ']': return -2;
1963     case '{': return  3;
1964     case '}': return -3;
1965     default:  return  0;
1966     }
1967 }
1968
1969 static int
1970 _rl_vi_change_char (int count, int c, char *mb)
1971 {
1972   int p;
1973
1974   if (c == '\033' || c == CTRL ('C'))
1975     return -1;
1976
1977   rl_begin_undo_group ();
1978   while (count-- && rl_point < rl_end)
1979     {
1980       p = rl_point;
1981       rl_vi_delete (1, c);
1982       if (rl_point < p)         /* Did we retreat at EOL? */
1983         _rl_vi_append_forward (c);
1984 #if defined (HANDLE_MULTIBYTE)
1985       if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
1986         rl_insert_text (mb);
1987       else
1988 #endif
1989         _rl_insert_char (1, c);
1990     }
1991
1992   /* The cursor shall be left on the last character changed. */
1993   rl_backward_char (1, c);
1994
1995   rl_end_undo_group ();
1996
1997   return (0);
1998 }
1999
2000 static int
2001 _rl_vi_callback_getchar (char *mb, int mlen)
2002 {
2003   int c;
2004
2005   RL_SETSTATE(RL_STATE_MOREINPUT);
2006   c = rl_read_key ();
2007   RL_UNSETSTATE(RL_STATE_MOREINPUT);
2008
2009   if (c < 0)
2010     return -1;
2011
2012 #if defined (HANDLE_MULTIBYTE)
2013   if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
2014     c = _rl_read_mbstring (c, mb, mlen);
2015 #endif
2016
2017   return c;
2018 }
2019
2020 #if defined (READLINE_CALLBACKS)
2021 static int
2022 _rl_vi_callback_change_char (_rl_callback_generic_arg *data)
2023 {
2024   int c;
2025   char mb[MB_LEN_MAX+1];
2026
2027   c = _rl_vi_callback_getchar (mb, MB_LEN_MAX);
2028 #if defined (HANDLE_MULTIBYTE)
2029   strncpy (_rl_vi_last_replacement, mb, MB_LEN_MAX);
2030 #else
2031   _rl_vi_last_replacement[0] = c;
2032 #endif
2033   _rl_vi_last_replacement[MB_LEN_MAX] = '\0';   /* XXX */
2034
2035   if (c < 0)
2036     return -1;
2037
2038   _rl_callback_func = 0;
2039   _rl_want_redisplay = 1;
2040
2041   return (_rl_vi_change_char (data->count, c, mb));
2042 }
2043 #endif
2044
2045 int
2046 rl_vi_change_char (int count, int key)
2047 {
2048   int c;
2049   char mb[MB_LEN_MAX+1];
2050
2051   if (_rl_vi_redoing)
2052     {
2053       strncpy (mb, _rl_vi_last_replacement, MB_LEN_MAX);
2054       c = (unsigned char)_rl_vi_last_replacement[0];    /* XXX */
2055       mb[MB_LEN_MAX] = '\0';
2056     }
2057 #if defined (READLINE_CALLBACKS)
2058   else if (RL_ISSTATE (RL_STATE_CALLBACK))
2059     {
2060       _rl_callback_data = _rl_callback_data_alloc (count);
2061       _rl_callback_func = _rl_vi_callback_change_char;
2062       return (0);
2063     }
2064 #endif
2065   else
2066     {
2067       c = _rl_vi_callback_getchar (mb, MB_LEN_MAX);
2068 #ifdef HANDLE_MULTIBYTE
2069       strncpy (_rl_vi_last_replacement, mb, MB_LEN_MAX);
2070 #else
2071       _rl_vi_last_replacement[0] = c;
2072 #endif
2073       _rl_vi_last_replacement[MB_LEN_MAX] = '\0';       /* just in case */      
2074     }
2075
2076   if (c < 0)
2077     return -1;
2078
2079   return (_rl_vi_change_char (count, c, mb));
2080 }
2081
2082 int
2083 rl_vi_subst (int count, int key)
2084 {
2085   /* If we are redoing, rl_vi_change_to will stuff the last motion char */
2086   if (_rl_vi_redoing == 0)
2087     rl_stuff_char ((key == 'S') ? 'c' : 'l');   /* `S' == `cc', `s' == `cl' */
2088
2089   return (rl_vi_change_to (count, 'c'));
2090 }
2091
2092 int
2093 rl_vi_overstrike (int count, int key)
2094 {
2095   if (_rl_vi_doing_insert == 0)
2096     {
2097       _rl_vi_doing_insert = 1;
2098       rl_begin_undo_group ();
2099     }
2100
2101   if (count > 0)
2102     {
2103       _rl_overwrite_char (count, key);
2104       vi_replace_count += count;
2105     }
2106
2107   return (0);
2108 }
2109
2110 int
2111 rl_vi_overstrike_delete (int count, int key)
2112 {
2113   int i, s;
2114
2115   for (i = 0; i < count; i++)
2116     {
2117       if (vi_replace_count == 0)
2118         {
2119           rl_ding ();
2120           break;
2121         }
2122       s = rl_point;
2123
2124       if (rl_do_undo ())
2125         vi_replace_count--;
2126
2127       if (rl_point == s)
2128         rl_backward_char (1, key);
2129     }
2130
2131   if (vi_replace_count == 0 && _rl_vi_doing_insert)
2132     {
2133       rl_end_undo_group ();
2134       rl_do_undo ();
2135       _rl_vi_doing_insert = 0;
2136     }
2137   return (0);
2138 }
2139
2140 int
2141 rl_vi_replace (int count, int key)
2142 {
2143   int i;
2144
2145   vi_replace_count = 0;
2146
2147   if (vi_replace_map == 0)
2148     {
2149       vi_replace_map = rl_make_bare_keymap ();
2150
2151       for (i = 0; i < ' '; i++)
2152         if (vi_insertion_keymap[i].type == ISFUNC)
2153           vi_replace_map[i].function = vi_insertion_keymap[i].function;
2154
2155       for (i = ' '; i < KEYMAP_SIZE; i++)
2156         vi_replace_map[i].function = rl_vi_overstrike;
2157
2158       vi_replace_map[RUBOUT].function = rl_vi_overstrike_delete;
2159
2160       /* Make sure these are what we want. */
2161       vi_replace_map[ESC].function = rl_vi_movement_mode;
2162       vi_replace_map[RETURN].function = rl_newline;
2163       vi_replace_map[NEWLINE].function = rl_newline;
2164
2165       /* If the normal vi insertion keymap has ^H bound to erase, do the
2166          same here.  Probably should remove the assignment to RUBOUT up
2167          there, but I don't think it will make a difference in real life. */
2168       if (vi_insertion_keymap[CTRL ('H')].type == ISFUNC &&
2169           vi_insertion_keymap[CTRL ('H')].function == rl_rubout)
2170         vi_replace_map[CTRL ('H')].function = rl_vi_overstrike_delete;
2171
2172       /* Make sure this is the value we need. */
2173       vi_replace_map[ANYOTHERKEY].type = ISFUNC;
2174       vi_replace_map[ANYOTHERKEY].function = (rl_command_func_t *)NULL;
2175     }
2176
2177   rl_vi_start_inserting (key, 1, rl_arg_sign);
2178
2179   _rl_vi_last_key_before_insert = key;
2180   _rl_keymap = vi_replace_map;
2181
2182   return (0);
2183 }
2184
2185 #if 0
2186 /* Try to complete the word we are standing on or the word that ends with
2187    the previous character.  A space matches everything.  Word delimiters are
2188    space and ;. */
2189 int
2190 rl_vi_possible_completions (void)
2191 {
2192   int save_pos = rl_point;
2193
2194   if (rl_line_buffer[rl_point] != ' ' && rl_line_buffer[rl_point] != ';')
2195     {
2196       while (rl_point < rl_end && rl_line_buffer[rl_point] != ' ' &&
2197              rl_line_buffer[rl_point] != ';')
2198         _rl_vi_advance_point ();
2199     }
2200   else if (rl_line_buffer[rl_point - 1] == ';')
2201     {
2202       rl_ding ();
2203       return (0);
2204     }
2205
2206   rl_possible_completions ();
2207   rl_point = save_pos;
2208
2209   return (0);
2210 }
2211 #endif
2212
2213 /* Functions to save and restore marks. */
2214 static int
2215 _rl_vi_set_mark (void)
2216 {
2217   int ch;
2218
2219   RL_SETSTATE(RL_STATE_MOREINPUT);
2220   ch = rl_read_key ();
2221   RL_UNSETSTATE(RL_STATE_MOREINPUT);
2222
2223   if (ch < 0 || ch < 'a' || ch > 'z')   /* make test against 0 explicit */
2224     {
2225       rl_ding ();
2226       return 1;
2227     }
2228   ch -= 'a';
2229   vi_mark_chars[ch] = rl_point;
2230   return 0;
2231 }
2232
2233 #if defined (READLINE_CALLBACKS)
2234 static int
2235 _rl_vi_callback_set_mark (_rl_callback_generic_arg *data)
2236 {
2237   _rl_callback_func = 0;
2238   _rl_want_redisplay = 1;
2239
2240   return (_rl_vi_set_mark ());
2241 }
2242 #endif
2243
2244 int
2245 rl_vi_set_mark (int count, int key)
2246 {
2247 #if defined (READLINE_CALLBACKS)
2248   if (RL_ISSTATE (RL_STATE_CALLBACK))
2249     {
2250       _rl_callback_data = 0;
2251       _rl_callback_func = _rl_vi_callback_set_mark;
2252       return (0);
2253     }
2254 #endif
2255
2256   return (_rl_vi_set_mark ());
2257 }
2258
2259 static int
2260 _rl_vi_goto_mark (void)
2261 {
2262   int ch;
2263
2264   RL_SETSTATE(RL_STATE_MOREINPUT);
2265   ch = rl_read_key ();
2266   RL_UNSETSTATE(RL_STATE_MOREINPUT);
2267
2268   if (ch == '`')
2269     {
2270       rl_point = rl_mark;
2271       return 0;
2272     }
2273   else if (ch < 0 || ch < 'a' || ch > 'z')      /* make test against 0 explicit */
2274     {
2275       rl_ding ();
2276       return 1;
2277     }
2278
2279   ch -= 'a';
2280   if (vi_mark_chars[ch] == -1)
2281     {
2282       rl_ding ();
2283       return 1;
2284     }
2285   rl_point = vi_mark_chars[ch];
2286   return 0;
2287 }
2288
2289 #if defined (READLINE_CALLBACKS)
2290 static int
2291 _rl_vi_callback_goto_mark (_rl_callback_generic_arg *data)
2292 {
2293   _rl_callback_func = 0;
2294   _rl_want_redisplay = 1;
2295
2296   return (_rl_vi_goto_mark ());
2297 }
2298 #endif
2299
2300 int
2301 rl_vi_goto_mark (int count, int key)
2302 {
2303 #if defined (READLINE_CALLBACKS)
2304   if (RL_ISSTATE (RL_STATE_CALLBACK))
2305     {
2306       _rl_callback_data = 0;
2307       _rl_callback_func = _rl_vi_callback_goto_mark;
2308       return (0);
2309     }
2310 #endif
2311
2312   return (_rl_vi_goto_mark ());
2313 }
2314 #endif /* VI_MODE */