isearch.c revision 75406
121308Sache/* **************************************************************** */
221308Sache/*								    */
321308Sache/*			I-Search and Searching			    */
421308Sache/*								    */
521308Sache/* **************************************************************** */
621308Sache
721308Sache/* Copyright (C) 1987,1989 Free Software Foundation, Inc.
821308Sache
921308Sache   This file contains the Readline Library (the Library), a set of
1021308Sache   routines for providing Emacs style line input to programs that ask
1121308Sache   for it.
1221308Sache
1321308Sache   The Library is free software; you can redistribute it and/or modify
1421308Sache   it under the terms of the GNU General Public License as published by
1558310Sache   the Free Software Foundation; either version 2, or (at your option)
1621308Sache   any later version.
1721308Sache
1821308Sache   The Library is distributed in the hope that it will be useful, but
1921308Sache   WITHOUT ANY WARRANTY; without even the implied warranty of
2021308Sache   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
2121308Sache   General Public License for more details.
2221308Sache
2321308Sache   The GNU General Public License is often shipped with GNU software, and
2421308Sache   is generally kept in a file called COPYING or LICENSE.  If you do not
2521308Sache   have a copy of the license, write to the Free Software Foundation,
2658310Sache   59 Temple Place, Suite 330, Boston, MA 02111 USA. */
2721308Sache#define READLINE_LIBRARY
2821308Sache
2921308Sache#if defined (HAVE_CONFIG_H)
3021308Sache#  include <config.h>
3121308Sache#endif
3221308Sache
3326497Sache#include <sys/types.h>
3426497Sache
3521308Sache#include <stdio.h>
3621308Sache
3721308Sache#if defined (HAVE_UNISTD_H)
3821308Sache#  include <unistd.h>
3921308Sache#endif
4021308Sache
4126497Sache#if defined (HAVE_STDLIB_H)
4226497Sache#  include <stdlib.h>
4326497Sache#else
4426497Sache#  include "ansi_stdlib.h"
4526497Sache#endif
4621308Sache
4721308Sache#include "rldefs.h"
4821308Sache#include "readline.h"
4921308Sache#include "history.h"
5021308Sache
5158310Sache#include "rlprivate.h"
5258310Sache#include "xmalloc.h"
5358310Sache
5447558Sache/* Variables exported to other files in the readline library. */
5547558Sacheunsigned char *_rl_isearch_terminators = (unsigned char *)NULL;
5647558Sache
5721308Sache/* Variables imported from other files in the readline library. */
5875406Sacheextern HIST_ENTRY *_rl_saved_line_for_history;
5921308Sache
6058310Sache/* Forward declarations */
6158310Sachestatic int rl_search_history __P((int, int));
6221308Sache
6321308Sache/* Last line found by the current incremental search, so we don't `find'
6421308Sache   identical lines many times in a row. */
6521308Sachestatic char *prev_line_found;
6621308Sache
6775406Sachestatic unsigned char *default_isearch_terminators = "\033\012";
6875406Sache
6921308Sache/* Search backwards through the history looking for a string which is typed
7021308Sache   interactively.  Start with the current line. */
7121308Sacheint
7221308Sacherl_reverse_search_history (sign, key)
7321308Sache     int sign, key;
7421308Sache{
7521308Sache  return (rl_search_history (-sign, key));
7621308Sache}
7721308Sache
7821308Sache/* Search forwards through the history looking for a string which is typed
7921308Sache   interactively.  Start with the current line. */
8021308Sacheint
8121308Sacherl_forward_search_history (sign, key)
8221308Sache     int sign, key;
8321308Sache{
8421308Sache  return (rl_search_history (sign, key));
8521308Sache}
8621308Sache
8721308Sache/* Display the current state of the search in the echo-area.
8821308Sache   SEARCH_STRING contains the string that is being searched for,
8921308Sache   DIRECTION is zero for forward, or 1 for reverse,
9021308Sache   WHERE is the history list number of the current line.  If it is
9121308Sache   -1, then this line is the starting one. */
9221308Sachestatic void
9321308Sacherl_display_search (search_string, reverse_p, where)
9421308Sache     char *search_string;
9521308Sache     int reverse_p, where;
9621308Sache{
9721308Sache  char *message;
9821308Sache  int msglen, searchlen;
9921308Sache
10021308Sache  searchlen = (search_string && *search_string) ? strlen (search_string) : 0;
10121308Sache
10221308Sache  message = xmalloc (searchlen + 33);
10321308Sache  msglen = 0;
10421308Sache
10521308Sache#if defined (NOTDEF)
10621308Sache  if (where != -1)
10721308Sache    {
10821308Sache      sprintf (message, "[%d]", where + history_base);
10921308Sache      msglen = strlen (message);
11021308Sache    }
11121308Sache#endif /* NOTDEF */
11221308Sache
11321308Sache  message[msglen++] = '(';
11421308Sache
11521308Sache  if (reverse_p)
11621308Sache    {
11721308Sache      strcpy (message + msglen, "reverse-");
11821308Sache      msglen += 8;
11921308Sache    }
12021308Sache
12121308Sache  strcpy (message + msglen, "i-search)`");
12221308Sache  msglen += 10;
12321308Sache
12421308Sache  if (search_string)
12521308Sache    {
12621308Sache      strcpy (message + msglen, search_string);
12721308Sache      msglen += searchlen;
12821308Sache    }
12921308Sache
13021308Sache  strcpy (message + msglen, "': ");
13121308Sache
13221308Sache  rl_message ("%s", message, 0);
13321308Sache  free (message);
13421308Sache  (*rl_redisplay_function) ();
13521308Sache}
13621308Sache
13721308Sache/* Search through the history looking for an interactively typed string.
13821308Sache   This is analogous to i-search.  We start the search in the current line.
13921308Sache   DIRECTION is which direction to search; >= 0 means forward, < 0 means
14021308Sache   backwards. */
14121308Sachestatic int
14221308Sacherl_search_history (direction, invoking_key)
14321308Sache     int direction, invoking_key;
14421308Sache{
14521308Sache  /* The string that the user types in to search for. */
14621308Sache  char *search_string;
14721308Sache
14821308Sache  /* The current length of SEARCH_STRING. */
14921308Sache  int search_string_index;
15021308Sache
15121308Sache  /* The amount of space that SEARCH_STRING has allocated to it. */
15221308Sache  int search_string_size;
15321308Sache
15421308Sache  /* The list of lines to search through. */
15521308Sache  char **lines, *allocated_line;
15621308Sache
15721308Sache  /* The length of LINES. */
15821308Sache  int hlen;
15921308Sache
16021308Sache  /* Where we get LINES from. */
16121308Sache  HIST_ENTRY **hlist;
16221308Sache
16321308Sache  register int i;
16421308Sache  int orig_point, orig_line, last_found_line;
16521308Sache  int c, found, failed, sline_len;
16621308Sache
16721308Sache  /* The line currently being searched. */
16821308Sache  char *sline;
16921308Sache
17021308Sache  /* Offset in that line. */
17121308Sache  int line_index;
17221308Sache
17321308Sache  /* Non-zero if we are doing a reverse search. */
17421308Sache  int reverse;
17521308Sache
17647558Sache  /* The list of characters which terminate the search, but are not
17747558Sache     subsequently executed.  If the variable isearch-terminators has
17847558Sache     been set, we use that value, otherwise we use ESC and C-J. */
17947558Sache  unsigned char *isearch_terminators;
18047558Sache
18175406Sache  RL_SETSTATE(RL_STATE_ISEARCH);
18221308Sache  orig_point = rl_point;
18321308Sache  last_found_line = orig_line = where_history ();
18421308Sache  reverse = direction < 0;
18521308Sache  hlist = history_list ();
18621308Sache  allocated_line = (char *)NULL;
18721308Sache
18847558Sache  isearch_terminators = _rl_isearch_terminators ? _rl_isearch_terminators
18975406Sache						: default_isearch_terminators;
19047558Sache
19121308Sache  /* Create an arrary of pointers to the lines that we want to search. */
19275406Sache  rl_maybe_replace_line ();
19321308Sache  i = 0;
19421308Sache  if (hlist)
19521308Sache    for (i = 0; hlist[i]; i++);
19621308Sache
19721308Sache  /* Allocate space for this many lines, +1 for the current input line,
19821308Sache     and remember those lines. */
19921308Sache  lines = (char **)xmalloc ((1 + (hlen = i)) * sizeof (char *));
20021308Sache  for (i = 0; i < hlen; i++)
20121308Sache    lines[i] = hlist[i]->line;
20221308Sache
20375406Sache  if (_rl_saved_line_for_history)
20475406Sache    lines[i] = _rl_saved_line_for_history->line;
20521308Sache  else
20621308Sache    {
20721308Sache      /* Keep track of this so we can free it. */
20821308Sache      allocated_line = xmalloc (1 + strlen (rl_line_buffer));
20921308Sache      strcpy (allocated_line, &rl_line_buffer[0]);
21021308Sache      lines[i] = allocated_line;
21121308Sache    }
21221308Sache
21321308Sache  hlen++;
21421308Sache
21521308Sache  /* The line where we start the search. */
21621308Sache  i = orig_line;
21721308Sache
21847558Sache  rl_save_prompt ();
21921308Sache
22021308Sache  /* Initialize search parameters. */
22121308Sache  search_string = xmalloc (search_string_size = 128);
22221308Sache  *search_string = '\0';
22321308Sache  search_string_index = 0;
22421308Sache  prev_line_found = (char *)0;		/* XXX */
22521308Sache
22621308Sache  /* Normalize DIRECTION into 1 or -1. */
22721308Sache  direction = (direction >= 0) ? 1 : -1;
22821308Sache
22921308Sache  rl_display_search (search_string, reverse, -1);
23021308Sache
23121308Sache  sline = rl_line_buffer;
23221308Sache  sline_len = strlen (sline);
23321308Sache  line_index = rl_point;
23421308Sache
23521308Sache  found = failed = 0;
23621308Sache  for (;;)
23721308Sache    {
23875406Sache      rl_command_func_t *f = (rl_command_func_t *)NULL;
23921308Sache
24021308Sache      /* Read a key and decide how to proceed. */
24175406Sache      RL_SETSTATE(RL_STATE_MOREINPUT);
24221308Sache      c = rl_read_key ();
24375406Sache      RL_UNSETSTATE(RL_STATE_MOREINPUT);
24421308Sache
24521308Sache      if (_rl_keymap[c].type == ISFUNC)
24621308Sache	{
24721308Sache	  f = _rl_keymap[c].function;
24821308Sache
24921308Sache	  if (f == rl_reverse_search_history)
25021308Sache	    c = reverse ? -1 : -2;
25121308Sache	  else if (f == rl_forward_search_history)
25221308Sache	    c =  !reverse ? -1 : -2;
25321308Sache	}
25421308Sache
25547558Sache#if 0
25621308Sache      /* Let NEWLINE (^J) terminate the search for people who don't like
25721308Sache	 using ESC.  ^M can still be used to terminate the search and
25821308Sache	 immediately execute the command. */
25921308Sache      if (c == ESC || c == NEWLINE)
26047558Sache#else
26147558Sache      /* The characters in isearch_terminators (set from the user-settable
26247558Sache	 variable isearch-terminators) are used to terminate the search but
26347558Sache	 not subsequently execute the character as a command.  The default
26447558Sache	 value is "\033\012" (ESC and C-J). */
26547558Sache      if (strchr (isearch_terminators, c))
26647558Sache#endif
26721308Sache	{
26821308Sache	  /* ESC still terminates the search, but if there is pending
26921308Sache	     input or if input arrives within 0.1 seconds (on systems
27021308Sache	     with select(2)) it is used as a prefix character
27121308Sache	     with rl_execute_next.  WATCH OUT FOR THIS!  This is intended
27221308Sache	     to allow the arrow keys to be used like ^F and ^B are used
27321308Sache	     to terminate the search and execute the movement command. */
27421308Sache	  if (c == ESC && _rl_input_available ())	/* XXX */
27521308Sache	    rl_execute_next (ESC);
27621308Sache	  break;
27721308Sache	}
27821308Sache
27935486Sache      if (c >= 0 && (CTRL_CHAR (c) || META_CHAR (c) || c == RUBOUT) && c != CTRL ('G'))
28021308Sache	{
28175406Sache	  /* This sets rl_pending_input to c; it will be picked up the next
28275406Sache	     time rl_read_key is called. */
28321308Sache	  rl_execute_next (c);
28421308Sache	  break;
28521308Sache	}
28621308Sache
28721308Sache      switch (c)
28821308Sache	{
28921308Sache	case -1:
29021308Sache	  if (search_string_index == 0)
29121308Sache	    continue;
29221308Sache	  else if (reverse)
29321308Sache	    --line_index;
29421308Sache	  else if (line_index != sline_len)
29521308Sache	    ++line_index;
29621308Sache	  else
29775406Sache	    rl_ding ();
29821308Sache	  break;
29921308Sache
30021308Sache	  /* switch directions */
30121308Sache	case -2:
30221308Sache	  direction = -direction;
30321308Sache	  reverse = direction < 0;
30421308Sache	  break;
30521308Sache
30621308Sache	case CTRL ('G'):
30721308Sache	  strcpy (rl_line_buffer, lines[orig_line]);
30821308Sache	  rl_point = orig_point;
30921308Sache	  rl_end = strlen (rl_line_buffer);
31047558Sache	  rl_restore_prompt();
31121308Sache	  rl_clear_message ();
31226497Sache	  if (allocated_line)
31326497Sache	    free (allocated_line);
31421308Sache	  free (lines);
31575406Sache	  RL_UNSETSTATE(RL_STATE_ISEARCH);
31621308Sache	  return 0;
31721308Sache
31835486Sache#if 0
31935486Sache	/* delete character from search string. */
32035486Sache	case -3:
32135486Sache	  if (search_string_index == 0)
32275406Sache	    rl_ding ();
32335486Sache	  else
32435486Sache	    {
32535486Sache	      search_string[--search_string_index] = '\0';
32635486Sache	      /* This is tricky.  To do this right, we need to keep a
32735486Sache		 stack of search positions for the current search, with
32835486Sache		 sentinels marking the beginning and end. */
32935486Sache	    }
33035486Sache	  break;
33135486Sache#endif
33235486Sache
33321308Sache	default:
33421308Sache	  /* Add character to search string and continue search. */
33521308Sache	  if (search_string_index + 2 >= search_string_size)
33621308Sache	    {
33721308Sache	      search_string_size += 128;
33821308Sache	      search_string = xrealloc (search_string, search_string_size);
33921308Sache	    }
34021308Sache	  search_string[search_string_index++] = c;
34121308Sache	  search_string[search_string_index] = '\0';
34221308Sache	  break;
34321308Sache	}
34421308Sache
34521308Sache      for (found = failed = 0;;)
34621308Sache	{
34721308Sache	  int limit = sline_len - search_string_index + 1;
34821308Sache
34921308Sache	  /* Search the current line. */
35021308Sache	  while (reverse ? (line_index >= 0) : (line_index < limit))
35121308Sache	    {
35221308Sache	      if (STREQN (search_string, sline + line_index, search_string_index))
35321308Sache		{
35421308Sache		  found++;
35521308Sache		  break;
35621308Sache		}
35721308Sache	      else
35821308Sache		line_index += direction;
35921308Sache	    }
36021308Sache	  if (found)
36121308Sache	    break;
36221308Sache
36321308Sache	  /* Move to the next line, but skip new copies of the line
36421308Sache	     we just found and lines shorter than the string we're
36521308Sache	     searching for. */
36621308Sache	  do
36721308Sache	    {
36821308Sache	      /* Move to the next line. */
36921308Sache	      i += direction;
37021308Sache
37121308Sache	      /* At limit for direction? */
37221308Sache	      if (reverse ? (i < 0) : (i == hlen))
37321308Sache		{
37421308Sache		  failed++;
37521308Sache		  break;
37621308Sache		}
37721308Sache
37821308Sache	      /* We will need these later. */
37921308Sache	      sline = lines[i];
38021308Sache	      sline_len = strlen (sline);
38121308Sache	    }
38221308Sache	  while ((prev_line_found && STREQ (prev_line_found, lines[i])) ||
38321308Sache		 (search_string_index > sline_len));
38421308Sache
38521308Sache	  if (failed)
38621308Sache	    break;
38721308Sache
38821308Sache	  /* Now set up the line for searching... */
38921308Sache	  line_index = reverse ? sline_len - search_string_index : 0;
39021308Sache	}
39121308Sache
39221308Sache      if (failed)
39321308Sache	{
39421308Sache	  /* We cannot find the search string.  Ding the bell. */
39575406Sache	  rl_ding ();
39621308Sache	  i = last_found_line;
39721308Sache	  continue; 		/* XXX - was break */
39821308Sache	}
39921308Sache
40021308Sache      /* We have found the search string.  Just display it.  But don't
40121308Sache	 actually move there in the history list until the user accepts
40221308Sache	 the location. */
40321308Sache      if (found)
40421308Sache	{
40521308Sache	  int line_len;
40621308Sache
40721308Sache	  prev_line_found = lines[i];
40821308Sache	  line_len = strlen (lines[i]);
40921308Sache
41021308Sache	  if (line_len >= rl_line_buffer_len)
41121308Sache	    rl_extend_line_buffer (line_len);
41221308Sache
41321308Sache	  strcpy (rl_line_buffer, lines[i]);
41421308Sache	  rl_point = line_index;
41521308Sache	  rl_end = line_len;
41621308Sache	  last_found_line = i;
41721308Sache	  rl_display_search (search_string, reverse, (i == orig_line) ? -1 : i);
41821308Sache	}
41921308Sache    }
42021308Sache
42121308Sache  /* The searching is over.  The user may have found the string that she
42221308Sache     was looking for, or else she may have exited a failing search.  If
42321308Sache     LINE_INDEX is -1, then that shows that the string searched for was
42421308Sache     not found.  We use this to determine where to place rl_point. */
42521308Sache
42621308Sache  /* First put back the original state. */
42721308Sache  strcpy (rl_line_buffer, lines[orig_line]);
42821308Sache
42947558Sache  rl_restore_prompt ();
43021308Sache
43121308Sache  /* Free the search string. */
43221308Sache  free (search_string);
43321308Sache
43421308Sache  if (last_found_line < orig_line)
43547558Sache    rl_get_previous_history (orig_line - last_found_line, 0);
43621308Sache  else
43747558Sache    rl_get_next_history (last_found_line - orig_line, 0);
43821308Sache
43921308Sache  /* If the string was not found, put point at the end of the line. */
44021308Sache  if (line_index < 0)
44121308Sache    line_index = strlen (rl_line_buffer);
44221308Sache  rl_point = line_index;
44321308Sache  rl_clear_message ();
44421308Sache
44526497Sache  if (allocated_line)
44626497Sache    free (allocated_line);
44721308Sache  free (lines);
44821308Sache
44975406Sache  RL_UNSETSTATE(RL_STATE_ISEARCH);
45075406Sache
45121308Sache  return 0;
45221308Sache}
453