2e3e16b8a9993edd99cf82a75e450b7a2b01cda7
[platform/upstream/bash.git] / lib / readline / isearch.c
1 /* **************************************************************** */
2 /*                                                                  */
3 /*                      I-Search and Searching                      */
4 /*                                                                  */
5 /* **************************************************************** */
6
7 /* Copyright (C) 1987,1989 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 "readline.h"
49 #include "history.h"
50
51 #include "rlprivate.h"
52 #include "xmalloc.h"
53
54 /* Variables exported to other files in the readline library. */
55 char *_rl_isearch_terminators = (char *)NULL;
56
57 /* Variables imported from other files in the readline library. */
58 extern HIST_ENTRY *_rl_saved_line_for_history;
59
60 /* Forward declarations */
61 static int rl_search_history PARAMS((int, int));
62
63 /* Last line found by the current incremental search, so we don't `find'
64    identical lines many times in a row. */
65 static char *prev_line_found;
66
67 /* Last search string and its length. */
68 static char *last_isearch_string;
69 static int last_isearch_string_len;
70
71 static char *default_isearch_terminators = "\033\012";
72
73 /* Search backwards through the history looking for a string which is typed
74    interactively.  Start with the current line. */
75 int
76 rl_reverse_search_history (sign, key)
77      int sign, key;
78 {
79   return (rl_search_history (-sign, key));
80 }
81
82 /* Search forwards through the history looking for a string which is typed
83    interactively.  Start with the current line. */
84 int
85 rl_forward_search_history (sign, key)
86      int sign, key;
87 {
88   return (rl_search_history (sign, key));
89 }
90
91 /* Display the current state of the search in the echo-area.
92    SEARCH_STRING contains the string that is being searched for,
93    DIRECTION is zero for forward, or 1 for reverse,
94    WHERE is the history list number of the current line.  If it is
95    -1, then this line is the starting one. */
96 static void
97 rl_display_search (search_string, reverse_p, where)
98      char *search_string;
99      int reverse_p, where;
100 {
101   char *message;
102   int msglen, searchlen;
103
104   searchlen = (search_string && *search_string) ? strlen (search_string) : 0;
105
106   message = (char *)xmalloc (searchlen + 33);
107   msglen = 0;
108
109 #if defined (NOTDEF)
110   if (where != -1)
111     {
112       sprintf (message, "[%d]", where + history_base);
113       msglen = strlen (message);
114     }
115 #endif /* NOTDEF */
116
117   message[msglen++] = '(';
118
119   if (reverse_p)
120     {
121       strcpy (message + msglen, "reverse-");
122       msglen += 8;
123     }
124
125   strcpy (message + msglen, "i-search)`");
126   msglen += 10;
127
128   if (search_string)
129     {
130       strcpy (message + msglen, search_string);
131       msglen += searchlen;
132     }
133
134   strcpy (message + msglen, "': ");
135
136   rl_message ("%s", message);
137   free (message);
138   (*rl_redisplay_function) ();
139 }
140
141 /* Search through the history looking for an interactively typed string.
142    This is analogous to i-search.  We start the search in the current line.
143    DIRECTION is which direction to search; >= 0 means forward, < 0 means
144    backwards. */
145 static int
146 rl_search_history (direction, invoking_key)
147      int direction, invoking_key;
148 {
149   /* The string that the user types in to search for. */
150   char *search_string;
151
152   /* The current length of SEARCH_STRING. */
153   int search_string_index;
154
155   /* The amount of space that SEARCH_STRING has allocated to it. */
156   int search_string_size;
157
158   /* The list of lines to search through. */
159   char **lines, *allocated_line;
160
161   /* The length of LINES. */
162   int hlen;
163
164   /* Where we get LINES from. */
165   HIST_ENTRY **hlist;
166
167   register int i;
168   int orig_point, orig_line, last_found_line;
169   int c, found, failed, sline_len;
170
171   /* The line currently being searched. */
172   char *sline;
173
174   /* Offset in that line. */
175   int line_index;
176
177   /* Non-zero if we are doing a reverse search. */
178   int reverse;
179
180   /* The list of characters which terminate the search, but are not
181      subsequently executed.  If the variable isearch-terminators has
182      been set, we use that value, otherwise we use ESC and C-J. */
183   char *isearch_terminators;
184
185   RL_SETSTATE(RL_STATE_ISEARCH);
186   orig_point = rl_point;
187   last_found_line = orig_line = where_history ();
188   reverse = direction < 0;
189   hlist = history_list ();
190   allocated_line = (char *)NULL;
191
192   isearch_terminators = _rl_isearch_terminators ? _rl_isearch_terminators
193                                                 : default_isearch_terminators;
194
195   /* Create an arrary of pointers to the lines that we want to search. */
196   rl_maybe_replace_line ();
197   i = 0;
198   if (hlist)
199     for (i = 0; hlist[i]; i++);
200
201   /* Allocate space for this many lines, +1 for the current input line,
202      and remember those lines. */
203   lines = (char **)xmalloc ((1 + (hlen = i)) * sizeof (char *));
204   for (i = 0; i < hlen; i++)
205     lines[i] = hlist[i]->line;
206
207   if (_rl_saved_line_for_history)
208     lines[i] = _rl_saved_line_for_history->line;
209   else
210     {
211       /* Keep track of this so we can free it. */
212       allocated_line = (char *)xmalloc (1 + strlen (rl_line_buffer));
213       strcpy (allocated_line, &rl_line_buffer[0]);
214       lines[i] = allocated_line;
215     }
216
217   hlen++;
218
219   /* The line where we start the search. */
220   i = orig_line;
221
222   rl_save_prompt ();
223
224   /* Initialize search parameters. */
225   search_string = (char *)xmalloc (search_string_size = 128);
226   *search_string = '\0';
227   search_string_index = 0;
228   prev_line_found = (char *)0;          /* XXX */
229
230   /* Normalize DIRECTION into 1 or -1. */
231   direction = (direction >= 0) ? 1 : -1;
232
233   rl_display_search (search_string, reverse, -1);
234
235   sline = rl_line_buffer;
236   sline_len = strlen (sline);
237   line_index = rl_point;
238
239   found = failed = 0;
240   for (;;)
241     {
242       rl_command_func_t *f = (rl_command_func_t *)NULL;
243
244       /* Read a key and decide how to proceed. */
245       RL_SETSTATE(RL_STATE_MOREINPUT);
246       c = rl_read_key ();
247       RL_UNSETSTATE(RL_STATE_MOREINPUT);
248
249       if (c >= 0 && _rl_keymap[c].type == ISFUNC)
250         {
251           f = _rl_keymap[c].function;
252
253           if (f == rl_reverse_search_history)
254             c = reverse ? -1 : -2;
255           else if (f == rl_forward_search_history)
256             c =  !reverse ? -1 : -2;
257         }
258
259 #if 0
260       /* Let NEWLINE (^J) terminate the search for people who don't like
261          using ESC.  ^M can still be used to terminate the search and
262          immediately execute the command. */
263       if (c == ESC || c == NEWLINE)
264 #else
265       /* The characters in isearch_terminators (set from the user-settable
266          variable isearch-terminators) are used to terminate the search but
267          not subsequently execute the character as a command.  The default
268          value is "\033\012" (ESC and C-J). */
269       if (strchr (isearch_terminators, c))
270 #endif
271         {
272           /* ESC still terminates the search, but if there is pending
273              input or if input arrives within 0.1 seconds (on systems
274              with select(2)) it is used as a prefix character
275              with rl_execute_next.  WATCH OUT FOR THIS!  This is intended
276              to allow the arrow keys to be used like ^F and ^B are used
277              to terminate the search and execute the movement command. */
278           if (c == ESC && _rl_input_available ())       /* XXX */
279             rl_execute_next (ESC);
280           break;
281         }
282
283       if (c >= 0 && (CTRL_CHAR (c) || META_CHAR (c) || c == RUBOUT) && c != CTRL ('G'))
284         {
285           /* This sets rl_pending_input to c; it will be picked up the next
286              time rl_read_key is called. */
287           rl_execute_next (c);
288           break;
289         }
290
291       switch (c)
292         {
293         case -1:
294           if (search_string_index == 0)
295             {
296               if (last_isearch_string)
297                 {
298                   search_string_size = 64 + last_isearch_string_len;
299                   search_string = (char *)xrealloc (search_string, search_string_size);
300                   strcpy (search_string, last_isearch_string);
301                   search_string_index = last_isearch_string_len;
302                   rl_display_search (search_string, reverse, -1);
303                   break;
304                 }
305               continue;
306             }
307           else if (reverse)
308             --line_index;
309           else if (line_index != sline_len)
310             ++line_index;
311           else
312             rl_ding ();
313           break;
314
315           /* switch directions */
316         case -2:
317           direction = -direction;
318           reverse = direction < 0;
319           break;
320
321         case CTRL ('G'):
322           strcpy (rl_line_buffer, lines[orig_line]);
323           rl_point = orig_point;
324           rl_end = strlen (rl_line_buffer);
325           rl_restore_prompt();
326           rl_clear_message ();
327           if (allocated_line)
328             free (allocated_line);
329           free (lines);
330           RL_UNSETSTATE(RL_STATE_ISEARCH);
331           return 0;
332
333 #if 0
334         /* delete character from search string. */
335         case -3:
336           if (search_string_index == 0)
337             rl_ding ();
338           else
339             {
340               search_string[--search_string_index] = '\0';
341               /* This is tricky.  To do this right, we need to keep a
342                  stack of search positions for the current search, with
343                  sentinels marking the beginning and end. */
344             }
345           break;
346 #endif
347
348         default:
349           /* Add character to search string and continue search. */
350           if (search_string_index + 2 >= search_string_size)
351             {
352               search_string_size += 128;
353               search_string = (char *)xrealloc (search_string, search_string_size);
354             }
355           search_string[search_string_index++] = c;
356           search_string[search_string_index] = '\0';
357           break;
358         }
359
360       for (found = failed = 0;;)
361         {
362           int limit = sline_len - search_string_index + 1;
363
364           /* Search the current line. */
365           while (reverse ? (line_index >= 0) : (line_index < limit))
366             {
367               if (STREQN (search_string, sline + line_index, search_string_index))
368                 {
369                   found++;
370                   break;
371                 }
372               else
373                 line_index += direction;
374             }
375           if (found)
376             break;
377
378           /* Move to the next line, but skip new copies of the line
379              we just found and lines shorter than the string we're
380              searching for. */
381           do
382             {
383               /* Move to the next line. */
384               i += direction;
385
386               /* At limit for direction? */
387               if (reverse ? (i < 0) : (i == hlen))
388                 {
389                   failed++;
390                   break;
391                 }
392
393               /* We will need these later. */
394               sline = lines[i];
395               sline_len = strlen (sline);
396             }
397           while ((prev_line_found && STREQ (prev_line_found, lines[i])) ||
398                  (search_string_index > sline_len));
399
400           if (failed)
401             break;
402
403           /* Now set up the line for searching... */
404           line_index = reverse ? sline_len - search_string_index : 0;
405         }
406
407       if (failed)
408         {
409           /* We cannot find the search string.  Ding the bell. */
410           rl_ding ();
411           i = last_found_line;
412           continue;             /* XXX - was break */
413         }
414
415       /* We have found the search string.  Just display it.  But don't
416          actually move there in the history list until the user accepts
417          the location. */
418       if (found)
419         {
420           int line_len;
421
422           prev_line_found = lines[i];
423           line_len = strlen (lines[i]);
424
425           if (line_len >= rl_line_buffer_len)
426             rl_extend_line_buffer (line_len);
427
428           strcpy (rl_line_buffer, lines[i]);
429           rl_point = line_index;
430           rl_end = line_len;
431           last_found_line = i;
432           rl_display_search (search_string, reverse, (i == orig_line) ? -1 : i);
433         }
434     }
435
436   /* The searching is over.  The user may have found the string that she
437      was looking for, or else she may have exited a failing search.  If
438      LINE_INDEX is -1, then that shows that the string searched for was
439      not found.  We use this to determine where to place rl_point. */
440
441   /* First put back the original state. */
442   strcpy (rl_line_buffer, lines[orig_line]);
443
444   rl_restore_prompt ();
445
446 #if 1
447   /* Save the search string for possible later use. */
448   FREE (last_isearch_string);
449   last_isearch_string = search_string;
450   last_isearch_string_len = search_string_index;
451 #else
452   /* Free the search string. */
453   free (search_string);
454 #endif
455
456   if (last_found_line < orig_line)
457     rl_get_previous_history (orig_line - last_found_line, 0);
458   else
459     rl_get_next_history (last_found_line - orig_line, 0);
460
461   /* If the string was not found, put point at the end of the line. */
462   if (line_index < 0)
463     line_index = strlen (rl_line_buffer);
464   rl_point = line_index;
465   rl_clear_message ();
466
467   FREE (allocated_line);
468   free (lines);
469
470   RL_UNSETSTATE(RL_STATE_ISEARCH);
471
472   return 0;
473 }