isearch.c revision 30971
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 default: 302 /* Add character to search string and continue search. */ 303 if (search_string_index + 2 >= search_string_size) 304 { 305 search_string_size += 128; 306 search_string = xrealloc (search_string, search_string_size); 307 } 308 search_string[search_string_index++] = c; 309 search_string[search_string_index] = '\0'; 310 break; 311 } 312 313 for (found = failed = 0;;) 314 { 315 int limit = sline_len - search_string_index + 1; 316 317 /* Search the current line. */ 318 while (reverse ? (line_index >= 0) : (line_index < limit)) 319 { 320 if (STREQN (search_string, sline + line_index, search_string_index)) 321 { 322 found++; 323 break; 324 } 325 else 326 line_index += direction; 327 } 328 if (found) 329 break; 330 331 /* Move to the next line, but skip new copies of the line 332 we just found and lines shorter than the string we're 333 searching for. */ 334 do 335 { 336 /* Move to the next line. */ 337 i += direction; 338 339 /* At limit for direction? */ 340 if (reverse ? (i < 0) : (i == hlen)) 341 { 342 failed++; 343 break; 344 } 345 346 /* We will need these later. */ 347 sline = lines[i]; 348 sline_len = strlen (sline); 349 } 350 while ((prev_line_found && STREQ (prev_line_found, lines[i])) || 351 (search_string_index > sline_len)); 352 353 if (failed) 354 break; 355 356 /* Now set up the line for searching... */ 357 line_index = reverse ? sline_len - search_string_index : 0; 358 } 359 360 if (failed) 361 { 362 /* We cannot find the search string. Ding the bell. */ 363 ding (); 364 i = last_found_line; 365 continue; /* XXX - was break */ 366 } 367 368 /* We have found the search string. Just display it. But don't 369 actually move there in the history list until the user accepts 370 the location. */ 371 if (found) 372 { 373 int line_len; 374 375 prev_line_found = lines[i]; 376 line_len = strlen (lines[i]); 377 378 if (line_len >= rl_line_buffer_len) 379 rl_extend_line_buffer (line_len); 380 381 strcpy (rl_line_buffer, lines[i]); 382 rl_point = line_index; 383 rl_end = line_len; 384 last_found_line = i; 385 rl_display_search (search_string, reverse, (i == orig_line) ? -1 : i); 386 } 387 } 388 389 /* The searching is over. The user may have found the string that she 390 was looking for, or else she may have exited a failing search. If 391 LINE_INDEX is -1, then that shows that the string searched for was 392 not found. We use this to determine where to place rl_point. */ 393 394 /* First put back the original state. */ 395 strcpy (rl_line_buffer, lines[orig_line]); 396 397 _rl_restore_prompt (); 398 399 /* Free the search string. */ 400 free (search_string); 401 402 if (last_found_line < orig_line) 403 rl_get_previous_history (orig_line - last_found_line); 404 else 405 rl_get_next_history (last_found_line - orig_line); 406 407 /* If the string was not found, put point at the end of the line. */ 408 if (line_index < 0) 409 line_index = strlen (rl_line_buffer); 410 rl_point = line_index; 411 rl_clear_message (); 412 413 if (allocated_line) 414 free (allocated_line); 415 free (lines); 416 417 return 0; 418} 419