1
0
forked from aniani/vim

patch 9.1.0409: too many strlen() calls in the regexp engine

Problem:  too many strlen() calls in the regexp engine
Solution: refactor code to retrieve strlen differently, make use
          of bsearch() for getting the character class
          (John Marriott)

closes: #14648

Signed-off-by: John Marriott <basilisk@internode.on.net>
Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
John Marriott
2024-05-12 00:07:17 +02:00
committed by Christian Brabandt
parent 86f6e2c2ee
commit 82792db631
4 changed files with 308 additions and 148 deletions

View File

@@ -161,6 +161,7 @@ re_multi_type(int c)
} }
static char_u *reg_prev_sub = NULL; static char_u *reg_prev_sub = NULL;
static size_t reg_prev_sublen = 0;
/* /*
* REGEXP_INRANGE contains all characters which are always special in a [] * REGEXP_INRANGE contains all characters which are always special in a []
@@ -197,6 +198,30 @@ backslash_trans(int c)
return c; return c;
} }
enum
{
CLASS_ALNUM = 0,
CLASS_ALPHA,
CLASS_BLANK,
CLASS_CNTRL,
CLASS_DIGIT,
CLASS_GRAPH,
CLASS_LOWER,
CLASS_PRINT,
CLASS_PUNCT,
CLASS_SPACE,
CLASS_UPPER,
CLASS_XDIGIT,
CLASS_TAB,
CLASS_RETURN,
CLASS_BACKSPACE,
CLASS_ESCAPE,
CLASS_IDENT,
CLASS_KEYWORD,
CLASS_FNAME,
CLASS_NONE = 99
};
/* /*
* Check for a character class name "[:name:]". "pp" points to the '['. * Check for a character class name "[:name:]". "pp" points to the '['.
* Returns one of the CLASS_ items. CLASS_NONE means that no item was * Returns one of the CLASS_ items. CLASS_NONE means that no item was
@@ -205,58 +230,56 @@ backslash_trans(int c)
static int static int
get_char_class(char_u **pp) get_char_class(char_u **pp)
{ {
static const char *(class_names[]) = // must be sorted by the 'value' field because it is used by bsearch()!
static keyvalue_T char_class_tab[] =
{ {
"alnum:]", KEYVALUE_ENTRY(CLASS_ALNUM, "alnum:]"),
#define CLASS_ALNUM 0 KEYVALUE_ENTRY(CLASS_ALPHA, "alpha:]"),
"alpha:]", KEYVALUE_ENTRY(CLASS_BACKSPACE, "backspace:]"),
#define CLASS_ALPHA 1 KEYVALUE_ENTRY(CLASS_BLANK, "blank:]"),
"blank:]", KEYVALUE_ENTRY(CLASS_CNTRL, "cntrl:]"),
#define CLASS_BLANK 2 KEYVALUE_ENTRY(CLASS_DIGIT, "digit:]"),
"cntrl:]", KEYVALUE_ENTRY(CLASS_ESCAPE, "escape:]"),
#define CLASS_CNTRL 3 KEYVALUE_ENTRY(CLASS_FNAME, "fname:]"),
"digit:]", KEYVALUE_ENTRY(CLASS_GRAPH, "graph:]"),
#define CLASS_DIGIT 4 KEYVALUE_ENTRY(CLASS_IDENT, "ident:]"),
"graph:]", KEYVALUE_ENTRY(CLASS_KEYWORD, "keyword:]"),
#define CLASS_GRAPH 5 KEYVALUE_ENTRY(CLASS_LOWER, "lower:]"),
"lower:]", KEYVALUE_ENTRY(CLASS_PRINT, "print:]"),
#define CLASS_LOWER 6 KEYVALUE_ENTRY(CLASS_PUNCT, "punct:]"),
"print:]", KEYVALUE_ENTRY(CLASS_RETURN, "return:]"),
#define CLASS_PRINT 7 KEYVALUE_ENTRY(CLASS_SPACE, "space:]"),
"punct:]", KEYVALUE_ENTRY(CLASS_TAB, "tab:]"),
#define CLASS_PUNCT 8 KEYVALUE_ENTRY(CLASS_UPPER, "upper:]"),
"space:]", KEYVALUE_ENTRY(CLASS_XDIGIT, "xdigit:]")
#define CLASS_SPACE 9
"upper:]",
#define CLASS_UPPER 10
"xdigit:]",
#define CLASS_XDIGIT 11
"tab:]",
#define CLASS_TAB 12
"return:]",
#define CLASS_RETURN 13
"backspace:]",
#define CLASS_BACKSPACE 14
"escape:]",
#define CLASS_ESCAPE 15
"ident:]",
#define CLASS_IDENT 16
"keyword:]",
#define CLASS_KEYWORD 17
"fname:]",
#define CLASS_FNAME 18
}; };
#define CLASS_NONE 99
int i;
if ((*pp)[1] == ':') // check that the value of "pp" has a chance of matching
if ((*pp)[1] == ':' && ASCII_ISLOWER((*pp)[2])
&& ASCII_ISLOWER((*pp)[3]) && ASCII_ISLOWER((*pp)[4]))
{ {
for (i = 0; i < (int)ARRAY_LENGTH(class_names); ++i) keyvalue_T target;
if (STRNCMP(*pp + 2, class_names[i], STRLEN(class_names[i])) == 0) keyvalue_T *entry;
{ // this function can be called repeatedly with the same value for "pp"
*pp += STRLEN(class_names[i]) + 2; // so we cache the last found entry.
return i; static keyvalue_T *last_entry = NULL;
}
target.key = 0;
target.value = (char *)*pp + 2;
target.length = 0; // not used, see cmp_keyvalue_value_n()
if (last_entry != NULL && cmp_keyvalue_value_n(&target, last_entry) == 0)
entry = last_entry;
else
entry = (keyvalue_T *)bsearch(&target, &char_class_tab,
ARRAY_LENGTH(char_class_tab),
sizeof(char_class_tab[0]), cmp_keyvalue_value_n);
if (entry != NULL)
{
last_entry = entry;
*pp += entry->length + 2;
return entry->key;
}
} }
return CLASS_NONE; return CLASS_NONE;
} }
@@ -619,17 +642,20 @@ skip_regexp_ex(
{ {
if (dirc == '?' && newp != NULL && p[1] == '?') if (dirc == '?' && newp != NULL && p[1] == '?')
{ {
size_t startplen;
// change "\?" to "?", make a copy first. // change "\?" to "?", make a copy first.
if (*newp == NULL) if (*newp == NULL)
{ {
*newp = vim_strsave(startp); startplen = STRLEN(startp);
*newp = vim_strnsave(startp, startplen);
if (*newp != NULL) if (*newp != NULL)
p = *newp + (p - startp); p = *newp + (p - startp);
} }
if (dropped != NULL) if (dropped != NULL)
++*dropped; ++*dropped;
if (*newp != NULL) if (*newp != NULL)
STRMOVE(p, p + 1); mch_memmove(p, p + 1, (startplen - ((p + 1) - *newp)) + 1);
else else
++p; ++p;
} }
@@ -1189,20 +1215,114 @@ reg_iswordc(int c)
return vim_iswordc_buf(c, rex.reg_buf); return vim_iswordc_buf(c, rex.reg_buf);
} }
#ifdef FEAT_EVAL
static int can_f_submatch = FALSE; // TRUE when submatch() can be used
// This struct is used for reg_submatch(). Needed for when the
// substitution string is an expression that contains a call to substitute()
// and submatch().
typedef struct {
regmatch_T *sm_match;
regmmatch_T *sm_mmatch;
linenr_T sm_firstlnum;
linenr_T sm_maxline;
int sm_line_lbr;
} regsubmatch_T;
static regsubmatch_T rsm; // can only be used when can_f_submatch is TRUE
#endif
typedef enum
{
RGLF_LINE = 0x01,
RGLF_LENGTH = 0x02
#ifdef FEAT_EVAL
,
RGLF_SUBMATCH = 0x04
#endif
} reg_getline_flags_T;
//
// common code for reg_getline(), reg_getline_len(), reg_getline_submatch() and
// reg_getline_submatch_len().
// the flags argument (which is a bitmask) controls what info is to be returned and whether
// or not submatch is in effect.
// note:
// submatch is available only if FEAT_EVAL is defined.
static void
reg_getline_common(linenr_T lnum, reg_getline_flags_T flags, char_u **line, colnr_T *length)
{
int get_line = flags & RGLF_LINE;
int get_length = flags & RGLF_LENGTH;
linenr_T firstlnum;
linenr_T maxline;
#ifdef FEAT_EVAL
if (flags & RGLF_SUBMATCH)
{
firstlnum = rsm.sm_firstlnum + lnum;
maxline = rsm.sm_maxline;
}
else
#endif
{
firstlnum = rex.reg_firstlnum + lnum;
maxline = rex.reg_maxline;
}
// when looking behind for a match/no-match lnum is negative. but we
// can't go before line 1.
if (firstlnum < 1)
{
if (get_line)
*line = NULL;
if (get_length)
*length = 0;
return;
}
if (lnum > maxline)
{
// must have matched the "\n" in the last line.
if (get_line)
*line = (char_u *)"";
if (get_length)
*length = 0;
return;
}
if (get_line)
*line = ml_get_buf(rex.reg_buf, firstlnum, FALSE);
if (get_length)
*length = ml_get_buf_len(rex.reg_buf, firstlnum);
}
/* /*
* Get pointer to the line "lnum", which is relative to "reg_firstlnum". * Get pointer to the line "lnum", which is relative to "reg_firstlnum".
*/ */
static char_u * static char_u *
reg_getline(linenr_T lnum) reg_getline(linenr_T lnum)
{ {
// when looking behind for a match/no-match lnum is negative. But we char_u *line;
// can't go before line 1
if (rex.reg_firstlnum + lnum < 1) reg_getline_common(lnum, RGLF_LINE, &line, NULL);
return NULL;
if (lnum > rex.reg_maxline) return line;
// Must have matched the "\n" in the last line. }
return (char_u *)"";
return ml_get_buf(rex.reg_buf, rex.reg_firstlnum + lnum, FALSE); /*
* Get length of line "lnum", which is relative to "reg_firstlnum".
*/
static colnr_T
reg_getline_len(linenr_T lnum)
{
colnr_T length;
reg_getline_common(lnum, RGLF_LENGTH, NULL, &length);
return length;
} }
#ifdef FEAT_SYN_HL #ifdef FEAT_SYN_HL
@@ -1484,7 +1604,7 @@ match_with_backref(
if (clnum == end_lnum) if (clnum == end_lnum)
len = end_col - ccol; len = end_col - ccol;
else else
len = (int)STRLEN(p + ccol); len = (int)reg_getline_len(clnum) - ccol;
if (cstrncmp(p + ccol, rex.input, &len) != 0) if (cstrncmp(p + ccol, rex.input, &len) != 0)
return RA_NOMATCH; // doesn't match return RA_NOMATCH; // doesn't match
@@ -1745,49 +1865,71 @@ regtilde(char_u *source, int magic)
{ {
char_u *newsub = source; char_u *newsub = source;
char_u *p; char_u *p;
size_t newsublen = 0;
char_u tilde[3] = {'~', NUL, NUL};
size_t tildelen = 1;
int error = FALSE;
if (!magic)
{
tilde[0] = '\\';
tilde[1] = '~';
tilde[2] = NUL;
tildelen = 2;
}
for (p = newsub; *p; ++p) for (p = newsub; *p; ++p)
{ {
if ((*p == '~' && magic) || (*p == '\\' && *(p + 1) == '~' && !magic)) if (STRNCMP(p, tilde, tildelen) == 0)
{ {
if (reg_prev_sub != NULL) size_t prefixlen = p - newsub; // not including the tilde
char_u *postfix = p + tildelen;
size_t postfixlen;
size_t tmpsublen;
if (newsublen == 0)
newsublen = STRLEN(newsub);
newsublen -= tildelen;
postfixlen = newsublen - prefixlen;
tmpsublen = prefixlen + reg_prev_sublen + postfixlen;
if (tmpsublen > 0 && reg_prev_sub != NULL)
{ {
// length = len(newsub) - 1 + len(prev_sub) + 1 char_u *tmpsub;
// Avoid making the text longer than MAXCOL, it will cause // Avoid making the text longer than MAXCOL, it will cause
// trouble at some point. // trouble at some point.
size_t prevsublen = STRLEN(reg_prev_sub); if (tmpsublen > MAXCOL)
size_t newsublen = STRLEN(newsub);
if (prevsublen > MAXCOL || newsublen > MAXCOL
|| newsublen + prevsublen > MAXCOL)
{ {
emsg(_(e_resulting_text_too_long)); emsg(_(e_resulting_text_too_long));
error = TRUE;
break; break;
} }
char_u *tmpsub = alloc(newsublen + prevsublen); tmpsub = alloc(tmpsublen + 1);
if (tmpsub != NULL) if (tmpsub == NULL)
{ {
// copy prefix emsg(_(e_out_of_memory));
size_t prefixlen = p - newsub; // not including ~ error = TRUE;
mch_memmove(tmpsub, newsub, prefixlen); break;
// interpret tilde
mch_memmove(tmpsub + prefixlen, reg_prev_sub,
prevsublen);
// copy postfix
if (!magic)
++p; // back off backslash
STRCPY(tmpsub + prefixlen + prevsublen, p + 1);
if (newsub != source) // allocated newsub before
vim_free(newsub);
newsub = tmpsub;
p = newsub + prefixlen + prevsublen;
} }
// copy prefix
mch_memmove(tmpsub, newsub, prefixlen);
// interpret tilde
mch_memmove(tmpsub + prefixlen, reg_prev_sub, reg_prev_sublen);
// copy postfix
STRCPY(tmpsub + prefixlen + reg_prev_sublen, postfix);
if (newsub != source) // allocated newsub before
vim_free(newsub);
newsub = tmpsub;
newsublen = tmpsublen;
p = newsub + prefixlen + reg_prev_sublen;
} }
else if (magic)
STRMOVE(p, p + 1); // remove '~'
else else
STRMOVE(p, p + 2); // remove '\~' mch_memmove(p, postfix, postfixlen + 1); // remove the tilde (+1 for the NUL)
--p; --p;
} }
else else
@@ -1799,31 +1941,33 @@ regtilde(char_u *source, int magic)
} }
} }
if (error)
{
if (newsub != source)
vim_free(newsub);
return source;
}
// Store a copy of newsub in reg_prev_sub. It is always allocated, // Store a copy of newsub in reg_prev_sub. It is always allocated,
// because recursive calls may make the returned string invalid. // because recursive calls may make the returned string invalid.
vim_free(reg_prev_sub); // Only store it if there something to store.
reg_prev_sub = vim_strsave(newsub); newsublen = p - newsub;
if (newsublen == 0)
VIM_CLEAR(reg_prev_sub);
else
{
vim_free(reg_prev_sub);
reg_prev_sub = vim_strnsave(newsub, newsublen);
}
if (reg_prev_sub == NULL)
reg_prev_sublen = 0;
else
reg_prev_sublen = newsublen;
return newsub; return newsub;
} }
#ifdef FEAT_EVAL
static int can_f_submatch = FALSE; // TRUE when submatch() can be used
// These pointers are used for reg_submatch(). Needed for when the
// substitution string is an expression that contains a call to substitute()
// and submatch().
typedef struct {
regmatch_T *sm_match;
regmmatch_T *sm_mmatch;
linenr_T sm_firstlnum;
linenr_T sm_maxline;
int sm_line_lbr;
} regsubmatch_T;
static regsubmatch_T rsm; // can only be used when can_f_submatch is TRUE
#endif
#ifdef FEAT_EVAL #ifdef FEAT_EVAL
/* /*
@@ -2028,12 +2172,16 @@ vim_regsub_both(
// "flags & REGSUB_COPY" != 0. // "flags & REGSUB_COPY" != 0.
if (copy) if (copy)
{ {
if (eval_result[nested] != NULL && if (eval_result[nested] != NULL)
(int)STRLEN(eval_result[nested]) < destlen)
{ {
STRCPY(dest, eval_result[nested]); int eval_len = (int)STRLEN(eval_result[nested]);
dst += STRLEN(eval_result[nested]);
VIM_CLEAR(eval_result[nested]); if (eval_len < destlen)
{
STRCPY(dest, eval_result[nested]);
dst += eval_len;
VIM_CLEAR(eval_result[nested]);
}
} }
} }
else else
@@ -2325,7 +2473,7 @@ vim_regsub_both(
len = rex.reg_mmatch->endpos[no].col len = rex.reg_mmatch->endpos[no].col
- rex.reg_mmatch->startpos[no].col; - rex.reg_mmatch->startpos[no].col;
else else
len = (int)STRLEN(s); len = (int)reg_getline_len(clnum) - rex.reg_mmatch->startpos[no].col;
} }
} }
else else
@@ -2360,7 +2508,7 @@ vim_regsub_both(
if (rex.reg_mmatch->endpos[no].lnum == clnum) if (rex.reg_mmatch->endpos[no].lnum == clnum)
len = rex.reg_mmatch->endpos[no].col; len = rex.reg_mmatch->endpos[no].col;
else else
len = (int)STRLEN(s); len = (int)reg_getline_len(clnum);
} }
else else
break; break;
@@ -2465,26 +2613,25 @@ exit:
} }
#ifdef FEAT_EVAL #ifdef FEAT_EVAL
/*
* Call reg_getline() with the line numbers from the submatch. If a
* substitute() was used the reg_maxline and other values have been
* overwritten.
*/
static char_u * static char_u *
reg_getline_submatch(linenr_T lnum) reg_getline_submatch(linenr_T lnum)
{ {
char_u *s; char_u *line;
linenr_T save_first = rex.reg_firstlnum;
linenr_T save_max = rex.reg_maxline;
rex.reg_firstlnum = rsm.sm_firstlnum; reg_getline_common(lnum, RGLF_LINE | RGLF_SUBMATCH, &line, NULL);
rex.reg_maxline = rsm.sm_maxline;
s = reg_getline(lnum); return line;
}
rex.reg_firstlnum = save_first; static colnr_T
rex.reg_maxline = save_max; reg_getline_submatch_len(linenr_T lnum)
return s; {
colnr_T length;
reg_getline_common(lnum, RGLF_LENGTH | RGLF_SUBMATCH, NULL, &length);
return length;
} }
/* /*
@@ -2533,7 +2680,7 @@ reg_submatch(int no)
{ {
// Multiple lines: take start line from start col, middle // Multiple lines: take start line from start col, middle
// lines completely and end line up to end col. // lines completely and end line up to end col.
len = (int)STRLEN(s); len = (int)reg_getline_submatch_len(lnum) - rsm.sm_mmatch->startpos[no].col;
if (round == 2) if (round == 2)
{ {
STRCPY(retval, s); STRCPY(retval, s);
@@ -2543,13 +2690,14 @@ reg_submatch(int no)
++lnum; ++lnum;
while (lnum < rsm.sm_mmatch->endpos[no].lnum) while (lnum < rsm.sm_mmatch->endpos[no].lnum)
{ {
s = reg_getline_submatch(lnum++); s = reg_getline_submatch(lnum);
if (round == 2) if (round == 2)
STRCPY(retval + len, s); STRCPY(retval + len, s);
len += (int)STRLEN(s); len += (int)reg_getline_submatch_len(lnum);
if (round == 2) if (round == 2)
retval[len] = '\n'; retval[len] = '\n';
++len; ++len;
++lnum;
} }
if (round == 2) if (round == 2)
STRNCPY(retval + len, reg_getline_submatch(lnum), STRNCPY(retval + len, reg_getline_submatch(lnum),
@@ -2624,9 +2772,11 @@ reg_submatch_list(int no)
} }
else else
{ {
int max_lnum = elnum - slnum;
if (list_append_string(list, s, -1) == FAIL) if (list_append_string(list, s, -1) == FAIL)
error = TRUE; error = TRUE;
for (i = 1; i < elnum - slnum; i++) for (i = 1; i < max_lnum; i++)
{ {
s = reg_getline_submatch(slnum + i); s = reg_getline_submatch(slnum + i);
if (list_append_string(list, s, -1) == FAIL) if (list_append_string(list, s, -1) == FAIL)

View File

@@ -2564,14 +2564,22 @@ bt_regcomp(char_u *expr, int re_flags)
if ((flags & SPSTART || OP(scan) == BOW || OP(scan) == EOW) if ((flags & SPSTART || OP(scan) == BOW || OP(scan) == EOW)
&& !(flags & HASNL)) && !(flags & HASNL))
{ {
size_t scanlen;
longest = NULL; longest = NULL;
len = 0; len = 0;
for (; scan != NULL; scan = regnext(scan)) for (; scan != NULL; scan = regnext(scan))
if (OP(scan) == EXACTLY && STRLEN(OPERAND(scan)) >= (size_t)len) {
if (OP(scan) == EXACTLY)
{ {
longest = OPERAND(scan); scanlen = STRLEN(OPERAND(scan));
len = (int)STRLEN(OPERAND(scan)); if (scanlen >= (size_t)len)
{
longest = OPERAND(scan);
len = (int)scanlen;
}
} }
}
r->regmust = longest; r->regmust = longest;
r->regmlen = len; r->regmlen = len;
} }
@@ -3406,8 +3414,7 @@ regmatch(
{ {
colnr_T pos_col = pos->lnum == rex.lnum + rex.reg_firstlnum colnr_T pos_col = pos->lnum == rex.lnum + rex.reg_firstlnum
&& pos->col == MAXCOL && pos->col == MAXCOL
? (colnr_T)STRLEN(reg_getline( ? reg_getline_len(pos->lnum - rex.reg_firstlnum)
pos->lnum - rex.reg_firstlnum))
: pos->col; : pos->col;
if ((pos->lnum == rex.lnum + rex.reg_firstlnum if ((pos->lnum == rex.lnum + rex.reg_firstlnum
@@ -4695,7 +4702,7 @@ regmatch(
// right. // right.
if (rex.line == NULL) if (rex.line == NULL)
break; break;
rex.input = rex.line + STRLEN(rex.line); rex.input = rex.line + reg_getline_len(rex.lnum);
fast_breakcheck(); fast_breakcheck();
} }
else else
@@ -5249,8 +5256,10 @@ regprop(char_u *op)
{ {
char *p; char *p;
static char buf[50]; static char buf[50];
static size_t buflen = 0;
STRCPY(buf, ":"); STRCPY(buf, ":");
buflen = 1;
switch ((int) OP(op)) switch ((int) OP(op))
{ {
@@ -5491,7 +5500,7 @@ regprop(char_u *op)
case MOPEN + 7: case MOPEN + 7:
case MOPEN + 8: case MOPEN + 8:
case MOPEN + 9: case MOPEN + 9:
sprintf(buf + STRLEN(buf), "MOPEN%d", OP(op) - MOPEN); buflen += sprintf(buf + buflen, "MOPEN%d", OP(op) - MOPEN);
p = NULL; p = NULL;
break; break;
case MCLOSE + 0: case MCLOSE + 0:
@@ -5506,7 +5515,7 @@ regprop(char_u *op)
case MCLOSE + 7: case MCLOSE + 7:
case MCLOSE + 8: case MCLOSE + 8:
case MCLOSE + 9: case MCLOSE + 9:
sprintf(buf + STRLEN(buf), "MCLOSE%d", OP(op) - MCLOSE); buflen += sprintf(buf + buflen, "MCLOSE%d", OP(op) - MCLOSE);
p = NULL; p = NULL;
break; break;
case BACKREF + 1: case BACKREF + 1:
@@ -5518,7 +5527,7 @@ regprop(char_u *op)
case BACKREF + 7: case BACKREF + 7:
case BACKREF + 8: case BACKREF + 8:
case BACKREF + 9: case BACKREF + 9:
sprintf(buf + STRLEN(buf), "BACKREF%d", OP(op) - BACKREF); buflen += sprintf(buf + buflen, "BACKREF%d", OP(op) - BACKREF);
p = NULL; p = NULL;
break; break;
case NOPEN: case NOPEN:
@@ -5537,7 +5546,7 @@ regprop(char_u *op)
case ZOPEN + 7: case ZOPEN + 7:
case ZOPEN + 8: case ZOPEN + 8:
case ZOPEN + 9: case ZOPEN + 9:
sprintf(buf + STRLEN(buf), "ZOPEN%d", OP(op) - ZOPEN); buflen += sprintf(buf + buflen, "ZOPEN%d", OP(op) - ZOPEN);
p = NULL; p = NULL;
break; break;
case ZCLOSE + 1: case ZCLOSE + 1:
@@ -5549,7 +5558,7 @@ regprop(char_u *op)
case ZCLOSE + 7: case ZCLOSE + 7:
case ZCLOSE + 8: case ZCLOSE + 8:
case ZCLOSE + 9: case ZCLOSE + 9:
sprintf(buf + STRLEN(buf), "ZCLOSE%d", OP(op) - ZCLOSE); buflen += sprintf(buf + buflen, "ZCLOSE%d", OP(op) - ZCLOSE);
p = NULL; p = NULL;
break; break;
case ZREF + 1: case ZREF + 1:
@@ -5561,7 +5570,7 @@ regprop(char_u *op)
case ZREF + 7: case ZREF + 7:
case ZREF + 8: case ZREF + 8:
case ZREF + 9: case ZREF + 9:
sprintf(buf + STRLEN(buf), "ZREF%d", OP(op) - ZREF); bulen += sprintf(buf + buflen, "ZREF%d", OP(op) - ZREF);
p = NULL; p = NULL;
break; break;
#endif #endif
@@ -5602,7 +5611,7 @@ regprop(char_u *op)
case BRACE_COMPLEX + 7: case BRACE_COMPLEX + 7:
case BRACE_COMPLEX + 8: case BRACE_COMPLEX + 8:
case BRACE_COMPLEX + 9: case BRACE_COMPLEX + 9:
sprintf(buf + STRLEN(buf), "BRACE_COMPLEX%d", OP(op) - BRACE_COMPLEX); buflen += sprintf(buf + buflen, "BRACE_COMPLEX%d", OP(op) - BRACE_COMPLEX);
p = NULL; p = NULL;
break; break;
case MULTIBYTECODE: case MULTIBYTECODE:
@@ -5612,12 +5621,12 @@ regprop(char_u *op)
p = "NEWL"; p = "NEWL";
break; break;
default: default:
sprintf(buf + STRLEN(buf), "corrupt %d", OP(op)); buflen += sprintf(buf + buflen, "corrupt %d", OP(op));
p = NULL; p = NULL;
break; break;
} }
if (p != NULL) if (p != NULL)
STRCAT(buf, p); STRCPY(buf + buflen, p);
return (char_u *)buf; return (char_u *)buf;
} }
#endif // DEBUG #endif // DEBUG

View File

@@ -5387,7 +5387,7 @@ recursive_regmatch(
rex.input = rex.line; rex.input = rex.line;
} }
else else
rex.input = rex.line + STRLEN(rex.line); rex.input = rex.line + reg_getline_len(rex.lnum);
} }
if ((int)(rex.input - rex.line) >= state->val) if ((int)(rex.input - rex.line) >= state->val)
{ {
@@ -6937,8 +6937,7 @@ nfa_regmatch(
{ {
colnr_T pos_col = pos->lnum == rex.lnum + rex.reg_firstlnum colnr_T pos_col = pos->lnum == rex.lnum + rex.reg_firstlnum
&& pos->col == MAXCOL && pos->col == MAXCOL
? (colnr_T)STRLEN(reg_getline( ? reg_getline_len(pos->lnum - rex.reg_firstlnum)
pos->lnum - rex.reg_firstlnum))
: pos->col; : pos->col;
result = (pos->lnum == rex.lnum + rex.reg_firstlnum result = (pos->lnum == rex.lnum + rex.reg_firstlnum

View File

@@ -704,6 +704,8 @@ static char *(features[]) =
static int included_patches[] = static int included_patches[] =
{ /* Add new patch number below this line */ { /* Add new patch number below this line */
/**/
409,
/**/ /**/
408, 408,
/**/ /**/