isearch.c revision 35486
1109864Sjeff/* **************************************************************** */
2113357Sjeff/*								    */
3109864Sjeff/*			I-Search and Searching			    */
4109864Sjeff/*								    */
5109864Sjeff/* **************************************************************** */
6109864Sjeff
7109864Sjeff/* Copyright (C) 1987,1989 Free Software Foundation, Inc.
8109864Sjeff
9109864Sjeff   This file contains the Readline Library (the Library), a set of
10109864Sjeff   routines for providing Emacs style line input to programs that ask
11109864Sjeff   for it.
12109864Sjeff
13109864Sjeff   The Library is free software; you can redistribute it and/or modify
14109864Sjeff   it under the terms of the GNU General Public License as published by
15109864Sjeff   the Free Software Foundation; either version 1, or (at your option)
16109864Sjeff   any later version.
17109864Sjeff
18109864Sjeff   The Library is distributed in the hope that it will be useful, but
19109864Sjeff   WITHOUT ANY WARRANTY; without even the implied warranty of
20109864Sjeff   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21109864Sjeff   General Public License for more details.
22109864Sjeff
23109864Sjeff   The GNU General Public License is often shipped with GNU software, and
24109864Sjeff   is generally kept in a file called COPYING or LICENSE.  If you do not
25109864Sjeff   have a copy of the license, write to the Free Software Foundation,
26109864Sjeff   675 Mass Ave, Cambridge, MA 02139, USA. */
27116182Sobrien#define READLINE_LIBRARY
28116182Sobrien
29116182Sobrien#if defined (HAVE_CONFIG_H)
30109864Sjeff#  include <config.h>
31109864Sjeff#endif
32109864Sjeff
33109864Sjeff#include <sys/types.h>
34109864Sjeff
35109864Sjeff#include <stdio.h>
36109864Sjeff
37112966Sjeff#if defined (HAVE_UNISTD_H)
38122038Sjeff#  include <unistd.h>
39109864Sjeff#endif
40109864Sjeff
41109864Sjeff#if defined (HAVE_STDLIB_H)
42109864Sjeff#  include <stdlib.h>
43109864Sjeff#else
44109864Sjeff#  include "ansi_stdlib.h"
45109864Sjeff#endif
46109864Sjeff
47109864Sjeff#include "rldefs.h"
48109864Sjeff#include "readline.h"
49109864Sjeff#include "history.h"
50109864Sjeff
51109864Sjeff/* Variables imported from other files in the readline library. */
52109864Sjeffextern Keymap _rl_keymap;
53109864Sjeffextern HIST_ENTRY *saved_line_for_history;
54121790Sjeffextern int rl_line_buffer_len;
55109864Sjeffextern int rl_point, rl_end;
56113357Sjeffextern char *rl_line_buffer;
57113357Sjeff
58109864Sjeffextern void _rl_save_prompt ();
59109864Sjeffextern void _rl_restore_prompt ();
60109864Sjeff
61109864Sjeffextern int rl_execute_next ();
62109864Sjeffextern void rl_extend_line_buffer ();
63109864Sjeff
64109864Sjeffextern int _rl_input_available ();
65109864Sjeff
66113357Sjeffextern char *xmalloc (), *xrealloc ();
67113357Sjeff
68113357Sjeffstatic int rl_search_history ();
69113357Sjeff
70113357Sjeff/* Last line found by the current incremental search, so we don't `find'
71116365Sjeff   identical lines many times in a row. */
72113357Sjeffstatic char *prev_line_found;
73113357Sjeff
74111857Sjeff/* Search backwards through the history looking for a string which is typed
75113357Sjeff   interactively.  Start with the current line. */
76111857Sjeffint
77116069Sjeffrl_reverse_search_history (sign, key)
78123487Sjeff     int sign, key;
79116069Sjeff{
80123487Sjeff  return (rl_search_history (-sign, key));
81116069Sjeff}
82116069Sjeff
83109864Sjeff/* Search forwards through the history looking for a string which is typed
84109864Sjeff   interactively.  Start with the current line. */
85109864Sjeffint
86109864Sjeffrl_forward_search_history (sign, key)
87109864Sjeff     int sign, key;
88109864Sjeff{
89109864Sjeff  return (rl_search_history (sign, key));
90109864Sjeff}
91109864Sjeff
92109864Sjeff/* Display the current state of the search in the echo-area.
93109864Sjeff   SEARCH_STRING contains the string that is being searched for,
94109864Sjeff   DIRECTION is zero for forward, or 1 for reverse,
95113357Sjeff   WHERE is the history list number of the current line.  If it is
96110260Sjeff   -1, then this line is the starting one. */
97109864Sjeffstatic void
98109864Sjeffrl_display_search (search_string, reverse_p, where)
99109864Sjeff     char *search_string;
100109864Sjeff     int reverse_p, where;
101109864Sjeff{
102109864Sjeff  char *message;
103110260Sjeff  int msglen, searchlen;
104121790Sjeff
105109864Sjeff  searchlen = (search_string && *search_string) ? strlen (search_string) : 0;
106121790Sjeff
107122158Sjeff  message = xmalloc (searchlen + 33);
108121790Sjeff  msglen = 0;
109109864Sjeff
110110645Sjeff#if defined (NOTDEF)
111110645Sjeff  if (where != -1)
112109864Sjeff    {
113109864Sjeff      sprintf (message, "[%d]", where + history_base);
114110645Sjeff      msglen = strlen (message);
115109864Sjeff    }
116109864Sjeff#endif /* NOTDEF */
117109864Sjeff
118109864Sjeff  message[msglen++] = '(';
119109864Sjeff
120109864Sjeff  if (reverse_p)
121110267Sjeff    {
122109864Sjeff      strcpy (message + msglen, "reverse-");
123109864Sjeff      msglen += 8;
124109864Sjeff    }
125109864Sjeff
126109864Sjeff  strcpy (message + msglen, "i-search)`");
127109864Sjeff  msglen += 10;
128109864Sjeff
129109864Sjeff  if (search_string)
130109864Sjeff    {
131116642Sjeff      strcpy (message + msglen, search_string);
132116642Sjeff      msglen += searchlen;
133116642Sjeff    }
134116642Sjeff
135111857Sjeff  strcpy (message + msglen, "': ");
136111857Sjeff
137116642Sjeff  rl_message ("%s", message, 0);
138111857Sjeff  free (message);
139109864Sjeff  (*rl_redisplay_function) ();
140111857Sjeff}
141121869Sjeff
142121869Sjeff/* Search through the history looking for an interactively typed string.
143116642Sjeff   This is analogous to i-search.  We start the search in the current line.
144113357Sjeff   DIRECTION is which direction to search; >= 0 means forward, < 0 means
145116642Sjeff   backwards. */
146109864Sjeffstatic int
147109864Sjeffrl_search_history (direction, invoking_key)
148111857Sjeff     int direction, invoking_key;
149109864Sjeff{
150110645Sjeff  /* The string that the user types in to search for. */
151110645Sjeff  char *search_string;
152121868Sjeff
153116365Sjeff  /* The current length of SEARCH_STRING. */
154111857Sjeff  int search_string_index;
155109864Sjeff
156121126Sjeff  /* The amount of space that SEARCH_STRING has allocated to it. */
157121868Sjeff  int search_string_size;
158116365Sjeff
159116365Sjeff  /* The list of lines to search through. */
160121126Sjeff  char **lines, *allocated_line;
161111857Sjeff
162109864Sjeff  /* The length of LINES. */
163109864Sjeff  int hlen;
164109864Sjeff
165109864Sjeff  /* Where we get LINES from. */
166109864Sjeff  HIST_ENTRY **hlist;
167109864Sjeff
168109864Sjeff  register int i;
169112966Sjeff  int orig_point, orig_line, last_found_line;
170112966Sjeff  int c, found, failed, sline_len;
171121871Sjeff
172109864Sjeff  /* The line currently being searched. */
173113357Sjeff  char *sline;
174113357Sjeff
175125299Sjeff  /* Offset in that line. */
176121871Sjeff  int line_index;
177111857Sjeff
178109864Sjeff  /* Non-zero if we are doing a reverse search. */
179112966Sjeff  int reverse;
180121871Sjeff
181109864Sjeff  orig_point = rl_point;
182109864Sjeff  last_found_line = orig_line = where_history ();
183109864Sjeff  reverse = direction < 0;
184109864Sjeff  hlist = history_list ();
185109864Sjeff  allocated_line = (char *)NULL;
186113357Sjeff
187113357Sjeff  /* Create an arrary of pointers to the lines that we want to search. */
188113417Sjeff  maybe_replace_line ();
189127278Sobrien  i = 0;
190121107Sjeff  if (hlist)
191109864Sjeff    for (i = 0; hlist[i]; i++);
192109864Sjeff
193109864Sjeff  /* Allocate space for this many lines, +1 for the current input line,
194109864Sjeff     and remember those lines. */
195109864Sjeff  lines = (char **)xmalloc ((1 + (hlen = i)) * sizeof (char *));
196109864Sjeff  for (i = 0; i < hlen; i++)
197109864Sjeff    lines[i] = hlist[i]->line;
198109864Sjeff
199112971Sjeff  if (saved_line_for_history)
200109864Sjeff    lines[i] = saved_line_for_history->line;
201109864Sjeff  else
202109864Sjeff    {
203113357Sjeff      /* Keep track of this so we can free it. */
204109864Sjeff      allocated_line = xmalloc (1 + strlen (rl_line_buffer));
205109864Sjeff      strcpy (allocated_line, &rl_line_buffer[0]);
206113357Sjeff      lines[i] = allocated_line;
207113357Sjeff    }
208113357Sjeff
209113357Sjeff  hlen++;
210121896Sjeff
211113357Sjeff  /* The line where we start the search. */
212121869Sjeff  i = orig_line;
213113357Sjeff
214110267Sjeff  _rl_save_prompt ();
215123433Sjeff
216123433Sjeff  /* Initialize search parameters. */
217123433Sjeff  search_string = xmalloc (search_string_size = 128);
218123433Sjeff  *search_string = '\0';
219125289Sjeff  search_string_index = 0;
220125289Sjeff  prev_line_found = (char *)0;		/* XXX */
221110267Sjeff
222109864Sjeff  /* Normalize DIRECTION into 1 or -1. */
223109864Sjeff  direction = (direction >= 0) ? 1 : -1;
224123433Sjeff
225109864Sjeff  rl_display_search (search_string, reverse, -1);
226123433Sjeff
227123433Sjeff  sline = rl_line_buffer;
228123433Sjeff  sline_len = strlen (sline);
229123433Sjeff  line_index = rl_point;
230123433Sjeff
231123433Sjeff  found = failed = 0;
232123433Sjeff  for (;;)
233123433Sjeff    {
234123433Sjeff      Function *f = (Function *)NULL;
235127498Smarcel
236127498Smarcel      /* Read a key and decide how to proceed. */
237127498Smarcel      c = rl_read_key ();
238123487Sjeff
239123433Sjeff      if (_rl_keymap[c].type == ISFUNC)
240123433Sjeff	{
241123433Sjeff	  f = _rl_keymap[c].function;
242123433Sjeff
243123433Sjeff	  if (f == rl_reverse_search_history)
244123433Sjeff	    c = reverse ? -1 : -2;
245109864Sjeff	  else if (f == rl_forward_search_history)
246109864Sjeff	    c =  !reverse ? -1 : -2;
247110028Sjeff	}
248127498Smarcel
249123487Sjeff      /* Let NEWLINE (^J) terminate the search for people who don't like
250121790Sjeff	 using ESC.  ^M can still be used to terminate the search and
251123433Sjeff	 immediately execute the command. */
252123433Sjeff      if (c == ESC || c == NEWLINE)
253123433Sjeff	{
254123487Sjeff	  /* ESC still terminates the search, but if there is pending
255123487Sjeff	     input or if input arrives within 0.1 seconds (on systems
256123433Sjeff	     with select(2)) it is used as a prefix character
257121790Sjeff	     with rl_execute_next.  WATCH OUT FOR THIS!  This is intended
258110028Sjeff	     to allow the arrow keys to be used like ^F and ^B are used
259110028Sjeff	     to terminate the search and execute the movement command. */
260110028Sjeff	  if (c == ESC && _rl_input_available ())	/* XXX */
261109864Sjeff	    rl_execute_next (ESC);
262112966Sjeff	  break;
263113357Sjeff	}
264111857Sjeff
265116463Sjeff      if (c >= 0 && (CTRL_CHAR (c) || META_CHAR (c) || c == RUBOUT) && c != CTRL ('G'))
266121868Sjeff	{
267121790Sjeff	  rl_execute_next (c);
268109864Sjeff	  break;
269110267Sjeff	}
270121790Sjeff
271110028Sjeff      switch (c)
272122744Sjeff	{
273122744Sjeff	case -1:
274122744Sjeff	  if (search_string_index == 0)
275122744Sjeff	    continue;
276113357Sjeff	  else if (reverse)
277113357Sjeff	    --line_index;
278113660Sjeff	  else if (line_index != sline_len)
279110267Sjeff	    ++line_index;
280123433Sjeff	  else
281121790Sjeff	    ding ();
282122744Sjeff	  break;
283123487Sjeff
284123487Sjeff	  /* switch directions */
285121790Sjeff	case -2:
286123433Sjeff	  direction = -direction;
287121790Sjeff	  reverse = direction < 0;
288121790Sjeff	  break;
289123433Sjeff
290123693Sjeff	case CTRL ('G'):
291123693Sjeff	  strcpy (rl_line_buffer, lines[orig_line]);
292123693Sjeff	  rl_point = orig_point;
293123693Sjeff	  rl_end = strlen (rl_line_buffer);
294123693Sjeff	  _rl_restore_prompt();
295123693Sjeff	  rl_clear_message ();
296122038Sjeff	  if (allocated_line)
297123693Sjeff	    free (allocated_line);
298123693Sjeff	  free (lines);
299123693Sjeff	  return 0;
300122158Sjeff
301122165Sjeff#if 0
302123693Sjeff	/* delete character from search string. */
303121790Sjeff	case -3:
304110028Sjeff	  if (search_string_index == 0)
305113357Sjeff	    ding ();
306113660Sjeff	  else
307110267Sjeff	    {
308113660Sjeff	      search_string[--search_string_index] = '\0';
309113357Sjeff	      /* This is tricky.  To do this right, we need to keep a
310112994Sjeff		 stack of search positions for the current search, with
311113660Sjeff		 sentinels marking the beginning and end. */
312112994Sjeff	    }
313113357Sjeff	  break;
314113357Sjeff#endif
315122744Sjeff
316121896Sjeff	default:
317123433Sjeff	  /* Add character to search string and continue search. */
318121896Sjeff	  if (search_string_index + 2 >= search_string_size)
319113357Sjeff	    {
320113357Sjeff	      search_string_size += 128;
321121869Sjeff	      search_string = xrealloc (search_string, search_string_size);
322113357Sjeff	    }
323113357Sjeff	  search_string[search_string_index++] = c;
324113357Sjeff	  search_string[search_string_index] = '\0';
325113357Sjeff	  break;
326112994Sjeff	}
327122744Sjeff
328122744Sjeff      for (found = failed = 0;;)
329122744Sjeff	{
330122744Sjeff	  int limit = sline_len - search_string_index + 1;
331123433Sjeff
332123433Sjeff	  /* Search the current line. */
333123433Sjeff	  while (reverse ? (line_index >= 0) : (line_index < limit))
334123433Sjeff	    {
335122744Sjeff	      if (STREQN (search_string, sline + line_index, search_string_index))
336122744Sjeff		{
337122744Sjeff		  found++;
338122744Sjeff		  break;
339122744Sjeff		}
340122744Sjeff	      else
341122744Sjeff		line_index += direction;
342122744Sjeff	    }
343123433Sjeff	  if (found)
344123433Sjeff	    break;
345123433Sjeff
346123433Sjeff	  /* Move to the next line, but skip new copies of the line
347122744Sjeff	     we just found and lines shorter than the string we're
348122744Sjeff	     searching for. */
349122744Sjeff	  do
350122744Sjeff	    {
351113357Sjeff	      /* Move to the next line. */
352122744Sjeff	      i += direction;
353113357Sjeff
354121896Sjeff	      /* At limit for direction? */
355115998Sjeff	      if (reverse ? (i < 0) : (i == hlen))
356121896Sjeff		{
357121896Sjeff		  failed++;
358121896Sjeff		  break;
359113357Sjeff		}
360125289Sjeff
361123487Sjeff	      /* We will need these later. */
362123487Sjeff	      sline = lines[i];
363125289Sjeff	      sline_len = strlen (sline);
364125289Sjeff	    }
365123487Sjeff	  while ((prev_line_found && STREQ (prev_line_found, lines[i])) ||
366113357Sjeff		 (search_string_index > sline_len));
367122744Sjeff
368122744Sjeff	  if (failed)
369122744Sjeff	    break;
370122744Sjeff
371113357Sjeff	  /* Now set up the line for searching... */
372113357Sjeff	  line_index = reverse ? sline_len - search_string_index : 0;
373110267Sjeff	}
374113357Sjeff
375112994Sjeff      if (failed)
376122744Sjeff	{
377110267Sjeff	  /* We cannot find the search string.  Ding the bell. */
378121896Sjeff	  ding ();
379115998Sjeff	  i = last_found_line;
380121896Sjeff	  continue; 		/* XXX - was break */
381121896Sjeff	}
382121896Sjeff
383125289Sjeff      /* We have found the search string.  Just display it.  But don't
384123487Sjeff	 actually move there in the history list until the user accepts
385123487Sjeff	 the location. */
386125289Sjeff      if (found)
387125289Sjeff	{
388123487Sjeff	  int line_len;
389113357Sjeff
390113357Sjeff	  prev_line_found = lines[i];
391113357Sjeff	  line_len = strlen (lines[i]);
392113357Sjeff
393110267Sjeff	  if (line_len >= rl_line_buffer_len)
394110267Sjeff	    rl_extend_line_buffer (line_len);
395113357Sjeff
396113357Sjeff	  strcpy (rl_line_buffer, lines[i]);
397110267Sjeff	  rl_point = line_index;
398115998Sjeff	  rl_end = line_len;
399113357Sjeff	  last_found_line = i;
400113357Sjeff	  rl_display_search (search_string, reverse, (i == orig_line) ? -1 : i);
401121896Sjeff	}
402113357Sjeff    }
403110267Sjeff
404110267Sjeff  /* The searching is over.  The user may have found the string that she
405113357Sjeff     was looking for, or else she may have exited a failing search.  If
406113357Sjeff     LINE_INDEX is -1, then that shows that the string searched for was
407110267Sjeff     not found.  We use this to determine where to place rl_point. */
408113357Sjeff
409113357Sjeff  /* First put back the original state. */
410115998Sjeff  strcpy (rl_line_buffer, lines[orig_line]);
411113357Sjeff
412113357Sjeff  _rl_restore_prompt ();
413113357Sjeff
414113357Sjeff  /* Free the search string. */
415113357Sjeff  free (search_string);
416113357Sjeff
417113357Sjeff  if (last_found_line < orig_line)
418113357Sjeff    rl_get_previous_history (orig_line - last_found_line);
419113357Sjeff  else
420113357Sjeff    rl_get_next_history (last_found_line - orig_line);
421113357Sjeff
422113357Sjeff  /* If the string was not found, put point at the end of the line. */
423121896Sjeff  if (line_index < 0)
424113357Sjeff    line_index = strlen (rl_line_buffer);
425113357Sjeff  rl_point = line_index;
426121869Sjeff  rl_clear_message ();
427113357Sjeff
428113357Sjeff  if (allocated_line)
429113357Sjeff    free (allocated_line);
430113357Sjeff  free (lines);
431110267Sjeff
432110267Sjeff  return 0;
433113357Sjeff}
434116069Sjeff