diff options
author | Sean Dewar <seandewar@users.noreply.github.com> | 2021-09-15 13:17:07 +0100 |
---|---|---|
committer | Sean Dewar <seandewar@users.noreply.github.com> | 2021-09-15 13:34:59 +0100 |
commit | cf62554e5aeac5e9417cafdd1fe4cfb8c48d08ba (patch) | |
tree | 7ed2995621a61974bea3086bd9a5ac576cbb23cb /src | |
parent | 6436100b6e4baeee8685677ae4319e71580caa3c (diff) | |
download | rneovim-cf62554e5aeac5e9417cafdd1fe4cfb8c48d08ba.tar.gz rneovim-cf62554e5aeac5e9417cafdd1fe4cfb8c48d08ba.tar.bz2 rneovim-cf62554e5aeac5e9417cafdd1fe4cfb8c48d08ba.zip |
backport: fix(:source): copy curbuf lines to memory before sourcing #15111
It's possible for weirdness to happen if curbuf is modified while
sourcing from it via :source (with no arguments). For example:
- Deleting lines from or wiping curbuf can cause internal error E315 to
be thrown from ml_get.
- Changing the curbuf to another buffer while sourcing can cause lines
from the new curbuf to then be sourced instead.
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/ex_cmds2.c | 104 |
1 files changed, 40 insertions, 64 deletions
diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 9eaa57a652..d9047d7a5a 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -2666,72 +2666,11 @@ static bool concat_continued_line(garray_T *const ga, const int init_growsize, return true; } -/// Concatenate (possibly many) VimL lines starting with line continuations into -/// a growarray. @see concat_continued_line -/// -/// @note All parameters, excluding `ga`, accept expressions that are evaluated -/// once for each line; side-effects may be triggered many times! -/// -/// @param ga the growarray to append to -/// @param cond should evaluate to true if a next line exists -/// @param line should evaluate to the current line -/// @param next should handle fetching the next line when evaluated -#define CONCAT_CONTINUED_LINES(ga, cond, line, next) \ - do { \ - garray_T *const ga_ = (ga); \ - const int init_growsize_ = ga_->ga_growsize; \ - for (; (cond); (next)) { \ - const char_u *const line_ = (line); \ - if (!concat_continued_line(ga_, init_growsize_, line_, STRLEN(line_))) { \ - break; \ - } \ - } \ - } while (false) - typedef struct { linenr_T curr_lnum; const linenr_T final_lnum; } GetBufferLineCookie; -/// Get one line from the current selection in the buffer. -/// Called by do_cmdline() when it's called from cmd_source_buffer(). -/// -/// @return pointer to allocated line, or NULL for end-of-file or -/// some error. -static char_u *get_buffer_line(int c, void *cookie, int indent, bool do_concat) -{ - GetBufferLineCookie *p = cookie; - if (p->curr_lnum > p->final_lnum) { - return NULL; - } - garray_T ga; - ga_init(&ga, sizeof(char_u), 400); - ga_concat(&ga, ml_get(p->curr_lnum++)); - if (do_concat && vim_strchr(p_cpo, CPO_CONCAT) == NULL) { - CONCAT_CONTINUED_LINES(&ga, p->curr_lnum <= p->final_lnum, - ml_get(p->curr_lnum), p->curr_lnum++); - } - ga_append(&ga, NUL); - return ga.ga_data; -} - -static void cmd_source_buffer(const exarg_T *eap) - FUNC_ATTR_NONNULL_ALL -{ - GetBufferLineCookie cookie = { - .curr_lnum = eap->line1, - .final_lnum = eap->line2, - }; - if (curbuf != NULL && curbuf->b_fname - && path_with_extension((const char *)curbuf->b_fname, "lua")) { - nlua_source_using_linegetter(get_buffer_line, (void *)&cookie, - ":source (no file)"); - } else { - source_using_linegetter((void *)&cookie, get_buffer_line, - ":source (no file)"); - } -} - /// ":source" and associated commands. /// /// @return address holding the next breakpoint line for a source cookie @@ -2839,6 +2778,40 @@ static int source_using_linegetter(void *cookie, return retval; } +static void cmd_source_buffer(const exarg_T *const eap) + FUNC_ATTR_NONNULL_ALL +{ + if (curbuf == NULL) { + return; + } + garray_T ga; + ga_init(&ga, sizeof(char_u), 400); + const linenr_T final_lnum = eap->line2; + // Copy the contents to be executed. + for (linenr_T curr_lnum = eap->line1; curr_lnum <= final_lnum; curr_lnum++) { + // Adjust growsize to current length to speed up concatenating many lines. + if (ga.ga_len > 400) { + ga_set_growsize(&ga, MAX(ga.ga_len, 8000)); + } + ga_concat(&ga, ml_get(curr_lnum)); + ga_append(&ga, NL); + } + ((char_u *)ga.ga_data)[ga.ga_len - 1] = NUL; + const GetStrLineCookie cookie = { + .buf = ga.ga_data, + .offset = 0, + }; + if (curbuf->b_fname + && path_with_extension((const char *)curbuf->b_fname, "lua")) { + nlua_source_using_linegetter(get_str_line, (void *)&cookie, + ":source (no file)"); + } else { + source_using_linegetter((void *)&cookie, get_str_line, + ":source (no file)"); + } + ga_clear(&ga); +} + /// Executes lines in `src` as Ex commands. /// /// @see do_source() @@ -3296,9 +3269,12 @@ char_u *getsourceline(int c, void *cookie, int indent, bool do_concat) ga_init(&ga, (int)sizeof(char_u), 400); ga_concat(&ga, line); - CONCAT_CONTINUED_LINES(&ga, sp->nextline != NULL, sp->nextline, - (xfree(sp->nextline), - sp->nextline = get_one_sourceline(sp))); + while (sp->nextline != NULL + && concat_continued_line(&ga, 400, sp->nextline, + STRLEN(sp->nextline))) { + xfree(sp->nextline); + sp->nextline = get_one_sourceline(sp); + } ga_append(&ga, NUL); xfree(line); line = ga.ga_data; |