]> git.ipfire.org Git - thirdparty/bash.git/blob - lib/readline/isearch.c
fa60fa49840545a5f6f065442055d57f2159368c
[thirdparty/bash.git] / lib / readline / isearch.c
1 /* **************************************************************** */
2 /* */
3 /* I-Search and Searching */
4 /* */
5 /* **************************************************************** */
6
7 /* Copyright (C) 1987,1989 Free Software Foundation, Inc.
8
9 This file contains the Readline Library (the Library), a set of
10 routines for providing Emacs style line input to programs that ask
11 for it.
12
13 The Library is free software; you can redistribute it and/or modify
14 it under the terms of the GNU General Public License as published by
15 the Free Software Foundation; either version 1, or (at your option)
16 any later version.
17
18 The Library is distributed in the hope that it will be useful, but
19 WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 General Public License for more details.
22
23 The GNU General Public License is often shipped with GNU software, and
24 is generally kept in a file called COPYING or LICENSE. If you do not
25 have a copy of the license, write to the Free Software Foundation,
26 675 Mass Ave, Cambridge, MA 02139, USA. */
27 #define READLINE_LIBRARY
28
29 #if defined (HAVE_CONFIG_H)
30 # include <config.h>
31 #endif
32
33 #include <stdio.h>
34
35 #if defined (HAVE_UNISTD_H)
36 # include <unistd.h>
37 #endif
38
39 #include <sys/types.h>
40
41 #include "rldefs.h"
42 #include "readline.h"
43 #include "history.h"
44
45 /* Variables imported from other files in the readline library. */
46 extern Keymap _rl_keymap;
47 extern HIST_ENTRY *saved_line_for_history;
48 extern int rl_line_buffer_len;
49 extern int rl_point, rl_end;
50 extern char *rl_line_buffer;
51
52 extern void _rl_save_prompt ();
53 extern void _rl_restore_prompt ();
54
55 extern int rl_execute_next ();
56 extern void rl_extend_line_buffer ();
57
58 extern int _rl_input_available ();
59
60 extern char *xmalloc (), *xrealloc ();
61
62 static int rl_search_history ();
63
64 /* Last line found by the current incremental search, so we don't `find'
65 identical lines many times in a row. */
66 static char *prev_line_found;
67
68 /* Search backwards through the history looking for a string which is typed
69 interactively. Start with the current line. */
70 int
71 rl_reverse_search_history (sign, key)
72 int sign, key;
73 {
74 return (rl_search_history (-sign, key));
75 }
76
77 /* Search forwards through the history looking for a string which is typed
78 interactively. Start with the current line. */
79 int
80 rl_forward_search_history (sign, key)
81 int sign, key;
82 {
83 return (rl_search_history (sign, key));
84 }
85
86 /* Display the current state of the search in the echo-area.
87 SEARCH_STRING contains the string that is being searched for,
88 DIRECTION is zero for forward, or 1 for reverse,
89 WHERE is the history list number of the current line. If it is
90 -1, then this line is the starting one. */
91 static void
92 rl_display_search (search_string, reverse_p, where)
93 char *search_string;
94 int reverse_p, where;
95 {
96 char *message;
97 int msglen, searchlen;
98
99 searchlen = (search_string && *search_string) ? strlen (search_string) : 0;
100
101 message = xmalloc (searchlen + 33);
102 msglen = 0;
103
104 #if defined (NOTDEF)
105 if (where != -1)
106 {
107 sprintf (message, "[%d]", where + history_base);
108 msglen = strlen (message);
109 }
110 #endif /* NOTDEF */
111
112 message[msglen++] = '(';
113
114 if (reverse_p)
115 {
116 strcpy (message + msglen, "reverse-");
117 msglen += 8;
118 }
119
120 strcpy (message + msglen, "i-search)`");
121 msglen += 10;
122
123 if (search_string)
124 {
125 strcpy (message + msglen, search_string);
126 msglen += searchlen;
127 }
128
129 strcpy (message + msglen, "': ");
130
131 rl_message ("%s", message, 0);
132 free (message);
133 (*rl_redisplay_function) ();
134 }
135
136 /* Search through the history looking for an interactively typed string.
137 This is analogous to i-search. We start the search in the current line.
138 DIRECTION is which direction to search; >= 0 means forward, < 0 means
139 backwards. */
140 static int
141 rl_search_history (direction, invoking_key)
142 int direction, invoking_key;
143 {
144 /* The string that the user types in to search for. */
145 char *search_string;
146
147 /* The current length of SEARCH_STRING. */
148 int search_string_index;
149
150 /* The amount of space that SEARCH_STRING has allocated to it. */
151 int search_string_size;
152
153 /* The list of lines to search through. */
154 char **lines, *allocated_line;
155
156 /* The length of LINES. */
157 int hlen;
158
159 /* Where we get LINES from. */
160 HIST_ENTRY **hlist;
161
162 register int i;
163 int orig_point, orig_line, last_found_line;
164 int c, found, failed, sline_len;
165
166 /* The line currently being searched. */
167 char *sline;
168
169 /* Offset in that line. */
170 int line_index;
171
172 /* Non-zero if we are doing a reverse search. */
173 int reverse;
174
175 orig_point = rl_point;
176 last_found_line = orig_line = where_history ();
177 reverse = direction < 0;
178 hlist = history_list ();
179 allocated_line = (char *)NULL;
180
181 /* Create an arrary of pointers to the lines that we want to search. */
182 maybe_replace_line ();
183 i = 0;
184 if (hlist)
185 for (i = 0; hlist[i]; i++);
186
187 /* Allocate space for this many lines, +1 for the current input line,
188 and remember those lines. */
189 lines = (char **)xmalloc ((1 + (hlen = i)) * sizeof (char *));
190 for (i = 0; i < hlen; i++)
191 lines[i] = hlist[i]->line;
192
193 if (saved_line_for_history)
194 lines[i] = saved_line_for_history->line;
195 else
196 {
197 /* Keep track of this so we can free it. */
198 allocated_line = xmalloc (1 + strlen (rl_line_buffer));
199 strcpy (allocated_line, &rl_line_buffer[0]);
200 lines[i] = allocated_line;
201 }
202
203 hlen++;
204
205 /* The line where we start the search. */
206 i = orig_line;
207
208 _rl_save_prompt ();
209
210 /* Initialize search parameters. */
211 search_string = xmalloc (search_string_size = 128);
212 *search_string = '\0';
213 search_string_index = 0;
214 prev_line_found = (char *)0; /* XXX */
215
216 /* Normalize DIRECTION into 1 or -1. */
217 direction = (direction >= 0) ? 1 : -1;
218
219 rl_display_search (search_string, reverse, -1);
220
221 sline = rl_line_buffer;
222 sline_len = strlen (sline);
223 line_index = rl_point;
224
225 found = failed = 0;
226 for (;;)
227 {
228 Function *f = (Function *)NULL;
229
230 /* Read a key and decide how to proceed. */
231 c = rl_read_key ();
232
233 if (_rl_keymap[c].type == ISFUNC)
234 {
235 f = _rl_keymap[c].function;
236
237 if (f == rl_reverse_search_history)
238 c = reverse ? -1 : -2;
239 else if (f == rl_forward_search_history)
240 c = !reverse ? -1 : -2;
241 }
242
243 /* Let NEWLINE (^J) terminate the search for people who don't like
244 using ESC. ^M can still be used to terminate the search and
245 immediately execute the command. */
246 if (c == ESC || c == NEWLINE)
247 {
248 /* ESC still terminates the search, but if there is pending
249 input or if input arrives within 0.1 seconds (on systems
250 with select(2)) it is used as a prefix character
251 with rl_execute_next. WATCH OUT FOR THIS! This is intended
252 to allow the arrow keys to be used like ^F and ^B are used
253 to terminate the search and execute the movement command. */
254 if (c == ESC && _rl_input_available ()) /* XXX */
255 rl_execute_next (ESC);
256 break;
257 }
258
259 if (c >= 0 && (CTRL_CHAR (c) || META_CHAR (c) || c == RUBOUT))
260 {
261 rl_execute_next (c);
262 break;
263 }
264
265 switch (c)
266 {
267 case -1:
268 if (search_string_index == 0)
269 continue;
270 else if (reverse)
271 --line_index;
272 else if (line_index != sline_len)
273 ++line_index;
274 else
275 ding ();
276 break;
277
278 /* switch directions */
279 case -2:
280 direction = -direction;
281 reverse = direction < 0;
282 break;
283
284 case CTRL ('G'):
285 strcpy (rl_line_buffer, lines[orig_line]);
286 rl_point = orig_point;
287 rl_end = strlen (rl_line_buffer);
288 _rl_restore_prompt();
289 rl_clear_message ();
290 free (allocated_line);
291 free (lines);
292 return 0;
293
294 default:
295 /* Add character to search string and continue search. */
296 if (search_string_index + 2 >= search_string_size)
297 {
298 search_string_size += 128;
299 search_string = xrealloc (search_string, search_string_size);
300 }
301 search_string[search_string_index++] = c;
302 search_string[search_string_index] = '\0';
303 break;
304 }
305
306 for (found = failed = 0;;)
307 {
308 int limit = sline_len - search_string_index + 1;
309
310 /* Search the current line. */
311 while (reverse ? (line_index >= 0) : (line_index < limit))
312 {
313 if (STREQN (search_string, sline + line_index, search_string_index))
314 {
315 found++;
316 break;
317 }
318 else
319 line_index += direction;
320 }
321 if (found)
322 break;
323
324 /* Move to the next line, but skip new copies of the line
325 we just found and lines shorter than the string we're
326 searching for. */
327 do
328 {
329 /* Move to the next line. */
330 i += direction;
331
332 /* At limit for direction? */
333 if (reverse ? (i < 0) : (i == hlen))
334 {
335 failed++;
336 break;
337 }
338
339 /* We will need these later. */
340 sline = lines[i];
341 sline_len = strlen (sline);
342 }
343 while ((prev_line_found && STREQ (prev_line_found, lines[i])) ||
344 (search_string_index > sline_len));
345
346 if (failed)
347 break;
348
349 /* Now set up the line for searching... */
350 line_index = reverse ? sline_len - search_string_index : 0;
351 }
352
353 if (failed)
354 {
355 /* We cannot find the search string. Ding the bell. */
356 ding ();
357 i = last_found_line;
358 continue; /* XXX - was break */
359 }
360
361 /* We have found the search string. Just display it. But don't
362 actually move there in the history list until the user accepts
363 the location. */
364 if (found)
365 {
366 int line_len;
367
368 prev_line_found = lines[i];
369 line_len = strlen (lines[i]);
370
371 if (line_len >= rl_line_buffer_len)
372 rl_extend_line_buffer (line_len);
373
374 strcpy (rl_line_buffer, lines[i]);
375 rl_point = line_index;
376 rl_end = line_len;
377 last_found_line = i;
378 rl_display_search (search_string, reverse, (i == orig_line) ? -1 : i);
379 }
380 }
381
382 /* The searching is over. The user may have found the string that she
383 was looking for, or else she may have exited a failing search. If
384 LINE_INDEX is -1, then that shows that the string searched for was
385 not found. We use this to determine where to place rl_point. */
386
387 /* First put back the original state. */
388 strcpy (rl_line_buffer, lines[orig_line]);
389
390 _rl_restore_prompt ();
391
392 /* Free the search string. */
393 free (search_string);
394
395 if (last_found_line < orig_line)
396 rl_get_previous_history (orig_line - last_found_line);
397 else
398 rl_get_next_history (last_found_line - orig_line);
399
400 /* If the string was not found, put point at the end of the line. */
401 if (line_index < 0)
402 line_index = strlen (rl_line_buffer);
403 rl_point = line_index;
404 rl_clear_message ();
405
406 free (allocated_line);
407 free (lines);
408
409 return 0;
410 }