]> granicus.if.org Git - vim/commitdiff
patch 7.4.2219 v7.4.2219
authorBram Moolenaar <Bram@vim.org>
Tue, 16 Aug 2016 19:04:41 +0000 (21:04 +0200)
committerBram Moolenaar <Bram@vim.org>
Tue, 16 Aug 2016 19:04:41 +0000 (21:04 +0200)
Problem:    Recursive call to substitute gets stuck in sandbox. (Nikolai
            Pavlov)
Solution:   Handle the recursive call. (Christian Brabandt, closes #950)
            Add a test.

src/ex_cmds.c
src/testdir/test_regexp_latin.vim
src/version.c

index 30b0d5c313057452e31711831142876ef2ab7c3e..aeb2bf952d21f668a324326a999746b4ee985009 100644 (file)
@@ -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;
+               subflags.do_all = FALSE;
+           subflags.do_ask = FALSE;
        }
-       do_error = TRUE;
-       do_print = FALSE;
-       do_count = FALSE;
-       do_number = FALSE;
-       do_ic = 0;
+       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;
+    if (subflags.do_count)
+       subflags.do_ask = FALSE;
 
-    save_do_all = do_all;
-    save_do_ask = do_ask;
+    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, &regmatch) == 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(&regmatch,
                                    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;
 }
 
 /*
index 8528412806c82f4d7f43f15067a7facf2dc518bb..ea3b2e73920b09c7cb7607fa05a18d1412e01157 100644 (file)
@@ -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
+
index bae215378a6a1bf894e9aa5f10f08d4e1f242c22..505231fec68ebb5ad6c8282c727909e9c6267c4e 100644 (file)
@@ -763,6 +763,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    2219,
 /**/
     2218,
 /**/