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