]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - contrib/libreadline/isearch.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / contrib / libreadline / isearch.c
1 /* **************************************************************** */
2 /*                                                                  */
3 /*                      I-Search and Searching                      */
4 /*                                                                  */
5 /* **************************************************************** */
6
7 /* Copyright (C) 1987-2005 Free Software Foundation, Inc.
8
9    This file contains the Readline Library (the Library), a set of
10    routines for providing Emacs style line input to programs that ask
11    for it.
12
13    The Library is free software; you can redistribute it and/or modify
14    it under the terms of the GNU General Public License as published by
15    the Free Software Foundation; either version 2, or (at your option)
16    any later version.
17
18    The Library is distributed in the hope that it will be useful, but
19    WITHOUT ANY WARRANTY; without even the implied warranty of
20    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21    General Public License for more details.
22
23    The GNU General Public License is often shipped with GNU software, and
24    is generally kept in a file called COPYING or LICENSE.  If you do not
25    have a copy of the license, write to the Free Software Foundation,
26    59 Temple Place, Suite 330, Boston, MA 02111 USA. */
27 #define READLINE_LIBRARY
28
29 #if defined (HAVE_CONFIG_H)
30 #  include <config.h>
31 #endif
32
33 #include <sys/types.h>
34
35 #include <stdio.h>
36
37 #if defined (HAVE_UNISTD_H)
38 #  include <unistd.h>
39 #endif
40
41 #if defined (HAVE_STDLIB_H)
42 #  include <stdlib.h>
43 #else
44 #  include "ansi_stdlib.h"
45 #endif
46
47 #include "rldefs.h"
48 #include "rlmbutil.h"
49
50 #include "readline.h"
51 #include "history.h"
52
53 #include "rlprivate.h"
54 #include "xmalloc.h"
55
56 /* Variables exported to other files in the readline library. */
57 char *_rl_isearch_terminators = (char *)NULL;
58
59 _rl_search_cxt *_rl_iscxt = 0;
60
61 /* Variables imported from other files in the readline library. */
62 extern HIST_ENTRY *_rl_saved_line_for_history;
63
64 static int rl_search_history PARAMS((int, int));
65
66 static _rl_search_cxt *_rl_isearch_init PARAMS((int));
67 static void _rl_isearch_fini PARAMS((_rl_search_cxt *));
68 static int _rl_isearch_cleanup PARAMS((_rl_search_cxt *, int));
69
70 /* Last line found by the current incremental search, so we don't `find'
71    identical lines many times in a row.  Now part of isearch context. */
72 /* static char *prev_line_found; */
73
74 /* Last search string and its length. */
75 static char *last_isearch_string;
76 static int last_isearch_string_len;
77
78 static char *default_isearch_terminators = "\033\012";
79
80 _rl_search_cxt *
81 _rl_scxt_alloc (type, flags)
82      int type, flags;
83 {
84   _rl_search_cxt *cxt;
85
86   cxt = (_rl_search_cxt *)xmalloc (sizeof (_rl_search_cxt));
87
88   cxt->type = type;
89   cxt->sflags = flags;
90
91   cxt->search_string = 0;
92   cxt->search_string_size = cxt->search_string_index = 0;
93
94   cxt->lines = 0;
95   cxt->allocated_line = 0;
96   cxt->hlen = cxt->hindex = 0;
97
98   cxt->save_point = rl_point;
99   cxt->save_mark = rl_mark;
100   cxt->save_line = where_history ();
101   cxt->last_found_line = cxt->save_line;
102   cxt->prev_line_found = 0;
103
104   cxt->save_undo_list = 0;
105
106   cxt->history_pos = 0;
107   cxt->direction = 0;
108
109   cxt->lastc = 0;
110
111   cxt->sline = 0;
112   cxt->sline_len = cxt->sline_index = 0;
113
114   cxt->search_terminators = 0;
115
116   return cxt;
117 }
118
119 void
120 _rl_scxt_dispose (cxt, flags)
121      _rl_search_cxt *cxt;
122      int flags;
123 {
124   FREE (cxt->search_string);
125   FREE (cxt->allocated_line);
126   FREE (cxt->lines);
127
128   free (cxt);
129 }
130
131 /* Search backwards through the history looking for a string which is typed
132    interactively.  Start with the current line. */
133 int
134 rl_reverse_search_history (sign, key)
135      int sign, key;
136 {
137   return (rl_search_history (-sign, key));
138 }
139
140 /* Search forwards through the history looking for a string which is typed
141    interactively.  Start with the current line. */
142 int
143 rl_forward_search_history (sign, key)
144      int sign, key;
145 {
146   return (rl_search_history (sign, key));
147 }
148
149 /* Display the current state of the search in the echo-area.
150    SEARCH_STRING contains the string that is being searched for,
151    DIRECTION is zero for forward, or non-zero for reverse,
152    WHERE is the history list number of the current line.  If it is
153    -1, then this line is the starting one. */
154 static void
155 rl_display_search (search_string, reverse_p, where)
156      char *search_string;
157      int reverse_p, where;
158 {
159   char *message;
160   int msglen, searchlen;
161
162   searchlen = (search_string && *search_string) ? strlen (search_string) : 0;
163
164   message = (char *)xmalloc (searchlen + 33);
165   msglen = 0;
166
167 #if defined (NOTDEF)
168   if (where != -1)
169     {
170       sprintf (message, "[%d]", where + history_base);
171       msglen = strlen (message);
172     }
173 #endif /* NOTDEF */
174
175   message[msglen++] = '(';
176
177   if (reverse_p)
178     {
179       strcpy (message + msglen, "reverse-");
180       msglen += 8;
181     }
182
183   strcpy (message + msglen, "i-search)`");
184   msglen += 10;
185
186   if (search_string)
187     {
188       strcpy (message + msglen, search_string);
189       msglen += searchlen;
190     }
191
192   strcpy (message + msglen, "': ");
193
194   rl_message ("%s", message);
195   free (message);
196   (*rl_redisplay_function) ();
197 }
198
199 static _rl_search_cxt *
200 _rl_isearch_init (direction)
201      int direction;
202 {
203   _rl_search_cxt *cxt;
204   register int i;
205   HIST_ENTRY **hlist;
206
207   cxt = _rl_scxt_alloc (RL_SEARCH_ISEARCH, 0);
208   if (direction < 0)
209     cxt->sflags |= SF_REVERSE;
210
211   cxt->search_terminators = _rl_isearch_terminators ? _rl_isearch_terminators
212                                                 : default_isearch_terminators;
213
214   /* Create an arrary of pointers to the lines that we want to search. */
215   hlist = history_list ();
216   rl_maybe_replace_line ();
217   i = 0;
218   if (hlist)
219     for (i = 0; hlist[i]; i++);
220
221   /* Allocate space for this many lines, +1 for the current input line,
222      and remember those lines. */
223   cxt->lines = (char **)xmalloc ((1 + (cxt->hlen = i)) * sizeof (char *));
224   for (i = 0; i < cxt->hlen; i++)
225     cxt->lines[i] = hlist[i]->line;
226
227   if (_rl_saved_line_for_history)
228     cxt->lines[i] = _rl_saved_line_for_history->line;
229   else
230     {
231       /* Keep track of this so we can free it. */
232       cxt->allocated_line = (char *)xmalloc (1 + strlen (rl_line_buffer));
233       strcpy (cxt->allocated_line, &rl_line_buffer[0]);
234       cxt->lines[i] = cxt->allocated_line;
235     }
236
237   cxt->hlen++;
238
239   /* The line where we start the search. */
240   cxt->history_pos = cxt->save_line;
241
242   rl_save_prompt ();
243
244   /* Initialize search parameters. */
245   cxt->search_string = (char *)xmalloc (cxt->search_string_size = 128);
246   cxt->search_string[cxt->search_string_index = 0] = '\0';
247
248   /* Normalize DIRECTION into 1 or -1. */
249   cxt->direction = (direction >= 0) ? 1 : -1;
250
251   cxt->sline = rl_line_buffer;
252   cxt->sline_len = strlen (cxt->sline);
253   cxt->sline_index = rl_point;
254
255   _rl_iscxt = cxt;              /* save globally */
256
257   return cxt;
258 }
259
260 static void
261 _rl_isearch_fini (cxt)
262      _rl_search_cxt *cxt;
263 {
264   /* First put back the original state. */
265   strcpy (rl_line_buffer, cxt->lines[cxt->save_line]);
266
267   rl_restore_prompt ();
268
269   /* Save the search string for possible later use. */
270   FREE (last_isearch_string);
271   last_isearch_string = cxt->search_string;
272   last_isearch_string_len = cxt->search_string_index;
273   cxt->search_string = 0;
274
275   if (cxt->last_found_line < cxt->save_line)
276     rl_get_previous_history (cxt->save_line - cxt->last_found_line, 0);
277   else
278     rl_get_next_history (cxt->last_found_line - cxt->save_line, 0);
279
280   /* If the string was not found, put point at the end of the last matching
281      line.  If last_found_line == orig_line, we didn't find any matching
282      history lines at all, so put point back in its original position. */
283   if (cxt->sline_index < 0)
284     {
285       if (cxt->last_found_line == cxt->save_line)
286         cxt->sline_index = cxt->save_point;
287       else
288         cxt->sline_index = strlen (rl_line_buffer);
289       rl_mark = cxt->save_mark;
290     }
291
292   rl_point = cxt->sline_index;
293   /* Don't worry about where to put the mark here; rl_get_previous_history
294      and rl_get_next_history take care of it. */
295
296   rl_clear_message ();
297 }
298
299 int
300 _rl_search_getchar (cxt)
301      _rl_search_cxt *cxt;
302 {
303   int c;
304
305   /* Read a key and decide how to proceed. */
306   RL_SETSTATE(RL_STATE_MOREINPUT);
307   c = cxt->lastc = rl_read_key ();
308   RL_UNSETSTATE(RL_STATE_MOREINPUT);
309
310 #if defined (HANDLE_MULTIBYTE)
311   if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
312     c = cxt->lastc = _rl_read_mbstring (cxt->lastc, cxt->mb, MB_LEN_MAX);
313 #endif
314
315   return c;
316 }
317
318 /* Process just-read character C according to isearch context CXT.  Return
319    -1 if the caller should just free the context and return, 0 if we should
320    break out of the loop, and 1 if we should continue to read characters. */
321 int
322 _rl_isearch_dispatch (cxt, c)
323      _rl_search_cxt *cxt;
324      int c;
325 {
326   int n, wstart, wlen, limit, cval;
327   rl_command_func_t *f;
328
329   f = (rl_command_func_t *)NULL;
330
331   if (c < 0)
332     {
333       cxt->sflags |= SF_FAILED;
334       cxt->history_pos = cxt->last_found_line;
335       return -1;
336     }
337
338   /* Translate the keys we do something with to opcodes. */
339   if (c >= 0 && _rl_keymap[c].type == ISFUNC)
340     {
341       f = _rl_keymap[c].function;
342
343       if (f == rl_reverse_search_history)
344         cxt->lastc = (cxt->sflags & SF_REVERSE) ? -1 : -2;
345       else if (f == rl_forward_search_history)
346         cxt->lastc = (cxt->sflags & SF_REVERSE) ? -2 : -1;
347       else if (f == rl_rubout)
348         cxt->lastc = -3;
349       else if (c == CTRL ('G'))
350         cxt->lastc = -4;
351       else if (c == CTRL ('W')) /* XXX */
352         cxt->lastc = -5;
353       else if (c == CTRL ('Y')) /* XXX */
354         cxt->lastc = -6;
355     }
356
357   /* The characters in isearch_terminators (set from the user-settable
358      variable isearch-terminators) are used to terminate the search but
359      not subsequently execute the character as a command.  The default
360      value is "\033\012" (ESC and C-J). */
361   if (strchr (cxt->search_terminators, cxt->lastc))
362     {
363       /* ESC still terminates the search, but if there is pending
364          input or if input arrives within 0.1 seconds (on systems
365          with select(2)) it is used as a prefix character
366          with rl_execute_next.  WATCH OUT FOR THIS!  This is intended
367          to allow the arrow keys to be used like ^F and ^B are used
368          to terminate the search and execute the movement command.
369          XXX - since _rl_input_available depends on the application-
370          settable keyboard timeout value, this could alternatively
371          use _rl_input_queued(100000) */
372       if (cxt->lastc == ESC && _rl_input_available ())
373         rl_execute_next (ESC);
374       return (0);
375     }
376
377 #define ENDSRCH_CHAR(c) \
378   ((CTRL_CHAR (c) || META_CHAR (c) || (c) == RUBOUT) && ((c) != CTRL ('G')))
379
380 #if defined (HANDLE_MULTIBYTE)
381   if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
382     {
383       if (cxt->lastc >= 0 && (cxt->mb[0] && cxt->mb[1] == '\0') && ENDSRCH_CHAR (cxt->lastc))
384         {
385           /* This sets rl_pending_input to c; it will be picked up the next
386              time rl_read_key is called. */
387           rl_execute_next (cxt->lastc);
388           return (0);
389         }
390     }
391   else
392 #endif
393     if (cxt->lastc >= 0 && ENDSRCH_CHAR (cxt->lastc))
394       {
395         /* This sets rl_pending_input to LASTC; it will be picked up the next
396            time rl_read_key is called. */
397         rl_execute_next (cxt->lastc);
398         return (0);
399       }
400
401   /* Now dispatch on the character.  `Opcodes' affect the search string or
402      state.  Other characters are added to the string.  */
403   switch (cxt->lastc)
404     {
405     /* search again */
406     case -1:
407       if (cxt->search_string_index == 0)
408         {
409           if (last_isearch_string)
410             {
411               cxt->search_string_size = 64 + last_isearch_string_len;
412               cxt->search_string = (char *)xrealloc (cxt->search_string, cxt->search_string_size);
413               strcpy (cxt->search_string, last_isearch_string);
414               cxt->search_string_index = last_isearch_string_len;
415               rl_display_search (cxt->search_string, (cxt->sflags & SF_REVERSE), -1);
416               break;
417             }
418           return (1);
419         }
420       else if (cxt->sflags & SF_REVERSE)
421         cxt->sline_index--;
422       else if (cxt->sline_index != cxt->sline_len)
423         cxt->sline_index++;
424       else
425         rl_ding ();
426       break;
427
428     /* switch directions */
429     case -2:
430       cxt->direction = -cxt->direction;
431       if (cxt->direction < 0)
432         cxt->sflags |= SF_REVERSE;
433       else
434         cxt->sflags &= ~SF_REVERSE;
435       break;
436
437     /* delete character from search string. */
438     case -3:    /* C-H, DEL */
439       /* This is tricky.  To do this right, we need to keep a
440          stack of search positions for the current search, with
441          sentinels marking the beginning and end.  But this will
442          do until we have a real isearch-undo. */
443       if (cxt->search_string_index == 0)
444         rl_ding ();
445       else
446         cxt->search_string[--cxt->search_string_index] = '\0';
447       break;
448
449     case -4:    /* C-G, abort */
450       rl_replace_line (cxt->lines[cxt->save_line], 0);
451       rl_point = cxt->save_point;
452       rl_mark = cxt->save_mark;
453       rl_restore_prompt();
454       rl_clear_message ();
455
456       return -1;
457
458     case -5:    /* C-W */
459       /* skip over portion of line we already matched and yank word */
460       wstart = rl_point + cxt->search_string_index;
461       if (wstart >= rl_end)
462         {
463           rl_ding ();
464           break;
465         }
466
467       /* if not in a word, move to one. */
468       cval = _rl_char_value (rl_line_buffer, wstart);
469       if (_rl_walphabetic (cval) == 0)
470         {
471           rl_ding ();
472           break;
473         }
474       n = MB_NEXTCHAR (rl_line_buffer, wstart, 1, MB_FIND_NONZERO);;
475       while (n < rl_end)
476         {
477           cval = _rl_char_value (rl_line_buffer, n);
478           if (_rl_walphabetic (cval) == 0)
479             break;
480           n = MB_NEXTCHAR (rl_line_buffer, n, 1, MB_FIND_NONZERO);;
481         }
482       wlen = n - wstart + 1;
483       if (cxt->search_string_index + wlen + 1 >= cxt->search_string_size)
484         {
485           cxt->search_string_size += wlen + 1;
486           cxt->search_string = (char *)xrealloc (cxt->search_string, cxt->search_string_size);
487         }
488       for (; wstart < n; wstart++)
489         cxt->search_string[cxt->search_string_index++] = rl_line_buffer[wstart];
490       cxt->search_string[cxt->search_string_index] = '\0';
491       break;
492
493     case -6:    /* C-Y */
494       /* skip over portion of line we already matched and yank rest */
495       wstart = rl_point + cxt->search_string_index;
496       if (wstart >= rl_end)
497         {
498           rl_ding ();
499           break;
500         }
501       n = rl_end - wstart + 1;
502       if (cxt->search_string_index + n + 1 >= cxt->search_string_size)
503         {
504           cxt->search_string_size += n + 1;
505           cxt->search_string = (char *)xrealloc (cxt->search_string, cxt->search_string_size);
506         }
507       for (n = wstart; n < rl_end; n++)
508         cxt->search_string[cxt->search_string_index++] = rl_line_buffer[n];
509       cxt->search_string[cxt->search_string_index] = '\0';
510       break;
511
512     /* Add character to search string and continue search. */
513     default:
514       if (cxt->search_string_index + 2 >= cxt->search_string_size)
515         {
516           cxt->search_string_size += 128;
517           cxt->search_string = (char *)xrealloc (cxt->search_string, cxt->search_string_size);
518         }
519 #if defined (HANDLE_MULTIBYTE)
520       if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
521         {
522           int j, l;
523           for (j = 0, l = strlen (cxt->mb); j < l; )
524             cxt->search_string[cxt->search_string_index++] = cxt->mb[j++];
525         }
526       else
527 #endif
528         cxt->search_string[cxt->search_string_index++] = c;
529       cxt->search_string[cxt->search_string_index] = '\0';
530       break;
531     }
532
533   for (cxt->sflags &= ~(SF_FOUND|SF_FAILED);; )
534     {
535       limit = cxt->sline_len - cxt->search_string_index + 1;
536
537       /* Search the current line. */
538       while ((cxt->sflags & SF_REVERSE) ? (cxt->sline_index >= 0) : (cxt->sline_index < limit))
539         {
540           if (STREQN (cxt->search_string, cxt->sline + cxt->sline_index, cxt->search_string_index))
541             {
542               cxt->sflags |= SF_FOUND;
543               break;
544             }
545           else
546             cxt->sline_index += cxt->direction;
547         }
548       if (cxt->sflags & SF_FOUND)
549         break;
550
551       /* Move to the next line, but skip new copies of the line
552          we just found and lines shorter than the string we're
553          searching for. */
554       do
555         {
556           /* Move to the next line. */
557           cxt->history_pos += cxt->direction;
558
559           /* At limit for direction? */
560           if ((cxt->sflags & SF_REVERSE) ? (cxt->history_pos < 0) : (cxt->history_pos == cxt->hlen))
561             {
562               cxt->sflags |= SF_FAILED;
563               break;
564             }
565
566           /* We will need these later. */
567           cxt->sline = cxt->lines[cxt->history_pos];
568           cxt->sline_len = strlen (cxt->sline);
569         }
570       while ((cxt->prev_line_found && STREQ (cxt->prev_line_found, cxt->lines[cxt->history_pos])) ||
571              (cxt->search_string_index > cxt->sline_len));
572
573       if (cxt->sflags & SF_FAILED)
574         break;
575
576       /* Now set up the line for searching... */
577       cxt->sline_index = (cxt->sflags & SF_REVERSE) ? cxt->sline_len - cxt->search_string_index : 0;
578     }
579
580   if (cxt->sflags & SF_FAILED)
581     {
582       /* We cannot find the search string.  Ding the bell. */
583       rl_ding ();
584       cxt->history_pos = cxt->last_found_line;
585       return 1;
586     }
587
588   /* We have found the search string.  Just display it.  But don't
589      actually move there in the history list until the user accepts
590      the location. */
591   if (cxt->sflags & SF_FOUND)
592     {
593       cxt->prev_line_found = cxt->lines[cxt->history_pos];
594       rl_replace_line (cxt->lines[cxt->history_pos], 0);
595       rl_point = cxt->sline_index;
596       cxt->last_found_line = cxt->history_pos;
597       rl_display_search (cxt->search_string, (cxt->sflags & SF_REVERSE), (cxt->history_pos == cxt->save_line) ? -1 : cxt->history_pos);
598     }
599
600   return 1;
601 }
602
603 static int
604 _rl_isearch_cleanup (cxt, r)
605      _rl_search_cxt *cxt;
606      int r;
607 {
608   if (r >= 0)
609     _rl_isearch_fini (cxt);
610   _rl_scxt_dispose (cxt, 0);
611   _rl_iscxt = 0;
612
613   RL_UNSETSTATE(RL_STATE_ISEARCH);
614
615   return (r != 0);
616 }
617
618 /* Search through the history looking for an interactively typed string.
619    This is analogous to i-search.  We start the search in the current line.
620    DIRECTION is which direction to search; >= 0 means forward, < 0 means
621    backwards. */
622 static int
623 rl_search_history (direction, invoking_key)
624      int direction, invoking_key;
625 {
626   _rl_search_cxt *cxt;          /* local for now, but saved globally */
627   int c, r;
628
629   RL_SETSTATE(RL_STATE_ISEARCH);
630   cxt = _rl_isearch_init (direction);
631
632   rl_display_search (cxt->search_string, (cxt->sflags & SF_REVERSE), -1);
633
634   /* If we are using the callback interface, all we do is set up here and
635       return.  The key is that we leave RL_STATE_ISEARCH set. */
636   if (RL_ISSTATE (RL_STATE_CALLBACK))
637     return (0);
638
639   r = -1;
640   for (;;)
641     {
642       c = _rl_search_getchar (cxt);
643       /* We might want to handle EOF here (c == 0) */
644       r = _rl_isearch_dispatch (cxt, cxt->lastc);
645       if (r <= 0)
646         break;
647     }
648
649   /* The searching is over.  The user may have found the string that she
650      was looking for, or else she may have exited a failing search.  If
651      LINE_INDEX is -1, then that shows that the string searched for was
652      not found.  We use this to determine where to place rl_point. */
653   return (_rl_isearch_cleanup (cxt, r));
654 }
655
656 #if defined (READLINE_CALLBACKS)
657 /* Called from the callback functions when we are ready to read a key.  The
658    callback functions know to call this because RL_ISSTATE(RL_STATE_ISEARCH).
659    If _rl_isearch_dispatch finishes searching, this function is responsible
660    for turning off RL_STATE_ISEARCH, which it does using _rl_isearch_cleanup. */
661 int
662 _rl_isearch_callback (cxt)
663      _rl_search_cxt *cxt;
664 {
665   int c, r;
666
667   c = _rl_search_getchar (cxt);
668   /* We might want to handle EOF here */
669   r = _rl_isearch_dispatch (cxt, cxt->lastc);
670
671   return (r <= 0) ? _rl_isearch_cleanup (cxt, r) : 0;
672 }
673 #endif