diff options
| author | James McCoy <jamessan@jamessan.com> | 2016-08-16 16:07:15 -0400 | 
|---|---|---|
| committer | James McCoy <jamessan@jamessan.com> | 2016-08-20 23:55:11 -0400 | 
| commit | c7e6b58012cc5cad8f1cb02876883cad7fcbbdeb (patch) | |
| tree | 338ebfb5ff87d343384eb08bc70e8b12d28e764e /src | |
| parent | 35ddcc5bb484d461e6381d9ddbdf0438446f1299 (diff) | |
| download | rneovim-c7e6b58012cc5cad8f1cb02876883cad7fcbbdeb.tar.gz rneovim-c7e6b58012cc5cad8f1cb02876883cad7fcbbdeb.tar.bz2 rneovim-c7e6b58012cc5cad8f1cb02876883cad7fcbbdeb.zip  | |
vim-patch:7.4.2219
Problem:    Recursive call to substitute gets stuck in sandbox. (Nikolai
            Pavlov)
Solution:   Handle the recursive call. (Christian Brabandt, closes vim/vim#950)
            Add a test.
https://github.com/vim/vim/commit/f5a39447a8ebe162ee62caa2ee502cd0e65eecaa
Closes #5118
Diffstat (limited to 'src')
| -rw-r--r-- | src/nvim/ex_cmds.c | 160 | ||||
| -rw-r--r-- | src/nvim/testdir/test_regexp_utf8.vim | 8 | ||||
| -rw-r--r-- | src/nvim/version.c | 1 | 
3 files changed, 102 insertions, 67 deletions
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 5de9ac0523..bae0ab6862 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -2878,6 +2878,25 @@ static SubReplacementString old_sub = {NULL, 0, NULL};  static int global_need_beginline;       // call beginline() after ":g" +/// Case matching style to use for :substitute +typedef enum { +  kSubHonorOptions = 0,  ///< Honor the user's 'ignorecase'/'smartcase' options +  kSubIgnoreCase,        ///< Ignore case of the search +  kSubMatchCase,         ///< Match case of the search +} SubIgnoreType; + +/// Flags kept between calls to :substitute. +typedef struct { +  bool do_all;          ///< do multiple substitutions per line +  bool do_ask;          ///< ask for confirmation +  bool do_count;        ///< count only +  bool do_error;        ///< if false, ignore errors +  bool do_print;        ///< print last line with subs +  bool do_list;         ///< list last line with subs +  bool do_number;       ///< list last line with line nr +  SubIgnoreType do_ic;  ///< ignore case flag +} subflags_T; +  /// Get old substitute replacement string  ///  /// @param[out]  ret_sub    Location where old string will be saved. @@ -2915,16 +2934,18 @@ void 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 bool 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 +  static subflags_T subflags = { +    .do_all = false, +    .do_ask = false, +    .do_count = false, +    .do_error = true, +    .do_print = false, +    .do_list = false, +    .do_number = false, +    .do_ic = kSubHonorOptions +  }; +  bool save_do_all;                      // remember user specified 'g' flag +  bool save_do_ask;                      // remember user specified 'c' flag    char_u      *pat = NULL, *sub = NULL;         /* init for GCC */    int delimiter;    int sublen; @@ -3075,57 +3096,55 @@ void do_sub(exarg_T *eap)    if (*cmd == '&') {      ++cmd;    } else { -    // default is global on -    do_all = p_gd ? TRUE : FALSE; - -    do_ask = FALSE; -    do_error = TRUE; -    do_print = FALSE; -    do_count = false; -    do_number = FALSE; -    do_ic = 0; +    subflags.do_all = p_gd; +    subflags.do_ask = false; +    subflags.do_error = true; +    subflags.do_print = false; +    subflags.do_count = false; +    subflags.do_number = false; +    subflags.do_ic = kSubHonorOptions;    }    while (*cmd) {      // Note that 'g' and 'c' are always inverted.      // '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 = kSubIgnoreCase;      else if (*cmd == 'I')           /* don't ignore case */ -      do_ic = 'I'; +      subflags.do_ic = kSubMatchCase;      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    cmd = skipwhite(cmd);    if (ascii_isdigit(*cmd)) {      i = getdigits_long(&cmd); -    if (i <= 0 && !eap->skip && do_error) { +    if (i <= 0 && !eap->skip && subflags.do_error) {        EMSG(_(e_zerocount));        return;      } @@ -3150,7 +3169,7 @@ void do_sub(exarg_T *eap)    if (eap->skip)            /* not executing commands, only parsing */      return; -  if (!do_count && !MODIFIABLE(curbuf)) { +  if (!subflags.do_count && !MODIFIABLE(curbuf)) {      /* Substitution is not allowed in non-'modifiable' buffer */      EMSG(_(e_modifiable));      return; @@ -3158,15 +3177,15 @@ void 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 == kSubIgnoreCase)      regmatch.rmm_ic = TRUE; -  else if (do_ic == 'I') +  else if (subflags.do_ic == kSubMatchCase)      regmatch.rmm_ic = FALSE;    sub_firstline = NULL; @@ -3261,9 +3280,9 @@ void do_sub(exarg_T *eap)        /*         * Loop until nothing more to replace in this line.         * 1. Handle match with empty string. -       * 2. If do_ask is set, ask for confirmation. +       * 2. If subflags.do_ask is set, ask for confirmation.         * 3. substitute the string. -       * 4. if do_all is set, find next match +       * 4. if subflags.do_all is set, find next match         * 5. break if there isn't another match in this line         */        for (;; ) { @@ -3315,10 +3334,10 @@ void do_sub(exarg_T *eap)          prev_matchcol = matchcol;          /* -         * 2. If do_count is set only increase the counter. +         * 2. If subflags.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             * we continue looking for a match on the next line. @@ -3336,7 +3355,7 @@ void do_sub(exarg_T *eap)              goto skip;          } -        if (do_ask) { +        if (subflags.do_ask) {            int typed = 0;            /* change State to CONFIRM, so that the mouse works @@ -3354,17 +3373,17 @@ void 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;                  ec += numw; @@ -3469,12 +3488,12 @@ void do_sub(exarg_T *eap)                break;              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;              }              if (typed == Ctrl_E) @@ -3510,19 +3529,25 @@ void do_sub(exarg_T *eap)          /*           * 3. substitute the string.           */ -        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_T subflags_save = subflags;          /* get length of substitution part */          sublen = vim_regsub_multi(®match,              sub_firstlnum - regmatch.startpos[0].lnum,              sub, sub_firstline, FALSE, p_magic, TRUE); -        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;          } @@ -3599,7 +3624,7 @@ void 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. */ @@ -3630,7 +3655,7 @@ void 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 {                  if (first_line == 0) @@ -3652,7 +3677,7 @@ void do_sub(exarg_T *eap)          }          /* -         * 4. If do_all is set, find next match. +         * 4. If subflags.do_all is set, find next match.           * Prevent endless loop with patterns that match empty           * strings, e.g. :s/$/pat/g or :s/[a-z]* /(&)/g.           * But ":s/\n/#/" is OK. @@ -3667,7 +3692,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; @@ -3718,7 +3743,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 */ @@ -3727,7 +3752,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 {                if (first_line == 0) @@ -3785,7 +3810,7 @@ skip:    xfree(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) { @@ -3795,28 +3820,29 @@ skip:      curbuf->b_op_start.col = curbuf->b_op_end.col = 0;      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) {      if (got_int)                /* interrupted */        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());    } -  if (do_ask && hasAnyFolding(curwin)) { +  if (subflags.do_ask && hasAnyFolding(curwin)) {      // Cursor position may require updating      changed_window_setting();    } @@ -3824,8 +3850,8 @@ skip:    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/nvim/testdir/test_regexp_utf8.vim b/src/nvim/testdir/test_regexp_utf8.vim index e1b1c2a9fb..38f9ed41d5 100644 --- a/src/nvim/testdir/test_regexp_utf8.vim +++ b/src/nvim/testdir/test_regexp_utf8.vim @@ -31,3 +31,11 @@ func Test_equivalence_re2()    call s:equivalence_test()    set re=0  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/nvim/version.c b/src/nvim/version.c index 9e3cea069c..978bc2e228 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -75,6 +75,7 @@ static char *features[] = {  // clang-format off  static int included_patches[] = { +  2219,    // 2200,    // 2199,    // 2198,  | 
