Imported from ../bash-2.03.tar.gz.
[platform/upstream/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 <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 }