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