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