isearch.c revision 35486
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. */
52extern Keymap _rl_keymap;
53extern HIST_ENTRY *saved_line_for_history;
54extern int rl_line_buffer_len;
55extern int rl_point, rl_end;
56extern char *rl_line_buffer;
57
58extern void _rl_save_prompt ();
59extern void _rl_restore_prompt ();
60
61extern int rl_execute_next ();
62extern void rl_extend_line_buffer ();
63
64extern int _rl_input_available ();
65
66extern char *xmalloc (), *xrealloc ();
67
68static 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. */
72static 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. */
76int
77rl_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. */
85int
86rl_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. */
97static void
98rl_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. */
146static int
147rl_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}
434