search.c revision 21308
1227825Stheraven/* search.c - code for non-incremental searching in emacs and vi modes. */
2227825Stheraven
3227825Stheraven/* Copyright (C) 1992 Free Software Foundation, Inc.
4227825Stheraven
5227825Stheraven   This file is part of the Readline Library (the Library), a set of
6227825Stheraven   routines for providing Emacs style line input to programs that ask
7227825Stheraven   for it.
8227825Stheraven
9227825Stheraven   The Library is free software; you can redistribute it and/or modify
10227825Stheraven   it under the terms of the GNU General Public License as published by
11227825Stheraven   the Free Software Foundation; either version 1, or (at your option)
12227825Stheraven   any later version.
13227825Stheraven
14227825Stheraven   The Library is distributed in the hope that it will be useful, but
15227825Stheraven   WITHOUT ANY WARRANTY; without even the implied warranty of
16227825Stheraven   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17227825Stheraven   General Public License for more details.
18227825Stheraven
19227825Stheraven   The GNU General Public License is often shipped with GNU software, and
20227825Stheraven   is generally kept in a file called COPYING or LICENSE.  If you do not
21227825Stheraven   have a copy of the license, write to the Free Software Foundation,
22227825Stheraven   675 Mass Ave, Cambridge, MA 02139, USA. */
23227825Stheraven#define READLINE_LIBRARY
24227825Stheraven
25227825Stheraven#if defined (HAVE_CONFIG_H)
26227825Stheraven#  include <config.h>
27227825Stheraven#endif
28227825Stheraven
29227825Stheraven#include <sys/types.h>
30227825Stheraven#include <stdio.h>
31227825Stheraven
32227825Stheraven#if defined (HAVE_UNISTD_H)
33227825Stheraven#  include <unistd.h>
34227825Stheraven#endif
35227825Stheraven
36227825Stheraven#include "rldefs.h"
37227825Stheraven#include "readline.h"
38227825Stheraven#include "history.h"
39227825Stheraven
40227825Stheraven#define abs(x)		(((x) > 0) ? (x) : -(x))
41227825Stheraven
42227825Stheravenextern char *xmalloc (), *xrealloc ();
43227825Stheraven
44227825Stheraven/* Variables imported from readline.c */
45227825Stheravenextern int rl_point, rl_end, rl_line_buffer_len;
46227825Stheravenextern int rl_editing_mode;
47227825Stheravenextern char *rl_prompt;
48227825Stheravenextern char *rl_line_buffer;
49227825Stheravenextern HIST_ENTRY *saved_line_for_history;
50227825Stheravenextern Function *rl_last_func;
51227825Stheraven
52227825Stheraven/* Functions imported from the rest of the library. */
53227825Stheravenextern int _rl_free_history_entry ();
54227825Stheravenextern char *_rl_make_prompt_for_search ();
55227825Stheravenextern void _rl_restore_prompt ();
56227825Stheravenextern void rl_extend_line_buffer ();
57227825Stheraven
58227825Stheravenstatic char *noninc_search_string = (char *) NULL;
59262801Sdimstatic int noninc_history_pos;
60227825Stheravenstatic char *prev_line_found = (char *) NULL;
61227825Stheraven
62227825Stheraven/* Search the history list for STRING starting at absolute history position
63227825Stheraven   POS.  If STRING begins with `^', the search must match STRING at the
64227825Stheraven   beginning of a history line, otherwise a full substring match is performed
65227825Stheraven   for STRING.  DIR < 0 means to search backwards through the history list,
66227825Stheraven   DIR >= 0 means to search forward. */
67227825Stheravenstatic int
68227825Stheravennoninc_search_from_pos (string, pos, dir)
69227825Stheraven     char *string;
70227825Stheraven     int pos, dir;
71262801Sdim{
72227825Stheraven  int ret, old;
73227825Stheraven
74227825Stheraven  old = where_history ();
75227825Stheraven  history_set_pos (pos);
76227825Stheraven
77262801Sdim  if (*string == '^')
78227825Stheraven    ret = history_search_prefix (string + 1, dir);
79227825Stheraven  else
80227825Stheraven    ret = history_search (string, dir);
81227825Stheraven
82227825Stheraven  if (ret != -1)
83262801Sdim    ret = where_history ();
84227825Stheraven
85227825Stheraven  history_set_pos (old);
86227825Stheraven  return (ret);
87227825Stheraven}
88227825Stheraven
89262801Sdim/* Search for a line in the history containing STRING.  If DIR is < 0, the
90227825Stheraven   search is backwards through previous entries, else through subsequent
91227825Stheraven   entries. */
92227825Stheravenstatic void
93227825Stheravennoninc_dosearch (string, dir)
94227825Stheraven     char *string;
95262801Sdim     int dir;
96227825Stheraven{
97227825Stheraven  int oldpos, pos, line_len;
98227825Stheraven  HIST_ENTRY *entry;
99227825Stheraven
100227825Stheraven  if (string == 0 || *string == '\0' || noninc_history_pos < 0)
101262801Sdim    {
102227825Stheraven      ding ();
103227825Stheraven      return;
104227825Stheraven    }
105227825Stheraven
106227825Stheraven  pos = noninc_search_from_pos (string, noninc_history_pos + dir, dir);
107262801Sdim  if (pos == -1)
108227825Stheraven    {
109227825Stheraven      /* Search failed, current history position unchanged. */
110227825Stheraven      maybe_unsave_line ();
111227825Stheraven      rl_clear_message ();
112227825Stheraven      rl_point = 0;
113262801Sdim      ding ();
114227825Stheraven      return;
115227825Stheraven    }
116227825Stheraven
117227825Stheraven  noninc_history_pos = pos;
118227825Stheraven
119262801Sdim  oldpos = where_history ();
120227825Stheraven  history_set_pos (noninc_history_pos);
121227825Stheraven  entry = current_history ();
122227825Stheraven#if defined (VI_MODE)
123227825Stheraven  if (rl_editing_mode != vi_mode)
124227825Stheraven#endif
125262801Sdim  history_set_pos (oldpos);
126227825Stheraven
127227825Stheraven  line_len = strlen (entry->line);
128227825Stheraven  if (line_len >= rl_line_buffer_len)
129227825Stheraven    rl_extend_line_buffer (line_len);
130227825Stheraven  strcpy (rl_line_buffer, entry->line);
131262801Sdim
132227825Stheraven  rl_undo_list = (UNDO_LIST *)entry->data;
133227825Stheraven  rl_end = strlen (rl_line_buffer);
134227825Stheraven  rl_point = 0;
135227825Stheraven  rl_clear_message ();
136227825Stheraven
137262801Sdim  if (saved_line_for_history)
138227825Stheraven    _rl_free_history_entry (saved_line_for_history);
139227825Stheraven  saved_line_for_history = (HIST_ENTRY *)NULL;
140227825Stheraven}
141227825Stheraven
142227825Stheraven/* Search non-interactively through the history list.  DIR < 0 means to
143262801Sdim   search backwards through the history of previous commands; otherwise
144227825Stheraven   the search is for commands subsequent to the current position in the
145227825Stheraven   history list.  PCHAR is the character to use for prompting when reading
146227825Stheraven   the search string; if not specified (0), it defaults to `:'. */
147227825Stheravenstatic void
148227825Stheravennoninc_search (dir, pchar)
149262801Sdim     int dir;
150227825Stheraven     int pchar;
151227825Stheraven{
152227825Stheraven  int saved_point, c;
153227825Stheraven  char *p;
154227825Stheraven
155262801Sdim  maybe_save_line ();
156227825Stheraven  saved_point = rl_point;
157227825Stheraven
158227825Stheraven  /* Use the line buffer to read the search string. */
159227825Stheraven  rl_line_buffer[0] = 0;
160227825Stheraven  rl_end = rl_point = 0;
161262801Sdim
162262801Sdim  p = _rl_make_prompt_for_search (pchar ? pchar : ':');
163262801Sdim  rl_message (p, 0, 0);
164262801Sdim  free (p);
165262801Sdim
166262801Sdim#define SEARCH_RETURN _rl_restore_prompt (); return
167262801Sdim
168262801Sdim  /* Read the search string. */
169262801Sdim  while (c = rl_read_key ())
170262801Sdim    {
171262801Sdim      switch (c)
172262801Sdim	{
173262801Sdim	case CTRL('H'):
174262801Sdim	case RUBOUT:
175262801Sdim	  if (rl_point == 0)
176262801Sdim	    {
177262801Sdim	      maybe_unsave_line ();
178262801Sdim	      rl_clear_message ();
179262801Sdim	      rl_point = saved_point;
180262801Sdim	      SEARCH_RETURN;
181262801Sdim	    }
182262801Sdim	  rl_rubout (1, c);
183262801Sdim	  break;
184262801Sdim
185227825Stheraven	case CTRL('W'):
186227825Stheraven	  rl_unix_word_rubout (1, c);
187227825Stheraven	  break;
188227825Stheraven
189227825Stheraven	case CTRL('U'):
190227825Stheraven	  rl_unix_line_discard (1, c);
191227825Stheraven	  break;
192227825Stheraven
193227825Stheraven	case RETURN:
194227825Stheraven	case NEWLINE:
195227825Stheraven	  goto dosearch;
196227825Stheraven	  /* NOTREACHED */
197227825Stheraven	  break;
198227825Stheraven
199227825Stheraven	case CTRL('C'):
200227825Stheraven	case CTRL('G'):
201227825Stheraven	  maybe_unsave_line ();
202227825Stheraven	  rl_clear_message ();
203227825Stheraven	  rl_point = saved_point;
204227825Stheraven	  ding ();
205227825Stheraven	  SEARCH_RETURN;
206227825Stheraven
207227825Stheraven	default:
208227825Stheraven	  rl_insert (1, c);
209227825Stheraven	  break;
210227825Stheraven	}
211227825Stheraven      (*rl_redisplay_function) ();
212227825Stheraven    }
213227825Stheraven
214227825Stheraven dosearch:
215227825Stheraven  /* If rl_point == 0, we want to re-use the previous search string and
216227825Stheraven     start from the saved history position.  If there's no previous search
217227825Stheraven     string, punt. */
218227825Stheraven  if (rl_point == 0)
219227825Stheraven    {
220227825Stheraven      if (!noninc_search_string)
221227825Stheraven	{
222227825Stheraven	  ding ();
223227825Stheraven	  SEARCH_RETURN;
224227825Stheraven	}
225232950Stheraven    }
226227825Stheraven  else
227227825Stheraven    {
228227825Stheraven      /* We want to start the search from the current history position. */
229227825Stheraven      noninc_history_pos = where_history ();
230227825Stheraven      if (noninc_search_string)
231227825Stheraven	free (noninc_search_string);
232227825Stheraven      noninc_search_string = savestring (rl_line_buffer);
233227825Stheraven    }
234227825Stheraven
235227825Stheraven  _rl_restore_prompt ();
236227825Stheraven  noninc_dosearch (noninc_search_string, dir);
237227825Stheraven}
238227825Stheraven
239227825Stheraven/* Search forward through the history list for a string.  If the vi-mode
240227825Stheraven   code calls this, KEY will be `?'. */
241227825Stheravenint
242227825Stheravenrl_noninc_forward_search (count, key)
243227825Stheraven     int count, key;
244227825Stheraven{
245227825Stheraven  noninc_search (1, (key == '?') ? '?' : 0);
246227825Stheraven  return 0;
247227825Stheraven}
248227825Stheraven
249227825Stheraven/* Reverse search the history list for a string.  If the vi-mode code
250227825Stheraven   calls this, KEY will be `/'. */
251227825Stheravenint
252227825Stheravenrl_noninc_reverse_search (count, key)
253227825Stheraven     int count, key;
254227825Stheraven{
255227825Stheraven  noninc_search (-1, (key == '/') ? '/' : 0);
256227825Stheraven  return 0;
257227825Stheraven}
258227825Stheraven
259227825Stheraven/* Search forward through the history list for the last string searched
260227825Stheraven   for.  If there is no saved search string, abort. */
261227825Stheravenint
262227825Stheravenrl_noninc_forward_search_again (count, key)
263227825Stheraven     int count, key;
264227825Stheraven{
265227825Stheraven  if (!noninc_search_string)
266227825Stheraven    {
267227825Stheraven      ding ();
268227825Stheraven      return (-1);
269227825Stheraven    }
270227825Stheraven  noninc_dosearch (noninc_search_string, 1);
271227825Stheraven  return 0;
272227825Stheraven}
273227825Stheraven
274227825Stheraven/* Reverse search in the history list for the last string searched
275227825Stheraven   for.  If there is no saved search string, abort. */
276227825Stheravenint
277227825Stheravenrl_noninc_reverse_search_again (count, key)
278227825Stheraven     int count, key;
279227825Stheraven{
280227825Stheraven  if (!noninc_search_string)
281227825Stheraven    {
282227825Stheraven      ding ();
283227825Stheraven      return (-1);
284227825Stheraven    }
285227825Stheraven  noninc_dosearch (noninc_search_string, -1);
286227825Stheraven  return 0;
287227825Stheraven}
288227825Stheraven
289227825Stheravenstatic int
290227825Stheravenrl_history_search_internal (count, direction)
291227825Stheraven     int count, direction;
292227825Stheraven{
293227825Stheraven  HIST_ENTRY *temp, *old_temp;
294227825Stheraven  int line_len;
295227825Stheraven
296227825Stheraven  maybe_save_line ();
297227825Stheraven
298227825Stheraven  temp = old_temp = (HIST_ENTRY *)NULL;
299227825Stheraven  while (count)
300227825Stheraven    {
301227825Stheraven      temp = (direction < 0) ? previous_history () : next_history ();
302227825Stheraven      if (temp == 0)
303227825Stheraven        break;
304227825Stheraven      /* On an empty prefix, make this the same as previous-history. */
305227825Stheraven      if (rl_point == 0)
306227825Stheraven	{
307227825Stheraven	  count--;
308227825Stheraven	  continue;
309227825Stheraven	}
310227825Stheraven      if (STREQN (rl_line_buffer, temp->line, rl_point))
311227825Stheraven	{
312227825Stheraven	  /* Don't find multiple instances of the same line. */
313227825Stheraven	  if (prev_line_found && STREQ (prev_line_found, temp->line))
314227825Stheraven	    continue;
315227825Stheraven          if (direction < 0)
316227825Stheraven            old_temp = temp;
317227825Stheraven          prev_line_found = temp->line;
318227825Stheraven          count--;
319227825Stheraven	}
320227825Stheraven    }
321227825Stheraven
322227825Stheraven  if (temp == 0)
323227825Stheraven    {
324227825Stheraven      if (direction < 0 && old_temp)
325227825Stheraven	temp = old_temp;
326227825Stheraven      else
327227825Stheraven	{
328227825Stheraven	  maybe_unsave_line ();
329227825Stheraven	  ding ();
330227825Stheraven	  return 1;
331227825Stheraven	}
332227825Stheraven    }
333227825Stheraven
334227825Stheraven  line_len = strlen (temp->line);
335227825Stheraven  if (line_len >= rl_line_buffer_len)
336227825Stheraven    rl_extend_line_buffer (line_len);
337227825Stheraven  strcpy (rl_line_buffer, temp->line);
338227825Stheraven  rl_undo_list = (UNDO_LIST *)temp->data;
339227825Stheraven  rl_end = line_len;
340227825Stheraven  return 0;
341227825Stheraven}
342227825Stheraven
343227825Stheraven/* Search forward in the history for the string of characters
344227825Stheraven   from the start of the line to rl_point.  This is a non-incremental
345227825Stheraven   search. */
346227825Stheravenint
347227825Stheravenrl_history_search_forward (count, ignore)
348227825Stheraven     int count, ignore;
349227825Stheraven{
350227825Stheraven  if (count == 0)
351227825Stheraven    return (0);
352227825Stheraven  if (rl_last_func != rl_history_search_forward)
353227825Stheraven    prev_line_found = (char *)NULL;
354227825Stheraven  return (rl_history_search_internal (abs (count), (count > 0) ? 1 : -1));
355227825Stheraven}
356227825Stheraven
357227825Stheraven/* Search backward through the history for the string of characters
358227825Stheraven   from the start of the line to rl_point.  This is a non-incremental
359227825Stheraven   search. */
360227825Stheravenint
361227825Stheravenrl_history_search_backward (count, ignore)
362227825Stheraven     int count, ignore;
363227825Stheraven{
364227825Stheraven  if (count == 0)
365227825Stheraven    return (0);
366227825Stheraven  if (rl_last_func != rl_history_search_backward)
367227825Stheraven    prev_line_found = (char *)NULL;
368227825Stheraven  return (rl_history_search_internal (abs (count), (count > 0) ? -1 : 1));
369227825Stheraven}
370227825Stheraven