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, &regmatch) == 
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, &regmatch) == 
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(&regmatch,
                                    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(&regmatch,
                                    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 -- [email protected] -- 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    ///

-- 
-- 
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].
For more options, visit https://groups.google.com/d/optout.

Raspunde prin e-mail lui