1/* histsearch.c -- searching the history list. */
2
3/* Copyright (C) 1989, 1992 Free Software Foundation, Inc.
4
5   This file contains the GNU History Library (the Library), a set of
6   routines for managing the text of previously typed lines.
7
8   The Library 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 2, or (at your option)
11   any later version.
12
13   The Library is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16   General Public License for more details.
17
18   The GNU General Public License is often shipped with GNU software, and
19   is generally kept in a file called COPYING or LICENSE.  If you do not
20   have a copy of the license, write to the Free Software Foundation,
21   59 Temple Place, Suite 330, Boston, MA 02111 USA. */
22
23#define READLINE_LIBRARY
24
25#if defined (HAVE_CONFIG_H)
26#  include <config.h>
27#endif
28
29#include <stdio.h>
30#if defined (HAVE_STDLIB_H)
31#  include <stdlib.h>
32#else
33#  include "ansi_stdlib.h"
34#endif /* HAVE_STDLIB_H */
35
36#if defined (HAVE_UNISTD_H)
37#  ifdef _MINIX
38#    include <sys/types.h>
39#  endif
40#  include <unistd.h>
41#endif
42
43#include "history.h"
44#include "histlib.h"
45
46/* The list of alternate characters that can delimit a history search
47   string. */
48char *history_search_delimiter_chars = (char *)NULL;
49
50static int history_search_internal PARAMS((const char *, int, int));
51
52/* Search the history for STRING, starting at history_offset.
53   If DIRECTION < 0, then the search is through previous entries, else
54   through subsequent.  If ANCHORED is non-zero, the string must
55   appear at the beginning of a history line, otherwise, the string
56   may appear anywhere in the line.  If the string is found, then
57   current_history () is the history entry, and the value of this
58   function is the offset in the line of that history entry that the
59   string was found in.  Otherwise, nothing is changed, and a -1 is
60   returned. */
61
62static int
63history_search_internal (string, direction, anchored)
64     const char *string;
65     int direction, anchored;
66{
67  register int i, reverse;
68  register char *line;
69  register int line_index;
70  int string_len;
71  HIST_ENTRY **the_history; 	/* local */
72
73  i = history_offset;
74  reverse = (direction < 0);
75
76  /* Take care of trivial cases first. */
77  if (string == 0 || *string == '\0')
78    return (-1);
79
80  if (!history_length || ((i >= history_length) && !reverse))
81    return (-1);
82
83  if (reverse && (i >= history_length))
84    i = history_length - 1;
85
86#define NEXT_LINE() do { if (reverse) i--; else i++; } while (0)
87
88  the_history = history_list ();
89  string_len = strlen (string);
90  while (1)
91    {
92      /* Search each line in the history list for STRING. */
93
94      /* At limit for direction? */
95      if ((reverse && i < 0) || (!reverse && i == history_length))
96	return (-1);
97
98      line = the_history[i]->line;
99      line_index = strlen (line);
100
101      /* If STRING is longer than line, no match. */
102      if (string_len > line_index)
103	{
104	  NEXT_LINE ();
105	  continue;
106	}
107
108      /* Handle anchored searches first. */
109      if (anchored == ANCHORED_SEARCH)
110	{
111	  if (STREQN (string, line, string_len))
112	    {
113	      history_offset = i;
114	      return (0);
115	    }
116
117	  NEXT_LINE ();
118	  continue;
119	}
120
121      /* Do substring search. */
122      if (reverse)
123	{
124	  line_index -= string_len;
125
126	  while (line_index >= 0)
127	    {
128	      if (STREQN (string, line + line_index, string_len))
129		{
130		  history_offset = i;
131		  return (line_index);
132		}
133	      line_index--;
134	    }
135	}
136      else
137	{
138	  register int limit;
139
140	  limit = line_index - string_len + 1;
141	  line_index = 0;
142
143	  while (line_index < limit)
144	    {
145	      if (STREQN (string, line + line_index, string_len))
146		{
147		  history_offset = i;
148		  return (line_index);
149		}
150	      line_index++;
151	    }
152	}
153      NEXT_LINE ();
154    }
155}
156
157/* Do a non-anchored search for STRING through the history in DIRECTION. */
158int
159history_search (string, direction)
160     const char *string;
161     int direction;
162{
163  return (history_search_internal (string, direction, NON_ANCHORED_SEARCH));
164}
165
166/* Do an anchored search for string through the history in DIRECTION. */
167int
168history_search_prefix (string, direction)
169     const char *string;
170     int direction;
171{
172  return (history_search_internal (string, direction, ANCHORED_SEARCH));
173}
174
175/* Search for STRING in the history list.  DIR is < 0 for searching
176   backwards.  POS is an absolute index into the history list at
177   which point to begin searching. */
178int
179history_search_pos (string, dir, pos)
180     const char *string;
181     int dir, pos;
182{
183  int ret, old;
184
185  old = where_history ();
186  history_set_pos (pos);
187  if (history_search (string, dir) == -1)
188    {
189      history_set_pos (old);
190      return (-1);
191    }
192  ret = where_history ();
193  history_set_pos (old);
194  return ret;
195}
196