aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSean Dewar <seandewar@users.noreply.github.com>2021-09-15 13:17:07 +0100
committerSean Dewar <seandewar@users.noreply.github.com>2021-09-15 13:34:59 +0100
commitcf62554e5aeac5e9417cafdd1fe4cfb8c48d08ba (patch)
tree7ed2995621a61974bea3086bd9a5ac576cbb23cb /src
parent6436100b6e4baeee8685677ae4319e71580caa3c (diff)
downloadrneovim-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.c104
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;