]> git.ipfire.org Git - thirdparty/bash.git/blob - lib/readline/isearch.c
2e3e16b8a9993edd99cf82a75e450b7a2b01cda7
[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 2, 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 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
27 #define READLINE_LIBRARY
28
29 #if defined (HAVE_CONFIG_H)
30 # include <config.h>
31 #endif
32
33 #include <sys/types.h>
34
35 #include <stdio.h>
36
37 #if defined (HAVE_UNISTD_H)
38 # include <unistd.h>
39 #endif
40
41 #if defined (HAVE_STDLIB_H)
42 # include <stdlib.h>
43 #else
44 # include "ansi_stdlib.h"
45 #endif
46
47 #include "rldefs.h"
48 #include "readline.h"
49 #include "history.h"
50
51 #include "rlprivate.h"
52 #include "xmalloc.h"
53
54 /* Variables exported to other files in the readline library. */
55 char *_rl_isearch_terminators = (char *)NULL;
56
57 /* Variables imported from other files in the readline library. */
58 extern HIST_ENTRY *_rl_saved_line_for_history;
59
60 /* Forward declarations */
61 static int rl_search_history PARAMS((int, int));
62
63 /* Last line found by the current incremental search, so we don't `find'
64 identical lines many times in a row. */
65 static char *prev_line_found;
66
67 /* Last search string and its length. */
68 static char *last_isearch_string;
69 static int last_isearch_string_len;
70
71 static char *default_isearch_terminators = "\033\012";
72
73 /* Search backwards through the history looking for a string which is typed
74 interactively. Start with the current line. */
75 int
76 rl_reverse_search_history (sign, key)
77 int sign, key;
78 {
79 return (rl_search_history (-sign, key));
80 }
81
82 /* Search forwards through the history looking for a string which is typed
83 interactively. Start with the current line. */
84 int
85 rl_forward_search_history (sign, key)
86 int sign, key;
87 {
88 return (rl_search_history (sign, key));
89 }
90
91 /* Display the current state of the search in the echo-area.
92 SEARCH_STRING contains the string that is being searched for,
93 DIRECTION is zero for forward, or 1 for reverse,
94 WHERE is the history list number of the current line. If it is
95 -1, then this line is the starting one. */
96 static void
97 rl_display_search (search_string, reverse_p, where)
98 char *search_string;
99 int reverse_p, where;
100 {
101 char *message;
102 int msglen, searchlen;
103
104 searchlen = (search_string && *search_string) ? strlen (search_string) : 0;
105
106 message = (char *)xmalloc (searchlen + 33);
107 msglen = 0;
108
109 #if defined (NOTDEF)
110 if (where != -1)
111 {
112 sprintf (message, "[%d]", where + history_base);
113 msglen = strlen (message);
114 }
115 #endif /* NOTDEF */
116
117 message[msglen++] = '(';
118
119 if (reverse_p)
120 {
121 strcpy (message + msglen, "reverse-");
122 msglen += 8;
123 }
124
125 strcpy (message + msglen, "i-search)`");
126 msglen += 10;
127
128 if (search_string)
129 {
130 strcpy (message + msglen, search_string);
131 msglen += searchlen;
132 }
133
134 strcpy (message + msglen, "': ");
135
136 rl_message ("%s", message);
137 free (message);
138 (*rl_redisplay_function) ();
139 }
140
141 /* Search through the history looking for an interactively typed string.
142 This is analogous to i-search. We start the search in the current line.
143 DIRECTION is which direction to search; >= 0 means forward, < 0 means
144 backwards. */
145 static int
146 rl_search_history (direction, invoking_key)
147 int direction, invoking_key;
148 {
149 /* The string that the user types in to search for. */
150 char *search_string;
151
152 /* The current length of SEARCH_STRING. */
153 int search_string_index;
154
155 /* The amount of space that SEARCH_STRING has allocated to it. */
156 int search_string_size;
157
158 /* The list of lines to search through. */
159 char **lines, *allocated_line;
160
161 /* The length of LINES. */
162 int hlen;
163
164 /* Where we get LINES from. */
165 HIST_ENTRY **hlist;
166
167 register int i;
168 int orig_point, orig_line, last_found_line;
169 int c, found, failed, sline_len;
170
171 /* The line currently being searched. */
172 char *sline;
173
174 /* Offset in that line. */
175 int line_index;
176
177 /* Non-zero if we are doing a reverse search. */
178 int reverse;
179
180 /* The list of characters which terminate the search, but are not
181 subsequently executed. If the variable isearch-terminators has
182 been set, we use that value, otherwise we use ESC and C-J. */
183 char *isearch_terminators;
184
185 RL_SETSTATE(RL_STATE_ISEARCH);
186 orig_point = rl_point;
187 last_found_line = orig_line = where_history ();
188 reverse = direction < 0;
189 hlist = history_list ();
190 allocated_line = (char *)NULL;
191
192 isearch_terminators = _rl_isearch_terminators ? _rl_isearch_terminators
193 : default_isearch_terminators;
194
195 /* Create an arrary of pointers to the lines that we want to search. */
196 rl_maybe_replace_line ();
197 i = 0;
198 if (hlist)
199 for (i = 0; hlist[i]; i++);
200
201 /* Allocate space for this many lines, +1 for the current input line,
202 and remember those lines. */
203 lines = (char **)xmalloc ((1 + (hlen = i)) * sizeof (char *));
204 for (i = 0; i < hlen; i++)
205 lines[i] = hlist[i]->line;
206
207 if (_rl_saved_line_for_history)
208 lines[i] = _rl_saved_line_for_history->line;
209 else
210 {
211 /* Keep track of this so we can free it. */
212 allocated_line = (char *)xmalloc (1 + strlen (rl_line_buffer));
213 strcpy (allocated_line, &rl_line_buffer[0]);
214 lines[i] = allocated_line;
215 }
216
217 hlen++;
218
219 /* The line where we start the search. */
220 i = orig_line;
221
222 rl_save_prompt ();
223
224 /* Initialize search parameters. */
225 search_string = (char *)xmalloc (search_string_size = 128);
226 *search_string = '\0';
227 search_string_index = 0;
228 prev_line_found = (char *)0; /* XXX */
229
230 /* Normalize DIRECTION into 1 or -1. */
231 direction = (direction >= 0) ? 1 : -1;
232
233 rl_display_search (search_string, reverse, -1);
234
235 sline = rl_line_buffer;
236 sline_len = strlen (sline);
237 line_index = rl_point;
238
239 found = failed = 0;
240 for (;;)
241 {
242 rl_command_func_t *f = (rl_command_func_t *)NULL;
243
244 /* Read a key and decide how to proceed. */
245 RL_SETSTATE(RL_STATE_MOREINPUT);
246 c = rl_read_key ();
247 RL_UNSETSTATE(RL_STATE_MOREINPUT);
248
249 if (c >= 0 && _rl_keymap[c].type == ISFUNC)
250 {
251 f = _rl_keymap[c].function;
252
253 if (f == rl_reverse_search_history)
254 c = reverse ? -1 : -2;
255 else if (f == rl_forward_search_history)
256 c = !reverse ? -1 : -2;
257 }
258
259 #if 0
260 /* Let NEWLINE (^J) terminate the search for people who don't like
261 using ESC. ^M can still be used to terminate the search and
262 immediately execute the command. */
263 if (c == ESC || c == NEWLINE)
264 #else
265 /* The characters in isearch_terminators (set from the user-settable
266 variable isearch-terminators) are used to terminate the search but
267 not subsequently execute the character as a command. The default
268 value is "\033\012" (ESC and C-J). */
269 if (strchr (isearch_terminators, c))
270 #endif
271 {
272 /* ESC still terminates the search, but if there is pending
273 input or if input arrives within 0.1 seconds (on systems
274 with select(2)) it is used as a prefix character
275 with rl_execute_next. WATCH OUT FOR THIS! This is intended
276 to allow the arrow keys to be used like ^F and ^B are used
277 to terminate the search and execute the movement command. */
278 if (c == ESC && _rl_input_available ()) /* XXX */
279 rl_execute_next (ESC);
280 break;
281 }
282
283 if (c >= 0 && (CTRL_CHAR (c) || META_CHAR (c) || c == RUBOUT) && c != CTRL ('G'))
284 {
285 /* This sets rl_pending_input to c; it will be picked up the next
286 time rl_read_key is called. */
287 rl_execute_next (c);
288 break;
289 }
290
291 switch (c)
292 {
293 case -1:
294 if (search_string_index == 0)
295 {
296 if (last_isearch_string)
297 {
298 search_string_size = 64 + last_isearch_string_len;
299 search_string = (char *)xrealloc (search_string, search_string_size);
300 strcpy (search_string, last_isearch_string);
301 search_string_index = last_isearch_string_len;
302 rl_display_search (search_string, reverse, -1);
303 break;
304 }
305 continue;
306 }
307 else if (reverse)
308 --line_index;
309 else if (line_index != sline_len)
310 ++line_index;
311 else
312 rl_ding ();
313 break;
314
315 /* switch directions */
316 case -2:
317 direction = -direction;
318 reverse = direction < 0;
319 break;
320
321 case CTRL ('G'):
322 strcpy (rl_line_buffer, lines[orig_line]);
323 rl_point = orig_point;
324 rl_end = strlen (rl_line_buffer);
325 rl_restore_prompt();
326 rl_clear_message ();
327 if (allocated_line)
328 free (allocated_line);
329 free (lines);
330 RL_UNSETSTATE(RL_STATE_ISEARCH);
331 return 0;
332
333 #if 0
334 /* delete character from search string. */
335 case -3:
336 if (search_string_index == 0)
337 rl_ding ();
338 else
339 {
340 search_string[--search_string_index] = '\0';
341 /* This is tricky. To do this right, we need to keep a
342 stack of search positions for the current search, with
343 sentinels marking the beginning and end. */
344 }
345 break;
346 #endif
347
348 default:
349 /* Add character to search string and continue search. */
350 if (search_string_index + 2 >= search_string_size)
351 {
352 search_string_size += 128;
353 search_string = (char *)xrealloc (search_string, search_string_size);
354 }
355 search_string[search_string_index++] = c;
356 search_string[search_string_index] = '\0';
357 break;
358 }
359
360 for (found = failed = 0;;)
361 {
362 int limit = sline_len - search_string_index + 1;
363
364 /* Search the current line. */
365 while (reverse ? (line_index >= 0) : (line_index < limit))
366 {
367 if (STREQN (search_string, sline + line_index, search_string_index))
368 {
369 found++;
370 break;
371 }
372 else
373 line_index += direction;
374 }
375 if (found)
376 break;
377
378 /* Move to the next line, but skip new copies of the line
379 we just found and lines shorter than the string we're
380 searching for. */
381 do
382 {
383 /* Move to the next line. */
384 i += direction;
385
386 /* At limit for direction? */
387 if (reverse ? (i < 0) : (i == hlen))
388 {
389 failed++;
390 break;
391 }
392
393 /* We will need these later. */
394 sline = lines[i];
395 sline_len = strlen (sline);
396 }
397 while ((prev_line_found && STREQ (prev_line_found, lines[i])) ||
398 (search_string_index > sline_len));
399
400 if (failed)
401 break;
402
403 /* Now set up the line for searching... */
404 line_index = reverse ? sline_len - search_string_index : 0;
405 }
406
407 if (failed)
408 {
409 /* We cannot find the search string. Ding the bell. */
410 rl_ding ();
411 i = last_found_line;
412 continue; /* XXX - was break */
413 }
414
415 /* We have found the search string. Just display it. But don't
416 actually move there in the history list until the user accepts
417 the location. */
418 if (found)
419 {
420 int line_len;
421
422 prev_line_found = lines[i];
423 line_len = strlen (lines[i]);
424
425 if (line_len >= rl_line_buffer_len)
426 rl_extend_line_buffer (line_len);
427
428 strcpy (rl_line_buffer, lines[i]);
429 rl_point = line_index;
430 rl_end = line_len;
431 last_found_line = i;
432 rl_display_search (search_string, reverse, (i == orig_line) ? -1 : i);
433 }
434 }
435
436 /* The searching is over. The user may have found the string that she
437 was looking for, or else she may have exited a failing search. If
438 LINE_INDEX is -1, then that shows that the string searched for was
439 not found. We use this to determine where to place rl_point. */
440
441 /* First put back the original state. */
442 strcpy (rl_line_buffer, lines[orig_line]);
443
444 rl_restore_prompt ();
445
446 #if 1
447 /* Save the search string for possible later use. */
448 FREE (last_isearch_string);
449 last_isearch_string = search_string;
450 last_isearch_string_len = search_string_index;
451 #else
452 /* Free the search string. */
453 free (search_string);
454 #endif
455
456 if (last_found_line < orig_line)
457 rl_get_previous_history (orig_line - last_found_line, 0);
458 else
459 rl_get_next_history (last_found_line - orig_line, 0);
460
461 /* If the string was not found, put point at the end of the line. */
462 if (line_index < 0)
463 line_index = strlen (rl_line_buffer);
464 rl_point = line_index;
465 rl_clear_message ();
466
467 FREE (allocated_line);
468 free (lines);
469
470 RL_UNSETSTATE(RL_STATE_ISEARCH);
471
472 return 0;
473 }