isearch.c revision 58310
1326941Sdim/* **************************************************************** */
2326941Sdim/*								    */
3353358Sdim/*			I-Search and Searching			    */
4353358Sdim/*								    */
5353358Sdim/* **************************************************************** */
6326941Sdim
7326941Sdim/* Copyright (C) 1987,1989 Free Software Foundation, Inc.
8326941Sdim
9326941Sdim   This file contains the Readline Library (the Library), a set of
10326941Sdim   routines for providing Emacs style line input to programs that ask
11326941Sdim   for it.
12326941Sdim
13326941Sdim   The Library is free software; you can redistribute it and/or modify
14326941Sdim   it under the terms of the GNU General Public License as published by
15326941Sdim   the Free Software Foundation; either version 2, or (at your option)
16326941Sdim   any later version.
17326941Sdim
18326941Sdim   The Library is distributed in the hope that it will be useful, but
19326941Sdim   WITHOUT ANY WARRANTY; without even the implied warranty of
20326941Sdim   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21326941Sdim   General Public License for more details.
22344779Sdim
23326941Sdim   The GNU General Public License is often shipped with GNU software, and
24326941Sdim   is generally kept in a file called COPYING or LICENSE.  If you do not
25326941Sdim   have a copy of the license, write to the Free Software Foundation,
26326941Sdim   59 Temple Place, Suite 330, Boston, MA 02111 USA. */
27326941Sdim#define READLINE_LIBRARY
28326941Sdim
29326941Sdim#if defined (HAVE_CONFIG_H)
30326941Sdim#  include <config.h>
31326941Sdim#endif
32326941Sdim
33326941Sdim#include <sys/types.h>
34326941Sdim
35326941Sdim#include <stdio.h>
36326941Sdim
37326941Sdim#if defined (HAVE_UNISTD_H)
38326941Sdim#  include <unistd.h>
39344779Sdim#endif
40344779Sdim
41326941Sdim#if defined (HAVE_STDLIB_H)
42326941Sdim#  include <stdlib.h>
43326941Sdim#else
44326941Sdim#  include "ansi_stdlib.h"
45326941Sdim#endif
46326941Sdim
47326941Sdim#include "rldefs.h"
48326941Sdim#include "readline.h"
49326941Sdim#include "history.h"
50326941Sdim
51326941Sdim#include "rlprivate.h"
52326941Sdim#include "xmalloc.h"
53326941Sdim
54326941Sdim/* Variables exported to other files in the readline library. */
55326941Sdimunsigned char *_rl_isearch_terminators = (unsigned char *)NULL;
56326941Sdim
57326941Sdim/* Variables imported from other files in the readline library. */
58326941Sdimextern HIST_ENTRY *saved_line_for_history;
59326941Sdim
60326941Sdim/* Forward declarations */
61326941Sdimstatic int rl_search_history __P((int, int));
62326941Sdim
63326941Sdim/* Last line found by the current incremental search, so we don't `find'
64326941Sdim   identical lines many times in a row. */
65326941Sdimstatic char *prev_line_found;
66326941Sdim
67326941Sdim/* Search backwards through the history looking for a string which is typed
68326941Sdim   interactively.  Start with the current line. */
69326941Sdimint
70326941Sdimrl_reverse_search_history (sign, key)
71326941Sdim     int sign, key;
72326941Sdim{
73326941Sdim  return (rl_search_history (-sign, key));
74326941Sdim}
75326941Sdim
76326941Sdim/* Search forwards through the history looking for a string which is typed
77326941Sdim   interactively.  Start with the current line. */
78326941Sdimint
79326941Sdimrl_forward_search_history (sign, key)
80326941Sdim     int sign, key;
81326941Sdim{
82326941Sdim  return (rl_search_history (sign, key));
83344779Sdim}
84344779Sdim
85344779Sdim/* Display the current state of the search in the echo-area.
86344779Sdim   SEARCH_STRING contains the string that is being searched for,
87326941Sdim   DIRECTION is zero for forward, or 1 for reverse,
88326941Sdim   WHERE is the history list number of the current line.  If it is
89326941Sdim   -1, then this line is the starting one. */
90344779Sdimstatic void
91326941Sdimrl_display_search (search_string, reverse_p, where)
92326941Sdim     char *search_string;
93326941Sdim     int reverse_p, where;
94326941Sdim{
95326941Sdim  char *message;
96326941Sdim  int msglen, searchlen;
97326941Sdim
98326941Sdim  searchlen = (search_string && *search_string) ? strlen (search_string) : 0;
99326941Sdim
100326941Sdim  message = xmalloc (searchlen + 33);
101326941Sdim  msglen = 0;
102326941Sdim
103326941Sdim#if defined (NOTDEF)
104326941Sdim  if (where != -1)
105326941Sdim    {
106326941Sdim      sprintf (message, "[%d]", where + history_base);
107326941Sdim      msglen = strlen (message);
108326941Sdim    }
109326941Sdim#endif /* NOTDEF */
110326941Sdim
111326941Sdim  message[msglen++] = '(';
112326941Sdim
113326941Sdim  if (reverse_p)
114326941Sdim    {
115326941Sdim      strcpy (message + msglen, "reverse-");
116326941Sdim      msglen += 8;
117326941Sdim    }
118326941Sdim
119326941Sdim  strcpy (message + msglen, "i-search)`");
120326941Sdim  msglen += 10;
121326941Sdim
122326941Sdim  if (search_string)
123326941Sdim    {
124326941Sdim      strcpy (message + msglen, search_string);
125326941Sdim      msglen += searchlen;
126326941Sdim    }
127326941Sdim
128326941Sdim  strcpy (message + msglen, "': ");
129326941Sdim
130326941Sdim  rl_message ("%s", message, 0);
131326941Sdim  free (message);
132326941Sdim  (*rl_redisplay_function) ();
133326941Sdim}
134326941Sdim
135326941Sdim/* Search through the history looking for an interactively typed string.
136326941Sdim   This is analogous to i-search.  We start the search in the current line.
137326941Sdim   DIRECTION is which direction to search; >= 0 means forward, < 0 means
138326941Sdim   backwards. */
139326941Sdimstatic int
140326941Sdimrl_search_history (direction, invoking_key)
141326941Sdim     int direction, invoking_key;
142326941Sdim{
143326941Sdim  /* The string that the user types in to search for. */
144326941Sdim  char *search_string;
145326941Sdim
146326941Sdim  /* The current length of SEARCH_STRING. */
147326941Sdim  int search_string_index;
148326941Sdim
149326941Sdim  /* The amount of space that SEARCH_STRING has allocated to it. */
150326941Sdim  int search_string_size;
151326941Sdim
152326941Sdim  /* The list of lines to search through. */
153326941Sdim  char **lines, *allocated_line;
154326941Sdim
155326941Sdim  /* The length of LINES. */
156326941Sdim  int hlen;
157326941Sdim
158326941Sdim  /* Where we get LINES from. */
159326941Sdim  HIST_ENTRY **hlist;
160326941Sdim
161326941Sdim  register int i;
162326941Sdim  int orig_point, orig_line, last_found_line;
163326941Sdim  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