isearch.c revision 35486
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 imported from other files in the readline library. */ 52extern Keymap _rl_keymap; 53extern HIST_ENTRY *saved_line_for_history; 54extern int rl_line_buffer_len; 55extern int rl_point, rl_end; 56extern char *rl_line_buffer; 57 58extern void _rl_save_prompt (); 59extern void _rl_restore_prompt (); 60 61extern int rl_execute_next (); 62extern void rl_extend_line_buffer (); 63 64extern int _rl_input_available (); 65 66extern char *xmalloc (), *xrealloc (); 67 68static 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. */ 72static 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. */ 76int 77rl_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. */ 85int 86rl_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. */ 97static void 98rl_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. */ 146static int 147rl_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 orig_point = rl_point; 182 last_found_line = orig_line = where_history (); 183 reverse = direction < 0; 184 hlist = history_list (); 185 allocated_line = (char *)NULL; 186 187 /* Create an arrary of pointers to the lines that we want to search. */ 188 maybe_replace_line (); 189 i = 0; 190 if (hlist) 191 for (i = 0; hlist[i]; i++); 192 193 /* Allocate space for this many lines, +1 for the current input line, 194 and remember those lines. */ 195 lines = (char **)xmalloc ((1 + (hlen = i)) * sizeof (char *)); 196 for (i = 0; i < hlen; i++) 197 lines[i] = hlist[i]->line; 198 199 if (saved_line_for_history) 200 lines[i] = saved_line_for_history->line; 201 else 202 { 203 /* Keep track of this so we can free it. */ 204 allocated_line = xmalloc (1 + strlen (rl_line_buffer)); 205 strcpy (allocated_line, &rl_line_buffer[0]); 206 lines[i] = allocated_line; 207 } 208 209 hlen++; 210 211 /* The line where we start the search. */ 212 i = orig_line; 213 214 _rl_save_prompt (); 215 216 /* Initialize search parameters. */ 217 search_string = xmalloc (search_string_size = 128); 218 *search_string = '\0'; 219 search_string_index = 0; 220 prev_line_found = (char *)0; /* XXX */ 221 222 /* Normalize DIRECTION into 1 or -1. */ 223 direction = (direction >= 0) ? 1 : -1; 224 225 rl_display_search (search_string, reverse, -1); 226 227 sline = rl_line_buffer; 228 sline_len = strlen (sline); 229 line_index = rl_point; 230 231 found = failed = 0; 232 for (;;) 233 { 234 Function *f = (Function *)NULL; 235 236 /* Read a key and decide how to proceed. */ 237 c = rl_read_key (); 238 239 if (_rl_keymap[c].type == ISFUNC) 240 { 241 f = _rl_keymap[c].function; 242 243 if (f == rl_reverse_search_history) 244 c = reverse ? -1 : -2; 245 else if (f == rl_forward_search_history) 246 c = !reverse ? -1 : -2; 247 } 248 249 /* Let NEWLINE (^J) terminate the search for people who don't like 250 using ESC. ^M can still be used to terminate the search and 251 immediately execute the command. */ 252 if (c == ESC || c == NEWLINE) 253 { 254 /* ESC still terminates the search, but if there is pending 255 input or if input arrives within 0.1 seconds (on systems 256 with select(2)) it is used as a prefix character 257 with rl_execute_next. WATCH OUT FOR THIS! This is intended 258 to allow the arrow keys to be used like ^F and ^B are used 259 to terminate the search and execute the movement command. */ 260 if (c == ESC && _rl_input_available ()) /* XXX */ 261 rl_execute_next (ESC); 262 break; 263 } 264 265 if (c >= 0 && (CTRL_CHAR (c) || META_CHAR (c) || c == RUBOUT) && c != CTRL ('G')) 266 { 267 rl_execute_next (c); 268 break; 269 } 270 271 switch (c) 272 { 273 case -1: 274 if (search_string_index == 0) 275 continue; 276 else if (reverse) 277 --line_index; 278 else if (line_index != sline_len) 279 ++line_index; 280 else 281 ding (); 282 break; 283 284 /* switch directions */ 285 case -2: 286 direction = -direction; 287 reverse = direction < 0; 288 break; 289 290 case CTRL ('G'): 291 strcpy (rl_line_buffer, lines[orig_line]); 292 rl_point = orig_point; 293 rl_end = strlen (rl_line_buffer); 294 _rl_restore_prompt(); 295 rl_clear_message (); 296 if (allocated_line) 297 free (allocated_line); 298 free (lines); 299 return 0; 300 301#if 0 302 /* delete character from search string. */ 303 case -3: 304 if (search_string_index == 0) 305 ding (); 306 else 307 { 308 search_string[--search_string_index] = '\0'; 309 /* This is tricky. To do this right, we need to keep a 310 stack of search positions for the current search, with 311 sentinels marking the beginning and end. */ 312 } 313 break; 314#endif 315 316 default: 317 /* Add character to search string and continue search. */ 318 if (search_string_index + 2 >= search_string_size) 319 { 320 search_string_size += 128; 321 search_string = xrealloc (search_string, search_string_size); 322 } 323 search_string[search_string_index++] = c; 324 search_string[search_string_index] = '\0'; 325 break; 326 } 327 328 for (found = failed = 0;;) 329 { 330 int limit = sline_len - search_string_index + 1; 331 332 /* Search the current line. */ 333 while (reverse ? (line_index >= 0) : (line_index < limit)) 334 { 335 if (STREQN (search_string, sline + line_index, search_string_index)) 336 { 337 found++; 338 break; 339 } 340 else 341 line_index += direction; 342 } 343 if (found) 344 break; 345 346 /* Move to the next line, but skip new copies of the line 347 we just found and lines shorter than the string we're 348 searching for. */ 349 do 350 { 351 /* Move to the next line. */ 352 i += direction; 353 354 /* At limit for direction? */ 355 if (reverse ? (i < 0) : (i == hlen)) 356 { 357 failed++; 358 break; 359 } 360 361 /* We will need these later. */ 362 sline = lines[i]; 363 sline_len = strlen (sline); 364 } 365 while ((prev_line_found && STREQ (prev_line_found, lines[i])) || 366 (search_string_index > sline_len)); 367 368 if (failed) 369 break; 370 371 /* Now set up the line for searching... */ 372 line_index = reverse ? sline_len - search_string_index : 0; 373 } 374 375 if (failed) 376 { 377 /* We cannot find the search string. Ding the bell. */ 378 ding (); 379 i = last_found_line; 380 continue; /* XXX - was break */ 381 } 382 383 /* We have found the search string. Just display it. But don't 384 actually move there in the history list until the user accepts 385 the location. */ 386 if (found) 387 { 388 int line_len; 389 390 prev_line_found = lines[i]; 391 line_len = strlen (lines[i]); 392 393 if (line_len >= rl_line_buffer_len) 394 rl_extend_line_buffer (line_len); 395 396 strcpy (rl_line_buffer, lines[i]); 397 rl_point = line_index; 398 rl_end = line_len; 399 last_found_line = i; 400 rl_display_search (search_string, reverse, (i == orig_line) ? -1 : i); 401 } 402 } 403 404 /* The searching is over. The user may have found the string that she 405 was looking for, or else she may have exited a failing search. If 406 LINE_INDEX is -1, then that shows that the string searched for was 407 not found. We use this to determine where to place rl_point. */ 408 409 /* First put back the original state. */ 410 strcpy (rl_line_buffer, lines[orig_line]); 411 412 _rl_restore_prompt (); 413 414 /* Free the search string. */ 415 free (search_string); 416 417 if (last_found_line < orig_line) 418 rl_get_previous_history (orig_line - last_found_line); 419 else 420 rl_get_next_history (last_found_line - orig_line); 421 422 /* If the string was not found, put point at the end of the line. */ 423 if (line_index < 0) 424 line_index = strlen (rl_line_buffer); 425 rl_point = line_index; 426 rl_clear_message (); 427 428 if (allocated_line) 429 free (allocated_line); 430 free (lines); 431 432 return 0; 433} 434