isearch.c revision 21308
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. */ 46extern Keymap _rl_keymap; 47extern HIST_ENTRY *saved_line_for_history; 48extern int rl_line_buffer_len; 49extern int rl_point, rl_end; 50extern char *rl_line_buffer; 51 52extern void _rl_save_prompt (); 53extern void _rl_restore_prompt (); 54 55extern int rl_execute_next (); 56extern void rl_extend_line_buffer (); 57 58extern int _rl_input_available (); 59 60extern char *xmalloc (), *xrealloc (); 61 62static 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. */ 66static 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. */ 70int 71rl_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. */ 79int 80rl_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. */ 91static void 92rl_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. */ 140static int 141rl_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} 411