isearch.c revision 75406
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. */
55unsigned char *_rl_isearch_terminators = (unsigned char *)NULL;
56
57/* Variables imported from other files in the readline library. */
58extern HIST_ENTRY *_rl_saved_line_for_history;
59
60/* Forward declarations */
61static 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. */
65static char *prev_line_found;
66
67static 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. */
71int
72rl_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. */
80int
81rl_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. */
92static void
93rl_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. */
141static int
142rl_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}
453