diff options
author | Famiu Haque <famiuhaque@protonmail.com> | 2022-06-28 11:00:05 +0600 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-06-28 13:00:05 +0800 |
commit | 7e1cf6b7642f0ab14656d853d44f4409b2987b9c (patch) | |
tree | c3f05b626f2d3e57da42f2d19dc713096246ec1f | |
parent | 274609a109fe7d4a72107e45fd1f13c2066a3663 (diff) | |
download | rneovim-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.c | 2 | ||||
-rw-r--r-- | src/nvim/ex_docmd.c | 41 | ||||
-rw-r--r-- | src/nvim/ex_getln.c | 28 | ||||
-rw-r--r-- | test/functional/ui/inccommand_spec.lua | 13 |
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) |