1/* search.c - code for non-incremental searching in emacs and vi modes. */
2
3/* Copyright (C) 1992-2020 Free Software Foundation, Inc.
4
5   This file is part of the GNU Readline Library (Readline), a library
6   for reading lines of text with interactive input and history editing.
7
8   Readline is free software: you can redistribute it and/or modify
9   it under the terms of the GNU General Public License as published by
10   the Free Software Foundation, either version 3 of the License, or
11   (at your option) any later version.
12
13   Readline is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18   You should have received a copy of the GNU General Public License
19   along with Readline.  If not, see <http://www.gnu.org/licenses/>.
20*/
21
22#define READLINE_LIBRARY
23
24#if defined (HAVE_CONFIG_H)
25#  include <config.h>
26#endif
27
28#include <sys/types.h>
29#include <stdio.h>
30
31#if defined (HAVE_UNISTD_H)
32#  include <unistd.h>
33#endif
34
35#if defined (HAVE_STDLIB_H)
36#  include <stdlib.h>
37#else
38#  include "ansi_stdlib.h"
39#endif
40
41#include "rldefs.h"
42#include "rlmbutil.h"
43
44#include "readline.h"
45#include "history.h"
46#include "histlib.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_rl_search_cxt *_rl_nscxt = 0;
57
58extern HIST_ENTRY *_rl_saved_line_for_history;
59
60/* Functions imported from the rest of the library. */
61extern void _rl_free_history_entry PARAMS((HIST_ENTRY *));
62
63static char *noninc_search_string = (char *) NULL;
64static int noninc_history_pos;
65
66static char *prev_line_found = (char *) NULL;
67
68static int rl_history_search_len;
69static int rl_history_search_pos;
70static int rl_history_search_flags;
71
72static char *history_search_string;
73static int history_string_size;
74
75static void make_history_line_current PARAMS((HIST_ENTRY *));
76static int noninc_search_from_pos PARAMS((char *, int, int, int, int *));
77static int noninc_dosearch PARAMS((char *, int, int));
78static int noninc_search PARAMS((int, int));
79static int rl_history_search_internal PARAMS((int, int));
80static void rl_history_search_reinit PARAMS((int));
81
82static _rl_search_cxt *_rl_nsearch_init PARAMS((int, int));
83static void _rl_nsearch_abort PARAMS((_rl_search_cxt *));
84static int _rl_nsearch_dispatch PARAMS((_rl_search_cxt *, int));
85
86/* Make the data from the history entry ENTRY be the contents of the
87   current line.  This doesn't do anything with rl_point; the caller
88   must set it. */
89static void
90make_history_line_current (HIST_ENTRY *entry)
91{
92  _rl_replace_text (entry->line, 0, rl_end);
93  _rl_fix_point (1);
94#if defined (VI_MODE)
95  if (rl_editing_mode == vi_mode)
96    /* POSIX.2 says that the `U' command doesn't affect the copy of any
97       command lines to the edit line.  We're going to implement that by
98       making the undo list start after the matching line is copied to the
99       current editing buffer. */
100    rl_free_undo_list ();
101#endif
102
103  if (_rl_saved_line_for_history)
104    _rl_free_history_entry (_rl_saved_line_for_history);
105  _rl_saved_line_for_history = (HIST_ENTRY *)NULL;
106}
107
108/* Search the history list for STRING starting at absolute history position
109   POS.  If STRING begins with `^', the search must match STRING at the
110   beginning of a history line, otherwise a full substring match is performed
111   for STRING.  DIR < 0 means to search backwards through the history list,
112   DIR >= 0 means to search forward. */
113static int
114noninc_search_from_pos (char *string, int pos, int dir, int flags, int *ncp)
115{
116  int ret, old, sflags;
117  char *s;
118
119  if (pos < 0)
120    return -1;
121
122  old = where_history ();
123  if (history_set_pos (pos) == 0)
124    return -1;
125
126  RL_SETSTATE(RL_STATE_SEARCH);
127  /* These functions return the match offset in the line; history_offset gives
128     the matching line in the history list */
129  if (flags & SF_PATTERN)
130    {
131      s = string;
132      sflags = 0;		/* Non-anchored search */
133      if (*s == '^')
134	{
135	  sflags |= ANCHORED_SEARCH;
136	  s++;
137	}
138      ret = _hs_history_patsearch (s, dir, sflags);
139    }
140  else if (*string == '^')
141    ret = history_search_prefix (string + 1, dir);
142  else
143    ret = history_search (string, dir);
144  RL_UNSETSTATE(RL_STATE_SEARCH);
145
146  if (ncp)
147    *ncp = ret;		/* caller will catch -1 to indicate no-op */
148
149  if (ret != -1)
150    ret = where_history ();
151
152  history_set_pos (old);
153  return (ret);
154}
155
156/* Search for a line in the history containing STRING.  If DIR is < 0, the
157   search is backwards through previous entries, else through subsequent
158   entries.  Returns 1 if the search was successful, 0 otherwise. */
159static int
160noninc_dosearch (char *string, int dir, int flags)
161{
162  int oldpos, pos, ind;
163  HIST_ENTRY *entry;
164
165  if (string == 0 || *string == '\0' || noninc_history_pos < 0)
166    {
167      rl_ding ();
168      return 0;
169    }
170
171  pos = noninc_search_from_pos (string, noninc_history_pos + dir, dir, flags, &ind);
172  if (pos == -1)
173    {
174      /* Search failed, current history position unchanged. */
175      rl_maybe_unsave_line ();
176      rl_clear_message ();
177      rl_point = 0;
178      rl_ding ();
179      return 0;
180    }
181
182  noninc_history_pos = pos;
183
184  oldpos = where_history ();
185  history_set_pos (noninc_history_pos);
186  entry = current_history ();		/* will never be NULL after successful search */
187
188#if defined (VI_MODE)
189  if (rl_editing_mode != vi_mode)
190#endif
191    history_set_pos (oldpos);
192
193  make_history_line_current (entry);
194
195  if (_rl_enable_active_region && ((flags & SF_PATTERN) == 0) && ind > 0 && ind < rl_end)
196    {
197      rl_point = ind;
198      rl_mark = ind + strlen (string);
199      if (rl_mark > rl_end)
200	rl_mark = rl_end;	/* can't happen? */
201      rl_activate_mark ();
202    }
203  else
204    {
205      rl_point = 0;
206      rl_mark = rl_end;
207    }
208
209  rl_clear_message ();
210  return 1;
211}
212
213static _rl_search_cxt *
214_rl_nsearch_init (int dir, int pchar)
215{
216  _rl_search_cxt *cxt;
217  char *p;
218
219  cxt = _rl_scxt_alloc (RL_SEARCH_NSEARCH, 0);
220  if (dir < 0)
221    cxt->sflags |= SF_REVERSE;		/* not strictly needed */
222#if defined (VI_MODE)
223  if (VI_COMMAND_MODE() && (pchar == '?' || pchar == '/'))
224    cxt->sflags |= SF_PATTERN;
225#endif
226
227  cxt->direction = dir;
228  cxt->history_pos = cxt->save_line;
229
230  rl_maybe_save_line ();
231
232  /* Clear the undo list, since reading the search string should create its
233     own undo list, and the whole list will end up being freed when we
234     finish reading the search string. */
235  rl_undo_list = 0;
236
237  /* Use the line buffer to read the search string. */
238  rl_line_buffer[0] = 0;
239  rl_end = rl_point = 0;
240
241  p = _rl_make_prompt_for_search (pchar ? pchar : ':');
242  rl_message ("%s", p);
243  xfree (p);
244
245  RL_SETSTATE(RL_STATE_NSEARCH);
246
247  _rl_nscxt = cxt;
248
249  return cxt;
250}
251
252int
253_rl_nsearch_cleanup (_rl_search_cxt *cxt, int r)
254{
255  _rl_scxt_dispose (cxt, 0);
256  _rl_nscxt = 0;
257
258  RL_UNSETSTATE(RL_STATE_NSEARCH);
259
260  return (r != 1);
261}
262
263static void
264_rl_nsearch_abort (_rl_search_cxt *cxt)
265{
266  rl_maybe_unsave_line ();
267  rl_clear_message ();
268  rl_point = cxt->save_point;
269  rl_mark = cxt->save_mark;
270  _rl_fix_point (1);
271  rl_restore_prompt ();
272
273  RL_UNSETSTATE (RL_STATE_NSEARCH);
274}
275
276/* Process just-read character C according to search context CXT.  Return -1
277   if the caller should abort the search, 0 if we should break out of the
278   loop, and 1 if we should continue to read characters. */
279static int
280_rl_nsearch_dispatch (_rl_search_cxt *cxt, int c)
281{
282  int n;
283
284  if (c < 0)
285    c = CTRL ('C');
286
287  switch (c)
288    {
289    case CTRL('W'):
290      rl_unix_word_rubout (1, c);
291      break;
292
293    case CTRL('U'):
294      rl_unix_line_discard (1, c);
295      break;
296
297    case RETURN:
298    case NEWLINE:
299      return 0;
300
301    case CTRL('H'):
302    case RUBOUT:
303      if (rl_point == 0)
304	{
305	  _rl_nsearch_abort (cxt);
306	  return -1;
307	}
308      _rl_rubout_char (1, c);
309      break;
310
311    case CTRL('C'):
312    case CTRL('G'):
313      rl_ding ();
314      _rl_nsearch_abort (cxt);
315      return -1;
316
317    case ESC:
318      /* XXX - experimental code to allow users to bracketed-paste into the
319	 search string. Similar code is in isearch.c:_rl_isearch_dispatch().
320	 The difference here is that the bracketed paste sometimes doesn't
321	 paste everything, so checking for the prefix and the suffix in the
322	 input queue doesn't work well. We just have to check to see if the
323	 number of chars in the input queue is enough for the bracketed paste
324	 prefix and hope for the best. */
325      if (_rl_enable_bracketed_paste && ((n = _rl_nchars_available ()) >= (BRACK_PASTE_SLEN-1)))
326	{
327	  if (_rl_read_bracketed_paste_prefix (c) == 1)
328	    rl_bracketed_paste_begin (1, c);
329	  else
330	    {
331	      c = rl_read_key ();	/* get the ESC that got pushed back */
332	      _rl_insert_char (1, c);
333	    }
334        }
335      else
336        _rl_insert_char (1, c);
337      break;
338
339    default:
340#if defined (HANDLE_MULTIBYTE)
341      if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
342	rl_insert_text (cxt->mb);
343      else
344#endif
345	_rl_insert_char (1, c);
346      break;
347    }
348
349  (*rl_redisplay_function) ();
350  rl_deactivate_mark ();
351  return 1;
352}
353
354/* Perform one search according to CXT, using NONINC_SEARCH_STRING.  Return
355   -1 if the search should be aborted, any other value means to clean up
356   using _rl_nsearch_cleanup ().  Returns 1 if the search was successful,
357   0 otherwise. */
358static int
359_rl_nsearch_dosearch (_rl_search_cxt *cxt)
360{
361  rl_mark = cxt->save_mark;
362
363  /* If rl_point == 0, we want to re-use the previous search string and
364     start from the saved history position.  If there's no previous search
365     string, punt. */
366  if (rl_point == 0)
367    {
368      if (noninc_search_string == 0)
369	{
370	  rl_ding ();
371	  rl_restore_prompt ();
372	  RL_UNSETSTATE (RL_STATE_NSEARCH);
373	  return -1;
374	}
375    }
376  else
377    {
378      /* We want to start the search from the current history position. */
379      noninc_history_pos = cxt->save_line;
380      FREE (noninc_search_string);
381      noninc_search_string = savestring (rl_line_buffer);
382
383      /* If we don't want the subsequent undo list generated by the search
384	 matching a history line to include the contents of the search string,
385	 we need to clear rl_line_buffer here.  For now, we just clear the
386	 undo list generated by reading the search string.  (If the search
387	 fails, the old undo list will be restored by rl_maybe_unsave_line.) */
388      rl_free_undo_list ();
389    }
390
391  rl_restore_prompt ();
392  return (noninc_dosearch (noninc_search_string, cxt->direction, cxt->sflags&SF_PATTERN));
393}
394
395/* Search non-interactively through the history list.  DIR < 0 means to
396   search backwards through the history of previous commands; otherwise
397   the search is for commands subsequent to the current position in the
398   history list.  PCHAR is the character to use for prompting when reading
399   the search string; if not specified (0), it defaults to `:'. */
400static int
401noninc_search (int dir, int pchar)
402{
403  _rl_search_cxt *cxt;
404  int c, r;
405
406  cxt = _rl_nsearch_init (dir, pchar);
407
408  if (RL_ISSTATE (RL_STATE_CALLBACK))
409    return (0);
410
411  /* Read the search string. */
412  r = 0;
413  while (1)
414    {
415      c = _rl_search_getchar (cxt);
416
417      if (c < 0)
418	{
419	  _rl_nsearch_abort (cxt);
420	  return 1;
421	}
422
423      if (c == 0)
424	break;
425
426      r = _rl_nsearch_dispatch (cxt, c);
427      if (r < 0)
428        return 1;
429      else if (r == 0)
430	break;
431    }
432
433  r = _rl_nsearch_dosearch (cxt);
434  return ((r >= 0) ? _rl_nsearch_cleanup (cxt, r) : (r != 1));
435}
436
437/* Search forward through the history list for a string.  If the vi-mode
438   code calls this, KEY will be `?'. */
439int
440rl_noninc_forward_search (int count, int key)
441{
442  return noninc_search (1, (key == '?') ? '?' : 0);
443}
444
445/* Reverse search the history list for a string.  If the vi-mode code
446   calls this, KEY will be `/'. */
447int
448rl_noninc_reverse_search (int count, int key)
449{
450  return noninc_search (-1, (key == '/') ? '/' : 0);
451}
452
453/* Search forward through the history list for the last string searched
454   for.  If there is no saved search string, abort.  If the vi-mode code
455   calls this, KEY will be `N'. */
456int
457rl_noninc_forward_search_again (int count, int key)
458{
459  int r;
460
461  if (!noninc_search_string)
462    {
463      rl_ding ();
464      return (1);
465    }
466#if defined (VI_MODE)
467  if (VI_COMMAND_MODE() && key == 'N')
468    r = noninc_dosearch (noninc_search_string, 1, SF_PATTERN);
469  else
470#endif
471    r = noninc_dosearch (noninc_search_string, 1, 0);
472  return (r != 1);
473}
474
475/* Reverse search in the history list for the last string searched
476   for.  If there is no saved search string, abort.  If the vi-mode code
477   calls this, KEY will be `n'. */
478int
479rl_noninc_reverse_search_again (int count, int key)
480{
481  int r;
482
483  if (!noninc_search_string)
484    {
485      rl_ding ();
486      return (1);
487    }
488#if defined (VI_MODE)
489  if (VI_COMMAND_MODE() && key == 'n')
490    r = noninc_dosearch (noninc_search_string, -1, SF_PATTERN);
491  else
492#endif
493    r = noninc_dosearch (noninc_search_string, -1, 0);
494  return (r != 1);
495}
496
497#if defined (READLINE_CALLBACKS)
498int
499_rl_nsearch_callback (_rl_search_cxt *cxt)
500{
501  int c, r;
502
503  c = _rl_search_getchar (cxt);
504  if (c <= 0)
505    {
506      if (c < 0)
507        _rl_nsearch_abort (cxt);
508      return 1;
509    }
510  r = _rl_nsearch_dispatch (cxt, c);
511  if (r != 0)
512    return 1;
513
514  r = _rl_nsearch_dosearch (cxt);
515  return ((r >= 0) ? _rl_nsearch_cleanup (cxt, r) : (r != 1));
516}
517#endif
518
519static int
520rl_history_search_internal (int count, int dir)
521{
522  HIST_ENTRY *temp;
523  int ret, oldpos, newcol;
524  char *t;
525
526  rl_maybe_save_line ();
527  temp = (HIST_ENTRY *)NULL;
528
529  /* Search COUNT times through the history for a line matching
530     history_search_string.  If history_search_string[0] == '^', the
531     line must match from the start; otherwise any substring can match.
532     When this loop finishes, TEMP, if non-null, is the history line to
533     copy into the line buffer. */
534  while (count)
535    {
536      RL_CHECK_SIGNALS ();
537      ret = noninc_search_from_pos (history_search_string, rl_history_search_pos + dir, dir, 0, &newcol);
538      if (ret == -1)
539	break;
540
541      /* Get the history entry we found. */
542      rl_history_search_pos = ret;
543      oldpos = where_history ();
544      history_set_pos (rl_history_search_pos);
545      temp = current_history ();	/* will never be NULL after successful search */
546      history_set_pos (oldpos);
547
548      /* Don't find multiple instances of the same line. */
549      if (prev_line_found && STREQ (prev_line_found, temp->line))
550        continue;
551      prev_line_found = temp->line;
552      count--;
553    }
554
555  /* If we didn't find anything at all, return. */
556  if (temp == 0)
557    {
558      rl_maybe_unsave_line ();
559      rl_ding ();
560      /* If you don't want the saved history line (last match) to show up
561         in the line buffer after the search fails, change the #if 0 to
562         #if 1 */
563#if 0
564      if (rl_point > rl_history_search_len)
565        {
566          rl_point = rl_end = rl_history_search_len;
567          rl_line_buffer[rl_end] = '\0';
568          rl_mark = 0;
569        }
570#else
571      rl_point = rl_history_search_len;	/* rl_maybe_unsave_line changes it */
572      rl_mark = rl_end;
573#endif
574      return 1;
575    }
576
577  /* Copy the line we found into the current line buffer. */
578  make_history_line_current (temp);
579
580  /* decide where to put rl_point -- need to change this for pattern search */
581  if (rl_history_search_flags & ANCHORED_SEARCH)
582    rl_point = rl_history_search_len;	/* easy case */
583  else
584    {
585#if 0
586      t = strstr (rl_line_buffer, history_search_string);	/* XXX */
587      rl_point = t ? (int)(t - rl_line_buffer) + rl_history_search_len : rl_end;
588#else
589      rl_point = (newcol >= 0) ? newcol : rl_end;
590#endif
591    }
592  rl_mark = rl_end;
593
594  return 0;
595}
596
597static void
598rl_history_search_reinit (int flags)
599{
600  int sind;
601
602  rl_history_search_pos = where_history ();
603  rl_history_search_len = rl_point;
604  rl_history_search_flags = flags;
605
606  prev_line_found = (char *)NULL;
607  if (rl_point)
608    {
609      /* Allocate enough space for anchored and non-anchored searches */
610      if (rl_history_search_len >= history_string_size - 2)
611	{
612	  history_string_size = rl_history_search_len + 2;
613	  history_search_string = (char *)xrealloc (history_search_string, history_string_size);
614	}
615      sind = 0;
616      if (flags & ANCHORED_SEARCH)
617	history_search_string[sind++] = '^';
618      strncpy (history_search_string + sind, rl_line_buffer, rl_point);
619      history_search_string[rl_point + sind] = '\0';
620    }
621  _rl_free_saved_history_line ();
622}
623
624/* Search forward in the history for the string of characters
625   from the start of the line to rl_point.  This is a non-incremental
626   search.  The search is anchored to the beginning of the history line. */
627int
628rl_history_search_forward (int count, int ignore)
629{
630  if (count == 0)
631    return (0);
632
633  if (rl_last_func != rl_history_search_forward &&
634      rl_last_func != rl_history_search_backward)
635    rl_history_search_reinit (ANCHORED_SEARCH);
636
637  if (rl_history_search_len == 0)
638    return (rl_get_next_history (count, ignore));
639  return (rl_history_search_internal (abs (count), (count > 0) ? 1 : -1));
640}
641
642/* Search backward through the history for the string of characters
643   from the start of the line to rl_point.  This is a non-incremental
644   search. */
645int
646rl_history_search_backward (int count, int ignore)
647{
648  if (count == 0)
649    return (0);
650
651  if (rl_last_func != rl_history_search_forward &&
652      rl_last_func != rl_history_search_backward)
653    rl_history_search_reinit (ANCHORED_SEARCH);
654
655  if (rl_history_search_len == 0)
656    return (rl_get_previous_history (count, ignore));
657  return (rl_history_search_internal (abs (count), (count > 0) ? -1 : 1));
658}
659
660/* Search forward in the history for the string of characters
661   from the start of the line to rl_point.  This is a non-incremental
662   search.  The search succeeds if the search string is present anywhere
663   in the history line. */
664int
665rl_history_substr_search_forward (int count, int ignore)
666{
667  if (count == 0)
668    return (0);
669
670  if (rl_last_func != rl_history_substr_search_forward &&
671      rl_last_func != rl_history_substr_search_backward)
672    rl_history_search_reinit (NON_ANCHORED_SEARCH);
673
674  if (rl_history_search_len == 0)
675    return (rl_get_next_history (count, ignore));
676  return (rl_history_search_internal (abs (count), (count > 0) ? 1 : -1));
677}
678
679/* Search backward through the history for the string of characters
680   from the start of the line to rl_point.  This is a non-incremental
681   search. */
682int
683rl_history_substr_search_backward (int count, int ignore)
684{
685  if (count == 0)
686    return (0);
687
688  if (rl_last_func != rl_history_substr_search_forward &&
689      rl_last_func != rl_history_substr_search_backward)
690    rl_history_search_reinit (NON_ANCHORED_SEARCH);
691
692  if (rl_history_search_len == 0)
693    return (rl_get_previous_history (count, ignore));
694  return (rl_history_search_internal (abs (count), (count > 0) ? -1 : 1));
695}
696