diff options
Diffstat (limited to 'src/nvim/ex_docmd.c')
| -rw-r--r-- | src/nvim/ex_docmd.c | 974 |
1 files changed, 557 insertions, 417 deletions
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index a24e8458a6..0b466bbe4e 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -1,6 +1,3 @@ -// This is an open source non-commercial project. Dear PVS-Studio, please check -// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - // ex_docmd.c: functions for executing an Ex command line. #include <assert.h> @@ -12,15 +9,18 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <sys/types.h> #include "auto/config.h" #include "nvim/arglist.h" -#include "nvim/ascii.h" +#include "nvim/ascii_defs.h" #include "nvim/autocmd.h" #include "nvim/buffer.h" +#include "nvim/buffer_defs.h" #include "nvim/change.h" #include "nvim/charset.h" #include "nvim/cmdexpand.h" +#include "nvim/cmdexpand_defs.h" #include "nvim/cursor.h" #include "nvim/debugger.h" #include "nvim/digraph.h" @@ -28,7 +28,6 @@ #include "nvim/edit.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" -#include "nvim/eval/typval_defs.h" #include "nvim/eval/userfunc.h" #include "nvim/event/loop.h" #include "nvim/ex_cmds.h" @@ -40,19 +39,20 @@ #include "nvim/file_search.h" #include "nvim/fileio.h" #include "nvim/fold.h" +#include "nvim/func_attr.h" #include "nvim/garray.h" #include "nvim/getchar.h" #include "nvim/gettext.h" #include "nvim/globals.h" -#include "nvim/highlight_defs.h" +#include "nvim/highlight.h" #include "nvim/highlight_group.h" #include "nvim/input.h" #include "nvim/keycodes.h" -#include "nvim/macros.h" +#include "nvim/lua/executor.h" +#include "nvim/macros_defs.h" #include "nvim/main.h" #include "nvim/mark.h" #include "nvim/mbyte.h" -#include "nvim/memfile_defs.h" #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" @@ -61,40 +61,52 @@ #include "nvim/normal.h" #include "nvim/ops.h" #include "nvim/option.h" +#include "nvim/option_vars.h" #include "nvim/optionstr.h" +#include "nvim/os/fs.h" #include "nvim/os/input.h" #include "nvim/os/os.h" #include "nvim/os/shell.h" #include "nvim/path.h" #include "nvim/popupmenu.h" -#include "nvim/pos.h" +#include "nvim/pos_defs.h" #include "nvim/profile.h" #include "nvim/quickfix.h" #include "nvim/regexp.h" #include "nvim/runtime.h" -#include "nvim/screen.h" #include "nvim/search.h" #include "nvim/shada.h" #include "nvim/state.h" #include "nvim/statusline.h" #include "nvim/strings.h" #include "nvim/tag.h" -#include "nvim/types.h" +#include "nvim/types_defs.h" #include "nvim/ui.h" #include "nvim/undo.h" #include "nvim/usercmd.h" -#include "nvim/vim.h" +#include "nvim/vim_defs.h" #include "nvim/window.h" +#include "nvim/winfloat.h" -static char e_ambiguous_use_of_user_defined_command[] +static const char e_ambiguous_use_of_user_defined_command[] = N_("E464: Ambiguous use of user-defined command"); -static char e_not_an_editor_command[] +static const char e_no_call_stack_to_substitute_for_stack[] + = N_("E489: No call stack to substitute for \"<stack>\""); +static const char e_not_an_editor_command[] = N_("E492: Not an editor command"); -static char e_no_source_file_name_to_substitute_for_sfile[] - = N_("E498: no :source file name to substitute for \"<sfile>\""); -static char e_no_call_stack_to_substitute_for_stack[] - = N_("E489: no call stack to substitute for \"<stack>\""); -static char e_no_script_file_name_to_substitute_for_script[] +static const char e_no_autocommand_file_name_to_substitute_for_afile[] + = N_("E495: No autocommand file name to substitute for \"<afile>\""); +static const char e_no_autocommand_buffer_number_to_substitute_for_abuf[] + = N_("E496: No autocommand buffer number to substitute for \"<abuf>\""); +static const char e_no_autocommand_match_name_to_substitute_for_amatch[] + = N_("E497: No autocommand match name to substitute for \"<amatch>\""); +static const char e_no_source_file_name_to_substitute_for_sfile[] + = N_("E498: No :source file name to substitute for \"<sfile>\""); +static const char e_no_line_number_to_use_for_slnum[] + = N_("E842: No line number to use for \"<slnum>\""); +static const char e_no_line_number_to_use_for_sflnum[] + = N_("E961: No line number to use for \"<sflnum>\""); +static const char e_no_script_file_name_to_substitute_for_script[] = N_("E1274: No script file name to substitute for \"<script>\""); static int quitmore = 0; @@ -139,10 +151,6 @@ struct dbg_stuff { # include "ex_docmd.c.generated.h" #endif -#ifndef HAVE_WORKING_LIBINTL -# define ex_language ex_ni -#endif - // Declare cmdnames[]. #ifdef INCLUDE_GENERATED_DECLARATIONS # include "ex_cmds_defs.generated.h" @@ -152,19 +160,28 @@ static char dollar_command[2] = { '$', 0 }; static void save_dbg_stuff(struct dbg_stuff *dsp) { - dsp->trylevel = trylevel; trylevel = 0; - dsp->force_abort = force_abort; force_abort = false; - dsp->caught_stack = caught_stack; caught_stack = NULL; - dsp->vv_exception = v_exception(NULL); - dsp->vv_throwpoint = v_throwpoint(NULL); + dsp->trylevel = trylevel; + trylevel = 0; + dsp->force_abort = force_abort; + force_abort = false; + dsp->caught_stack = caught_stack; + caught_stack = NULL; + dsp->vv_exception = v_exception(NULL); + dsp->vv_throwpoint = v_throwpoint(NULL); // Necessary for debugging an inactive ":catch", ":finally", ":endtry". - dsp->did_emsg = did_emsg; did_emsg = false; - dsp->got_int = got_int; got_int = false; - dsp->did_throw = did_throw; did_throw = false; - dsp->need_rethrow = need_rethrow; need_rethrow = false; - dsp->check_cstack = check_cstack; check_cstack = false; - dsp->current_exception = current_exception; current_exception = NULL; + dsp->did_emsg = did_emsg; + did_emsg = false; + dsp->got_int = got_int; + got_int = false; + dsp->did_throw = did_throw; + did_throw = false; + dsp->need_rethrow = need_rethrow; + need_rethrow = false; + dsp->check_cstack = check_cstack; + check_cstack = false; + dsp->current_exception = current_exception; + current_exception = NULL; } static void restore_dbg_stuff(struct dbg_stuff *dsp) @@ -200,7 +217,7 @@ void do_exmode(void) RedrawingDisabled++; // don't redisplay the window no_wait_return++; // don't wait for return - msg(_("Entering Ex mode. Type \"visual\" to go to Normal mode.")); + msg(_("Entering Ex mode. Type \"visual\" to go to Normal mode."), 0); while (exmode_active) { // Check for a ":normal" command and no more characters left. if (ex_normal_busy > 0 && typebuf.tb_len == 0) { @@ -221,7 +238,7 @@ void do_exmode(void) if ((prev_line != curwin->w_cursor.lnum || changedtick != buf_get_changedtick(curbuf)) && !ex_no_reprint) { if (curbuf->b_ml.ml_flags & ML_EMPTY) { - emsg(_(e_emptybuf)); + emsg(_(e_empty_buffer)); } else { if (ex_pressedreturn) { // Make sure the message overwrites the right line and isn't throttled. @@ -239,7 +256,7 @@ void do_exmode(void) } } else if (ex_pressedreturn && !ex_no_reprint) { // must be at EOF if (curbuf->b_ml.ml_flags & ML_EMPTY) { - emsg(_(e_emptybuf)); + emsg(_(e_empty_buffer)); } else { emsg(_("E501: At end-of-file")); } @@ -264,9 +281,9 @@ static void msg_verbose_cmd(linenr_T lnum, char *cmd) verbose_enter_scroll(); if (lnum == 0) { - smsg(_("Executing: %s"), cmd); + smsg(0, _("Executing: %s"), cmd); } else { - smsg(_("line %" PRIdLINENR ": %s"), lnum, cmd); + smsg(0, _("line %" PRIdLINENR ": %s"), lnum, cmd); } if (msg_silent == 0) { msg_puts("\n"); // don't overwrite this @@ -279,7 +296,7 @@ static void msg_verbose_cmd(linenr_T lnum, char *cmd) /// Execute a simple command line. Used for translated commands like "*". int do_cmdline_cmd(const char *cmd) { - return do_cmdline((char *)cmd, NULL, NULL, DOCMD_NOWAIT|DOCMD_KEYTYPED); + return do_cmdline((char *)cmd, NULL, NULL, DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_KEYTYPED); } /// do_cmdline(): execute one Ex command line @@ -320,16 +337,12 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags) linenr_T *breakpoint = NULL; // ptr to breakpoint field in cookie int *dbg_tick = NULL; // ptr to dbg_tick field in cookie struct dbg_stuff debug_saved; // saved things for debug mode - int initial_trylevel; - msglist_T **saved_msg_list = NULL; msglist_T *private_msg_list; // "fgetline" and "cookie" passed to do_one_cmd() char *(*cmd_getline)(int, void *, int, bool); void *cmd_cookie; struct loop_cookie cmd_loop_cookie; - void *real_cookie; - int getline_is_func; static int call_depth = 0; // recursiveness // For every pair of do_cmdline()/do_one_cmd() calls, use an extra memory @@ -338,7 +351,7 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags) // combine the messages stored by an earlier invocation of do_one_cmd() // with the command name of the later one. This would happen when // BufWritePost autocommands are executed after a write error. - saved_msg_list = msg_list; + msglist_T **saved_msg_list = msg_list; msg_list = &private_msg_list; private_msg_list = NULL; @@ -358,10 +371,10 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags) ga_init(&lines_ga, (int)sizeof(wcmd_T), 10); - real_cookie = getline_cookie(fgetline, cookie); + void *real_cookie = getline_cookie(fgetline, cookie); // Inside a function use a higher nesting level. - getline_is_func = getline_equal(fgetline, cookie, get_func_line); + bool getline_is_func = getline_equal(fgetline, cookie, get_func_line); if (getline_is_func && ex_nesting_level == func_level(real_cookie)) { ex_nesting_level++; } @@ -393,7 +406,7 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags) CLEAR_FIELD(debug_saved); } - initial_trylevel = trylevel; + int initial_trylevel = trylevel; // "did_throw" will be set to true when an exception is being thrown. did_throw = false; @@ -485,24 +498,6 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags) } } - if (cstack.cs_looplevel > 0) { - // Inside a while/for loop we need to store the lines and use them - // again. Pass a different "fgetline" function to do_one_cmd() - // below, so that it stores lines in or reads them from - // "lines_ga". Makes it possible to define a function inside a - // while/for loop. - cmd_getline = get_loop_line; - cmd_cookie = (void *)&cmd_loop_cookie; - cmd_loop_cookie.lines_gap = &lines_ga; - cmd_loop_cookie.current_line = current_line; - cmd_loop_cookie.getline = fgetline; - cmd_loop_cookie.cookie = cookie; - cmd_loop_cookie.repeating = (current_line < lines_ga.ga_len); - } else { - cmd_getline = fgetline; - cmd_cookie = cookie; - } - // 2. If no line given, get an allocated line with fgetline(). if (next_cmdline == NULL) { // Need to set msg_didout for the first line after an ":if", @@ -541,15 +536,37 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags) } cmdline_copy = next_cmdline; - // Save the current line when inside a ":while" or ":for", and when - // the command looks like a ":while" or ":for", because we may need it - // later. When there is a '|' and another command, it is stored - // separately, because we need to be able to jump back to it from an + int current_line_before = 0; + // Inside a while/for loop, and when the command looks like a ":while" + // or ":for", the line is stored, because we may need it later when + // looping. + // + // When there is a '|' and another command, it is stored separately, + // because we need to be able to jump back to it from an // :endwhile/:endfor. - if (current_line == lines_ga.ga_len - && (cstack.cs_looplevel || has_loop_cmd(next_cmdline))) { - store_loop_line(&lines_ga, next_cmdline); + // + // Pass a different "fgetline" function to do_one_cmd() below, + // that it stores lines in or reads them from "lines_ga". Makes it + // possible to define a function inside a while/for loop. + if ((cstack.cs_looplevel > 0 || has_loop_cmd(next_cmdline))) { + cmd_getline = get_loop_line; + cmd_cookie = (void *)&cmd_loop_cookie; + cmd_loop_cookie.lines_gap = &lines_ga; + cmd_loop_cookie.current_line = current_line; + cmd_loop_cookie.getline = fgetline; + cmd_loop_cookie.cookie = cookie; + cmd_loop_cookie.repeating = (current_line < lines_ga.ga_len); + + // Save the current line when encountering it the first time. + if (current_line == lines_ga.ga_len) { + store_loop_line(&lines_ga, next_cmdline); + } + current_line_before = current_line; + } else { + cmd_getline = fgetline; + cmd_cookie = cookie; } + did_endif = false; if (count++ == 0) { @@ -652,7 +669,7 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags) } else if (cstack.cs_lflags & CSL_HAD_LOOP) { // For a ":while" or ":for" we need to remember the line number. cstack.cs_lflags &= ~CSL_HAD_LOOP; - cstack.cs_line[cstack.cs_idx] = current_line - 1; + cstack.cs_line[cstack.cs_idx] = current_line_before; } } @@ -819,8 +836,8 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags) // Cleanup if "cs_emsg_silent_list" remains. if (cstack.cs_emsg_silent_list != NULL) { - eslist_T *elem, *temp; - for (elem = cstack.cs_emsg_silent_list; elem != NULL; elem = temp) { + eslist_T *temp; + for (eslist_T *elem = cstack.cs_emsg_silent_list; elem != NULL; elem = temp) { temp = elem->next; xfree(elem); } @@ -893,7 +910,7 @@ void handle_did_throw(void) if (messages != NULL) { do { msglist_T *next = messages->next; - emsg(messages->msg); + emsg_multiline(messages->msg, messages->multiline); xfree(messages->msg); xfree(messages->sfile); xfree(messages); @@ -919,7 +936,7 @@ static char *get_loop_line(int c, void *cookie, int indent, bool do_concat) char *line; // First time inside the ":while"/":for": get line normally. if (cp->getline == NULL) { - line = getcmdline(c, 0L, indent, do_concat); + line = getcmdline(c, 0, indent, do_concat); } else { line = cp->getline(c, cp->cookie, indent, do_concat); } @@ -986,10 +1003,10 @@ void *getline_cookie(LineGetter fgetline, void *cookie) /// ":bwipeout", etc. /// /// @return the buffer number. -static int compute_buffer_local_count(cmd_addr_T addr_type, linenr_T lnum, long offset) +static int compute_buffer_local_count(cmd_addr_T addr_type, linenr_T lnum, int offset) { buf_T *nextbuf; - long count = offset; + int count = offset; buf_T *buf = firstbuf; while (buf->b_next != NULL && buf->b_fnum < lnum) { @@ -1107,7 +1124,7 @@ static void get_wincmd_addr_type(const char *arg, exarg_T *eap) case 'd': case Ctrl_D: // window size or any count - eap->addr_type = ADDR_OTHER; // -V1037 + eap->addr_type = ADDR_OTHER; break; case Ctrl_HAT: @@ -1311,16 +1328,16 @@ static void parse_register(exarg_T *eap) } // Change line1 and line2 of Ex command to use count -void set_cmd_count(exarg_T *eap, long count, bool validate) +void set_cmd_count(exarg_T *eap, linenr_T count, bool validate) { if (eap->addr_type != ADDR_LINES) { // e.g. :buffer 2, :sleep 3 - eap->line2 = (linenr_T)count; + eap->line2 = count; if (eap->addr_count == 0) { eap->addr_count = 1; } } else { eap->line1 = eap->line2; - eap->line2 += (linenr_T)count - 1; + eap->line2 += count - 1; eap->addr_count++; // Be vi compatible: no error message for out of range. if (validate && eap->line2 > curbuf->b_ml.ml_line_count) { @@ -1329,7 +1346,7 @@ void set_cmd_count(exarg_T *eap, long count, bool validate) } } -static int parse_count(exarg_T *eap, char **errormsg, bool validate) +static int parse_count(exarg_T *eap, const char **errormsg, bool validate) { // Check for a count. When accepting a EX_BUFNAME, don't use "123foo" as a // count, it's a buffer name. @@ -1338,7 +1355,7 @@ static int parse_count(exarg_T *eap, char **errormsg, bool validate) if ((eap->argt & EX_COUNT) && ascii_isdigit(*eap->arg) && (!(eap->argt & EX_BUFNAME) || *(p = skipdigits(eap->arg + 1)) == NUL || ascii_iswhite(*p))) { - long n = getdigits_long(&eap->arg, false, -1); + linenr_T n = getdigits_int32(&eap->arg, false, -1); eap->arg = skipwhite(eap->arg); if (eap->args != NULL) { @@ -1383,7 +1400,7 @@ bool is_cmd_ni(cmdidx_T cmdidx) /// @param[out] errormsg Error message, if any /// /// @return Success or failure -bool parse_cmdline(char *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo, char **errormsg) +bool parse_cmdline(char *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo, const char **errormsg) { char *after_modifier = NULL; bool retval = false; @@ -1441,7 +1458,7 @@ bool parse_cmdline(char *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo, char **er } // Fail if command is invalid if (eap->cmdidx == CMD_SIZE) { - STRCPY(IObuff, _(e_not_an_editor_command)); + xstrlcpy(IObuff, _(e_not_an_editor_command), IOSIZE); // If the modifier was parsed OK the error must be in the following command char *cmdname = after_modifier ? after_modifier : cmdline; append_command(cmdname); @@ -1551,7 +1568,7 @@ static void shift_cmd_args(exarg_T *eap) xfree(oldarglens); } -static int execute_cmd0(int *retv, exarg_T *eap, char **errormsg, bool preview) +static int execute_cmd0(int *retv, exarg_T *eap, const char **errormsg, bool preview) { // If filename expansion is enabled, expand filenames if (eap->argt & EX_XFILE) { @@ -1636,7 +1653,7 @@ static int execute_cmd0(int *retv, exarg_T *eap, char **errormsg, bool preview) /// @param preview Execute command preview callback instead of actual command int execute_cmd(exarg_T *eap, CmdParseInfo *cmdinfo, bool preview) { - char *errormsg = NULL; + const char *errormsg = NULL; int retv = 0; #undef ERROR @@ -1677,7 +1694,7 @@ int execute_cmd(exarg_T *eap, CmdParseInfo *cmdinfo, bool preview) && !(eap->cmdidx == CMD_file && *eap->arg == NUL) && !IS_USER_CMDIDX(eap->cmdidx) && curbuf_locked()) { - ERROR(_(e_cannot_edit_other_buf)); + goto end; } correct_range(eap); @@ -1849,7 +1866,8 @@ static bool skip_cmd(const exarg_T *eap) /// Execute one Ex command. /// -/// If 'sourcing' is true, the command will be included in the error message. +/// If "flags" has DOCMD_VERBOSE, the command will be included in the error +/// message. /// /// 1. skip comment lines and leading space /// 2. handle command modifiers @@ -1867,7 +1885,7 @@ static bool skip_cmd(const exarg_T *eap) static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter fgetline, void *cookie) { - char *errormsg = NULL; // error message + const char *errormsg = NULL; // error message const int save_reg_executing = reg_executing; const bool save_pending_end_reg_executing = pending_end_reg_executing; @@ -1882,8 +1900,7 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter // avoid that a function call in 'statusline' does this && !getline_equal(fgetline, cookie, get_func_line) // avoid that an autocommand, e.g. QuitPre, does this - && !getline_equal(fgetline, cookie, - getnextac)) { + && !getline_equal(fgetline, cookie, getnextac)) { quitmore--; } @@ -2013,7 +2030,7 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter while (ASCII_ISALNUM(*p)) { p++; } - p = xstrnsave(ea.cmd, (size_t)(p - ea.cmd)); + p = xmemdupz(ea.cmd, (size_t)(p - ea.cmd)); int ret = apply_autocmds(EVENT_CMDUNDEFINED, p, p, true, NULL); xfree(p); // If the autocommands did something and didn't cause an error, try @@ -2031,7 +2048,7 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter // Check for wrong commands. if (ea.cmdidx == CMD_SIZE) { if (!ea.skip) { - STRCPY(IObuff, _(e_not_an_editor_command)); + xstrlcpy(IObuff, _(e_not_an_editor_command), IOSIZE); // If the modifier was parsed OK the error must be in the following // command char *cmdname = after_modifier ? after_modifier : *cmdlinep; @@ -2308,7 +2325,7 @@ doend: if (errormsg != NULL && *errormsg != NUL && !did_emsg) { if (flags & DOCMD_VERBOSE) { if (errormsg != IObuff) { - STRCPY(IObuff, errormsg); + xstrlcpy(IObuff, errormsg, IOSIZE); errormsg = IObuff; } append_command(*ea.cmdlinep); @@ -2329,6 +2346,7 @@ doend: } ex_nesting_level--; + xfree(ea.cmdline_tofree); return ea.nextcmd; } @@ -2362,12 +2380,12 @@ char *ex_errmsg(const char *const msg, const char *const arg) /// - set 'eventignore' to "all" for ":noautocmd" /// /// @return FAIL when the command is not to be executed. -int parse_command_modifiers(exarg_T *eap, char **errormsg, cmdmod_T *cmod, bool skip_only) +int parse_command_modifiers(exarg_T *eap, const char **errormsg, cmdmod_T *cmod, bool skip_only) { CLEAR_POINTER(cmod); // Repeat until no more command modifiers are found. - for (;;) { + while (true) { while (*eap->cmd == ' ' || *eap->cmd == '\t' || *eap->cmd == ':') { @@ -2397,7 +2415,7 @@ int parse_command_modifiers(exarg_T *eap, char **errormsg, cmdmod_T *cmod, bool char *p = skip_range(eap->cmd, NULL); switch (*p) { - // When adding an entry, also modify cmd_exists(). + // When adding an entry, also modify cmdmods[] case 'a': if (!checkforcmd(&eap->cmd, "aboveleft", 3)) { break; @@ -2543,7 +2561,10 @@ int parse_command_modifiers(exarg_T *eap, char **errormsg, cmdmod_T *cmod, bool if (checkforcmd(&p, "tab", 3)) { if (!skip_only) { int tabnr = (int)get_address(eap, &eap->cmd, ADDR_TABS, eap->skip, skip_only, - false, 1); + false, 1, errormsg); + if (eap->cmd == NULL) { + return false; + } if (tabnr == MAXLNUM) { cmod->cmod_tab = tabpage_index(curtab) + 1; @@ -2687,7 +2708,7 @@ void undo_cmdmod(cmdmod_T *cmod) /// May set the last search pattern, unless "silent" is true. /// /// @return FAIL and set "errormsg" or return OK. -int parse_cmd_address(exarg_T *eap, char **errormsg, bool silent) +int parse_cmd_address(exarg_T *eap, const char **errormsg, bool silent) FUNC_ATTR_NONNULL_ALL { int address_count = 1; @@ -2696,12 +2717,12 @@ int parse_cmd_address(exarg_T *eap, char **errormsg, bool silent) int ret = FAIL; // Repeat for all ',' or ';' separated addresses. - for (;;) { + while (true) { eap->line1 = eap->line2; eap->line2 = get_cmd_default_range(eap); eap->cmd = skipwhite(eap->cmd); lnum = get_address(eap, &eap->cmd, eap->addr_type, eap->skip, silent, - eap->addr_count == 0, address_count++); + eap->addr_count == 0, address_count++, errormsg); if (eap->cmd == NULL) { // error detected goto theend; } @@ -2737,7 +2758,7 @@ int parse_cmd_address(exarg_T *eap, char **errormsg, bool silent) if (IS_USER_CMDIDX(eap->cmdidx)) { eap->line1 = 1; eap->line2 = eap->addr_type == ADDR_WINDOWS - ? LAST_WIN_NR : LAST_TAB_NR; + ? LAST_WIN_NR : LAST_TAB_NR; } else { // there is no Vim command which uses '%' and // ADDR_WINDOWS or ADDR_TABS @@ -2780,13 +2801,13 @@ int parse_cmd_address(exarg_T *eap, char **errormsg, bool silent) eap->cmd++; if (!eap->skip) { fmark_T *fm = mark_get_visual(curbuf, '<'); - if (!mark_check(fm)) { + if (!mark_check(fm, errormsg)) { goto theend; } assert(fm != NULL); eap->line1 = fm->mark.lnum; fm = mark_get_visual(curbuf, '>'); - if (!mark_check(fm)) { + if (!mark_check(fm, errormsg)) { goto theend; } assert(fm != NULL); @@ -2862,10 +2883,10 @@ bool checkforcmd(char **pp, const char *cmd, int len) /// Append "cmd" to the error message in IObuff. /// Takes care of limiting the length and handling 0xa0, which would be /// invisible otherwise. -static void append_command(char *cmd) +static void append_command(const char *cmd) { size_t len = strlen(IObuff); - char *s = cmd; + const char *s = cmd; char *d; if (len > IOSIZE - 100) { @@ -2874,7 +2895,7 @@ static void append_command(char *cmd) d -= utf_head_off(IObuff, d); STRCPY(d, "..."); } - STRCAT(IObuff, ": "); + xstrlcat(IObuff, ": ", IOSIZE); d = IObuff + strlen(IObuff); while (*s != NUL && d - IObuff + 5 < IOSIZE) { if ((uint8_t)s[0] == 0xc2 && (uint8_t)s[1] == 0xa0) { @@ -2884,7 +2905,7 @@ static void append_command(char *cmd) } else if (d - IObuff + utfc_ptr2len(s) + 1 >= IOSIZE) { break; } else { - mb_copy_char((const char **)&s, &d); + mb_copy_char(&s, &d); } } *d = NUL; @@ -2988,6 +3009,11 @@ char *find_ex_command(exarg_T *eap, int *full) } assert(eap->cmdidx >= 0); + if (len == 3 && strncmp("def", eap->cmd, 3) == 0) { + // Make :def an unknown command to avoid confusing behavior. #23149 + eap->cmdidx = CMD_SIZE; + } + for (; (int)eap->cmdidx < CMD_SIZE; eap->cmdidx = (cmdidx_T)((int)eap->cmdidx + 1)) { if (strncmp(cmdnames[(int)eap->cmdidx].cmd_name, eap->cmd, @@ -3029,6 +3055,7 @@ static struct cmdmod { { "confirm", 4, false }, { "filter", 4, false }, { "hide", 3, false }, + { "horizontal", 3, false }, { "keepalt", 5, false }, { "keepjumps", 5, false }, { "keepmarks", 3, false }, @@ -3094,7 +3121,7 @@ int cmd_exists(const char *const name) // For ":2match" and ":3match" we need to skip the number. exarg_T ea; ea.cmd = (char *)((*name == '2' || *name == '3') ? name + 1 : name); - ea.cmdidx = (cmdidx_T)0; + ea.cmdidx = 0; ea.flags = 0; int full = false; char *p = find_ex_command(&ea, &full); @@ -3113,13 +3140,10 @@ int cmd_exists(const char *const name) /// "fullcommand" function void f_fullcommand(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { - char *name = argvars[0].vval.v_string; + char *name = (char *)tv_get_string(&argvars[0]); rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; - if (name == NULL) { - return; - } while (*name == ':') { name++; @@ -3128,7 +3152,7 @@ void f_fullcommand(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) exarg_T ea; ea.cmd = (*name == '2' || *name == '3') ? name + 1 : name; - ea.cmdidx = (cmdidx_T)0; + ea.cmdidx = 0; ea.flags = 0; char *p = find_ex_command(&ea, NULL); if (p == NULL || ea.cmdidx == CMD_SIZE) { @@ -3142,10 +3166,15 @@ void f_fullcommand(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) cmdidx_T excmd_get_cmdidx(const char *cmd, size_t len) { + if (len == 3 && strncmp("def", cmd, 3) == 0) { + // Make :def an unknown command to avoid confusing behavior. #23149 + return CMD_SIZE; + } + cmdidx_T idx; if (!one_letter_cmd(cmd, &idx)) { - for (idx = (cmdidx_T)0; (int)idx < CMD_SIZE; idx = (cmdidx_T)((int)idx + 1)) { + for (idx = 0; (int)idx < CMD_SIZE; idx = (cmdidx_T)((int)idx + 1)) { if (strncmp(cmdnames[(int)idx].cmd_name, cmd, len) == 0) { break; } @@ -3200,34 +3229,35 @@ char *skip_range(const char *cmd, int *ctx) } // Skip ":" and white space. - cmd = skip_colon_white((char *)cmd, false); + cmd = skip_colon_white(cmd, false); return (char *)cmd; } -static void addr_error(cmd_addr_T addr_type) +static const char *addr_error(cmd_addr_T addr_type) { if (addr_type == ADDR_NONE) { - emsg(_(e_norange)); + return _(e_norange); } else { - emsg(_(e_invrange)); + return _(e_invrange); } } -/// Get a single EX address +/// Gets a single EX address. /// -/// Set ptr to the next character after the part that was interpreted. -/// Set ptr to NULL when an error is encountered. -/// This may set the last used search pattern. +/// Sets ptr to the next character after the part that was interpreted. +/// Sets ptr to NULL when an error is encountered (stored in `errormsg`). +/// May set the last used search pattern. /// /// @param skip only skip the address, don't use it /// @param silent no errors or side effects /// @param to_other_file flag: may jump to other file /// @param address_count 1 for first, >1 after comma +/// @param errormsg Error message, if any /// /// @return MAXLNUM when no Ex address was found. static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, int skip, bool silent, - int to_other_file, int address_count) + int to_other_file, int address_count, const char **errormsg) FUNC_ATTR_NONNULL_ALL { int c; @@ -3263,7 +3293,7 @@ static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, int case ADDR_NONE: case ADDR_TABS_RELATIVE: case ADDR_UNSIGNED: - addr_error(addr_type); + *errormsg = addr_error(addr_type); cmd = NULL; goto error; break; @@ -3308,7 +3338,7 @@ static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, int case ADDR_NONE: case ADDR_TABS_RELATIVE: case ADDR_UNSIGNED: - addr_error(addr_type); + *errormsg = addr_error(addr_type); cmd = NULL; goto error; break; @@ -3333,7 +3363,7 @@ static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, int goto error; } if (addr_type != ADDR_LINES) { - addr_error(addr_type); + *errormsg = addr_error(addr_type); cmd = NULL; goto error; } @@ -3346,10 +3376,11 @@ static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, int fmark_T *fm = mark_get(curbuf, curwin, NULL, flag, *cmd); cmd++; if (fm != NULL && fm->fnum != curbuf->handle) { + (void)mark_move_to(fm, 0); // Jumped to another file. lnum = curwin->w_cursor.lnum; } else { - if (!mark_check(fm)) { + if (!mark_check(fm, errormsg)) { cmd = NULL; goto error; } @@ -3363,7 +3394,7 @@ static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, int case '?': // '/' or '?' - search c = (uint8_t)(*cmd++); if (addr_type != ADDR_LINES) { - addr_error(addr_type); + *errormsg = addr_error(addr_type); cmd = NULL; goto error; } @@ -3397,7 +3428,7 @@ static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, int } searchcmdlen = 0; flags = silent ? 0 : SEARCH_HIS | SEARCH_MSG; - if (!do_search(NULL, c, c, cmd, 1L, flags, NULL)) { + if (!do_search(NULL, c, c, cmd, 1, flags, NULL)) { curwin->w_cursor = pos; cmd = NULL; goto error; @@ -3412,7 +3443,7 @@ static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, int case '\\': // "\?", "\/" or "\&", repeat search cmd++; if (addr_type != ADDR_LINES) { - addr_error(addr_type); + *errormsg = addr_error(addr_type); cmd = NULL; goto error; } @@ -3421,7 +3452,7 @@ static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, int } else if (*cmd == '?' || *cmd == '/') { i = RE_SEARCH; } else { - emsg(_(e_backslash)); + *errormsg = _(e_backslash); cmd = NULL; goto error; } @@ -3434,7 +3465,7 @@ static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, int pos.coladd = 0; if (searchit(curwin, curbuf, &pos, NULL, *cmd == '?' ? BACKWARD : FORWARD, - "", 1L, SEARCH_MSG, i, NULL) != FAIL) { + "", 1, SEARCH_MSG, i, NULL) != FAIL) { lnum = pos.lnum; } else { cmd = NULL; @@ -3450,7 +3481,7 @@ static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, int } } - for (;;) { + while (true) { cmd = skipwhite(cmd); if (*cmd != '-' && *cmd != '+' && !ascii_isdigit(*cmd)) { break; @@ -3503,13 +3534,13 @@ static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, int // "number", "+number" or "-number" n = getdigits_int32(&cmd, false, MAXLNUM); if (n == MAXLNUM) { - emsg(_(e_line_number_out_of_range)); + *errormsg = _(e_line_number_out_of_range); goto error; } } if (addr_type == ADDR_TABS_RELATIVE) { - emsg(_(e_invrange)); + *errormsg = _(e_invrange); cmd = NULL; goto error; } else if (addr_type == ADDR_LOADED_BUFFERS || addr_type == ADDR_BUFFERS) { @@ -3524,8 +3555,8 @@ static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, int if (i == '-') { lnum -= n; } else { - if (n >= INT32_MAX - lnum) { - emsg(_(e_line_number_out_of_range)); + if (lnum >= 0 && n >= INT32_MAX - lnum) { + *errormsg = _(e_line_number_out_of_range); goto error; } lnum += n; @@ -3738,7 +3769,7 @@ char *replace_makeprg(exarg_T *eap, char *arg, char **cmdlinep) /// When an error is detected, "errormsgp" is set to a non-NULL pointer. /// /// @return FAIL for failure, OK otherwise. -int expand_filename(exarg_T *eap, char **cmdlinep, char **errormsgp) +int expand_filename(exarg_T *eap, char **cmdlinep, const char **errormsgp) { // Skip a regexp pattern for ":vimgrep[add] pat file..." char *p = skip_grep_pat(eap); @@ -3751,7 +3782,7 @@ int expand_filename(exarg_T *eap, char **cmdlinep, char **errormsgp) // Skip over `=expr`, wildcards in it are not expanded. if (p[0] == '`' && p[1] == '=') { p += 2; - (void)skip_expr(&p); + (void)skip_expr(&p, NULL); if (*p == '`') { p++; } @@ -3970,7 +4001,7 @@ void separate_nextcmd(exarg_T *eap) } else if (p[0] == '`' && p[1] == '=' && (eap->argt & EX_XFILE)) { // Skip over `=expr` when wildcards are expanded. p += 2; - (void)skip_expr(&p); + (void)skip_expr(&p, NULL); if (*p == NUL) { // stop at NUL after CTRL-V break; } @@ -4011,7 +4042,7 @@ static char *getargcmd(char **argp) if (*arg == '+') { // +[command] arg++; if (ascii_isspace(*arg) || *arg == '\0') { - command = (char *)dollar_command; + command = dollar_command; } else { command = arg; arg = skip_cmd_arg(command, true); @@ -4059,6 +4090,22 @@ int get_bad_opt(const char *p, exarg_T *eap) return OK; } +/// Function given to ExpandGeneric() to obtain the list of bad= names. +static char *get_bad_name(expand_T *xp FUNC_ATTR_UNUSED, int idx) +{ + // Note: Keep this in sync with get_bad_opt(). + static char *(p_bad_values[]) = { + "?", + "keep", + "drop", + }; + + if (idx < (int)ARRAY_SIZE(p_bad_values)) { + return p_bad_values[idx]; + } + return NULL; +} + /// Get "++opt=arg" argument. /// /// @return FAIL or OK. @@ -4068,6 +4115,8 @@ static int getargopt(exarg_T *eap) int *pp = NULL; int bad_char_idx; + // Note: Keep this in sync with get_argopt_name. + // ":edit ++[no]bin[ary] file" if (strncmp(arg, "bin", 3) == 0 || strncmp(arg, "nobin", 5) == 0) { if (*arg == 'n') { @@ -4146,6 +4195,71 @@ static int getargopt(exarg_T *eap) return OK; } +/// Function given to ExpandGeneric() to obtain the list of ++opt names. +static char *get_argopt_name(expand_T *xp FUNC_ATTR_UNUSED, int idx) +{ + // Note: Keep this in sync with getargopt(). + static char *(p_opt_values[]) = { + "fileformat=", + "encoding=", + "binary", + "nobinary", + "bad=", + "edit", + "p", + |