| /* histsearch.c -- searching the history list. */ |
| |
| /* Copyright (C) 1989, 1992-2009,2017 Free Software Foundation, Inc. |
| |
| This file contains the GNU History Library (History), a set of |
| routines for managing the text of previously typed lines. |
| |
| History is free software: you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation, either version 3 of the License, or |
| (at your option) any later version. |
| |
| History is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with History. If not, see <http://www.gnu.org/licenses/>. |
| */ |
| |
| #define READLINE_LIBRARY |
| |
| #if defined (HAVE_CONFIG_H) |
| # include <config.h> |
| #endif |
| |
| #include <stdio.h> |
| #if defined (HAVE_STDLIB_H) |
| # include <stdlib.h> |
| #else |
| # include "ansi_stdlib.h" |
| #endif /* HAVE_STDLIB_H */ |
| |
| #if defined (HAVE_UNISTD_H) |
| # ifdef _MINIX |
| # include <sys/types.h> |
| # endif |
| # include <unistd.h> |
| #endif |
| |
| #if defined (HAVE_FNMATCH) |
| # include <fnmatch.h> |
| #endif |
| |
| #include "history.h" |
| #include "histlib.h" |
| #include "xmalloc.h" |
| |
| /* The list of alternate characters that can delimit a history search |
| string. */ |
| char *history_search_delimiter_chars = (char *)NULL; |
| |
| static int history_search_internal PARAMS((const char *, int, int)); |
| |
| /* Search the history for STRING, starting at history_offset. |
| If DIRECTION < 0, then the search is through previous entries, else |
| through subsequent. If ANCHORED is non-zero, the string must |
| appear at the beginning of a history line, otherwise, the string |
| may appear anywhere in the line. If the string is found, then |
| current_history () is the history entry, and the value of this |
| function is the offset in the line of that history entry that the |
| string was found in. Otherwise, nothing is changed, and a -1 is |
| returned. */ |
| |
| static int |
| history_search_internal (const char *string, int direction, int flags) |
| { |
| register int i, reverse; |
| register char *line; |
| register int line_index; |
| int string_len, anchored, patsearch; |
| HIST_ENTRY **the_history; /* local */ |
| |
| i = history_offset; |
| reverse = (direction < 0); |
| anchored = (flags & ANCHORED_SEARCH); |
| #if defined (HAVE_FNMATCH) |
| patsearch = (flags & PATTERN_SEARCH); |
| #else |
| patsearch = 0; |
| #endif |
| |
| /* Take care of trivial cases first. */ |
| if (string == 0 || *string == '\0') |
| return (-1); |
| |
| if (!history_length || ((i >= history_length) && !reverse)) |
| return (-1); |
| |
| if (reverse && (i >= history_length)) |
| i = history_length - 1; |
| |
| #define NEXT_LINE() do { if (reverse) i--; else i++; } while (0) |
| |
| the_history = history_list (); |
| string_len = strlen (string); |
| while (1) |
| { |
| /* Search each line in the history list for STRING. */ |
| |
| /* At limit for direction? */ |
| if ((reverse && i < 0) || (!reverse && i == history_length)) |
| return (-1); |
| |
| line = the_history[i]->line; |
| line_index = strlen (line); |
| |
| /* If STRING is longer than line, no match. */ |
| if (patsearch == 0 && (string_len > line_index)) |
| { |
| NEXT_LINE (); |
| continue; |
| } |
| |
| /* Handle anchored searches first. */ |
| if (anchored == ANCHORED_SEARCH) |
| { |
| #if defined (HAVE_FNMATCH) |
| if (patsearch) |
| { |
| if (fnmatch (string, line, 0) == 0) |
| { |
| history_offset = i; |
| return (0); |
| } |
| } |
| else |
| #endif |
| if (STREQN (string, line, string_len)) |
| { |
| history_offset = i; |
| return (0); |
| } |
| |
| NEXT_LINE (); |
| continue; |
| } |
| |
| /* Do substring search. */ |
| if (reverse) |
| { |
| line_index -= (patsearch == 0) ? string_len : 1; |
| |
| while (line_index >= 0) |
| { |
| #if defined (HAVE_FNMATCH) |
| if (patsearch) |
| { |
| if (fnmatch (string, line + line_index, 0) == 0) |
| { |
| history_offset = i; |
| return (line_index); |
| } |
| } |
| else |
| #endif |
| if (STREQN (string, line + line_index, string_len)) |
| { |
| history_offset = i; |
| return (line_index); |
| } |
| line_index--; |
| } |
| } |
| else |
| { |
| register int limit; |
| |
| limit = line_index - string_len + 1; |
| line_index = 0; |
| |
| while (line_index < limit) |
| { |
| #if defined (HAVE_FNMATCH) |
| if (patsearch) |
| { |
| if (fnmatch (string, line + line_index, 0) == 0) |
| { |
| history_offset = i; |
| return (line_index); |
| } |
| } |
| else |
| #endif |
| if (STREQN (string, line + line_index, string_len)) |
| { |
| history_offset = i; |
| return (line_index); |
| } |
| line_index++; |
| } |
| } |
| NEXT_LINE (); |
| } |
| } |
| |
| int |
| _hs_history_patsearch (const char *string, int direction, int flags) |
| { |
| char *pat; |
| size_t len, start; |
| int ret, unescaped_backslash; |
| |
| #if defined (HAVE_FNMATCH) |
| /* Assume that the string passed does not have a leading `^' and any |
| anchored search request is captured in FLAGS */ |
| len = strlen (string); |
| ret = len - 1; |
| /* fnmatch is required to reject a pattern that ends with an unescaped |
| backslash */ |
| if (unescaped_backslash = (string[ret] == '\\')) |
| { |
| while (ret > 0 && string[--ret] == '\\') |
| unescaped_backslash = 1 - unescaped_backslash; |
| } |
| if (unescaped_backslash) |
| return -1; |
| pat = (char *)xmalloc (len + 3); |
| /* If the search string is not anchored, we'll be calling fnmatch (assuming |
| we have it). Prefix a `*' to the front of the search string so we search |
| anywhere in the line. */ |
| if ((flags & ANCHORED_SEARCH) == 0 && string[0] != '*') |
| { |
| pat[0] = '*'; |
| start = 1; |
| len++; |
| } |
| else |
| { |
| start = 0; |
| } |
| |
| /* Attempt to reduce the number of searches by tacking a `*' onto the end |
| of a pattern that doesn't have one. Assume a pattern that ends in a |
| backslash contains an even number of trailing backslashes; we check |
| above */ |
| strcpy (pat + start, string); |
| if (pat[len - 1] != '*') |
| { |
| pat[len] = '*'; /* XXX */ |
| pat[len+1] = '\0'; |
| } |
| #else |
| pat = string; |
| #endif |
| |
| ret = history_search_internal (pat, direction, flags|PATTERN_SEARCH); |
| |
| if (pat != string) |
| free (pat); |
| return ret; |
| } |
| |
| /* Do a non-anchored search for STRING through the history in DIRECTION. */ |
| int |
| history_search (const char *string, int direction) |
| { |
| return (history_search_internal (string, direction, NON_ANCHORED_SEARCH)); |
| } |
| |
| /* Do an anchored search for string through the history in DIRECTION. */ |
| int |
| history_search_prefix (const char *string, int direction) |
| { |
| return (history_search_internal (string, direction, ANCHORED_SEARCH)); |
| } |
| |
| /* Search for STRING in the history list. DIR is < 0 for searching |
| backwards. POS is an absolute index into the history list at |
| which point to begin searching. */ |
| int |
| history_search_pos (const char *string, int dir, int pos) |
| { |
| int ret, old; |
| |
| old = where_history (); |
| history_set_pos (pos); |
| if (history_search (string, dir) == -1) |
| { |
| history_set_pos (old); |
| return (-1); |
| } |
| ret = where_history (); |
| history_set_pos (old); |
| return ret; |
| } |