vi: allow delimiter in ':s' to be escaped

When regular expressions are allowed in search commands it becomes
possible to escape the delimiter in search/replace commands.  For
example, this command will replace '/abc' with '/abc/':

   :s/\/abc/\/abc\//g

The code to split the command into 'find' and 'replace' strings
should allow for this possibility.

VI_REGEX_SEARCH isn't enabled by default.  When it is:

function                                             old     new   delta
strchr_backslash                                       -      38     +38
colon                                               4378    4373      -5
------------------------------------------------------------------------------
(add/remove: 1/0 grow/shrink: 0/1 up/down: 38/-5)              Total: 33 bytes

Signed-off-by: Ron Yorston <rmy@pobox.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
Ron Yorston 2021-07-10 11:00:04 +01:00 committed by Denys Vlasenko
parent 95ac4a48f1
commit 2759201401

View File

@ -2680,6 +2680,19 @@ static char *expand_args(char *args)
#if ENABLE_FEATURE_VI_REGEX_SEARCH
# define MAX_SUBPATTERN 10 // subpatterns \0 .. \9
// Like strchr() but skipping backslash-escaped characters
static char *strchr_backslash(const char *s, int c)
{
for (; *s; ++s) {
if (*s == c) {
return (char *)s;
} else if (*s == '\\' && *++s == '\0') {
break;
}
}
return NULL;
}
// If the return value is not NULL the caller should free R
static char *regex_search(char *q, regex_t *preg, const char *Rorig,
size_t *len_F, size_t *len_R, char **R)
@ -2728,6 +2741,8 @@ static char *regex_search(char *q, regex_t *preg, const char *Rorig,
return found;
}
#else /* !ENABLE_FEATURE_VI_REGEX_SEARCH */
# define strchr_backslash(s, c) strchr(s, c)
#endif /* ENABLE_FEATURE_VI_REGEX_SEARCH */
// buf must be no longer than MAX_INPUT_LEN!
@ -3151,12 +3166,12 @@ static void colon(char *buf)
// replace the cmd line delimiters "/" with NULs
c = buf[1]; // what is the delimiter
F = buf + 2; // start of "find"
R = strchr(F, c); // middle delimiter
R = strchr_backslash(F, c); // middle delimiter
if (!R)
goto colon_s_fail;
len_F = R - F;
*R++ = '\0'; // terminate "find"
flags = strchr(R, c);
flags = strchr_backslash(R, c);
if (flags) {
*flags++ = '\0'; // terminate "replace"
gflag = *flags;