xref: /openbsd/gnu/lib/libreadline/search.c (revision af70c2df)
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 
56 extern HIST_ENTRY *_rl_saved_line_for_history;
57 
58 /* Functions imported from the rest of the library. */
59 extern int _rl_free_history_entry PARAMS((HIST_ENTRY *));
60 
61 static char *noninc_search_string = (char *) NULL;
62 static int noninc_history_pos;
63 
64 static char *prev_line_found = (char *) NULL;
65 
66 static int rl_history_search_len;
67 static int rl_history_search_pos;
68 static char *history_search_string;
69 static int history_string_size;
70 
71 static void make_history_line_current PARAMS((HIST_ENTRY *));
72 static int noninc_search_from_pos PARAMS((char *, int, int));
73 static void noninc_dosearch PARAMS((char *, int));
74 static void noninc_search PARAMS((int, int));
75 static int rl_history_search_internal PARAMS((int, int));
76 static 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. */
81 static void
make_history_line_current(entry)82 make_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. */
98 static int
noninc_search_from_pos(string,pos,dir)99 noninc_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. */
129 static void
noninc_dosearch(string,dir)130 noninc_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 `:'. */
177 static void
noninc_search(dir,pchar)178 noninc_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 `?'. */
297 int
rl_noninc_forward_search(count,key)298 rl_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 `/'. */
307 int
rl_noninc_reverse_search(count,key)308 rl_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. */
317 int
rl_noninc_forward_search_again(count,key)318 rl_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. */
332 int
rl_noninc_reverse_search_again(count,key)333 rl_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 
345 static int
rl_history_search_internal(count,dir)346 rl_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 
409 static void
rl_history_search_reinit()410 rl_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. */
432 int
rl_history_search_forward(count,ignore)433 rl_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. */
451 int
rl_history_search_backward(count,ignore)452 rl_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