summaryrefslogtreecommitdiff
path: root/histsearch.c
diff options
context:
space:
mode:
Diffstat (limited to 'histsearch.c')
-rw-r--r--histsearch.c125
1 files changed, 109 insertions, 16 deletions
diff --git a/histsearch.c b/histsearch.c
index 1ad55d2..7a426c9 100644
--- a/histsearch.c
+++ b/histsearch.c
@@ -1,6 +1,6 @@
/* histsearch.c -- searching the history list. */
-/* Copyright (C) 1989, 1992-2009 Free Software Foundation, Inc.
+/* 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.
@@ -39,8 +39,13 @@
# 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. */
@@ -59,18 +64,22 @@ static int history_search_internal PARAMS((const char *, int, int));
returned. */
static int
-history_search_internal (string, direction, anchored)
- const char *string;
- int direction, anchored;
+history_search_internal (const char *string, int direction, int flags)
{
register int i, reverse;
register char *line;
register int line_index;
- int string_len;
+ 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')
@@ -98,7 +107,7 @@ history_search_internal (string, direction, anchored)
line_index = strlen (line);
/* If STRING is longer than line, no match. */
- if (string_len > line_index)
+ if (patsearch == 0 && (string_len > line_index))
{
NEXT_LINE ();
continue;
@@ -107,6 +116,17 @@ history_search_internal (string, direction, anchored)
/* 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;
@@ -120,10 +140,21 @@ history_search_internal (string, direction, anchored)
/* Do substring search. */
if (reverse)
{
- line_index -= string_len;
+ 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;
@@ -141,6 +172,17 @@ history_search_internal (string, direction, anchored)
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;
@@ -153,20 +195,73 @@ history_search_internal (string, direction, anchored)
}
}
+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 (string, direction)
- const char *string;
- int direction;
+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 (string, direction)
- const char *string;
- int direction;
+history_search_prefix (const char *string, int direction)
{
return (history_search_internal (string, direction, ANCHORED_SEARCH));
}
@@ -175,9 +270,7 @@ history_search_prefix (string, direction)
backwards. POS is an absolute index into the history list at
which point to begin searching. */
int
-history_search_pos (string, dir, pos)
- const char *string;
- int dir, pos;
+history_search_pos (const char *string, int dir, int pos)
{
int ret, old;