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