To: vim_dev@googlegroups.com Subject: Patch 7.4.2219 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 7.4.2219 Problem: Recursive call to substitute gets stuck in sandbox. (Nikolai Pavlov) Solution: Handle the recursive call. (Christian Brabandt, closes #950) Add a test. Files: src/ex_cmds.c, src/testdir/test_regexp_latin.vim *** ../vim-7.4.2218/src/ex_cmds.c 2016-07-30 23:18:44.644927811 +0200 --- src/ex_cmds.c 2016-08-16 20:58:17.915413520 +0200 *************** *** 4747,4752 **** --- 4747,4766 ---- 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,4775 **** 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 */ 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 */ --- 4776,4786 ---- linenr_T lnum; long i = 0; regmmatch_T regmatch; ! 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,4972 **** if (!p_ed) { if (p_gd) /* default is global on */ ! 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; } while (*cmd) { --- 4968,4983 ---- if (!p_ed) { if (p_gd) /* default is global on */ ! subflags.do_all = TRUE; else ! 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,5014 **** * 'r' is never inverted. */ if (*cmd == 'g') ! do_all = !do_all; else if (*cmd == 'c') ! do_ask = !do_ask; else if (*cmd == 'n') ! do_count = TRUE; else if (*cmd == 'e') ! do_error = !do_error; else if (*cmd == 'r') /* use last used regexp */ which_pat = RE_LAST; else if (*cmd == 'p') ! do_print = TRUE; else if (*cmd == '#') { ! do_print = TRUE; ! do_number = TRUE; } else if (*cmd == 'l') { ! do_print = TRUE; ! do_list = TRUE; } else if (*cmd == 'i') /* ignore case */ ! do_ic = 'i'; else if (*cmd == 'I') /* don't ignore case */ ! do_ic = 'I'; else break; ++cmd; } ! if (do_count) ! do_ask = FALSE; ! save_do_all = do_all; ! save_do_ask = do_ask; /* * check for a trailing count --- 4986,5025 ---- * 'r' is never inverted. */ if (*cmd == 'g') ! subflags.do_all = !subflags.do_all; else if (*cmd == 'c') ! subflags.do_ask = !subflags.do_ask; else if (*cmd == 'n') ! subflags.do_count = TRUE; else if (*cmd == 'e') ! subflags.do_error = !subflags.do_error; else if (*cmd == 'r') /* use last used regexp */ which_pat = RE_LAST; else if (*cmd == 'p') ! subflags.do_print = TRUE; else if (*cmd == '#') { ! subflags.do_print = TRUE; ! subflags.do_number = TRUE; } else if (*cmd == 'l') { ! subflags.do_print = TRUE; ! subflags.do_list = TRUE; } else if (*cmd == 'i') /* ignore case */ ! subflags.do_ic = 'i'; else if (*cmd == 'I') /* don't ignore case */ ! subflags.do_ic = 'I'; else break; ++cmd; } ! 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,5023 **** if (VIM_ISDIGIT(*cmd)) { i = getdigits(&cmd); ! if (i <= 0 && !eap->skip && do_error) { EMSG(_(e_zerocount)); return; --- 5028,5034 ---- if (VIM_ISDIGIT(*cmd)) { i = getdigits(&cmd); ! if (i <= 0 && !eap->skip && subflags.do_error) { EMSG(_(e_zerocount)); return; *************** *** 5045,5051 **** if (eap->skip) /* not executing commands, only parsing */ return; ! if (!do_count && !curbuf->b_p_ma) { /* Substitution is not allowed in non-'modifiable' buffer */ EMSG(_(e_modifiable)); --- 5056,5062 ---- if (eap->skip) /* not executing commands, only parsing */ return; ! if (!subflags.do_count && !curbuf->b_p_ma) { /* Substitution is not allowed in non-'modifiable' buffer */ EMSG(_(e_modifiable)); *************** *** 5054,5068 **** if (search_regcomp(pat, RE_SUBST, which_pat, SEARCH_HIS, ®match) == FAIL) { ! if (do_error) EMSG(_(e_invcmd)); return; } /* the 'i' or 'I' flag overrules 'ignorecase' and 'smartcase' */ ! if (do_ic == 'i') regmatch.rmm_ic = TRUE; ! else if (do_ic == 'I') regmatch.rmm_ic = FALSE; sub_firstline = NULL; --- 5065,5079 ---- if (search_regcomp(pat, RE_SUBST, which_pat, SEARCH_HIS, ®match) == FAIL) { ! if (subflags.do_error) EMSG(_(e_invcmd)); return; } /* the 'i' or 'I' flag overrules 'ignorecase' and 'smartcase' */ ! if (subflags.do_ic == 'i') regmatch.rmm_ic = TRUE; ! else if (subflags.do_ic == 'I') regmatch.rmm_ic = FALSE; sub_firstline = NULL; *************** *** 5231,5237 **** * 2. If do_count is set only increase the counter. * If do_ask is set, ask for confirmation. */ ! if (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 --- 5242,5248 ---- * 2. If do_count is set only increase the counter. * If do_ask is set, ask for confirmation. */ ! 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,5259 **** goto skip; } ! if (do_ask) { int typed = 0; --- 5264,5270 ---- goto skip; } ! if (subflags.do_ask) { int typed = 0; *************** *** 5274,5292 **** /* * Loop until 'y', 'n', 'q', CTRL-E or CTRL-Y typed. */ ! while (do_ask) { if (exmode_active) { char_u *resp; colnr_T sc, ec; ! print_line_no_prefix(lnum, do_number, 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) { int numw = number_width(curwin) + 1; sc += numw; --- 5285,5304 ---- /* * Loop until 'y', 'n', 'q', CTRL-E or CTRL-Y typed. */ ! while (subflags.do_ask) { if (exmode_active) { char_u *resp; colnr_T sc, ec; ! 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 (subflags.do_number || curwin->w_p_nu) { int numw = number_width(curwin) + 1; sc += numw; *************** *** 5420,5432 **** if (typed == 'l') { /* last: replace and then stop */ ! do_all = FALSE; line2 = lnum; break; } if (typed == 'a') { ! do_ask = FALSE; break; } #ifdef FEAT_INS_EXPAND --- 5432,5444 ---- if (typed == 'l') { /* last: replace and then stop */ ! subflags.do_all = FALSE; line2 = lnum; break; } if (typed == 'a') { ! subflags.do_ask = FALSE; break; } #ifdef FEAT_INS_EXPAND *************** *** 5469,5491 **** * 3. substitute the string. */ #ifdef FEAT_EVAL ! if (do_count) { /* prevent accidentally changing the buffer by a function */ save_ma = curbuf->b_p_ma; curbuf->b_p_ma = FALSE; sandbox++; } #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) { curbuf->b_p_ma = save_ma; ! sandbox--; goto skip; } #endif --- 5481,5509 ---- * 3. substitute the string. */ #ifdef FEAT_EVAL ! 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 ! /* Don't keep flags set by a recursive call. */ ! subflags = subflags_save; ! if (subflags.do_count) { curbuf->b_p_ma = save_ma; ! if (sandbox > 0) ! sandbox--; goto skip; } #endif *************** *** 5578,5584 **** if (sub_firstlnum <= line2) do_again = TRUE; else ! do_all = FALSE; } /* Remember next character to be copied. */ --- 5596,5602 ---- if (sub_firstlnum <= line2) do_again = TRUE; else ! subflags.do_all = FALSE; } /* Remember next character to be copied. */ *************** *** 5613,5619 **** 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) appended_lines(lnum - 1, 1L); else { --- 5631,5637 ---- ml_append(lnum - 1, new_start, (colnr_T)(p1 - new_start + 1), FALSE); mark_adjust(lnum + 1, (linenr_T)MAXLNUM, 1L, 0L); ! if (subflags.do_ask) appended_lines(lnum - 1, 1L); else { *************** *** 5654,5660 **** || got_int || got_quit || lnum > line2 ! || !(do_all || do_again) || (sub_firstline[matchcol] == NUL && nmatch <= 1 && !re_multiline(regmatch.regprog))); nmatch = -1; --- 5672,5678 ---- || got_int || got_quit || lnum > line2 ! || !(subflags.do_all || do_again) || (sub_firstline[matchcol] == NUL && nmatch <= 1 && !re_multiline(regmatch.regprog))); nmatch = -1; *************** *** 5708,5714 **** ml_delete(lnum, (int)FALSE); mark_adjust(lnum, lnum + nmatch_tl - 1, (long)MAXLNUM, -nmatch_tl); ! if (do_ask) deleted_lines(lnum, nmatch_tl); --lnum; line2 -= nmatch_tl; /* nr of lines decreases */ --- 5726,5732 ---- ml_delete(lnum, (int)FALSE); mark_adjust(lnum, lnum + nmatch_tl - 1, (long)MAXLNUM, -nmatch_tl); ! if (subflags.do_ask) deleted_lines(lnum, nmatch_tl); --lnum; line2 -= nmatch_tl; /* nr of lines decreases */ *************** *** 5717,5723 **** /* When asking, undo is saved each time, must also set * changed flag each time. */ ! if (do_ask) changed_bytes(lnum, 0); else { --- 5735,5741 ---- /* When asking, undo is saved each time, must also set * changed flag each time. */ ! if (subflags.do_ask) changed_bytes(lnum, 0); else { *************** *** 5779,5785 **** vim_free(sub_firstline); /* may have to free allocated copy of the line */ /* ":s/pat//n" doesn't move the cursor */ ! if (do_count) curwin->w_cursor = old_cursor; if (sub_nsubs > start_nsubs) --- 5797,5803 ---- vim_free(sub_firstline); /* may have to free allocated copy of the line */ /* ":s/pat//n" doesn't move the cursor */ ! if (subflags.do_count) curwin->w_cursor = old_cursor; if (sub_nsubs > start_nsubs) *************** *** 5791,5810 **** if (!global_busy) { ! if (!do_ask) /* when interactive leave cursor on the match */ { if (endcolumn) coladvance((colnr_T)MAXCOL); else beginline(BL_WHITE | BL_FIX); } ! if (!do_sub_msg(do_count) && do_ask) MSG(""); } else global_need_beginline = TRUE; ! if (do_print) ! print_line(curwin->w_cursor.lnum, do_number, do_list); } else if (!global_busy) { --- 5809,5830 ---- if (!global_busy) { ! /* 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(subflags.do_count) && subflags.do_ask) MSG(""); } else global_need_beginline = TRUE; ! if (subflags.do_print) ! print_line(curwin->w_cursor.lnum, ! subflags.do_number, subflags.do_list); } else if (!global_busy) { *************** *** 5812,5823 **** EMSG(_(e_interr)); else if (got_match) /* did find something but nothing substituted */ MSG(""); ! else if (do_error) /* nothing found */ EMSG2(_(e_patnotf2), get_search_pat()); } #ifdef FEAT_FOLDING ! if (do_ask && hasAnyFolding(curwin)) /* Cursor position may require updating */ changed_window_setting(); #endif --- 5832,5843 ---- EMSG(_(e_interr)); else if (got_match) /* did find something but nothing substituted */ MSG(""); ! else if (subflags.do_error) /* nothing found */ EMSG2(_(e_patnotf2), get_search_pat()); } #ifdef FEAT_FOLDING ! if (subflags.do_ask && hasAnyFolding(curwin)) /* Cursor position may require updating */ changed_window_setting(); #endif *************** *** 5825,5832 **** vim_regfree(regmatch.regprog); /* Restore the flag values, they can be used for ":&&". */ ! do_all = save_do_all; ! do_ask = save_do_ask; } /* --- 5845,5852 ---- vim_regfree(regmatch.regprog); /* Restore the flag values, they can be used for ":&&". */ ! subflags.do_all = save_do_all; ! subflags.do_ask = save_do_ask; } /* *** ../vim-7.4.2218/src/testdir/test_regexp_latin.vim 2016-04-03 14:00:29.324148917 +0200 --- src/testdir/test_regexp_latin.vim 2016-08-16 20:56:37.128331249 +0200 *************** *** 30,32 **** --- 30,41 ---- 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 + *** ../vim-7.4.2218/src/version.c 2016-08-16 19:21:05.528394945 +0200 --- src/version.c 2016-08-16 21:03:20.316658751 +0200 *************** *** 765,766 **** --- 765,768 ---- { /* Add new patch number below this line */ + /**/ + 2219, /**/ -- How To Keep A Healthy Level Of Insanity: 12. Sing along at the opera. /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///