This commit was generated by cvs2svn to track changes on a CVS vendor
[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) 1988, 1991 Free Software Foundation, Inc.
5
6    This file is part of the GNU Readline Library (the Library), a set of
7    routines for providing Emacs style line input to programs that ask
8    for it.
9
10    The Library is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 1, or (at your option)
13    any later version.
14
15    The Library is distributed in the hope that it will be useful, but
16    WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18    General Public License for more details.
19
20    The GNU General Public License is often shipped with GNU software, and
21    is generally kept in a file called COPYING or LICENSE.  If you do not
22    have a copy of the license, write to the Free Software Foundation,
23    675 Mass Ave, Cambridge, MA 02139, USA. */
24 \f
25 /* **************************************************************** */
26 /*                                                                  */
27 /*                      VI Emulation Mode                           */
28 /*                                                                  */
29 /* **************************************************************** */
30 #if defined (VI_MODE)
31
32 #include <stdio.h>
33
34 #if defined (__GNUC__)
35 #  define alloca __builtin_alloca
36 #else
37 #  if defined (sparc) || defined (HAVE_ALLOCA_H)
38 #    include <alloca.h>
39 #  endif
40 #endif
41
42 /* Some standard library routines. */
43 #include "readline.h"
44 #include "history.h"
45
46 #ifndef digit
47 #define digit(c)  ((c) >= '0' && (c) <= '9')
48 #endif
49
50 #ifndef isletter
51 #define isletter(c) (((c) >= 'A' && (c) <= 'Z') || ((c) >= 'a' && (c) <= 'z'))
52 #endif
53
54 #ifndef digit_value
55 #define digit_value(c) ((c) - '0')
56 #endif
57
58 #ifndef member
59 #define member(c, s) ((c) ? index ((s), (c)) : 0)
60 #endif
61
62 #ifndef isident
63 #define isident(c) ((isletter(c) || digit(c) || c == '_'))
64 #endif
65
66 #ifndef exchange
67 #define exchange(x, y) {int temp = x; x = y; y = temp;}
68 #endif
69
70 /* Variables imported from readline.c */
71 extern int rl_point, rl_end, rl_mark, rl_done;
72 extern FILE *rl_instream;
73 extern int rl_line_buffer_len, rl_explicit_arg, rl_numeric_arg;
74 extern Keymap keymap;
75 extern char *rl_prompt;
76 extern char *rl_line_buffer;
77 extern int rl_arg_sign;
78
79 extern char *xmalloc (), *xrealloc ();
80 extern void rl_extend_line_buffer ();
81
82 /* Last string searched for from `/' or `?'. */
83 static char *vi_last_search = (char *)NULL;
84 static int vi_histpos;
85
86 /* Non-zero means enter insertion mode. */
87 int vi_doing_insert = 0;
88
89 /* String inserted into the line by rl_vi_comment (). */
90 char *rl_vi_comment_begin = (char *)NULL;
91
92 /* *** UNCLEAN *** */
93 /* Command keys which do movement for xxx_to commands. */
94 static char *vi_motion = " hl^$0ftFt;,%wbeWBE|";
95
96 /* Keymap used for vi replace characters.  Created dynamically since
97    rarely used. */
98 static Keymap vi_replace_map = (Keymap)NULL;
99
100 /* The number of characters inserted in the last replace operation. */
101 static int vi_replace_count = 0;
102
103 /* Yank the nth arg from the previous line into this line at point. */
104 rl_vi_yank_arg (count)
105      int count;
106 {
107   /* Readline thinks that the first word on a line is the 0th, while vi
108      thinks the first word on a line is the 1st.  Compensate. */
109   if (rl_explicit_arg)
110     rl_yank_nth_arg (count - 1, 0);
111   else
112     rl_yank_nth_arg ('$', 0);
113 }
114
115 /* With an argument, move back that many history lines, else move to the
116    beginning of history. */
117 rl_vi_fetch_history (count, c)
118      int count, c;
119 {
120   extern int rl_explicit_arg;
121   int current = where_history ();
122
123   /* Giving an argument of n means we want the nth command in the history
124      file.  The command number is interpreted the same way that the bash
125      `history' command does it -- that is, giving an argument count of 450
126      to this command would get the command listed as number 450 in the
127      output of `history'. */
128   if (rl_explicit_arg)
129     {
130       int wanted = history_base + current - count;
131       if (wanted <= 0)
132         rl_beginning_of_history (0, 0);
133       else
134         rl_get_previous_history (wanted);
135     }
136   else
137     rl_beginning_of_history (count, 0);
138 }
139
140 /* Search again for the last thing searched for. */
141 rl_vi_search_again (ignore, key)
142      int ignore, key;
143 {
144   switch (key)
145     {
146     case 'n':
147       rl_vi_dosearch (vi_last_search, -1);
148       break;
149
150     case 'N':
151       rl_vi_dosearch (vi_last_search, 1);
152       break;
153     }
154 }
155
156 /* Do a vi style search. */
157 rl_vi_search (count, key)
158      int count, key;
159 {
160   int dir, c, save_pos;
161   char *p;
162
163   switch (key)
164     {
165     case '?':
166       dir = 1;
167       break;
168
169     case '/':
170       dir = -1;
171       break;
172
173     default:
174       ding ();
175       return;
176     }
177
178   vi_histpos = where_history ();
179   maybe_save_line ();
180   save_pos = rl_point;
181
182   /* Reuse the line input buffer to read the search string. */
183   rl_line_buffer[0] = 0;
184   rl_end = rl_point = 0;
185   p = (char *)alloca (2 + (rl_prompt ? strlen (rl_prompt) : 0));
186
187   sprintf (p, "%s%c", rl_prompt ? rl_prompt : "", key);
188
189   rl_message (p, 0, 0);
190
191   while (c = rl_read_key ())
192     {
193       switch (c)
194         {
195         case CTRL('H'):
196         case RUBOUT:
197           if (rl_point == 0)
198             {
199               maybe_unsave_line ();
200               rl_clear_message ();
201               rl_point = save_pos;
202               return;
203             }
204
205         case CTRL('W'):
206         case CTRL('U'):
207           rl_dispatch (c, keymap);
208           break;
209
210         case ESC:
211         case RETURN:
212         case NEWLINE:
213           goto dosearch;
214           break;
215
216         case CTRL('C'):
217           maybe_unsave_line ();
218           rl_clear_message ();
219           rl_point = 0;
220           ding ();
221           return;
222
223         default:
224           rl_insert (1, c);
225           break;
226         }
227       rl_redisplay ();
228     }
229  dosearch:
230   if (vi_last_search)
231     free (vi_last_search);
232
233   vi_last_search = savestring (rl_line_buffer);
234   rl_vi_dosearch (rl_line_buffer, dir);
235 }
236
237 /* Search for STRING in the history list.  DIR is < 0 for searching
238    backwards.  POS is an absolute index into the history list at
239    which point to begin searching.  If the first character of STRING
240    is `^', the string must match a prefix of a history line, otherwise
241    a full substring match is performed. */
242 static int
243 vi_history_search_pos (string, dir, pos)
244      char *string;
245      int dir, pos;
246 {
247   int ret, old = where_history ();
248
249   history_set_pos (pos);
250
251   if (*string == '^')
252     ret = history_search_prefix (string + 1, dir);
253   else
254     ret = history_search (string, dir);
255     
256   if (ret == -1)
257     {
258       history_set_pos (old);
259       return (-1);
260     }
261
262   ret = where_history ();
263   history_set_pos (old);
264   return ret;
265 }
266
267 rl_vi_dosearch (string, dir)
268      char *string;
269      int dir;
270 {
271   int old, save = vi_histpos;
272   HIST_ENTRY *h;
273
274   if (string == 0 || *string == 0 || vi_histpos < 0)
275     {
276       ding ();
277       return;
278     }
279
280   if ((save = vi_history_search_pos (string, dir, vi_histpos + dir)) == -1)
281     {
282       maybe_unsave_line ();
283       rl_clear_message ();
284       rl_point = 0;
285       ding ();
286       return;
287     }
288
289   vi_histpos = save;
290
291   old = where_history ();
292   history_set_pos (vi_histpos);
293   h = current_history ();
294   history_set_pos (old);
295
296   {
297     int line_len = strlen (h->line);
298
299     if (line_len >= rl_line_buffer_len)
300       rl_extend_line_buffer (line_len);
301     strcpy (rl_line_buffer, h->line);
302   }
303
304   rl_undo_list = (UNDO_LIST *)h->data;
305   rl_end = strlen (rl_line_buffer);
306   rl_point = 0;
307   rl_clear_message ();
308 }
309
310 /* Completion, from vi's point of view. */
311 rl_vi_complete (ignore, key)
312      int ignore, key;
313 {
314   if ((rl_point < rl_end) && (!whitespace (rl_line_buffer[rl_point])))
315     {
316       if (!whitespace (rl_line_buffer[rl_point + 1]))
317         rl_vi_end_word (1, 'E');
318       rl_point++;
319     }
320
321   if (key == '*')
322     rl_complete_internal ('*'); /* Expansion and replacement. */
323   else if (key == '=')
324     rl_complete_internal ('?'); /* List possible completions. */
325   else if (key == '\\')
326     rl_complete_internal (TAB); /* Standard Readline completion. */
327   else
328     rl_complete (0, key);
329 }
330
331 /* Previous word in vi mode. */
332 rl_vi_prev_word (count, key)
333      int count, key;
334 {
335   if (count < 0)
336     {
337       rl_vi_next_word (-count, key);
338       return;
339     }
340
341   if (rl_point == 0)
342     {
343       ding ();
344       return;
345     }
346
347   if (uppercase_p (key))
348     rl_vi_bWord (count);
349   else
350     rl_vi_bword (count);
351 }
352
353 /* Next word in vi mode. */
354 rl_vi_next_word (count, key)
355      int count;
356 {
357   if (count < 0)
358     {
359       rl_vi_prev_word (-count, key);
360       return;
361     }
362
363   if (rl_point >= (rl_end - 1))
364     {
365       ding ();
366       return;
367     }
368
369   if (uppercase_p (key))
370     rl_vi_fWord (count);
371   else
372     rl_vi_fword (count);
373 }
374
375 /* Move to the end of the ?next? word. */
376 rl_vi_end_word (count, key)
377      int count, key;
378 {
379   if (count < 0)
380     {
381       ding ();
382       return;
383     }
384
385   if (uppercase_p (key))
386     rl_vi_eWord (count);
387   else
388     rl_vi_eword (count);
389 }
390
391 /* Move forward a word the way that 'W' does. */
392 rl_vi_fWord (count)
393      int count;
394 {
395   while (count-- && rl_point < (rl_end - 1))
396     {
397       /* Skip until whitespace. */
398       while (!whitespace (rl_line_buffer[rl_point]) && rl_point < rl_end)
399         rl_point++;
400
401       /* Now skip whitespace. */
402       while (whitespace (rl_line_buffer[rl_point]) && rl_point < rl_end)
403         rl_point++;
404     }
405 }
406
407 rl_vi_bWord (count)
408      int count;
409 {
410   while (count-- && rl_point > 0)
411     {
412       /* If we are at the start of a word, move back to whitespace so
413          we will go back to the start of the previous word. */
414       if (!whitespace (rl_line_buffer[rl_point]) &&
415           whitespace (rl_line_buffer[rl_point - 1]))
416         rl_point--;
417
418       while (rl_point > 0 && whitespace (rl_line_buffer[rl_point]))
419         rl_point--;
420
421       if (rl_point > 0)
422         {
423           while (--rl_point >= 0 && !whitespace (rl_line_buffer[rl_point]));
424           rl_point++;
425         }
426     }
427 }
428
429 rl_vi_eWord (count)
430      int count;
431 {
432   while (count-- && rl_point < (rl_end - 1))
433     {
434       if (!whitespace (rl_line_buffer[rl_point]))
435         rl_point++;
436
437       /* Move to the next non-whitespace character (to the start of the
438          next word). */
439       while (++rl_point < rl_end && whitespace (rl_line_buffer[rl_point]));
440
441       if (rl_point && rl_point < rl_end)
442         {
443           /* Skip whitespace. */
444           while (rl_point < rl_end && whitespace (rl_line_buffer[rl_point]))
445             rl_point++;
446
447           /* Skip until whitespace. */
448           while (rl_point < rl_end && !whitespace (rl_line_buffer[rl_point]))
449             rl_point++;
450
451           /* Move back to the last character of the word. */
452           rl_point--;
453         }
454     }
455 }
456
457 rl_vi_fword (count)
458      int count;
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 }
480
481 rl_vi_bword (count)
482      int count;
483 {
484   while (count-- && rl_point > 0)
485     {
486       int last_is_ident;
487
488       /* If we are at the start of a word, move back to whitespace
489          so we will go back to the start of the previous word. */
490       if (!whitespace (rl_line_buffer[rl_point]) &&
491           whitespace (rl_line_buffer[rl_point - 1]))
492         rl_point--;
493
494       /* If this character and the previous character are `opposite', move
495          back so we don't get messed up by the rl_point++ down there in
496          the while loop.  Without this code, words like `l;' screw up the
497          function. */
498       last_is_ident = isident (rl_line_buffer[rl_point - 1]);
499       if ((isident (rl_line_buffer[rl_point]) && !last_is_ident) ||
500           (!isident (rl_line_buffer[rl_point]) && last_is_ident))
501         rl_point--;
502
503       while (rl_point > 0 && whitespace (rl_line_buffer[rl_point]))
504         rl_point--;
505
506       if (rl_point > 0)
507         {
508           if (isident (rl_line_buffer[rl_point]))
509             while (--rl_point >= 0 && isident (rl_line_buffer[rl_point]));
510           else
511             while (--rl_point >= 0 && !isident (rl_line_buffer[rl_point]) &&
512                    !whitespace (rl_line_buffer[rl_point]));
513           rl_point++;
514         }
515     }
516 }
517
518 rl_vi_eword (count)
519      int count;
520 {
521   while (count-- && rl_point < rl_end - 1)
522     {
523       if (!whitespace (rl_line_buffer[rl_point]))
524         rl_point++;
525
526       while (rl_point < rl_end && whitespace (rl_line_buffer[rl_point]))
527         rl_point++;
528
529       if (rl_point < rl_end)
530         {
531           if (isident (rl_line_buffer[rl_point]))
532             while (++rl_point < rl_end && isident (rl_line_buffer[rl_point]));
533           else
534             while (++rl_point < rl_end && !isident (rl_line_buffer[rl_point])
535                    && !whitespace (rl_line_buffer[rl_point]));
536         }
537       rl_point--;
538     }
539 }
540
541 rl_vi_insert_beg ()
542 {
543   rl_beg_of_line ();
544   rl_vi_insertion_mode ();
545   return 0;
546 }
547
548 rl_vi_append_mode ()
549 {
550   if (rl_point < rl_end)
551     rl_point += 1;
552   rl_vi_insertion_mode ();
553   return 0;
554 }
555
556 rl_vi_append_eol ()
557 {
558   rl_end_of_line ();
559   rl_vi_append_mode ();
560   return 0;
561 }
562
563 /* What to do in the case of C-d. */
564 rl_vi_eof_maybe (count, c)
565      int count, c;
566 {
567   rl_newline (1, '\n');
568 }
569
570 /* Insertion mode stuff. */
571
572 /* Switching from one mode to the other really just involves
573    switching keymaps. */
574 rl_vi_insertion_mode ()
575 {
576   keymap = vi_insertion_keymap;
577 }
578
579 rl_vi_movement_mode ()
580 {
581   if (rl_point > 0)
582     rl_backward (1);
583
584   keymap = vi_movement_keymap;
585   vi_done_inserting ();
586 }
587
588 vi_done_inserting ()
589 {
590   if (vi_doing_insert)
591     {
592       rl_end_undo_group ();
593       vi_doing_insert = 0;
594     }
595 }
596
597 rl_vi_arg_digit (count, c)
598      int count, c;
599 {
600   if (c == '0' && rl_numeric_arg == 1 && !rl_explicit_arg)
601     rl_beg_of_line ();
602   else
603     rl_digit_argument (count, c);
604 }
605
606 rl_vi_change_case (count, ignore)
607      int count, ignore;
608 {
609   char c = 0;
610
611   /* Don't try this on an empty line. */
612   if (rl_point >= rl_end)
613     return;
614
615   while (count-- && rl_point < rl_end)
616     {
617       if (uppercase_p (rl_line_buffer[rl_point]))
618         c = to_lower (rl_line_buffer[rl_point]);
619       else if (lowercase_p (rl_line_buffer[rl_point]))
620         c = to_upper (rl_line_buffer[rl_point]);
621
622       /* Vi is kind of strange here. */
623       if (c)
624         {
625           rl_begin_undo_group ();
626           rl_delete (1, c);
627           rl_insert (1, c);
628           rl_end_undo_group ();
629           rl_vi_check ();
630         }
631       else
632         rl_forward (1);
633     }
634 }
635
636 rl_vi_put (count, key)
637      int count, key;
638 {
639   if (!uppercase_p (key) && (rl_point + 1 <= rl_end))
640     rl_point++;
641
642   rl_yank ();
643   rl_backward (1);
644 }
645
646 rl_vi_check ()
647 {
648   if (rl_point && rl_point == rl_end)
649     rl_point--;
650 }
651
652 rl_vi_column (count)
653 {
654   if (count > rl_end)
655     rl_end_of_line ();
656   else
657     rl_point = count - 1;
658 }
659
660 int
661 rl_vi_domove (key, nextkey)
662      int key, *nextkey;
663 {
664   int c, save;
665   int old_end, added_blank;
666
667   added_blank = 0;
668
669   rl_mark = rl_point;
670   c = rl_read_key ();
671   *nextkey = c;
672
673   if (!member (c, vi_motion))
674     {
675       if (digit (c))
676         {
677           save = rl_numeric_arg;
678           rl_numeric_arg = digit_value (c);
679           rl_digit_loop1 ();
680           rl_numeric_arg *= save;
681           c = rl_read_key ();   /* real command */
682           *nextkey = c;
683         }
684       else if ((key == 'd' && c == 'd') ||
685                (key == 'y' && c == 'y') ||
686                (key == 'c' && c == 'c'))
687         {
688           rl_mark = rl_end;
689           rl_beg_of_line ();
690           return (0);
691         }
692       else
693         return (-1);
694     }
695
696   /* Append a blank character temporarily so that the motion routines
697      work right at the end of the line. */
698   old_end = rl_end;
699   rl_line_buffer[rl_end++] = ' ';       /* This looks pretty bogus to me??? */
700   rl_line_buffer[rl_end] = '\0';
701   added_blank++;
702
703   rl_dispatch (c, keymap);
704
705   /* Remove the blank that we added. */
706   rl_end = old_end;
707   rl_line_buffer[rl_end] = '\0';
708   if (rl_point > rl_end)
709     rl_point = rl_end - 1;
710
711   /* No change in position means the command failed. */
712   if (rl_mark == rl_point)
713     return (-1);
714
715   /* rl_vi_f[wW]ord () leaves the cursor on the first character of the next
716      word.  If we are not at the end of the line, and we are on a
717      non-whitespace character, move back one (presumably to whitespace). */
718   if ((c == 'w' || c == 'W') && (rl_point < rl_end) &&
719       !whitespace (rl_line_buffer[rl_point]))
720     rl_point--;
721
722   /* If cw or cW, back up to the end of a word, so the behaviour of ce
723      or cE is the actual result.  Brute-force, no subtlety.  Do the same
724      thing for dw or dW. */
725   if (key == 'c' && (to_upper (c) == 'W'))
726     {
727       while (rl_point && whitespace (rl_line_buffer[rl_point]))
728         rl_point--;
729     }
730
731   if (rl_mark < rl_point)
732     exchange (rl_point, rl_mark);
733
734   return (0);
735 }
736
737 /* A simplified loop for vi. Don't dispatch key at end.
738    Don't recognize minus sign? */
739 rl_digit_loop1 ()
740 {
741   int key, c;
742
743   while (1)
744     {
745       rl_message ("(arg: %d) ", rl_arg_sign * rl_numeric_arg, 0);
746       key = c = rl_read_key ();
747
748       if (keymap[c].type == ISFUNC &&
749           keymap[c].function == rl_universal_argument)
750         {
751           rl_numeric_arg *= 4;
752           continue;
753         }
754
755       c = UNMETA (c);
756       if (numeric (c))
757         {
758           if (rl_explicit_arg)
759             rl_numeric_arg = (rl_numeric_arg * 10) + digit_value (c);
760           else
761             rl_numeric_arg = digit_value (c);
762           rl_explicit_arg = 1;
763         }
764       else
765         {
766           rl_clear_message ();
767           rl_stuff_char (key);
768           break;
769         }
770     }
771 }
772
773 rl_vi_delete_to (count, key)
774      int count, key;
775 {
776   int c;
777
778   if (uppercase_p (key))
779     rl_stuff_char ('$');
780
781   if (rl_vi_domove (key, &c))
782     {
783       ding ();
784       return;
785     }
786
787   if ((c != 'l') && (c != '|') && (c != 'h') && rl_mark < rl_end)
788     rl_mark++;
789
790   rl_kill_text (rl_point, rl_mark);
791 }
792
793 rl_vi_change_to (count, key)
794      int count, key;
795 {
796   int c;
797
798   if (uppercase_p (key))
799     rl_stuff_char ('$');
800
801   if (rl_vi_domove (key, &c))
802     {
803       ding ();
804       return;
805     }
806
807   if ((c != 'l') && (c != '|') && (c != 'h') && rl_mark < rl_end)
808     rl_mark++;
809
810   rl_begin_undo_group ();
811   vi_doing_insert = 1;
812   rl_kill_text (rl_point, rl_mark);
813   rl_vi_insertion_mode ();
814 }
815
816 rl_vi_yank_to (count, key)
817      int count, key;
818 {
819   int c, save = rl_point;
820
821   if (uppercase_p (key))
822     rl_stuff_char ('$');
823
824   if (rl_vi_domove (key, &c))
825     {
826       ding ();
827       return;
828     }
829
830   if ((c != 'l') && (c != '|') && (c != 'h') && rl_mark < rl_end)
831     rl_mark++;
832
833   rl_begin_undo_group ();
834   rl_kill_text (rl_point, rl_mark);
835   rl_end_undo_group ();
836   rl_do_undo ();
837   rl_point = save;
838 }
839
840 rl_vi_delete (count)
841 {
842   int end;
843
844   if (rl_end == 0)
845     {
846       ding ();
847       return;
848     }
849
850   end = rl_point + count;
851
852   if (end >= rl_end)
853     end = rl_end;
854
855   rl_kill_text (rl_point, end);
856   
857   if (rl_point > 0 && rl_point == rl_end)
858     rl_backward (1);
859 }
860
861 /* Turn the current line into a comment in shell history.
862    A K*rn shell style function. */
863 rl_vi_comment ()
864 {
865   rl_beg_of_line ();
866
867   if (rl_vi_comment_begin != (char *)NULL)
868     rl_insert_text (rl_vi_comment_begin);
869   else
870     rl_insert_text (": ");      /* Default. */
871
872   rl_redisplay ();
873   rl_newline (1, '\010');
874 }
875
876 rl_vi_first_print ()
877 {
878   rl_back_to_indent ();
879 }
880
881 rl_back_to_indent (ignore1, ignore2)
882      int ignore1, ignore2;
883 {
884   rl_beg_of_line ();
885   while (rl_point < rl_end && whitespace (rl_line_buffer[rl_point]))
886     rl_point++;
887 }
888
889 /* NOTE: it is necessary that opposite directions are inverses */
890 #define FTO      1              /* forward to */
891 #define BTO     -1              /* backward to */
892 #define FFIND    2              /* forward find */
893 #define BFIND   -2              /* backward find */
894
895 rl_vi_char_search (count, key)
896      int count, key;
897 {
898   static char target;
899   static int orig_dir, dir;
900   int pos;
901
902   if (key == ';' || key == ',')
903     dir = (key == ';' ? orig_dir : -orig_dir);
904   else
905     {
906       target = rl_getc (rl_instream);
907
908       switch (key)
909         {
910         case 't':
911           orig_dir = dir = FTO;
912           break;
913
914         case 'T':
915           orig_dir = dir = BTO;
916           break;
917
918         case 'f':
919           orig_dir = dir = FFIND;
920           break;
921
922         case 'F':
923           orig_dir = dir = BFIND;
924           break;
925         }
926     }
927
928   pos = rl_point;
929
930   while (count--)
931     {
932       if (dir < 0)
933         {
934           if (pos == 0)
935             {
936               ding ();
937               return;
938             }
939
940           pos--;
941           do
942             {
943               if (rl_line_buffer[pos] == target)
944                 {
945                   if (dir == BTO)
946                     rl_point = pos + 1;
947                   else
948                     rl_point = pos;
949                   break;
950                 }
951             }
952           while (pos--);
953
954           if (pos < 0)
955             {
956               ding ();
957               return;
958             }
959         }
960       else
961         {                       /* dir > 0 */
962           if (pos >= rl_end)
963             {
964               ding ();
965               return;
966             }
967
968           pos++;
969           do
970             {
971               if (rl_line_buffer[pos] == target)
972                 {
973                   if (dir == FTO)
974                     rl_point = pos - 1;
975                   else
976                     rl_point = pos;
977                   break;
978                 }
979             }
980           while (++pos < rl_end);
981
982           if (pos >= (rl_end - 1))
983             {
984               ding ();
985               return;
986             }
987         }
988     }
989 }
990
991 /* Match brackets */
992 rl_vi_match ()
993 {
994   int count = 1, brack, pos;
995
996   pos = rl_point;
997   if ((brack = rl_vi_bracktype (rl_line_buffer[rl_point])) == 0)
998     {
999       while ((brack = rl_vi_bracktype (rl_line_buffer[rl_point])) == 0 &&
1000              rl_point < rl_end - 1)
1001         rl_forward (1);
1002
1003       if (brack <= 0)
1004         {
1005           rl_point = pos;
1006           ding ();
1007           return;
1008         }
1009     }
1010
1011   pos = rl_point;
1012
1013   if (brack < 0)
1014     {
1015       while (count)
1016         {
1017           if (--pos >= 0)
1018             {
1019               int b = rl_vi_bracktype (rl_line_buffer[pos]);
1020               if (b == -brack)
1021                 count--;
1022               else if (b == brack)
1023                 count++;
1024             }
1025           else
1026             {
1027               ding ();
1028               return;
1029             }
1030         }
1031     }
1032   else
1033     {                   /* brack > 0 */
1034       while (count)
1035         {
1036           if (++pos < rl_end)
1037             {
1038               int b = rl_vi_bracktype (rl_line_buffer[pos]);
1039               if (b == -brack)
1040                 count--;
1041               else if (b == brack)
1042                 count++;
1043             }
1044           else
1045             {
1046               ding ();
1047               return;
1048             }
1049         }
1050     }
1051   rl_point = pos;
1052 }
1053
1054 int
1055 rl_vi_bracktype (c)
1056      int c;
1057 {
1058   switch (c)
1059     {
1060     case '(': return  1;
1061     case ')': return -1;
1062     case '[': return  2;
1063     case ']': return -2;
1064     case '{': return  3;
1065     case '}': return -3;
1066     default:  return  0;
1067     }
1068 }
1069
1070 rl_vi_change_char (count, key)
1071      int count, key;
1072 {
1073   int c;
1074
1075   c = rl_getc (rl_instream);
1076
1077   if (c == '\033' || c == CTRL ('C'))
1078     return;
1079
1080   while (count-- && rl_point < rl_end)
1081     {
1082       rl_begin_undo_group ();
1083
1084       rl_delete (1, c);
1085       rl_insert (1, c);
1086       if (count == 0)
1087         rl_backward (1);
1088
1089       rl_end_undo_group ();
1090     }
1091 }
1092
1093 rl_vi_subst (count, key)
1094      int count, key;
1095 {
1096   rl_begin_undo_group ();
1097   vi_doing_insert = 1;
1098
1099   if (uppercase_p (key))
1100     {
1101       rl_beg_of_line ();
1102       rl_kill_line (1);
1103     }
1104   else
1105     rl_delete (count, key);
1106
1107   rl_vi_insertion_mode ();
1108 }
1109
1110 rl_vi_overstrike (count, key)
1111      int count, key;
1112 {
1113   int i;
1114
1115   if (vi_doing_insert == 0)
1116     {
1117       vi_doing_insert = 1;
1118       rl_begin_undo_group ();
1119     }
1120
1121   for (i = 0; i < count; i++)
1122     {
1123       vi_replace_count++;
1124       rl_begin_undo_group ();
1125
1126       if (rl_point < rl_end)
1127         {
1128           rl_delete (1, key);
1129           rl_insert (1, key);
1130         }
1131       else
1132         rl_insert (1, key);
1133
1134       rl_end_undo_group ();
1135     }
1136 }
1137
1138 rl_vi_overstrike_delete (count)
1139      int count;
1140 {
1141   int i, s;
1142
1143   for (i = 0; i < count; i++)
1144     {
1145       if (vi_replace_count == 0)
1146         {
1147           ding ();
1148           break;
1149         }
1150       s = rl_point;
1151
1152       if (rl_do_undo ())
1153         vi_replace_count--;
1154
1155       if (rl_point == s)
1156         rl_backward (1);
1157     }
1158
1159   if (vi_replace_count == 0 && vi_doing_insert)
1160     {
1161       rl_end_undo_group ();
1162       rl_do_undo ();
1163       vi_doing_insert = 0;
1164     }
1165 }
1166
1167 rl_vi_replace (count, key)
1168      int count, key;
1169 {
1170   int i;
1171
1172   vi_replace_count = 0;
1173
1174   if (!vi_replace_map)
1175     {
1176       vi_replace_map = rl_make_bare_keymap ();
1177
1178       for (i = ' '; i < 127; i++)
1179         vi_replace_map[i].function = rl_vi_overstrike;
1180
1181       vi_replace_map[RUBOUT].function = rl_vi_overstrike_delete;
1182       vi_replace_map[ESC].function = rl_vi_movement_mode;
1183       vi_replace_map[RETURN].function = rl_newline;
1184       vi_replace_map[NEWLINE].function = rl_newline;
1185
1186       /* If the normal vi insertion keymap has ^H bound to erase, do the
1187          same here.  Probably should remove the assignment to RUBOUT up
1188          there, but I don't think it will make a difference in real life. */
1189       if (vi_insertion_keymap[CTRL ('H')].type == ISFUNC &&
1190           vi_insertion_keymap[CTRL ('H')].function == rl_rubout)
1191         vi_replace_map[CTRL ('H')].function = rl_vi_overstrike_delete;
1192
1193     }
1194   keymap = vi_replace_map;
1195 }
1196
1197 /*
1198  * Try to complete the word we are standing on or the word that ends with
1199  * the previous character. A space matches everything.
1200  * Word delimiters are space and ;.
1201  */
1202 rl_vi_possible_completions()
1203 {
1204   int save_pos = rl_point;
1205
1206   if (!index (" ;", rl_line_buffer[rl_point]))
1207     {
1208       while (!index(" ;", rl_line_buffer[++rl_point]))
1209         ;
1210     }
1211   else if (rl_line_buffer[rl_point-1] == ';')
1212     {
1213       ding ();
1214       return (0);
1215     }
1216
1217   rl_possible_completions ();
1218   rl_point = save_pos;
1219
1220   return (0);
1221 }
1222
1223 #endif /* VI_MODE */