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