Wed Dec 3 16:48:20 1997 Michael Snyder (msnyder@cleaver.cygnus.com)
[external/binutils.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
28 #include <stdio.h>
29
30 #if defined (__GNUC__)
31 #  define alloca __builtin_alloca
32 #else
33 #  if defined (sparc) || defined (HAVE_ALLOCA_H)
34 #    include <alloca.h>
35 #  endif
36 #endif
37
38 #include "readline.h"
39 #include "history.h"
40
41 extern Keymap _rl_keymap;
42 extern HIST_ENTRY *saved_line_for_history;
43 extern int rl_line_buffer_len;
44 extern int rl_point, rl_end;
45 extern char *rl_prompt, *rl_line_buffer;
46
47 /* Remove these declarations when we have a complete libgnu.a. */
48 extern char *xmalloc (), *xrealloc ();
49
50 static void rl_search_history ();
51
52 /* Search backwards through the history looking for a string which is typed
53    interactively.  Start with the current line. */
54 rl_reverse_search_history (sign, key)
55      int sign;
56      int key;
57 {
58   rl_search_history (-sign, key);
59 }
60
61 /* Search forwards through the history looking for a string which is typed
62    interactively.  Start with the current line. */
63 rl_forward_search_history (sign, key)
64      int sign;
65      int key;
66 {
67   rl_search_history (sign, key);
68 }
69
70 /* Display the current state of the search in the echo-area.
71    SEARCH_STRING contains the string that is being searched for,
72    DIRECTION is zero for forward, or 1 for reverse,
73    WHERE is the history list number of the current line.  If it is
74    -1, then this line is the starting one. */
75 static void
76 rl_display_search (search_string, reverse_p, where)
77      char *search_string;
78      int reverse_p, where;
79 {
80   char *message = (char *)NULL;
81
82   message =
83     (char *)xmalloc (1 + (search_string ? strlen (search_string) : 0) + 30);
84
85   *message = '\0';
86
87 #if defined (NOTDEF)
88   if (where != -1)
89     sprintf (message, "[%d]", where + history_base);
90 #endif /* NOTDEF */
91
92   strcat (message, "(");
93
94   if (reverse_p)
95     strcat (message, "reverse-");
96
97   strcat (message, "i-search)`");
98
99   if (search_string)
100     strcat (message, search_string);
101
102   strcat (message, "': ");
103   rl_message ("%s", message, 0);
104   free (message);
105   rl_redisplay ();
106 }
107
108 /* Search through the history looking for an interactively typed string.
109    This is analogous to i-search.  We start the search in the current line.
110    DIRECTION is which direction to search; >= 0 means forward, < 0 means
111    backwards. */
112 static void
113 rl_search_history (direction, invoking_key)
114      int direction;
115      int invoking_key;
116 {
117   /* The string that the user types in to search for. */
118   char *search_string;
119
120   /* The current length of SEARCH_STRING. */
121   int search_string_index;
122
123   /* The amount of space that SEARCH_STRING has allocated to it. */
124   int search_string_size;
125
126   /* The list of lines to search through. */
127   char **lines;
128
129   /* The length of LINES. */
130   int hlen;
131
132   /* Where we get LINES from. */
133   HIST_ENTRY **hlist = history_list ();
134
135   register int i = 0;
136   int orig_point = rl_point;
137   int orig_line = where_history ();
138   int last_found_line = orig_line;
139   int c, done = 0;
140
141   /* The line currently being searched. */
142   char *sline;
143
144   /* Offset in that line. */
145   int index;
146
147   /* Non-zero if we are doing a reverse search. */
148   int reverse = (direction < 0);
149
150   /* Create an arrary of pointers to the lines that we want to search. */
151   maybe_replace_line ();
152   if (hlist)
153     for (i = 0; hlist[i]; i++);
154
155   /* Allocate space for this many lines, +1 for the current input line,
156      and remember those lines. */
157   lines = (char **)alloca ((1 + (hlen = i)) * sizeof (char *));
158   for (i = 0; i < hlen; i++)
159     lines[i] = hlist[i]->line;
160
161   if (saved_line_for_history)
162     lines[i] = saved_line_for_history->line;
163   else
164     /* So I have to type it in this way instead. */
165     {
166       char *alloced_line;
167
168       /* Keep that MIPS alloca () happy. */
169       alloced_line = (char *)alloca (1 + strlen (rl_line_buffer));
170       lines[i] = alloced_line;
171       strcpy (lines[i], &rl_line_buffer[0]);
172     }
173
174   hlen++;
175
176   /* The line where we start the search. */
177   i = orig_line;
178
179   /* Initialize search parameters. */
180   search_string = (char *)xmalloc (search_string_size = 128);
181   *search_string = '\0';
182   search_string_index = 0;
183
184   /* Normalize DIRECTION into 1 or -1. */
185   if (direction >= 0)
186     direction = 1;
187   else
188     direction = -1;
189
190   rl_display_search (search_string, reverse, -1);
191
192   sline = rl_line_buffer;
193   index = rl_point;
194
195   while (!done)
196     {
197       c = rl_read_key ();
198
199       /* Hack C to Do What I Mean. */
200       {
201         Function *f = (Function *)NULL;
202
203         if (_rl_keymap[c].type == ISFUNC)
204           {
205             f = _rl_keymap[c].function;
206
207             if (f == rl_reverse_search_history)
208               c = reverse ? -1 : -2;
209             else if (f == rl_forward_search_history)
210               c =  !reverse ? -1 : -2;
211           }
212       }
213
214       switch (c)
215         {
216         case ESC:
217           done = 1;
218           continue;
219
220           /* case invoking_key: */
221         case -1:
222           goto search_again;
223
224           /* switch directions */
225         case -2:
226           direction = -direction;
227           reverse = (direction < 0);
228
229           goto do_search;
230
231         case CTRL ('G'):
232           strcpy (rl_line_buffer, lines[orig_line]);
233           rl_point = orig_point;
234           rl_end = strlen (rl_line_buffer);
235           rl_clear_message ();
236           return;
237
238         default:
239           if (c < 32 || c > 126)
240             {
241               rl_execute_next (c);
242               done = 1;
243               continue;
244             }
245           else
246             {
247               if (search_string_index + 2 >= search_string_size)
248                 search_string = (char *)xrealloc
249                   (search_string, (search_string_size += 128));
250               search_string[search_string_index++] = c;
251               search_string[search_string_index] = '\0';
252               goto do_search;
253
254             search_again:
255
256               if (!search_string_index)
257                 continue;
258               else
259                 {
260                   if (reverse)
261                     --index;
262                   else
263                     if (index != strlen (sline))
264                       ++index;
265                     else
266                       ding ();
267                 }
268             do_search:
269
270               while (1)
271                 {
272                   if (reverse)
273                     {
274                       while (index >= 0)
275                         if (strncmp
276                             (search_string, sline + index, search_string_index)
277                             == 0)
278                           goto string_found;
279                         else
280                           index--;
281                     }
282                   else
283                     {
284                       register int limit =
285                         (strlen (sline) - search_string_index) + 1;
286
287                       while (index < limit)
288                         {
289                           if (strncmp (search_string,
290                                        sline + index,
291                                        search_string_index) == 0)
292                             goto string_found;
293                           index++;
294                         }
295                     }
296
297                 next_line:
298                   i += direction;
299
300                   /* At limit for direction? */
301                   if ((reverse && i < 0) ||
302                       (!reverse && i == hlen))
303                     goto search_failed;
304
305                   sline = lines[i];
306                   if (reverse)
307                     index = strlen (sline);
308                   else
309                     index = 0;
310
311                   /* If the search string is longer than the current
312                      line, no match. */
313                   if (search_string_index > (int)strlen (sline))
314                     goto next_line;
315
316                   /* Start actually searching. */
317                   if (reverse)
318                     index -= search_string_index;
319                 }
320
321             search_failed:
322               /* We cannot find the search string.  Ding the bell. */
323               ding ();
324               i = last_found_line;
325               break;
326
327             string_found:
328               /* We have found the search string.  Just display it.  But don't
329                  actually move there in the history list until the user accepts
330                  the location. */
331               {
332                 int line_len;
333
334                 line_len = strlen (lines[i]);
335
336                 if (line_len >= rl_line_buffer_len)
337                   rl_extend_line_buffer (line_len);
338
339                 strcpy (rl_line_buffer, lines[i]);
340                 rl_point = index;
341                 rl_end = line_len;
342                 last_found_line = i;
343                 rl_display_search
344                   (search_string, reverse, (i == orig_line) ? -1 : i);
345               }
346             }
347         }
348       continue;
349     }
350
351   /* The searching is over.  The user may have found the string that she
352      was looking for, or else she may have exited a failing search.  If
353      INDEX is -1, then that shows that the string searched for was not
354      found.  We use this to determine where to place rl_point. */
355   {
356     int now = last_found_line;
357
358     /* First put back the original state. */
359     strcpy (rl_line_buffer, lines[orig_line]);
360
361     /* Free the search string. */
362     free (search_string);
363
364     if (now < orig_line)
365       rl_get_previous_history (orig_line - now);
366     else
367       rl_get_next_history (now - orig_line);
368
369     /* If the index of the "matched" string is less than zero, then the
370        final search string was never matched, so put point somewhere
371        reasonable. */
372     if (index < 0)
373       index = strlen (rl_line_buffer);
374
375     rl_point = index;
376     rl_clear_message ();
377   }
378 }