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