From cbf7e57ed0a0800d35f84573e353b5a1b4af0334 Mon Sep 17 00:00:00 2001 From: Benno Schulenberg <bensberg@justemail.net> Date: Sun, 1 Jan 2017 20:16:18 +0100 Subject: [PATCH] search: make a regex with a beginning-of-word anchor work correctly The search routine begins searching right after the cursor and behaves as if the line starts there, which means that a beginning-of-word anchor (\< or \b) will match there also when in fact the cursor is sitting in the middle of a word. To prevent finding a false match, verify that for a regex that starts with a BOW anchor the found match is actually the start of a word. This fixes https://savannah.gnu.org/bugs/?45630. --- src/search.c | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/search.c b/src/search.c index fcd95ef6..8f205a02 100644 --- a/src/search.c +++ b/src/search.c @@ -38,6 +38,8 @@ static bool history_changed = FALSE; #ifdef HAVE_REGEX_H static bool regexp_compiled = FALSE; /* Have we compiled any regular expressions? */ +static bool bow_anchored = FALSE; + /* Whether a regex starts with a beginning-of-word anchor. */ /* Compile the given regular expression and store it in search_regexp. * Return TRUE if the expression is valid, and FALSE otherwise. */ @@ -60,6 +62,10 @@ bool regexp_init(const char *regexp) regexp_compiled = TRUE; + /* Remember whether the regex starts with a beginning-of-word anchor. */ + bow_anchored = (strncmp(regexp, "\\<", 2) == 0 || + strncmp(regexp, "\\b", 2) == 0); + return TRUE; } @@ -292,8 +298,24 @@ int findnextstr(const char *needle, bool whole_word_only, size_t *match_len, if (found != NULL) { #ifdef HAVE_REGEX_H /* When doing a regex search, compute the length of the match. */ - if (ISSET(USE_REGEXP)) + if (ISSET(USE_REGEXP)) { found_len = regmatches[0].rm_eo - regmatches[0].rm_so; + + /* If the regex starts with a BOW anchor, check that the found + * match actually is the start of a word. If not, continue. */ + if (bow_anchored && found != fileptr->data) { + size_t before = move_mbleft(fileptr->data, found - fileptr->data); + + /* If a word char is before the match, skip this match. */ + if (is_word_mbchar(fileptr->data + before, FALSE)) { + if (ISSET(BACKWARDS_SEARCH)) + rev_start = fileptr->data + before; + else + rev_start = found + move_mbright(found, 0); + continue; + } + } + } #endif #ifndef DISABLE_SPELLER /* When we're spell checking, a match is only a true match when @@ -304,7 +326,7 @@ int findnextstr(const char *needle, bool whole_word_only, size_t *match_len, break; else { /* Maybe there is a whole word in the rest of the line. */ - rev_start = found + 1; + rev_start = found + move_mbright(found, 0); continue; } } else -- GitLab