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