search.c revision 26497
1/* search.c - code for non-incremental searching in emacs and vi modes. */
2
3/* Copyright (C) 1992 Free Software Foundation, Inc.
4
5   This file is part of the Readline Library (the Library), a set of
6   routines for providing Emacs style line input to programs that ask
7   for it.
8
9   The Library is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; either version 1, or (at your option)
12   any later version.
13
14   The Library is distributed in the hope that it will be useful, but
15   WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17   General Public License for more details.
18
19   The GNU General Public License is often shipped with GNU software, and
20   is generally kept in a file called COPYING or LICENSE.  If you do not
21   have a copy of the license, write to the Free Software Foundation,
22   675 Mass Ave, Cambridge, MA 02139, USA. */
23#define READLINE_LIBRARY
24
25#if defined (HAVE_CONFIG_H)
26#  include <config.h>
27#endif
28
29#include <sys/types.h>
30#include <stdio.h>
31
32#if defined (HAVE_UNISTD_H)
33#  include <unistd.h>
34#endif
35
36#if defined (HAVE_STDLIB_H)
37#  include <stdlib.h>
38#else
39#  include "ansi_stdlib.h"
40#endif
41
42#include "rldefs.h"
43#include "readline.h"
44#include "history.h"
45
46#ifdef abs
47#  undef abs
48#endif
49#define abs(x)		(((x) >= 0) ? (x) : -(x))
50
51extern char *xmalloc (), *xrealloc ();
52
53/* Variables imported from readline.c */
54extern int rl_point, rl_end, rl_line_buffer_len;
55extern int rl_editing_mode;
56extern char *rl_prompt;
57extern char *rl_line_buffer;
58extern HIST_ENTRY *saved_line_for_history;
59extern Function *rl_last_func;
60
61/* Functions imported from the rest of the library. */
62extern int _rl_free_history_entry ();
63extern char *_rl_make_prompt_for_search ();
64extern void _rl_restore_prompt ();
65extern void rl_extend_line_buffer ();
66
67static char *noninc_search_string = (char *) NULL;
68static int noninc_history_pos;
69static char *prev_line_found = (char *) NULL;
70
71/* Search the history list for STRING starting at absolute history position
72   POS.  If STRING begins with `^', the search must match STRING at the
73   beginning of a history line, otherwise a full substring match is performed
74   for STRING.  DIR < 0 means to search backwards through the history list,
75   DIR >= 0 means to search forward. */
76static int
77noninc_search_from_pos (string, pos, dir)
78     char *string;
79     int pos, dir;
80{
81  int ret, old;
82
83  old = where_history ();
84  history_set_pos (pos);
85
86  if (*string == '^')
87    ret = history_search_prefix (string + 1, dir);
88  else
89    ret = history_search (string, dir);
90
91  if (ret != -1)
92    ret = where_history ();
93
94  history_set_pos (old);
95  return (ret);
96}
97
98/* Search for a line in the history containing STRING.  If DIR is < 0, the
99   search is backwards through previous entries, else through subsequent
100   entries. */
101static void
102noninc_dosearch (string, dir)
103     char *string;
104     int dir;
105{
106  int oldpos, pos, line_len;
107  HIST_ENTRY *entry;
108
109  if (string == 0 || *string == '\0' || noninc_history_pos < 0)
110    {
111      ding ();
112      return;
113    }
114
115  pos = noninc_search_from_pos (string, noninc_history_pos + dir, dir);
116  if (pos == -1)
117    {
118      /* Search failed, current history position unchanged. */
119      maybe_unsave_line ();
120      rl_clear_message ();
121      rl_point = 0;
122      ding ();
123      return;
124    }
125
126  noninc_history_pos = pos;
127
128  oldpos = where_history ();
129  history_set_pos (noninc_history_pos);
130  entry = current_history ();
131#if defined (VI_MODE)
132  if (rl_editing_mode != vi_mode)
133#endif
134  history_set_pos (oldpos);
135
136  line_len = strlen (entry->line);
137  if (line_len >= rl_line_buffer_len)
138    rl_extend_line_buffer (line_len);
139  strcpy (rl_line_buffer, entry->line);
140
141  rl_undo_list = (UNDO_LIST *)entry->data;
142  rl_end = strlen (rl_line_buffer);
143  rl_point = 0;
144  rl_clear_message ();
145
146  if (saved_line_for_history)
147    _rl_free_history_entry (saved_line_for_history);
148  saved_line_for_history = (HIST_ENTRY *)NULL;
149}
150
151/* Search non-interactively through the history list.  DIR < 0 means to
152   search backwards through the history of previous commands; otherwise
153   the search is for commands subsequent to the current position in the
154   history list.  PCHAR is the character to use for prompting when reading
155   the search string; if not specified (0), it defaults to `:'. */
156static void
157noninc_search (dir, pchar)
158     int dir;
159     int pchar;
160{
161  int saved_point, c;
162  char *p;
163
164  maybe_save_line ();
165  saved_point = rl_point;
166
167  /* Use the line buffer to read the search string. */
168  rl_line_buffer[0] = 0;
169  rl_end = rl_point = 0;
170
171  p = _rl_make_prompt_for_search (pchar ? pchar : ':');
172  rl_message (p, 0, 0);
173  free (p);
174
175#define SEARCH_RETURN _rl_restore_prompt (); return
176
177  /* Read the search string. */
178  while (c = rl_read_key ())
179    {
180      switch (c)
181	{
182	case CTRL('H'):
183	case RUBOUT:
184	  if (rl_point == 0)
185	    {
186	      maybe_unsave_line ();
187	      rl_clear_message ();
188	      rl_point = saved_point;
189	      SEARCH_RETURN;
190	    }
191	  rl_rubout (1, c);
192	  break;
193
194	case CTRL('W'):
195	  rl_unix_word_rubout (1, c);
196	  break;
197
198	case CTRL('U'):
199	  rl_unix_line_discard (1, c);
200	  break;
201
202	case RETURN:
203	case NEWLINE:
204	  goto dosearch;
205	  /* NOTREACHED */
206	  break;
207
208	case CTRL('C'):
209	case CTRL('G'):
210	  maybe_unsave_line ();
211	  rl_clear_message ();
212	  rl_point = saved_point;
213	  ding ();
214	  SEARCH_RETURN;
215
216	default:
217	  rl_insert (1, c);
218	  break;
219	}
220      (*rl_redisplay_function) ();
221    }
222
223 dosearch:
224  /* If rl_point == 0, we want to re-use the previous search string and
225     start from the saved history position.  If there's no previous search
226     string, punt. */
227  if (rl_point == 0)
228    {
229      if (!noninc_search_string)
230	{
231	  ding ();
232	  SEARCH_RETURN;
233	}
234    }
235  else
236    {
237      /* We want to start the search from the current history position. */
238      noninc_history_pos = where_history ();
239      if (noninc_search_string)
240	free (noninc_search_string);
241      noninc_search_string = savestring (rl_line_buffer);
242    }
243
244  _rl_restore_prompt ();
245  noninc_dosearch (noninc_search_string, dir);
246}
247
248/* Search forward through the history list for a string.  If the vi-mode
249   code calls this, KEY will be `?'. */
250int
251rl_noninc_forward_search (count, key)
252     int count, key;
253{
254  noninc_search (1, (key == '?') ? '?' : 0);
255  return 0;
256}
257
258/* Reverse search the history list for a string.  If the vi-mode code
259   calls this, KEY will be `/'. */
260int
261rl_noninc_reverse_search (count, key)
262     int count, key;
263{
264  noninc_search (-1, (key == '/') ? '/' : 0);
265  return 0;
266}
267
268/* Search forward through the history list for the last string searched
269   for.  If there is no saved search string, abort. */
270int
271rl_noninc_forward_search_again (count, key)
272     int count, key;
273{
274  if (!noninc_search_string)
275    {
276      ding ();
277      return (-1);
278    }
279  noninc_dosearch (noninc_search_string, 1);
280  return 0;
281}
282
283/* Reverse search in the history list for the last string searched
284   for.  If there is no saved search string, abort. */
285int
286rl_noninc_reverse_search_again (count, key)
287     int count, key;
288{
289  if (!noninc_search_string)
290    {
291      ding ();
292      return (-1);
293    }
294  noninc_dosearch (noninc_search_string, -1);
295  return 0;
296}
297
298static int
299rl_history_search_internal (count, direction)
300     int count, direction;
301{
302  HIST_ENTRY *temp, *old_temp;
303  int line_len;
304
305  maybe_save_line ();
306
307  temp = old_temp = (HIST_ENTRY *)NULL;
308  while (count)
309    {
310      temp = (direction < 0) ? previous_history () : next_history ();
311      if (temp == 0)
312        break;
313      /* On an empty prefix, make this the same as previous-history. */
314      if (rl_point == 0)
315	{
316	  count--;
317	  continue;
318	}
319      if (STREQN (rl_line_buffer, temp->line, rl_point))
320	{
321	  /* Don't find multiple instances of the same line. */
322	  if (prev_line_found && STREQ (prev_line_found, temp->line))
323	    continue;
324          if (direction < 0)
325            old_temp = temp;
326          prev_line_found = temp->line;
327          count--;
328	}
329    }
330
331  if (temp == 0)
332    {
333      if (direction < 0 && old_temp)
334	temp = old_temp;
335      else
336	{
337	  maybe_unsave_line ();
338	  ding ();
339	  return 1;
340	}
341    }
342
343  line_len = strlen (temp->line);
344  if (line_len >= rl_line_buffer_len)
345    rl_extend_line_buffer (line_len);
346  strcpy (rl_line_buffer, temp->line);
347  rl_undo_list = (UNDO_LIST *)temp->data;
348  rl_end = line_len;
349  return 0;
350}
351
352/* Search forward in the history for the string of characters
353   from the start of the line to rl_point.  This is a non-incremental
354   search. */
355int
356rl_history_search_forward (count, ignore)
357     int count, ignore;
358{
359  if (count == 0)
360    return (0);
361  if (rl_last_func != rl_history_search_forward)
362    prev_line_found = (char *)NULL;
363  return (rl_history_search_internal (abs (count), (count > 0) ? 1 : -1));
364}
365
366/* Search backward through the history for the string of characters
367   from the start of the line to rl_point.  This is a non-incremental
368   search. */
369int
370rl_history_search_backward (count, ignore)
371     int count, ignore;
372{
373  if (count == 0)
374    return (0);
375  if (rl_last_func != rl_history_search_backward)
376    prev_line_found = (char *)NULL;
377  return (rl_history_search_internal (abs (count), (count > 0) ? -1 : 1));
378}
379