aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFamiu Haque <famiuhaque@protonmail.com>2022-06-28 11:00:05 +0600
committerGitHub <noreply@github.com>2022-06-28 13:00:05 +0800
commit7e1cf6b7642f0ab14656d853d44f4409b2987b9c (patch)
treec3f05b626f2d3e57da42f2d19dc713096246ec1f
parent274609a109fe7d4a72107e45fd1f13c2066a3663 (diff)
downloadrneovim-7e1cf6b7642f0ab14656d853d44f4409b2987b9c.tar.gz
rneovim-7e1cf6b7642f0ab14656d853d44f4409b2987b9c.tar.bz2
rneovim-7e1cf6b7642f0ab14656d853d44f4409b2987b9c.zip
fix(inccommand): parse the command to check if it is previewable
Free regprog if command isn't previewable Co-authored-by: zeertzjq <zeertzjq@outlook.com>
-rw-r--r--src/nvim/api/command.c2
-rw-r--r--src/nvim/ex_docmd.c41
-rw-r--r--src/nvim/ex_getln.c28
-rw-r--r--test/functional/ui/inccommand_spec.lua13
4 files changed, 34 insertions, 50 deletions
diff --git a/src/nvim/api/command.c b/src/nvim/api/command.c
index 141e1256ff..2b2fb446a0 100644
--- a/src/nvim/api/command.c
+++ b/src/nvim/api/command.c
@@ -13,6 +13,7 @@
#include "nvim/ex_docmd.h"
#include "nvim/lua/executor.h"
#include "nvim/ops.h"
+#include "nvim/regexp.h"
#include "nvim/window.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
@@ -94,6 +95,7 @@ Dictionary nvim_parse_cmd(String str, Dictionary opts, Error *err)
}
goto end;
}
+ vim_regfree(cmdinfo.cmdmod.cmod_filter_regmatch.regprog);
// Parse arguments
Array args = ARRAY_DICT_INIT;
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 00aadc2af5..88177533ab 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -1405,6 +1405,8 @@ bool is_cmd_ni(cmdidx_T cmdidx)
}
/// Parse command line and return information about the first command.
+/// If parsing is done successfully, need to free cmod_filter_regmatch.regprog after calling,
+/// usually done using undo_cmdmod() or execute_cmd().
///
/// @param cmdline Command line string
/// @param[out] eap Ex command arguments
@@ -1432,6 +1434,7 @@ bool parse_cmdline(char *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo, char **er
// Parse command modifiers
if (parse_command_modifiers(eap, errormsg, &cmdinfo->cmdmod, false) == FAIL) {
+ vim_regfree(cmdinfo->cmdmod.cmod_filter_regmatch.regprog);
return false;
}
after_modifier = eap->cmd;
@@ -10040,44 +10043,6 @@ static void ex_terminal(exarg_T *eap)
do_cmdline_cmd(ex_cmd);
}
-/// Checks if `cmd` is "previewable" (i.e. supported by 'inccommand').
-///
-/// @param[in] cmd Commandline to check. May start with a range or modifier.
-///
-/// @return true if `cmd` is previewable
-bool cmd_can_preview(char *cmd)
-{
- if (cmd == NULL) {
- return false;
- }
-
- // Ignore additional colons at the start...
- cmd = skip_colon_white(cmd, true);
-
- // Ignore any leading modifiers (:keeppatterns, :verbose, etc.)
- for (int len = modifier_len(cmd); len != 0; len = modifier_len(cmd)) {
- cmd += len;
- cmd = skip_colon_white(cmd, true);
- }
-
- exarg_T ea;
- memset(&ea, 0, sizeof(ea));
- // parse the command line
- ea.cmd = skip_range(cmd, NULL);
- if (*ea.cmd == '*') {
- ea.cmd = skipwhite(ea.cmd + 1);
- }
-
- if (find_ex_command(&ea, NULL) == NULL || ea.cmdidx == CMD_SIZE) {
- return false;
- } else if (!IS_USER_CMDIDX(ea.cmdidx)) {
- // find_ex_command sets the flags for user commands automatically
- ea.argt = cmdnames[(int)ea.cmdidx].cmd_argt;
- }
-
- return (ea.argt & EX_PREVIEW);
-}
-
/// Gets a map of maps describing user-commands defined for buffer `buf` or
/// defined globally if `buf` is NULL.
///
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index e442c907b3..420f3f73af 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -2372,7 +2372,7 @@ static void cmdpreview_close_win(void)
}
}
-/// Show 'inccommand' preview. It works like this:
+/// Show 'inccommand' preview if command is previewable. It works like this:
/// 1. Store current undo information so we can revert to current state later.
/// 2. Execute the preview callback with the parsed command, preview buffer number and preview
/// namespace number as arguments. The preview callback sets the highlight and does the
@@ -2385,12 +2385,15 @@ static void cmdpreview_close_win(void)
/// 5. If the return value of the preview callback is not 0, update the screen while the effects
/// of the preview are still in place.
/// 6. Revert all changes made by the preview callback.
-static void cmdpreview_show(CommandLineState *s)
+///
+/// @return whether preview is shown or not.
+static bool cmdpreview_may_show(CommandLineState *s)
{
// Parse the command line and return if it fails.
exarg_T ea;
CmdParseInfo cmdinfo;
// Copy the command line so we can modify it.
+ int cmdpreview_type = 0;
char *cmdline = xstrdup((char *)ccline.cmdbuff);
char *errormsg = NULL;
emsg_off++; // Block errors when parsing the command line, and don't update v:errmsg
@@ -2400,6 +2403,12 @@ static void cmdpreview_show(CommandLineState *s)
}
emsg_off--;
+ // Check if command is previewable, if not, don't attempt to show preview
+ if (!(ea.argt & EX_PREVIEW)) {
+ vim_regfree(cmdinfo.cmdmod.cmod_filter_regmatch.regprog);
+ goto end;
+ }
+
// Swap invalid command range if needed
if ((ea.argt & EX_RANGE) && ea.line1 > ea.line2) {
linenr_T lnum = ea.line1;
@@ -2454,7 +2463,7 @@ static void cmdpreview_show(CommandLineState *s)
// the preview.
Error err = ERROR_INIT;
try_start();
- int cmdpreview_type = execute_cmd(&ea, &cmdinfo, true);
+ cmdpreview_type = execute_cmd(&ea, &cmdinfo, true);
if (try_end(&err)) {
api_clear_error(&err);
cmdpreview_type = 0;
@@ -2518,14 +2527,9 @@ static void cmdpreview_show(CommandLineState *s)
update_topline(curwin);
redrawcmdline();
-
- // If preview callback returned 0, update screen to clear remnants of an earlier preview.
- if (cmdpreview_type == 0) {
- cmdpreview = false;
- update_screen(SOME_VALID);
- }
end:
xfree(cmdline);
+ return cmdpreview_type != 0;
}
static int command_line_changed(CommandLineState *s)
@@ -2564,9 +2568,9 @@ static int command_line_changed(CommandLineState *s)
&& *p_icm != NUL // 'inccommand' is set
&& curbuf->b_p_ma // buffer is modifiable
&& cmdline_star == 0 // not typing a password
- && cmd_can_preview((char *)ccline.cmdbuff)
- && !vpeekc_any()) {
- cmdpreview_show(s);
+ && !vpeekc_any()
+ && cmdpreview_may_show(s)) {
+ // 'inccommand' preview has been shown.
} else if (cmdpreview) {
cmdpreview = false;
update_screen(SOME_VALID); // Clear 'inccommand' preview.
diff --git a/test/functional/ui/inccommand_spec.lua b/test/functional/ui/inccommand_spec.lua
index 7a42e72732..4286446af7 100644
--- a/test/functional/ui/inccommand_spec.lua
+++ b/test/functional/ui/inccommand_spec.lua
@@ -3008,3 +3008,16 @@ it('long :%s/ with inccommand does not collapse cmdline', function()
AAAAAAA^ |
]])
end)
+
+it("with 'inccommand' typing :filter doesn't segfault or leak memory #19057", function()
+ clear()
+ common_setup(nil, 'nosplit')
+ feed(':filter s')
+ assert_alive()
+ feed(' ')
+ assert_alive()
+ feed('h')
+ assert_alive()
+ feed('i')
+ assert_alive()
+end)