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