aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/ex_docmd.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/ex_docmd.c')
-rw-r--r--src/nvim/ex_docmd.c974
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",
+