patch 9.2.0555: too many strlen() in ex_substitute()
Commit:
https://github.com/vim/vim/commit/8e7e5d5488bbb9a6196b322c21bc324e6f58d525
Author: John Marriott <[email protected]>
Date: Thu May 28 21:24:12 2026 +0000
patch 9.2.0555: too many strlen() in ex_substitute()
Problem: too many strlen() in ex_substitute()
Solution: Use string_T type instead of recomputing the length
(John Marriott).
closes: #20336
Signed-off-by: John Marriott <[email protected]>
Signed-off-by: Christian Brabandt <[email protected]>
diff --git a/src/ex_cmds.c b/src/ex_cmds.c
index 3a598ddfa..7999cba0e 100644
--- a/src/ex_cmds.c
+++ b/src/ex_cmds.c
@@ -4001,10 +4001,10 @@ ex_substitute(exarg_T *eap)
#endif
int save_do_all; // remember user specified 'g'
flag
int save_do_ask; // remember user specified 'c'
flag
- char_u *pat = NULL, *sub = NULL; // init for GCC
- size_t patlen = 0;
+ char_u *sub = NULL; // init for GCC
+ string_T pat = {NULL, 0};
int delimiter;
- int sublen;
+ size_t sublen;
int got_quit = FALSE;
int got_match = FALSE;
int which_pat;
@@ -4012,12 +4012,12 @@ ex_substitute(exarg_T *eap)
char_u *p;
int save_State;
linenr_T first_line = 0; // first changed line
- linenr_T last_line= 0; // below last changed line AFTER the
+ linenr_T last_line = 0; // below last changed line AFTER the
// change
linenr_T old_line_count = curbuf->b_ml.ml_line_count;
linenr_T line2;
long nmatch; // number of lines in match
- char_u *sub_firstline; // allocated copy of first sub line
+ string_T sub_firstline; // allocated copy of first sub line
int endcolumn = FALSE; // cursor in last column when
done
pos_T old_cursor = curwin->w_cursor;
int start_nsubs;
@@ -4047,7 +4047,7 @@ ex_substitute(exarg_T *eap)
if (eap->cmd[0] == 's' && *cmd != NUL && !VIM_ISWHITE(*cmd)
&& vim_strchr((char_u *)"0123456789cegriIp|\"", *cmd) == NULL)
{
- // don't accept alphanumeric for separator
+ // don't accept alphanumeric for separator
if (check_regexp_delim(*cmd) == FAIL)
return;
#ifdef FEAT_EVAL
@@ -4076,20 +4076,19 @@ ex_substitute(exarg_T *eap)
}
if (*cmd != '&')
which_pat = RE_SEARCH; // use last '/' pattern
- pat = (char_u *)""; // empty search pattern
- patlen = 0;
+ STR_LITERAL_SET(pat, ""); // empty search pattern
delimiter = *cmd++; // remember delimiter character
}
else // find the end of the regexp
{
which_pat = RE_LAST; // use last used regexp
delimiter = *cmd++; // remember delimiter character
- pat = cmd; // remember start of search pat
+ pat.string = cmd; // remember start of search pat
cmd = skip_regexp_ex(cmd, delimiter, magic_isset(),
&eap->arg, NULL, NULL);
+ pat.length = (size_t)(cmd - pat.string);
if (cmd[0] == delimiter) // end delimiter found
*cmd++ = NUL; // replace it with a NUL
- patlen = STRLEN(pat);
}
/*
@@ -4141,8 +4140,8 @@ ex_substitute(exarg_T *eap)
emsg(_(e_no_previous_substitute_regular_expression));
return;
}
- pat = NULL; // search_regcomp() will use previous pattern
- patlen = 0;
+ pat.string = NULL; // search_regcomp() will use previous
pattern
+ pat.length = 0;
sub = vim_strsave(old_sub);
if (sub == NULL)
return;
@@ -4156,7 +4155,7 @@ ex_substitute(exarg_T *eap)
// more efficient.
// TODO: find a generic solution to make line-joining operations more
// efficient, avoid allocating a string that grows in size.
- if (pat != NULL && STRCMP(pat, "\n") == 0
+ if (pat.string != NULL && STRCMP(pat.string, "\n") == 0
&& *sub == NUL
&& (*cmd == NUL || (cmd[1] == NUL && (*cmd == 'g' || *cmd == 'l'
|| *cmd == 'p' || *cmd == '#'))))
@@ -4191,9 +4190,9 @@ ex_substitute(exarg_T *eap)
}
if (!keeppatterns)
- save_re_pat(RE_SUBST, pat, patlen, magic_isset());
+ save_re_pat(RE_SUBST, pat.string, pat.length, magic_isset());
// put pattern in history
- add_to_history(HIST_SEARCH, pat, patlen, TRUE, NUL);
+ add_to_history(HIST_SEARCH, pat.string, pat.length, TRUE, NUL);
vim_free(sub);
return;
@@ -4328,7 +4327,7 @@ ex_substitute(exarg_T *eap)
return;
}
- if (search_regcomp(pat, patlen, NULL, RE_SUBST, which_pat, SEARCH_HIS,
®match) == FAIL)
+ if (search_regcomp(pat.string, pat.length, NULL, RE_SUBST, which_pat,
SEARCH_HIS, ®match) == FAIL)
{
if (subflags.do_error)
emsg(_(e_invalid_command));
@@ -4342,7 +4341,8 @@ ex_substitute(exarg_T *eap)
else if (subflags.do_ic == 'I')
regmatch.rmm_ic = FALSE;
- sub_firstline = NULL;
+ sub_firstline.string = NULL;
+ sub_firstline.length = 0;
/*
* If the substitute pattern starts with "\=" then it's an expression.
@@ -4387,12 +4387,13 @@ ex_substitute(exarg_T *eap)
colnr_T copycol;
colnr_T matchcol;
colnr_T prev_matchcol = MAXCOL;
- char_u *new_end, *new_start = NULL;
- unsigned new_start_len = 0;
- char_u *p1;
+ string_T new_start = {NULL, 0};
+ size_t new_start_size = 0;
+ char_u *new_end;
int did_sub = FALSE;
int lastone;
- int len, copy_len, needed_len;
+ size_t copy_len;
+ size_t needed_size;
long nmatch_tl = 0; // nr of lines matched below lnum
int do_again; // do it again after joining lines
int skip_match = FALSE;
@@ -4441,7 +4442,7 @@ ex_substitute(exarg_T *eap)
* accordingly.
*
* The new text is built up in new_start[]. It has some extra
- * room to avoid using alloc()/free() too often. new_start_len is
+ * room to avoid using alloc()/free() too often. new_start_size is
* the length of the allocated memory at new_start.
*
* Make a copy of the old line, so it won't be taken away when
@@ -4469,6 +4470,10 @@ ex_substitute(exarg_T *eap)
*/
for (;;)
{
+ string_T tmp;
+ char_u *p1;
+ size_t n;
+
// Advance "lnum" to the line where the match starts. The
// match does not start in the first line when there is a line
// break before \zs.
@@ -4477,7 +4482,7 @@ ex_substitute(exarg_T *eap)
lnum += regmatch.startpos[0].lnum;
sub_firstlnum += regmatch.startpos[0].lnum;
nmatch -= regmatch.startpos[0].lnum;
- VIM_CLEAR(sub_firstline);
+ VIM_CLEAR_STRING(sub_firstline);
}
// Match might be after the last line for "
\zs" matching at
@@ -4485,13 +4490,14 @@ ex_substitute(exarg_T *eap)
if (lnum > curbuf->b_ml.ml_line_count)
break;
- if (sub_firstline == NULL)
+ if (sub_firstline.string == NULL)
{
- sub_firstline = vim_strnsave(ml_get(sub_firstlnum),
- ml_get_len(sub_firstlnum));
- if (sub_firstline == NULL)
+ sub_firstline.length = ml_get_len(sub_firstlnum);
+ sub_firstline.string =
+ vim_strnsave(ml_get(sub_firstlnum),
sub_firstline.length);
+ if (sub_firstline.string == NULL)
{
- vim_free(new_start);
+ vim_free(new_start.string);
goto outofmem;
}
}
@@ -4510,7 +4516,7 @@ ex_substitute(exarg_T *eap)
&& regmatch.endpos[0].lnum == 0
&& matchcol == regmatch.endpos[0].col)
{
- if (sub_firstline[matchcol] == NUL)
+ if (sub_firstline.string[matchcol] == NUL)
// We already were at the end of the line. Don't look
// for a match in this line again.
skip_match = TRUE;
@@ -4518,7 +4524,7 @@ ex_substitute(exarg_T *eap)
{
// search for a match at next column
if (has_mbyte)
- matchcol += mb_ptr2len(sub_firstline + matchcol);
+ matchcol += mb_ptr2len(sub_firstline.string +
matchcol);
else
++matchcol;
}
@@ -4542,7 +4548,7 @@ ex_substitute(exarg_T *eap)
// Avoids that ":s/
B\@=//gc" get stuck.
if (nmatch > 1)
{
- matchcol = (colnr_T)STRLEN(sub_firstline);
+ matchcol = (colnr_T)sub_firstline.length;
nmatch = 1;
skip_match = TRUE;
}
@@ -4621,7 +4627,7 @@ ex_substitute(exarg_T *eap)
}
else
{
- char_u *orig_line = NULL;
+ string_T orig_line = {NULL, 0};
int len_change = 0;
int save_p_lz = p_lz;
#ifdef FEAT_FOLDING
@@ -4637,33 +4643,36 @@ ex_substitute(exarg_T *eap)
// avoid calling update_screen() in vgetorpeek()
p_lz = FALSE;
- if (new_start != NULL)
+ if (new_start.string != NULL)
{
// There already was a substitution, we would
// like to show this to the user. We cannot
// really update the line, it would change
// what matches. Temporarily replace the line
// and change it back afterwards.
- orig_line = vim_strnsave(ml_get(lnum),
- ml_get_len(lnum));
- if (orig_line != NULL)
+ orig_line.length = ml_get_len(lnum);
+ orig_line.string = vim_strnsave(ml_get(lnum),
orig_line.length);
+ if (orig_line.string != NULL)
{
- char_u *new_line = concat_str(new_start,
- sub_firstline + copycol);
+ string_T new_line;
- if (new_line == NULL)
- VIM_CLEAR(orig_line);
+ new_line.string =
concat_str(new_start.string,
+ sub_firstline.string +
copycol);
+ if (new_line.string == NULL)
+ VIM_CLEAR_STRING(orig_line);
else
{
+ new_line.length =
+ new_start.length +
(sub_firstline.length - copycol);
// Position the cursor relative to the
// end of the line, the previous
// substitute may have inserted or
// deleted characters before the
// cursor.
- len_change = (int)STRLEN(new_line)
- - (int)STRLEN(orig_line);
+ len_change =
+ (int)new_line.length -
(int)orig_line.length;
curwin->w_cursor.col += len_change;
- ml_replace(lnum, new_line, FALSE);
+ ml_replace(lnum, new_line.string,
FALSE);
}
}
}
@@ -4720,8 +4729,8 @@ ex_substitute(exarg_T *eap)
p_lz = save_p_lz;
// restore the line
- if (orig_line != NULL)
- ml_replace(lnum, orig_line, FALSE);
+ if (orig_line.string != NULL)
+ ml_replace(lnum, orig_line.string, FALSE);
}
need_wait_return = FALSE; // no hit-return prompt
@@ -4769,7 +4778,7 @@ ex_substitute(exarg_T *eap)
// get stuck when pressing 'n'.
if (nmatch > 1)
{
- matchcol = (colnr_T)STRLEN(sub_firstline);
+ matchcol = (colnr_T)sub_firstline.length;
skip_match = TRUE;
}
goto skip;
@@ -4803,11 +4812,10 @@ ex_substitute(exarg_T *eap)
#endif
// Get length of substitution part, including the NUL.
// When it fails sublen is zero.
- sublen = vim_regsub_multi(®match,
- sub_firstlnum - regmatch.startpos[0].lnum,
- sub, sub_firstline, 0,
- REGSUB_BACKSLASH
- | (magic_isset() ? REGSUB_MAGIC : 0));
+ sublen = (size_t)vim_regsub_multi(®match,
+ sub_firstlnum - regmatch.startpos[0].lnum,
+ sub, sub_firstline.string, 0,
+ REGSUB_BACKSLASH | (magic_isset() ? REGSUB_MAGIC : 0));
#ifdef FEAT_EVAL
--textlock;
@@ -4843,7 +4851,8 @@ ex_substitute(exarg_T *eap)
// needed.
if (nmatch == 1)
{
- p1 = sub_firstline;
+ tmp.string = sub_firstline.string;
+ tmp.length = sub_firstline.length;
#ifdef FEAT_PROP_POPUP
if (curbuf->b_has_textprop)
{
@@ -4858,7 +4867,7 @@ ex_substitute(exarg_T *eap)
apc_flags &= ~APC_SAVE_FOR_UNDO;
// Offset for column byte number of the text property
// in the resulting buffer afterwards.
- total_added += bytes_added;
+ total_added += (colnr_T)bytes_added;
}
#endif
}
@@ -4875,8 +4884,8 @@ ex_substitute(exarg_T *eap)
total_added + regmatch.startpos[0].col,
-MAXCOL, apc_flags))
apc_flags &= ~APC_SAVE_FOR_UNDO;
- total_added -= (colnr_T)STRLEN(
- sub_firstline + regmatch.startpos[0].col);
+ total_added -=
+ (colnr_T)(sub_firstline.length -
regmatch.startpos[0].col);
// Props in the last line may be moved or deleted
if (adjust_prop_columns(lastlnum,
@@ -4922,29 +4931,32 @@ ex_substitute(exarg_T *eap)
}
}
#endif
- p1 = ml_get(lastlnum);
+ tmp.string = ml_get(lastlnum);
+ tmp.length = ml_get_len(lastlnum);
nmatch_tl += nmatch - 1;
#ifdef FEAT_PROP_POPUP
if (curbuf->b_has_textprop)
- total_added += (colnr_T)STRLEN(
- p1 + regmatch.endpos[0].col);
+ total_added +=
+ (colnr_T)(tmp.length - regmatch.endpos[0].col);
#endif
}
- copy_len = regmatch.startpos[0].col - copycol;
- needed_len = copy_len + ((unsigned)STRLEN(p1)
- - regmatch.endpos[0].col) + sublen + 1;
- if (new_start == NULL)
+
+ copy_len = (size_t)(regmatch.startpos[0].col - copycol);
+ needed_size = copy_len
+ + (tmp.length - (size_t)regmatch.endpos[0].col) + sublen +
1;
+
+ if (new_start.string == NULL)
{
/*
* Get some space for a temporary buffer to do the
* substitution into (and some extra space to avoid
* too many calls to alloc()/free()).
*/
- new_start_len = needed_len + 50;
- if ((new_start = alloc_clear(new_start_len)) == NULL)
+ new_start_size = needed_size + 50;
+ if ((new_start.string = alloc_clear(new_start_size)) ==
NULL)
goto outofmem;
- *new_start = NUL;
- new_end = new_start;
+ *new_start.string = NUL;
+ new_start.length = 0;
}
else
{
@@ -4953,40 +4965,45 @@ ex_substitute(exarg_T *eap)
* substitution into. If not, make it larger (with a bit
* extra to avoid too many calls to alloc()/free()).
*/
- len = (unsigned)STRLEN(new_start);
- needed_len += len;
- if (needed_len > (int)new_start_len)
+ needed_size += new_start.length;
+ if (needed_size > new_start_size)
{
- new_start_len = needed_len + 50;
- if ((p1 = alloc_clear(new_start_len)) == NULL)
+ new_start_size = needed_size + 50;
+ if ((p1 = alloc_clear(new_start_size)) == NULL)
{
- vim_free(new_start);
+ vim_free(new_start.string);
goto outofmem;
}
- mch_memmove(p1, new_start, (size_t)(len + 1));
- vim_free(new_start);
- new_start = p1;
+ mch_memmove(p1, new_start.string, new_start.length + 1);
+ vim_free(new_start.string);
+ new_start.string = p1;
}
- new_end = new_start + len;
}
/*
* copy the text up to the part that matched
*/
- mch_memmove(new_end, sub_firstline + copycol, (size_t)copy_len);
- new_end += copy_len;
+ mch_memmove(new_start.string + new_start.length,
+ sub_firstline.string + copycol, copy_len);
+ new_start.length += copy_len;
- if ((int)new_start_len - copy_len < sublen)
- sublen = new_start_len - copy_len - 1;
+ // new text added here
+ new_end = new_start.string + new_start.length;
+
+ if (new_start_size - copy_len < sublen)
+ sublen = new_start_size - copy_len - 1;
#ifdef FEAT_EVAL
++textlock;
#endif
- (void)vim_regsub_multi(®match,
- sub_firstlnum - regmatch.startpos[0].lnum,
- sub, new_end, sublen,
- REGSUB_COPY | REGSUB_BACKSLASH
- | (magic_isset() ? REGSUB_MAGIC : 0));
+ n = (size_t)vim_regsub_multi(®match,
+ sub_firstlnum - regmatch.startpos[0].lnum,
+ sub, new_end, (int)sublen,
+ REGSUB_COPY | REGSUB_BACKSLASH | (magic_isset() ?
REGSUB_MAGIC : 0));
+
+ if (n > 0)
+ new_start.length += n - 1; // remove 1 for the NUL
+
#ifdef FEAT_EVAL
--textlock;
#endif
@@ -5002,15 +5019,15 @@ ex_substitute(exarg_T *eap)
if (nmatch > 1)
{
sub_firstlnum += nmatch - 1;
- vim_free(sub_firstline);
- sub_firstline = vim_strnsave(ml_get(sub_firstlnum),
- ml_get_len(sub_firstlnum));
- if (sub_firstline == NULL)
+ vim_free(sub_firstline.string);
+ sub_firstline.length = ml_get_len(sub_firstlnum);
+ sub_firstline.string =
+ vim_strnsave(ml_get(sub_firstlnum),
sub_firstline.length);
+ if (sub_firstline.string == NULL)
{
- vim_free(new_start);
+ vim_free(new_start.string);
goto outofmem;
}
-
// When going beyond the last line, stop substituting.
if (sub_firstlnum <= line2)
do_again = TRUE;
@@ -5025,13 +5042,14 @@ ex_substitute(exarg_T *eap)
{
// Already hit end of the buffer, sub_firstlnum is one
// less than what it ought to be.
- vim_free(sub_firstline);
- sub_firstline = vim_strsave((char_u *)"");
- if (sub_firstline == NULL)
+ vim_free(sub_firstline.string);
+ sub_firstline.string = vim_strnsave((char_u *)"", 0);
+ if (sub_firstline.string == NULL)
{
- vim_free(new_start);
+ vim_free(new_start.string);
goto outofmem;
}
+ sub_firstline.length = 0;
copycol = 0;
}
@@ -5047,14 +5065,16 @@ ex_substitute(exarg_T *eap)
{
if (p1[0] == '\' && p1[1] != NUL) // remove backslash
{
- STRMOVE(p1, p1 + 1);
+ n = (size_t)(new_start.length - (p1 -
new_start.string));
+ mch_memmove(p1, p1 + 1, n + 1);
+ --new_start.length;
#ifdef FEAT_PROP_POPUP
if (curbuf->b_has_textprop)
{
// When text properties are changed, need to save
// for undo first, unless done already.
if (adjust_prop_columns(lnum,
- (colnr_T)(p1 - new_start), -1,
+ (colnr_T)(p1 - new_start.string), -1,
apc_flags))
apc_flags &= ~APC_SAVE_FOR_UNDO;
}
@@ -5064,10 +5084,10 @@ ex_substitute(exarg_T *eap)
{
if (u_inssub(lnum) == OK) // prepare for undo
{
- colnr_T plen = (colnr_T)(p1 - new_start + 1);
+ colnr_T plen = (colnr_T)(p1 - new_start.string
+ 1);
*p1 = NUL; // truncate up to the CR
- ml_append(lnum - 1, new_start, plen, FALSE);
+ ml_append(lnum - 1, new_start.string, plen, FALSE);
mark_adjust(lnum + 1, (linenr_T)MAXLNUM, 1L, 0L);
if (subflags.do_ask)
appended_lines(lnum - 1, 1L);
@@ -5088,8 +5108,10 @@ ex_substitute(exarg_T *eap)
// move the cursor to the new line, like Vi
++curwin->w_cursor.lnum;
// copy the rest
- STRMOVE(new_start, p1 + 1);
- p1 = new_start - 1;
+ n = new_start.length - (size_t)plen;
+ mch_memmove(new_start.string, p1 + 1, n + 1);
+ new_start.length = n;
+ p1 = new_start.string - 1;
}
}
else if (has_mbyte)
@@ -5113,7 +5135,7 @@ skip:
|| got_quit
|| lnum > line2
|| !(subflags.do_all || do_again)
- || (sub_firstline[matchcol] == NUL && nmatch <= 1
+ || (sub_firstline.string[matchcol] == NUL && nmatch <= 1
&& !re_multiline(regmatch.regprog)));
nmatch = -1;
@@ -5133,7 +5155,7 @@ skip:
matchcol, NULL)) == 0
|| regmatch.startpos[0].lnum > 0)
{
- if (new_start != NULL)
+ if (new_start.string != NULL)
{
/*
* Copy the rest of the line, that didn't match.
@@ -5142,14 +5164,16 @@ skip:
* have changed the number of characters. Same for
* "prev_matchcol".
*/
- STRCAT(new_start, sub_firstline + copycol);
- matchcol = (colnr_T)STRLEN(sub_firstline) - matchcol;
- prev_matchcol = (colnr_T)STRLEN(sub_firstline)
- - prev_matchcol;
+ STRCPY(new_start.string + new_start.length,
+ sub_firstline.string + copycol);
+ new_start.length += sub_firstline.length -
(size_t)copycol;
+ matchcol = (colnr_T)(sub_firstline.length - matchcol);
+ prev_matchcol = (colnr_T)(sub_firstline.length
+ - prev_matchcol);
if (u_savesub(lnum) != OK)
break;
- ml_replace(lnum, new_start, TRUE);
+ ml_replace(lnum, new_start.string, TRUE);
#ifdef FEAT_PROP_POPUP
if (text_props != NULL)
add_text_props(lnum, text_props, text_prop_count);
@@ -5195,12 +5219,14 @@ skip:
}
sub_firstlnum = lnum;
- vim_free(sub_firstline); // free the temp buffer
- sub_firstline = new_start;
- new_start = NULL;
- matchcol = (colnr_T)STRLEN(sub_firstline) - matchcol;
- prev_matchcol = (colnr_T)STRLEN(sub_firstline)
- - prev_matchcol;
+ vim_free(sub_firstline.string); // free the temp
buffer
+ sub_firstline.string = new_start.string;
+ sub_firstline.length = new_start.length;
+ new_start.string = NULL;
+ new_start.length = 0;
+ matchcol = (colnr_T)(sub_firstline.length - matchcol);
+ prev_matchcol = (colnr_T)(sub_firstline.length
+ - prev_matchcol);
copycol = 0;
}
if (nmatch == -1 && !lastone)
@@ -5226,8 +5252,8 @@ skip:
if (did_sub)
++sub_nlines;
- vim_free(new_start); // for when substitute was cancelled
- VIM_CLEAR(sub_firstline); // free the copy of the original line
+ vim_free(new_start.string); // for when substitute was cancelled
+ VIM_CLEAR_STRING(sub_firstline); // free the copy of the
original line
}
line_breakcheck();
@@ -5243,7 +5269,7 @@ skip:
}
outofmem:
- vim_free(sub_firstline); // may have to free allocated copy of the line
+ vim_free(sub_firstline.string); // may have to free allocated copy of the
line
#ifdef FEAT_PROP_POPUP
vim_free(text_props);
diff --git a/src/version.c b/src/version.c
index 9e5b5cc02..2a6eb7972 100644
--- a/src/version.c
+++ b/src/version.c
@@ -729,6 +729,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
+/**/
+ 555,
/**/
554,
/**/
--
--
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php
---
You received this message because you are subscribed to the Google Groups
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion visit
https://groups.google.com/d/msgid/vim_dev/E1wSiIn-00BXIY-9w%40256bit.org.