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