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