isearch.c revision 173403
1/* **************************************************************** */ 2/* */ 3/* I-Search and Searching */ 4/* */ 5/* **************************************************************** */ 6 7/* Copyright (C) 1987-2005 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 "rlmbutil.h" 49 50#include "readline.h" 51#include "history.h" 52 53#include "rlprivate.h" 54#include "xmalloc.h" 55 56/* Variables exported to other files in the readline library. */ 57char *_rl_isearch_terminators = (char *)NULL; 58 59_rl_search_cxt *_rl_iscxt = 0; 60 61/* Variables imported from other files in the readline library. */ 62extern HIST_ENTRY *_rl_saved_line_for_history; 63 64static int rl_search_history PARAMS((int, int)); 65 66static _rl_search_cxt *_rl_isearch_init PARAMS((int)); 67static void _rl_isearch_fini PARAMS((_rl_search_cxt *)); 68static int _rl_isearch_cleanup PARAMS((_rl_search_cxt *, int)); 69 70/* Last line found by the current incremental search, so we don't `find' 71 identical lines many times in a row. Now part of isearch context. */ 72/* static char *prev_line_found; */ 73 74/* Last search string and its length. */ 75static char *last_isearch_string; 76static int last_isearch_string_len; 77 78static char *default_isearch_terminators = "\033\012"; 79 80_rl_search_cxt * 81_rl_scxt_alloc (type, flags) 82 int type, flags; 83{ 84 _rl_search_cxt *cxt; 85 86 cxt = (_rl_search_cxt *)xmalloc (sizeof (_rl_search_cxt)); 87 88 cxt->type = type; 89 cxt->sflags = flags; 90 91 cxt->search_string = 0; 92 cxt->search_string_size = cxt->search_string_index = 0; 93 94 cxt->lines = 0; 95 cxt->allocated_line = 0; 96 cxt->hlen = cxt->hindex = 0; 97 98 cxt->save_point = rl_point; 99 cxt->save_mark = rl_mark; 100 cxt->save_line = where_history (); 101 cxt->last_found_line = cxt->save_line; 102 cxt->prev_line_found = 0; 103 104 cxt->save_undo_list = 0; 105 106 cxt->history_pos = 0; 107 cxt->direction = 0; 108 109 cxt->lastc = 0; 110 111 cxt->sline = 0; 112 cxt->sline_len = cxt->sline_index = 0; 113 114 cxt->search_terminators = 0; 115 116 return cxt; 117} 118 119void 120_rl_scxt_dispose (cxt, flags) 121 _rl_search_cxt *cxt; 122 int flags; 123{ 124 FREE (cxt->search_string); 125 FREE (cxt->allocated_line); 126 FREE (cxt->lines); 127 128 free (cxt); 129} 130 131/* Search backwards through the history looking for a string which is typed 132 interactively. Start with the current line. */ 133int 134rl_reverse_search_history (sign, key) 135 int sign, key; 136{ 137 return (rl_search_history (-sign, key)); 138} 139 140/* Search forwards through the history looking for a string which is typed 141 interactively. Start with the current line. */ 142int 143rl_forward_search_history (sign, key) 144 int sign, key; 145{ 146 return (rl_search_history (sign, key)); 147} 148 149/* Display the current state of the search in the echo-area. 150 SEARCH_STRING contains the string that is being searched for, 151 DIRECTION is zero for forward, or non-zero for reverse, 152 WHERE is the history list number of the current line. If it is 153 -1, then this line is the starting one. */ 154static void 155rl_display_search (search_string, reverse_p, where) 156 char *search_string; 157 int reverse_p, where; 158{ 159 char *message; 160 int msglen, searchlen; 161 162 searchlen = (search_string && *search_string) ? strlen (search_string) : 0; 163 164 message = (char *)xmalloc (searchlen + 33); 165 msglen = 0; 166 167#if defined (NOTDEF) 168 if (where != -1) 169 { 170 sprintf (message, "[%d]", where + history_base); 171 msglen = strlen (message); 172 } 173#endif /* NOTDEF */ 174 175 message[msglen++] = '('; 176 177 if (reverse_p) 178 { 179 strcpy (message + msglen, "reverse-"); 180 msglen += 8; 181 } 182 183 strcpy (message + msglen, "i-search)`"); 184 msglen += 10; 185 186 if (search_string) 187 { 188 strcpy (message + msglen, search_string); 189 msglen += searchlen; 190 } 191 192 strcpy (message + msglen, "': "); 193 194 rl_message ("%s", message); 195 free (message); 196 (*rl_redisplay_function) (); 197} 198 199static _rl_search_cxt * 200_rl_isearch_init (direction) 201 int direction; 202{ 203 _rl_search_cxt *cxt; 204 register int i; 205 HIST_ENTRY **hlist; 206 207 cxt = _rl_scxt_alloc (RL_SEARCH_ISEARCH, 0); 208 if (direction < 0) 209 cxt->sflags |= SF_REVERSE; 210 211 cxt->search_terminators = _rl_isearch_terminators ? _rl_isearch_terminators 212 : default_isearch_terminators; 213 214 /* Create an arrary of pointers to the lines that we want to search. */ 215 hlist = history_list (); 216 rl_maybe_replace_line (); 217 i = 0; 218 if (hlist) 219 for (i = 0; hlist[i]; i++); 220 221 /* Allocate space for this many lines, +1 for the current input line, 222 and remember those lines. */ 223 cxt->lines = (char **)xmalloc ((1 + (cxt->hlen = i)) * sizeof (char *)); 224 for (i = 0; i < cxt->hlen; i++) 225 cxt->lines[i] = hlist[i]->line; 226 227 if (_rl_saved_line_for_history) 228 cxt->lines[i] = _rl_saved_line_for_history->line; 229 else 230 { 231 /* Keep track of this so we can free it. */ 232 cxt->allocated_line = (char *)xmalloc (1 + strlen (rl_line_buffer)); 233 strcpy (cxt->allocated_line, &rl_line_buffer[0]); 234 cxt->lines[i] = cxt->allocated_line; 235 } 236 237 cxt->hlen++; 238 239 /* The line where we start the search. */ 240 cxt->history_pos = cxt->save_line; 241 242 rl_save_prompt (); 243 244 /* Initialize search parameters. */ 245 cxt->search_string = (char *)xmalloc (cxt->search_string_size = 128); 246 cxt->search_string[cxt->search_string_index = 0] = '\0'; 247 248 /* Normalize DIRECTION into 1 or -1. */ 249 cxt->direction = (direction >= 0) ? 1 : -1; 250 251 cxt->sline = rl_line_buffer; 252 cxt->sline_len = strlen (cxt->sline); 253 cxt->sline_index = rl_point; 254 255 _rl_iscxt = cxt; /* save globally */ 256 257 return cxt; 258} 259 260static void 261_rl_isearch_fini (cxt) 262 _rl_search_cxt *cxt; 263{ 264 /* First put back the original state. */ 265 strcpy (rl_line_buffer, cxt->lines[cxt->save_line]); 266 267 rl_restore_prompt (); 268 269 /* Save the search string for possible later use. */ 270 FREE (last_isearch_string); 271 last_isearch_string = cxt->search_string; 272 last_isearch_string_len = cxt->search_string_index; 273 cxt->search_string = 0; 274 275 if (cxt->last_found_line < cxt->save_line) 276 rl_get_previous_history (cxt->save_line - cxt->last_found_line, 0); 277 else 278 rl_get_next_history (cxt->last_found_line - cxt->save_line, 0); 279 280 /* If the string was not found, put point at the end of the last matching 281 line. If last_found_line == orig_line, we didn't find any matching 282 history lines at all, so put point back in its original position. */ 283 if (cxt->sline_index < 0) 284 { 285 if (cxt->last_found_line == cxt->save_line) 286 cxt->sline_index = cxt->save_point; 287 else 288 cxt->sline_index = strlen (rl_line_buffer); 289 rl_mark = cxt->save_mark; 290 } 291 292 rl_point = cxt->sline_index; 293 /* Don't worry about where to put the mark here; rl_get_previous_history 294 and rl_get_next_history take care of it. */ 295 296 rl_clear_message (); 297} 298 299int 300_rl_search_getchar (cxt) 301 _rl_search_cxt *cxt; 302{ 303 int c; 304 305 /* Read a key and decide how to proceed. */ 306 RL_SETSTATE(RL_STATE_MOREINPUT); 307 c = cxt->lastc = rl_read_key (); 308 RL_UNSETSTATE(RL_STATE_MOREINPUT); 309 310#if defined (HANDLE_MULTIBYTE) 311 if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) 312 c = cxt->lastc = _rl_read_mbstring (cxt->lastc, cxt->mb, MB_LEN_MAX); 313#endif 314 315 return c; 316} 317 318/* Process just-read character C according to isearch context CXT. Return 319 -1 if the caller should just free the context and return, 0 if we should 320 break out of the loop, and 1 if we should continue to read characters. */ 321int 322_rl_isearch_dispatch (cxt, c) 323 _rl_search_cxt *cxt; 324 int c; 325{ 326 int n, wstart, wlen, limit, cval; 327 rl_command_func_t *f; 328 329 f = (rl_command_func_t *)NULL; 330 331 if (c < 0) 332 { 333 cxt->sflags |= SF_FAILED; 334 cxt->history_pos = cxt->last_found_line; 335 return -1; 336 } 337 338 /* Translate the keys we do something with to opcodes. */ 339 if (c >= 0 && _rl_keymap[c].type == ISFUNC) 340 { 341 f = _rl_keymap[c].function; 342 343 if (f == rl_reverse_search_history) 344 cxt->lastc = (cxt->sflags & SF_REVERSE) ? -1 : -2; 345 else if (f == rl_forward_search_history) 346 cxt->lastc = (cxt->sflags & SF_REVERSE) ? -2 : -1; 347 else if (f == rl_rubout) 348 cxt->lastc = -3; 349 else if (c == CTRL ('G')) 350 cxt->lastc = -4; 351 else if (c == CTRL ('W')) /* XXX */ 352 cxt->lastc = -5; 353 else if (c == CTRL ('Y')) /* XXX */ 354 cxt->lastc = -6; 355 } 356 357 /* The characters in isearch_terminators (set from the user-settable 358 variable isearch-terminators) are used to terminate the search but 359 not subsequently execute the character as a command. The default 360 value is "\033\012" (ESC and C-J). */ 361 if (strchr (cxt->search_terminators, cxt->lastc)) 362 { 363 /* ESC still terminates the search, but if there is pending 364 input or if input arrives within 0.1 seconds (on systems 365 with select(2)) it is used as a prefix character 366 with rl_execute_next. WATCH OUT FOR THIS! This is intended 367 to allow the arrow keys to be used like ^F and ^B are used 368 to terminate the search and execute the movement command. 369 XXX - since _rl_input_available depends on the application- 370 settable keyboard timeout value, this could alternatively 371 use _rl_input_queued(100000) */ 372 if (cxt->lastc == ESC && _rl_input_available ()) 373 rl_execute_next (ESC); 374 return (0); 375 } 376 377#define ENDSRCH_CHAR(c) \ 378 ((CTRL_CHAR (c) || META_CHAR (c) || (c) == RUBOUT) && ((c) != CTRL ('G'))) 379 380#if defined (HANDLE_MULTIBYTE) 381 if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) 382 { 383 if (cxt->lastc >= 0 && (cxt->mb[0] && cxt->mb[1] == '\0') && ENDSRCH_CHAR (cxt->lastc)) 384 { 385 /* This sets rl_pending_input to c; it will be picked up the next 386 time rl_read_key is called. */ 387 rl_execute_next (cxt->lastc); 388 return (0); 389 } 390 } 391 else 392#endif 393 if (cxt->lastc >= 0 && ENDSRCH_CHAR (cxt->lastc)) 394 { 395 /* This sets rl_pending_input to LASTC; it will be picked up the next 396 time rl_read_key is called. */ 397 rl_execute_next (cxt->lastc); 398 return (0); 399 } 400 401 /* Now dispatch on the character. `Opcodes' affect the search string or 402 state. Other characters are added to the string. */ 403 switch (cxt->lastc) 404 { 405 /* search again */ 406 case -1: 407 if (cxt->search_string_index == 0) 408 { 409 if (last_isearch_string) 410 { 411 cxt->search_string_size = 64 + last_isearch_string_len; 412 cxt->search_string = (char *)xrealloc (cxt->search_string, cxt->search_string_size); 413 strcpy (cxt->search_string, last_isearch_string); 414 cxt->search_string_index = last_isearch_string_len; 415 rl_display_search (cxt->search_string, (cxt->sflags & SF_REVERSE), -1); 416 break; 417 } 418 return (1); 419 } 420 else if (cxt->sflags & SF_REVERSE) 421 cxt->sline_index--; 422 else if (cxt->sline_index != cxt->sline_len) 423 cxt->sline_index++; 424 else 425 rl_ding (); 426 break; 427 428 /* switch directions */ 429 case -2: 430 cxt->direction = -cxt->direction; 431 if (cxt->direction < 0) 432 cxt->sflags |= SF_REVERSE; 433 else 434 cxt->sflags &= ~SF_REVERSE; 435 break; 436 437 /* delete character from search string. */ 438 case -3: /* C-H, DEL */ 439 /* This is tricky. To do this right, we need to keep a 440 stack of search positions for the current search, with 441 sentinels marking the beginning and end. But this will 442 do until we have a real isearch-undo. */ 443 if (cxt->search_string_index == 0) 444 rl_ding (); 445 else 446 cxt->search_string[--cxt->search_string_index] = '\0'; 447 break; 448 449 case -4: /* C-G, abort */ 450 rl_replace_line (cxt->lines[cxt->save_line], 0); 451 rl_point = cxt->save_point; 452 rl_mark = cxt->save_mark; 453 rl_restore_prompt(); 454 rl_clear_message (); 455 456 return -1; 457 458 case -5: /* C-W */ 459 /* skip over portion of line we already matched and yank word */ 460 wstart = rl_point + cxt->search_string_index; 461 if (wstart >= rl_end) 462 { 463 rl_ding (); 464 break; 465 } 466 467 /* if not in a word, move to one. */ 468 cval = _rl_char_value (rl_line_buffer, wstart); 469 if (_rl_walphabetic (cval) == 0) 470 { 471 rl_ding (); 472 break; 473 } 474 n = MB_NEXTCHAR (rl_line_buffer, wstart, 1, MB_FIND_NONZERO);; 475 while (n < rl_end) 476 { 477 cval = _rl_char_value (rl_line_buffer, n); 478 if (_rl_walphabetic (cval) == 0) 479 break; 480 n = MB_NEXTCHAR (rl_line_buffer, n, 1, MB_FIND_NONZERO);; 481 } 482 wlen = n - wstart + 1; 483 if (cxt->search_string_index + wlen + 1 >= cxt->search_string_size) 484 { 485 cxt->search_string_size += wlen + 1; 486 cxt->search_string = (char *)xrealloc (cxt->search_string, cxt->search_string_size); 487 } 488 for (; wstart < n; wstart++) 489 cxt->search_string[cxt->search_string_index++] = rl_line_buffer[wstart]; 490 cxt->search_string[cxt->search_string_index] = '\0'; 491 break; 492 493 case -6: /* C-Y */ 494 /* skip over portion of line we already matched and yank rest */ 495 wstart = rl_point + cxt->search_string_index; 496 if (wstart >= rl_end) 497 { 498 rl_ding (); 499 break; 500 } 501 n = rl_end - wstart + 1; 502 if (cxt->search_string_index + n + 1 >= cxt->search_string_size) 503 { 504 cxt->search_string_size += n + 1; 505 cxt->search_string = (char *)xrealloc (cxt->search_string, cxt->search_string_size); 506 } 507 for (n = wstart; n < rl_end; n++) 508 cxt->search_string[cxt->search_string_index++] = rl_line_buffer[n]; 509 cxt->search_string[cxt->search_string_index] = '\0'; 510 break; 511 512 /* Add character to search string and continue search. */ 513 default: 514 if (cxt->search_string_index + 2 >= cxt->search_string_size) 515 { 516 cxt->search_string_size += 128; 517 cxt->search_string = (char *)xrealloc (cxt->search_string, cxt->search_string_size); 518 } 519#if defined (HANDLE_MULTIBYTE) 520 if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) 521 { 522 int j, l; 523 for (j = 0, l = strlen (cxt->mb); j < l; ) 524 cxt->search_string[cxt->search_string_index++] = cxt->mb[j++]; 525 } 526 else 527#endif 528 cxt->search_string[cxt->search_string_index++] = c; 529 cxt->search_string[cxt->search_string_index] = '\0'; 530 break; 531 } 532 533 for (cxt->sflags &= ~(SF_FOUND|SF_FAILED);; ) 534 { 535 limit = cxt->sline_len - cxt->search_string_index + 1; 536 537 /* Search the current line. */ 538 while ((cxt->sflags & SF_REVERSE) ? (cxt->sline_index >= 0) : (cxt->sline_index < limit)) 539 { 540 if (STREQN (cxt->search_string, cxt->sline + cxt->sline_index, cxt->search_string_index)) 541 { 542 cxt->sflags |= SF_FOUND; 543 break; 544 } 545 else 546 cxt->sline_index += cxt->direction; 547 } 548 if (cxt->sflags & SF_FOUND) 549 break; 550 551 /* Move to the next line, but skip new copies of the line 552 we just found and lines shorter than the string we're 553 searching for. */ 554 do 555 { 556 /* Move to the next line. */ 557 cxt->history_pos += cxt->direction; 558 559 /* At limit for direction? */ 560 if ((cxt->sflags & SF_REVERSE) ? (cxt->history_pos < 0) : (cxt->history_pos == cxt->hlen)) 561 { 562 cxt->sflags |= SF_FAILED; 563 break; 564 } 565 566 /* We will need these later. */ 567 cxt->sline = cxt->lines[cxt->history_pos]; 568 cxt->sline_len = strlen (cxt->sline); 569 } 570 while ((cxt->prev_line_found && STREQ (cxt->prev_line_found, cxt->lines[cxt->history_pos])) || 571 (cxt->search_string_index > cxt->sline_len)); 572 573 if (cxt->sflags & SF_FAILED) 574 break; 575 576 /* Now set up the line for searching... */ 577 cxt->sline_index = (cxt->sflags & SF_REVERSE) ? cxt->sline_len - cxt->search_string_index : 0; 578 } 579 580 if (cxt->sflags & SF_FAILED) 581 { 582 /* We cannot find the search string. Ding the bell. */ 583 rl_ding (); 584 cxt->history_pos = cxt->last_found_line; 585 return 1; 586 } 587 588 /* We have found the search string. Just display it. But don't 589 actually move there in the history list until the user accepts 590 the location. */ 591 if (cxt->sflags & SF_FOUND) 592 { 593 cxt->prev_line_found = cxt->lines[cxt->history_pos]; 594 rl_replace_line (cxt->lines[cxt->history_pos], 0); 595 rl_point = cxt->sline_index; 596 cxt->last_found_line = cxt->history_pos; 597 rl_display_search (cxt->search_string, (cxt->sflags & SF_REVERSE), (cxt->history_pos == cxt->save_line) ? -1 : cxt->history_pos); 598 } 599 600 return 1; 601} 602 603static int 604_rl_isearch_cleanup (cxt, r) 605 _rl_search_cxt *cxt; 606 int r; 607{ 608 if (r >= 0) 609 _rl_isearch_fini (cxt); 610 _rl_scxt_dispose (cxt, 0); 611 _rl_iscxt = 0; 612 613 RL_UNSETSTATE(RL_STATE_ISEARCH); 614 615 return (r != 0); 616} 617 618/* Search through the history looking for an interactively typed string. 619 This is analogous to i-search. We start the search in the current line. 620 DIRECTION is which direction to search; >= 0 means forward, < 0 means 621 backwards. */ 622static int 623rl_search_history (direction, invoking_key) 624 int direction, invoking_key; 625{ 626 _rl_search_cxt *cxt; /* local for now, but saved globally */ 627 int c, r; 628 629 RL_SETSTATE(RL_STATE_ISEARCH); 630 cxt = _rl_isearch_init (direction); 631 632 rl_display_search (cxt->search_string, (cxt->sflags & SF_REVERSE), -1); 633 634 /* If we are using the callback interface, all we do is set up here and 635 return. The key is that we leave RL_STATE_ISEARCH set. */ 636 if (RL_ISSTATE (RL_STATE_CALLBACK)) 637 return (0); 638 639 r = -1; 640 for (;;) 641 { 642 c = _rl_search_getchar (cxt); 643 /* We might want to handle EOF here (c == 0) */ 644 r = _rl_isearch_dispatch (cxt, cxt->lastc); 645 if (r <= 0) 646 break; 647 } 648 649 /* The searching is over. The user may have found the string that she 650 was looking for, or else she may have exited a failing search. If 651 LINE_INDEX is -1, then that shows that the string searched for was 652 not found. We use this to determine where to place rl_point. */ 653 return (_rl_isearch_cleanup (cxt, r)); 654} 655 656#if defined (READLINE_CALLBACKS) 657/* Called from the callback functions when we are ready to read a key. The 658 callback functions know to call this because RL_ISSTATE(RL_STATE_ISEARCH). 659 If _rl_isearch_dispatch finishes searching, this function is responsible 660 for turning off RL_STATE_ISEARCH, which it does using _rl_isearch_cleanup. */ 661int 662_rl_isearch_callback (cxt) 663 _rl_search_cxt *cxt; 664{ 665 int c, r; 666 667 c = _rl_search_getchar (cxt); 668 /* We might want to handle EOF here */ 669 r = _rl_isearch_dispatch (cxt, cxt->lastc); 670 671 return (r <= 0) ? _rl_isearch_cleanup (cxt, r) : 0; 672} 673#endif 674