# HG changeset patch # User Christian Brabandt # Date 1471374906 -7200 # Node ID 064effd222a3bbbb83450cb4b97fa92dafa23ea0 # Parent d5f40458a686b6b7c0b4c290aee76ef13c3dab20 commit https://github.com/vim/vim/commit/f5a39447a8ebe162ee62caa2ee502cd0e65eecaa Author: Bram Moolenaar Date: Tue Aug 16 21:04:41 2016 +0200 patch 7.4.2219 Problem: Recursive call to substitute gets stuck in sandbox. (Nikolai Pavlov) Solution: Handle the recursive call. (Christian Brabandt, closes https://github.com/vim/vim/issues/950) Add a test. diff --git a/src/ex_cmds.c b/src/ex_cmds.c --- a/src/ex_cmds.c +++ b/src/ex_cmds.c @@ -4747,6 +4747,20 @@ check_secure(void) static char_u *old_sub = NULL; /* previous substitute pattern */ static int global_need_beginline; /* call beginline() after ":g" */ +/* + * Flags that are kept between calls to :substitute. + */ +typedef struct { + int do_all; /* do multiple substitutions per line */ + int do_ask; /* ask for confirmation */ + int do_count; /* count only */ + int do_error; /* if false, ignore errors */ + int do_print; /* print last line with subs. */ + int do_list; /* list last line with subs. */ + int do_number; /* list last line with line nr*/ + int do_ic; /* ignore case flag */ +} subflags_T; + /* do_sub() * * Perform a substitution from line eap->line1 to line eap->line2 using the @@ -4762,14 +4776,11 @@ do_sub(exarg_T *eap) linenr_T lnum; long i = 0; regmmatch_T regmatch; - static int do_all = FALSE; /* do multiple substitutions per line */ - static int do_ask = FALSE; /* ask for confirmation */ - static int do_count = FALSE; /* count only */ - static int do_error = TRUE; /* if false, ignore errors */ - static int do_print = FALSE; /* print last line with subs. */ - static int do_list = FALSE; /* list last line with subs. */ - static int do_number = FALSE; /* list last line with line nr*/ - static int do_ic = 0; /* ignore case flag */ + static subflags_T subflags = {FALSE, FALSE, FALSE, TRUE, FALSE, + FALSE, FALSE, 0}; +#ifdef FEAT_EVAL + subflags_T subflags_save; +#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 */ @@ -4957,16 +4968,16 @@ do_sub(exarg_T *eap) if (!p_ed) { if (p_gd) /* default is global on */ - do_all = TRUE; + subflags.do_all = TRUE; else - do_all = FALSE; - do_ask = FALSE; - } - do_error = TRUE; - do_print = FALSE; - do_count = FALSE; - do_number = FALSE; - do_ic = 0; + subflags.do_all = FALSE; + subflags.do_ask = FALSE; + } + subflags.do_error = TRUE; + subflags.do_print = FALSE; + subflags.do_count = FALSE; + subflags.do_number = FALSE; + subflags.do_ic = 0; } while (*cmd) { @@ -4975,40 +4986,40 @@ do_sub(exarg_T *eap) * 'r' is never inverted. */ if (*cmd == 'g') - do_all = !do_all; + subflags.do_all = !subflags.do_all; else if (*cmd == 'c') - do_ask = !do_ask; + subflags.do_ask = !subflags.do_ask; else if (*cmd == 'n') - do_count = TRUE; + subflags.do_count = TRUE; else if (*cmd == 'e') - do_error = !do_error; + subflags.do_error = !subflags.do_error; else if (*cmd == 'r') /* use last used regexp */ which_pat = RE_LAST; else if (*cmd == 'p') - do_print = TRUE; + subflags.do_print = TRUE; else if (*cmd == '#') { - do_print = TRUE; - do_number = TRUE; + subflags.do_print = TRUE; + subflags.do_number = TRUE; } else if (*cmd == 'l') { - do_print = TRUE; - do_list = TRUE; + subflags.do_print = TRUE; + subflags.do_list = TRUE; } else if (*cmd == 'i') /* ignore case */ - do_ic = 'i'; + subflags.do_ic = 'i'; else if (*cmd == 'I') /* don't ignore case */ - do_ic = 'I'; + subflags.do_ic = 'I'; else break; ++cmd; } - if (do_count) - do_ask = FALSE; - - save_do_all = do_all; - save_do_ask = do_ask; + if (subflags.do_count) + subflags.do_ask = FALSE; + + save_do_all = subflags.do_all; + save_do_ask = subflags.do_ask; /* * check for a trailing count @@ -5017,7 +5028,7 @@ do_sub(exarg_T *eap) if (VIM_ISDIGIT(*cmd)) { i = getdigits(&cmd); - if (i <= 0 && !eap->skip && do_error) + if (i <= 0 && !eap->skip && subflags.do_error) { EMSG(_(e_zerocount)); return; @@ -5045,7 +5056,7 @@ do_sub(exarg_T *eap) if (eap->skip) /* not executing commands, only parsing */ return; - if (!do_count && !curbuf->b_p_ma) + if (!subflags.do_count && !curbuf->b_p_ma) { /* Substitution is not allowed in non-'modifiable' buffer */ EMSG(_(e_modifiable)); @@ -5054,15 +5065,15 @@ do_sub(exarg_T *eap) if (search_regcomp(pat, RE_SUBST, which_pat, SEARCH_HIS, ®match) == FAIL) { - if (do_error) + if (subflags.do_error) EMSG(_(e_invcmd)); return; } /* the 'i' or 'I' flag overrules 'ignorecase' and 'smartcase' */ - if (do_ic == 'i') + if (subflags.do_ic == 'i') regmatch.rmm_ic = TRUE; - else if (do_ic == 'I') + else if (subflags.do_ic == 'I') regmatch.rmm_ic = FALSE; sub_firstline = NULL; @@ -5231,7 +5242,7 @@ do_sub(exarg_T *eap) * 2. If do_count is set only increase the counter. * If do_ask is set, ask for confirmation. */ - if (do_count) + if (subflags.do_count) { /* For a multi-line match, put matchcol at the NUL at * the end of the line and set nmatch to one, so that @@ -5253,7 +5264,7 @@ do_sub(exarg_T *eap) goto skip; } - if (do_ask) + if (subflags.do_ask) { int typed = 0; @@ -5274,19 +5285,20 @@ do_sub(exarg_T *eap) /* * Loop until 'y', 'n', 'q', CTRL-E or CTRL-Y typed. */ - while (do_ask) + while (subflags.do_ask) { if (exmode_active) { char_u *resp; colnr_T sc, ec; - print_line_no_prefix(lnum, do_number, do_list); + print_line_no_prefix(lnum, + subflags.do_number, subflags.do_list); getvcol(curwin, &curwin->w_cursor, &sc, NULL, NULL); curwin->w_cursor.col = regmatch.endpos[0].col - 1; getvcol(curwin, &curwin->w_cursor, NULL, NULL, &ec); - if (do_number || curwin->w_p_nu) + if (subflags.do_number || curwin->w_p_nu) { int numw = number_width(curwin) + 1; sc += numw; @@ -5420,13 +5432,13 @@ do_sub(exarg_T *eap) if (typed == 'l') { /* last: replace and then stop */ - do_all = FALSE; + subflags.do_all = FALSE; line2 = lnum; break; } if (typed == 'a') { - do_ask = FALSE; + subflags.do_ask = FALSE; break; } #ifdef FEAT_INS_EXPAND @@ -5469,23 +5481,29 @@ do_sub(exarg_T *eap) * 3. substitute the string. */ #ifdef FEAT_EVAL - if (do_count) + if (subflags.do_count) { /* prevent accidentally changing the buffer by a function */ save_ma = curbuf->b_p_ma; curbuf->b_p_ma = FALSE; sandbox++; } + /* Save flags for recursion. They can change for e.g. + * :s/^/\=execute("s#^##gn") */ + subflags_save = subflags; #endif /* get length of substitution part */ sublen = vim_regsub_multi(®match, sub_firstlnum - regmatch.startpos[0].lnum, sub, sub_firstline, FALSE, p_magic, TRUE); #ifdef FEAT_EVAL - if (do_count) + /* Don't keep flags set by a recursive call. */ + subflags = subflags_save; + if (subflags.do_count) { curbuf->b_p_ma = save_ma; - sandbox--; + if (sandbox > 0) + sandbox--; goto skip; } #endif @@ -5578,7 +5596,7 @@ do_sub(exarg_T *eap) if (sub_firstlnum <= line2) do_again = TRUE; else - do_all = FALSE; + subflags.do_all = FALSE; } /* Remember next character to be copied. */ @@ -5613,7 +5631,7 @@ do_sub(exarg_T *eap) ml_append(lnum - 1, new_start, (colnr_T)(p1 - new_start + 1), FALSE); mark_adjust(lnum + 1, (linenr_T)MAXLNUM, 1L, 0L); - if (do_ask) + if (subflags.do_ask) appended_lines(lnum - 1, 1L); else { @@ -5654,7 +5672,7 @@ skip: || got_int || got_quit || lnum > line2 - || !(do_all || do_again) + || !(subflags.do_all || do_again) || (sub_firstline[matchcol] == NUL && nmatch <= 1 && !re_multiline(regmatch.regprog))); nmatch = -1; @@ -5708,7 +5726,7 @@ skip: ml_delete(lnum, (int)FALSE); mark_adjust(lnum, lnum + nmatch_tl - 1, (long)MAXLNUM, -nmatch_tl); - if (do_ask) + if (subflags.do_ask) deleted_lines(lnum, nmatch_tl); --lnum; line2 -= nmatch_tl; /* nr of lines decreases */ @@ -5717,7 +5735,7 @@ skip: /* When asking, undo is saved each time, must also set * changed flag each time. */ - if (do_ask) + if (subflags.do_ask) changed_bytes(lnum, 0); else { @@ -5779,7 +5797,7 @@ outofmem: vim_free(sub_firstline); /* may have to free allocated copy of the line */ /* ":s/pat//n" doesn't move the cursor */ - if (do_count) + if (subflags.do_count) curwin->w_cursor = old_cursor; if (sub_nsubs > start_nsubs) @@ -5791,20 +5809,22 @@ outofmem: if (!global_busy) { - if (!do_ask) /* when interactive leave cursor on the match */ + /* when interactive leave cursor on the match */ + if (!subflags.do_ask) { if (endcolumn) coladvance((colnr_T)MAXCOL); else beginline(BL_WHITE | BL_FIX); } - if (!do_sub_msg(do_count) && do_ask) + if (!do_sub_msg(subflags.do_count) && subflags.do_ask) MSG(""); } else global_need_beginline = TRUE; - if (do_print) - print_line(curwin->w_cursor.lnum, do_number, do_list); + if (subflags.do_print) + print_line(curwin->w_cursor.lnum, + subflags.do_number, subflags.do_list); } else if (!global_busy) { @@ -5812,12 +5832,12 @@ outofmem: EMSG(_(e_interr)); else if (got_match) /* did find something but nothing substituted */ MSG(""); - else if (do_error) /* nothing found */ + else if (subflags.do_error) /* nothing found */ EMSG2(_(e_patnotf2), get_search_pat()); } #ifdef FEAT_FOLDING - if (do_ask && hasAnyFolding(curwin)) + if (subflags.do_ask && hasAnyFolding(curwin)) /* Cursor position may require updating */ changed_window_setting(); #endif @@ -5825,8 +5845,8 @@ outofmem: vim_regfree(regmatch.regprog); /* Restore the flag values, they can be used for ":&&". */ - do_all = save_do_all; - do_ask = save_do_ask; + subflags.do_all = save_do_all; + subflags.do_ask = save_do_ask; } /* diff --git a/src/testdir/test_regexp_latin.vim b/src/testdir/test_regexp_latin.vim --- a/src/testdir/test_regexp_latin.vim +++ b/src/testdir/test_regexp_latin.vim @@ -30,3 +30,12 @@ func Test_equivalence_re2() set re=2 call s:equivalence_test() endfunc + +func Test_recursive_substitute() + new + s/^/\=execute("s#^##gn") + " check we are now not in the sandbox + call setwinvar(1, 'myvar', 1) + bwipe! +endfunc + diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -764,6 +764,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 2219, +/**/ 2218, /**/ 2217,