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