search.c revision 75406
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 2, 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   59 Temple Place, Suite 330, Boston, MA 02111 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#include "rlprivate.h"
47#include "xmalloc.h"
48
49#ifdef abs
50#  undef abs
51#endif
52#define abs(x)		(((x) >= 0) ? (x) : -(x))
53
54extern HIST_ENTRY *_rl_saved_line_for_history;
55
56/* Functions imported from the rest of the library. */
57extern int _rl_free_history_entry __P((HIST_ENTRY *));
58
59static char *noninc_search_string = (char *) NULL;
60static int noninc_history_pos;
61
62static char *prev_line_found = (char *) NULL;
63
64static int rl_history_search_len;
65static int rl_history_search_pos;
66static char *history_search_string;
67static int history_string_size;
68
69/* Make the data from the history entry ENTRY be the contents of the
70   current line.  This doesn't do anything with rl_point; the caller
71   must set it. */
72static void
73make_history_line_current (entry)
74     HIST_ENTRY *entry;
75{
76  int line_len;
77
78  line_len = strlen (entry->line);
79  if (line_len >= rl_line_buffer_len)
80    rl_extend_line_buffer (line_len);
81  strcpy (rl_line_buffer, entry->line);
82
83  rl_undo_list = (UNDO_LIST *)entry->data;
84  rl_end = line_len;
85
86  if (_rl_saved_line_for_history)
87    _rl_free_history_entry (_rl_saved_line_for_history);
88  _rl_saved_line_for_history = (HIST_ENTRY *)NULL;
89}
90
91/* Search the history list for STRING starting at absolute history position
92   POS.  If STRING begins with `^', the search must match STRING at the
93   beginning of a history line, otherwise a full substring match is performed
94   for STRING.  DIR < 0 means to search backwards through the history list,
95   DIR >= 0 means to search forward. */
96static int
97noninc_search_from_pos (string, pos, dir)
98     char *string;
99     int pos, dir;
100{
101  int ret, old;
102
103  if (pos < 0)
104    return -1;
105
106  old = where_history ();
107  if (history_set_pos (pos) == 0)
108    return -1;
109
110  RL_SETSTATE(RL_STATE_SEARCH);
111  if (*string == '^')
112    ret = history_search_prefix (string + 1, dir);
113  else
114    ret = history_search (string, dir);
115  RL_UNSETSTATE(RL_STATE_SEARCH);
116
117  if (ret != -1)
118    ret = where_history ();
119
120  history_set_pos (old);
121  return (ret);
122}
123
124/* Search for a line in the history containing STRING.  If DIR is < 0, the
125   search is backwards through previous entries, else through subsequent
126   entries. */
127static void
128noninc_dosearch (string, dir)
129     char *string;
130     int dir;
131{
132  int oldpos, pos;
133  HIST_ENTRY *entry;
134
135  if (string == 0 || *string == '\0' || noninc_history_pos < 0)
136    {
137      rl_ding ();
138      return;
139    }
140
141  pos = noninc_search_from_pos (string, noninc_history_pos + dir, dir);
142  if (pos == -1)
143    {
144      /* Search failed, current history position unchanged. */
145      rl_maybe_unsave_line ();
146      rl_clear_message ();
147      rl_point = 0;
148      rl_ding ();
149      return;
150    }
151
152  noninc_history_pos = pos;
153
154  oldpos = where_history ();
155  history_set_pos (noninc_history_pos);
156  entry = current_history ();
157#if defined (VI_MODE)
158  if (rl_editing_mode != vi_mode)
159#endif
160  history_set_pos (oldpos);
161
162  make_history_line_current (entry);
163
164  rl_point = 0;
165  rl_clear_message ();
166}
167
168/* Search non-interactively through the history list.  DIR < 0 means to
169   search backwards through the history of previous commands; otherwise
170   the search is for commands subsequent to the current position in the
171   history list.  PCHAR is the character to use for prompting when reading
172   the search string; if not specified (0), it defaults to `:'. */
173static void
174noninc_search (dir, pchar)
175     int dir;
176     int pchar;
177{
178  int saved_point, c;
179  char *p;
180
181  rl_maybe_save_line ();
182  saved_point = rl_point;
183
184  /* Use the line buffer to read the search string. */
185  rl_line_buffer[0] = 0;
186  rl_end = rl_point = 0;
187
188  p = _rl_make_prompt_for_search (pchar ? pchar : ':');
189  rl_message (p, 0, 0);
190  free (p);
191
192#define SEARCH_RETURN rl_restore_prompt (); RL_UNSETSTATE(RL_STATE_NSEARCH); return
193
194  RL_SETSTATE(RL_STATE_NSEARCH);
195  /* Read the search string. */
196  while (1)
197    {
198      RL_SETSTATE(RL_STATE_MOREINPUT);
199      c = rl_read_key ();
200      RL_UNSETSTATE(RL_STATE_MOREINPUT);
201
202      if (c == 0)
203	break;
204
205      switch (c)
206	{
207	case CTRL('H'):
208	case RUBOUT:
209	  if (rl_point == 0)
210	    {
211	      rl_maybe_unsave_line ();
212	      rl_clear_message ();
213	      rl_point = saved_point;
214	      SEARCH_RETURN;
215	    }
216	  rl_rubout (1, c);
217	  break;
218
219	case CTRL('W'):
220	  rl_unix_word_rubout (1, c);
221	  break;
222
223	case CTRL('U'):
224	  rl_unix_line_discard (1, c);
225	  break;
226
227	case RETURN:
228	case NEWLINE:
229	  goto dosearch;
230	  /* NOTREACHED */
231	  break;
232
233	case CTRL('C'):
234	case CTRL('G'):
235	  rl_maybe_unsave_line ();
236	  rl_clear_message ();
237	  rl_point = saved_point;
238	  rl_ding ();
239	  SEARCH_RETURN;
240
241	default:
242	  rl_insert (1, c);
243	  break;
244	}
245      (*rl_redisplay_function) ();
246    }
247
248 dosearch:
249  /* If rl_point == 0, we want to re-use the previous search string and
250     start from the saved history position.  If there's no previous search
251     string, punt. */
252  if (rl_point == 0)
253    {
254      if (!noninc_search_string)
255	{
256	  rl_ding ();
257	  SEARCH_RETURN;
258	}
259    }
260  else
261    {
262      /* We want to start the search from the current history position. */
263      noninc_history_pos = where_history ();
264      FREE (noninc_search_string);
265      noninc_search_string = savestring (rl_line_buffer);
266    }
267
268  rl_restore_prompt ();
269  noninc_dosearch (noninc_search_string, dir);
270  RL_UNSETSTATE(RL_STATE_NSEARCH);
271}
272
273/* Search forward through the history list for a string.  If the vi-mode
274   code calls this, KEY will be `?'. */
275int
276rl_noninc_forward_search (count, key)
277     int count, key;
278{
279  noninc_search (1, (key == '?') ? '?' : 0);
280  return 0;
281}
282
283/* Reverse search the history list for a string.  If the vi-mode code
284   calls this, KEY will be `/'. */
285int
286rl_noninc_reverse_search (count, key)
287     int count, key;
288{
289  noninc_search (-1, (key == '/') ? '/' : 0);
290  return 0;
291}
292
293/* Search forward through the history list for the last string searched
294   for.  If there is no saved search string, abort. */
295int
296rl_noninc_forward_search_again (count, key)
297     int count, key;
298{
299  if (!noninc_search_string)
300    {
301      rl_ding ();
302      return (-1);
303    }
304  noninc_dosearch (noninc_search_string, 1);
305  return 0;
306}
307
308/* Reverse search in the history list for the last string searched
309   for.  If there is no saved search string, abort. */
310int
311rl_noninc_reverse_search_again (count, key)
312     int count, key;
313{
314  if (!noninc_search_string)
315    {
316      rl_ding ();
317      return (-1);
318    }
319  noninc_dosearch (noninc_search_string, -1);
320  return 0;
321}
322
323static int
324rl_history_search_internal (count, dir)
325     int count, dir;
326{
327  HIST_ENTRY *temp;
328  int ret, oldpos;
329
330  rl_maybe_save_line ();
331  temp = (HIST_ENTRY *)NULL;
332
333  /* Search COUNT times through the history for a line whose prefix
334     matches history_search_string.  When this loop finishes, TEMP,
335     if non-null, is the history line to copy into the line buffer. */
336  while (count)
337    {
338      ret = noninc_search_from_pos (history_search_string, rl_history_search_pos + dir, dir);
339      if (ret == -1)
340	break;
341
342      /* Get the history entry we found. */
343      rl_history_search_pos = ret;
344      oldpos = where_history ();
345      history_set_pos (rl_history_search_pos);
346      temp = current_history ();
347      history_set_pos (oldpos);
348
349      /* Don't find multiple instances of the same line. */
350      if (prev_line_found && STREQ (prev_line_found, temp->line))
351        continue;
352      prev_line_found = temp->line;
353      count--;
354    }
355
356  /* If we didn't find anything at all, return. */
357  if (temp == 0)
358    {
359      rl_maybe_unsave_line ();
360      rl_ding ();
361      /* If you don't want the saved history line (last match) to show up
362         in the line buffer after the search fails, change the #if 0 to
363         #if 1 */
364#if 0
365      if (rl_point > rl_history_search_len)
366        {
367          rl_point = rl_end = rl_history_search_len;
368          rl_line_buffer[rl_end] = '\0';
369        }
370#else
371      rl_point = rl_history_search_len;	/* rl_maybe_unsave_line changes it */
372#endif
373      return 1;
374    }
375
376  /* Copy the line we found into the current line buffer. */
377  make_history_line_current (temp);
378
379  rl_point = rl_history_search_len;
380  return 0;
381}
382
383static void
384rl_history_search_reinit ()
385{
386  rl_history_search_pos = where_history ();
387  rl_history_search_len = rl_point;
388  prev_line_found = (char *)NULL;
389  if (rl_point)
390    {
391      if (rl_history_search_len >= history_string_size - 2)
392	{
393	  history_string_size = rl_history_search_len + 2;
394	  history_search_string = xrealloc (history_search_string, history_string_size);
395	}
396      history_search_string[0] = '^';
397      strncpy (history_search_string + 1, rl_line_buffer, rl_point);
398      history_search_string[rl_point + 1] = '\0';
399    }
400  _rl_free_saved_history_line ();
401}
402
403/* Search forward in the history for the string of characters
404   from the start of the line to rl_point.  This is a non-incremental
405   search. */
406int
407rl_history_search_forward (count, ignore)
408     int count, ignore;
409{
410  if (count == 0)
411    return (0);
412
413  if (rl_last_func != rl_history_search_forward &&
414      rl_last_func != rl_history_search_backward)
415    rl_history_search_reinit ();
416
417  if (rl_history_search_len == 0)
418    return (rl_get_next_history (count, ignore));
419  return (rl_history_search_internal (abs (count), (count > 0) ? 1 : -1));
420}
421
422/* Search backward through the history for the string of characters
423   from the start of the line to rl_point.  This is a non-incremental
424   search. */
425int
426rl_history_search_backward (count, ignore)
427     int count, ignore;
428{
429  if (count == 0)
430    return (0);
431
432  if (rl_last_func != rl_history_search_forward &&
433      rl_last_func != rl_history_search_backward)
434    rl_history_search_reinit ();
435
436  if (rl_history_search_len == 0)
437    return (rl_get_previous_history (count, ignore));
438  return (rl_history_search_internal (abs (count), (count > 0) ? -1 : 1));
439}
440