From 71223e2db87c2bf3b09aecb46266b56cda26191d Mon Sep 17 00:00:00 2001
From: Bram Moolenaar <Bram@vim.org>
Date: Mon, 30 May 2022 15:23:09 +0100
Subject: [PATCH] patch 8.2.5043: can open a cmdline window from a substitute
 expression

Problem:    Can open a cmdline window from a substitute expression.
Solution:   Disallow opening a command line window when text or buffer is
            locked.

---
 src/buffer.c                    |  7 +------
 src/ex_getln.c                  | 19 +++++++++++++++++++
 src/proto/ex_getln.pro          |  1 +
 src/testdir/test_substitute.vim | 24 ++++++++++++++++++++++++
 src/window.c                    |  5 +----
 5 files changed, 46 insertions(+), 10 deletions(-)

diff --git a/src/buffer.c b/src/buffer.c
index 88094ee..8fabbdb 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -2364,12 +2364,7 @@ buflist_getfile(
     if (buf == curbuf)
 	return OK;
 
-    if (text_locked())
-    {
-	text_locked_msg();
-	return FAIL;
-    }
-    if (curbuf_locked())
+    if (text_or_buf_locked())
 	return FAIL;
 
     // altfpos may be changed by getfile(), get it now
diff --git a/src/ex_getln.c b/src/ex_getln.c
index 64b393d..d5fc38d 100644
--- a/src/ex_getln.c
+++ b/src/ex_getln.c
@@ -2588,6 +2588,21 @@ text_locked(void)
     return text_and_win_locked() || textlock != 0;
 }
 
+/*
+ * Check for text, window or buffer locked.
+ * Give an error message and return TRUE if something is locked.
+ */
+    int
+text_or_buf_locked(void)
+{
+    if (text_locked())
+    {
+	text_locked_msg();
+	return TRUE;
+    }
+    return curbuf_locked();
+}
+
 /*
  * Check if "curbuf_lock" or "allbuf_lock" is set and return TRUE when it is
  * and give an error message.
@@ -4170,6 +4185,10 @@ open_cmdwin(void)
     int			save_KeyTyped;
 #endif
 
+    // Can't do this when text or buffer is locked.
+    if (text_or_buf_locked())
+	return K_IGNORE;
+
     // Can't do this recursively.  Can't do it when typing a password.
     if (cmdwin_type != 0
 # if defined(FEAT_CRYPT) || defined(FEAT_EVAL)
diff --git a/src/proto/ex_getln.pro b/src/proto/ex_getln.pro
index f64bb1f..7597457 100644
--- a/src/proto/ex_getln.pro
+++ b/src/proto/ex_getln.pro
@@ -7,6 +7,7 @@ int text_and_win_locked(void);
 void text_locked_msg(void);
 char *get_text_locked_msg(void);
 int text_locked(void);
+int text_or_buf_locked(void);
 int curbuf_locked(void);
 int allbuf_locked(void);
 char_u *getexline(int c, void *cookie, int indent, int do_concat);
diff --git a/src/testdir/test_substitute.vim b/src/testdir/test_substitute.vim
index bda96f6..ebc0839 100644
--- a/src/testdir/test_substitute.vim
+++ b/src/testdir/test_substitute.vim
@@ -775,3 +775,27 @@ func Test_sub_change_window()
   delfunc Repl
 endfunc
 
+" This was opening a command line window from the expression
+func Test_sub_open_cmdline_win()
+  " the error only happens in a very specific setup, run a new Vim instance to
+  " get a clean starting point.
+  let lines =<< trim [SCRIPT]
+    norm o0000000000000000000000000000000000000000000000000000
+    func Replace()
+      norm q/
+    endfunc
+    s/\%')/\=Replace()
+    redir >Xresult
+    messages
+    redir END
+    qall!
+  [SCRIPT]
+  call writefile(lines, 'Xscript')
+  if RunVim([], [], '-u NONE -S Xscript')
+    let messages = readfile('Xresult')
+    call assert_match('E565: Not allowed to change text or change window', messages[3])
+  endif
+
+  call delete('Xscript')
+  call delete('Xresult')
+endfunc
diff --git a/src/window.c b/src/window.c
index 0a154b0..d8091f9 100644
--- a/src/window.c
+++ b/src/window.c
@@ -4343,14 +4343,11 @@ win_goto(win_T *wp)
 
     if (ERROR_IF_POPUP_WINDOW)
 	return;
-    if (text_and_win_locked())
+    if (text_or_buf_locked())
     {
 	beep_flush();
-	text_locked_msg();
 	return;
     }
-    if (curbuf_locked())
-	return;
 
     if (wp->w_buffer != curbuf)
 	reset_VIsual_and_resel();
-- 
2.27.0

