From Brad Lucier <lucier@math.purdue.edu>:
[deliverable/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 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 <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 /* Variables exported to other files in the readline library. */
52 unsigned char *_rl_isearch_terminators = (unsigned char *)NULL;
53
54 /* Variables imported from other files in the readline library. */
55 extern Keymap _rl_keymap;
56 extern HIST_ENTRY *saved_line_for_history;
57 extern int rl_line_buffer_len;
58 extern int rl_point, rl_end;
59 extern char *rl_line_buffer;
60
61 extern int rl_execute_next ();
62 extern void rl_extend_line_buffer ();
63
64 extern int _rl_input_available ();
65
66 extern char *xmalloc (), *xrealloc ();
67
68 static int rl_search_history ();
69
70 /* Last line found by the current incremental search, so we don't `find'
71 identical lines many times in a row. */
72 static char *prev_line_found;
73
74 /* Search backwards through the history looking for a string which is typed
75 interactively. Start with the current line. */
76 int
77 rl_reverse_search_history (sign, key)
78 int sign, key;
79 {
80 return (rl_search_history (-sign, key));
81 }
82
83 /* Search forwards through the history looking for a string which is typed
84 interactively. Start with the current line. */
85 int
86 rl_forward_search_history (sign, key)
87 int sign, key;
88 {
89 return (rl_search_history (sign, key));
90 }
91
92 /* Display the current state of the search in the echo-area.
93 SEARCH_STRING contains the string that is being searched for,
94 DIRECTION is zero for forward, or 1 for reverse,
95 WHERE is the history list number of the current line. If it is
96 -1, then this line is the starting one. */
97 static void
98 rl_display_search (search_string, reverse_p, where)
99 char *search_string;
100 int reverse_p, where;
101 {
102 char *message;
103 int msglen, searchlen;
104
105 searchlen = (search_string && *search_string) ? strlen (search_string) : 0;
106
107 message = xmalloc (searchlen + 33);
108 msglen = 0;
109
110 #if defined (NOTDEF)
111 if (where != -1)
112 {
113 sprintf (message, "[%d]", where + history_base);
114 msglen = strlen (message);
115 }
116 #endif /* NOTDEF */
117
118 message[msglen++] = '(';
119
120 if (reverse_p)
121 {
122 strcpy (message + msglen, "reverse-");
123 msglen += 8;
124 }
125
126 strcpy (message + msglen, "i-search)`");
127 msglen += 10;
128
129 if (search_string)
130 {
131 strcpy (message + msglen, search_string);
132 msglen += searchlen;
133 }
134
135 strcpy (message + msglen, "': ");
136
137 rl_message ("%s", message, 0);
138 free (message);
139 (*rl_redisplay_function) ();
140 }
141
142 /* Search through the history looking for an interactively typed string.
143 This is analogous to i-search. We start the search in the current line.
144 DIRECTION is which direction to search; >= 0 means forward, < 0 means
145 backwards. */
146 static int
147 rl_search_history (direction, invoking_key)
148 int direction, invoking_key;
149 {
150 /* The string that the user types in to search for. */
151 char *search_string;
152
153 /* The current length of SEARCH_STRING. */
154 int search_string_index;
155
156 /* The amount of space that SEARCH_STRING has allocated to it. */
157 int search_string_size;
158
159 /* The list of lines to search through. */
160 char **lines, *allocated_line;
161
162 /* The length of LINES. */
163 int hlen;
164
165 /* Where we get LINES from. */
166 HIST_ENTRY **hlist;
167
168 register int i;
169 int orig_point, orig_line, last_found_line;
170 int c, found, failed, sline_len;
171
172 /* The line currently being searched. */
173 char *sline;
174
175 /* Offset in that line. */
176 int line_index;
177
178 /* Non-zero if we are doing a reverse search. */
179 int reverse;
180
181 /* The list of characters which terminate the search, but are not
182 subsequently executed. If the variable isearch-terminators has
183 been set, we use that value, otherwise we use ESC and C-J. */
184 unsigned char *isearch_terminators;
185
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 : (unsigned char *)"\033\012";
194
195 /* Create an arrary of pointers to the lines that we want to search. */
196 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 (saved_line_for_history)
208 lines[i] = saved_line_for_history->line;
209 else
210 {
211 /* Keep track of this so we can free it. */
212 allocated_line = 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 = 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 Function *f = (Function *)NULL;
243
244 /* Read a key and decide how to proceed. */
245 c = rl_read_key ();
246
247 if (_rl_keymap[c].type == ISFUNC)
248 {
249 f = _rl_keymap[c].function;
250
251 if (f == rl_reverse_search_history)
252 c = reverse ? -1 : -2;
253 else if (f == rl_forward_search_history)
254 c = !reverse ? -1 : -2;
255 }
256
257 #if 0
258 /* Let NEWLINE (^J) terminate the search for people who don't like
259 using ESC. ^M can still be used to terminate the search and
260 immediately execute the command. */
261 if (c == ESC || c == NEWLINE)
262 #else
263 /* The characters in isearch_terminators (set from the user-settable
264 variable isearch-terminators) are used to terminate the search but
265 not subsequently execute the character as a command. The default
266 value is "\033\012" (ESC and C-J). */
267 if (strchr (isearch_terminators, c))
268 #endif
269 {
270 /* ESC still terminates the search, but if there is pending
271 input or if input arrives within 0.1 seconds (on systems
272 with select(2)) it is used as a prefix character
273 with rl_execute_next. WATCH OUT FOR THIS! This is intended
274 to allow the arrow keys to be used like ^F and ^B are used
275 to terminate the search and execute the movement command. */
276 if (c == ESC && _rl_input_available ()) /* XXX */
277 rl_execute_next (ESC);
278 break;
279 }
280
281 if (c >= 0 && (CTRL_CHAR (c) || META_CHAR (c) || c == RUBOUT) && c != CTRL ('G'))
282 {
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 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 return 0;
316
317 #if 0
318 /* delete character from search string. */
319 case -3:
320 if (search_string_index == 0)
321 ding ();
322 else
323 {
324 search_string[--search_string_index] = '\0';
325 /* This is tricky. To do this right, we need to keep a
326 stack of search positions for the current search, with
327 sentinels marking the beginning and end. */
328 }
329 break;
330 #endif
331
332 default:
333 /* Add character to search string and continue search. */
334 if (search_string_index + 2 >= search_string_size)
335 {
336 search_string_size += 128;
337 search_string = xrealloc (search_string, search_string_size);
338 }
339 search_string[search_string_index++] = c;
340 search_string[search_string_index] = '\0';
341 break;
342 }
343
344 for (found = failed = 0;;)
345 {
346 int limit = sline_len - search_string_index + 1;
347
348 /* Search the current line. */
349 while (reverse ? (line_index >= 0) : (line_index < limit))
350 {
351 if (STREQN (search_string, sline + line_index, search_string_index))
352 {
353 found++;
354 break;
355 }
356 else
357 line_index += direction;
358 }
359 if (found)
360 break;
361
362 /* Move to the next line, but skip new copies of the line
363 we just found and lines shorter than the string we're
364 searching for. */
365 do
366 {
367 /* Move to the next line. */
368 i += direction;
369
370 /* At limit for direction? */
371 if (reverse ? (i < 0) : (i == hlen))
372 {
373 failed++;
374 break;
375 }
376
377 /* We will need these later. */
378 sline = lines[i];
379 sline_len = strlen (sline);
380 }
381 while ((prev_line_found && STREQ (prev_line_found, lines[i])) ||
382 (search_string_index > sline_len));
383
384 if (failed)
385 break;
386
387 /* Now set up the line for searching... */
388 line_index = reverse ? sline_len - search_string_index : 0;
389 }
390
391 if (failed)
392 {
393 /* We cannot find the search string. Ding the bell. */
394 ding ();
395 i = last_found_line;
396 continue; /* XXX - was break */
397 }
398
399 /* We have found the search string. Just display it. But don't
400 actually move there in the history list until the user accepts
401 the location. */
402 if (found)
403 {
404 int line_len;
405
406 prev_line_found = lines[i];
407 line_len = strlen (lines[i]);
408
409 if (line_len >= rl_line_buffer_len)
410 rl_extend_line_buffer (line_len);
411
412 strcpy (rl_line_buffer, lines[i]);
413 rl_point = line_index;
414 rl_end = line_len;
415 last_found_line = i;
416 rl_display_search (search_string, reverse, (i == orig_line) ? -1 : i);
417 }
418 }
419
420 /* The searching is over. The user may have found the string that she
421 was looking for, or else she may have exited a failing search. If
422 LINE_INDEX is -1, then that shows that the string searched for was
423 not found. We use this to determine where to place rl_point. */
424
425 /* First put back the original state. */
426 strcpy (rl_line_buffer, lines[orig_line]);
427
428 rl_restore_prompt ();
429
430 /* Free the search string. */
431 free (search_string);
432
433 if (last_found_line < orig_line)
434 rl_get_previous_history (orig_line - last_found_line, 0);
435 else
436 rl_get_next_history (last_found_line - orig_line, 0);
437
438 /* If the string was not found, put point at the end of the line. */
439 if (line_index < 0)
440 line_index = strlen (rl_line_buffer);
441 rl_point = line_index;
442 rl_clear_message ();
443
444 if (allocated_line)
445 free (allocated_line);
446 free (lines);
447
448 return 0;
449 }
This page took 0.053592 seconds and 4 git commands to generate.