diff options
Diffstat (limited to 'src/nvim/ex_docmd.c')
-rw-r--r-- | src/nvim/ex_docmd.c | 3570 |
1 files changed, 1933 insertions, 1637 deletions
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 3a285cdf90..c4d2821e79 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -38,12 +38,15 @@ #include "nvim/getchar.h" #include "nvim/globals.h" #include "nvim/hardcopy.h" +#include "nvim/highlight_group.h" #include "nvim/if_cscope.h" #include "nvim/input.h" -#include "nvim/keymap.h" +#include "nvim/keycodes.h" #include "nvim/lua/executor.h" #include "nvim/main.h" +#include "nvim/mapping.h" #include "nvim/mark.h" +#include "nvim/match.h" #include "nvim/mbyte.h" #include "nvim/memline.h" #include "nvim/memory.h" @@ -59,6 +62,7 @@ #include "nvim/os/time.h" #include "nvim/os_unix.h" #include "nvim/path.h" +#include "nvim/popupmnu.h" #include "nvim/quickfix.h" #include "nvim/regexp.h" #include "nvim/screen.h" @@ -74,21 +78,28 @@ #include "nvim/terminal.h" #include "nvim/ui.h" #include "nvim/undo.h" +#include "nvim/undo_defs.h" #include "nvim/version.h" #include "nvim/vim.h" #include "nvim/window.h" +static char e_no_such_user_defined_command_str[] + = N_("E184: No such user-defined command: %s"); +static char e_ambiguous_use_of_user_defined_command[] + = N_("E464: Ambiguous use of user-defined command"); +static char e_not_an_editor_command[] + = N_("E492: Not an editor command"); +static char e_no_such_user_defined_command_in_current_buffer_str[] + = N_("E1237: No such user-defined command in current buffer: %s"); + static int quitmore = 0; static bool ex_pressedreturn = false; garray_T ucmds = { 0, 0, sizeof(ucmd_T), 4, NULL }; -// Whether a command index indicates a user command. -#define IS_USER_CMDIDX(idx) ((int)(idx) < 0) - // Struct for storing a line inside a while/for loop typedef struct { - char_u *line; // command line + char *line; // command line linenr_T lnum; // sourcing_lnum of the line } wcmd_T; @@ -104,18 +115,17 @@ struct loop_cookie { int current_line; // last read line from growarray int repeating; // TRUE when looping a second time // When "repeating" is FALSE use "getline" and "cookie" to get lines - char_u *(*getline)(int, void *, int, bool); + char *(*getline)(int, void *, int, bool); void *cookie; }; - // Struct to save a few things while debugging. Used in do_cmdline() only. struct dbg_stuff { int trylevel; int force_abort; except_T *caught_stack; - char_u *vv_exception; - char_u *vv_throwpoint; + char *vv_exception; + char *vv_throwpoint; int did_emsg; int got_int; int need_rethrow; @@ -138,7 +148,7 @@ struct dbg_stuff { # include "ex_cmds_defs.generated.h" #endif -static char_u dollar_command[2] = { '$', 0 }; +static char dollar_command[2] = { '$', 0 }; static void save_dbg_stuff(struct dbg_stuff *dsp) { @@ -177,11 +187,11 @@ void do_exmode(void) int save_msg_scroll; int prev_msg_row; linenr_T prev_line; - int changedtick; + varnumber_T changedtick; exmode_active = true; - State = NORMAL; - trigger_modechanged(); + State = MODE_NORMAL; + may_trigger_modechanged(); // When using ":global /pat/ visual" and then "Q" we return to continue // the :global command. @@ -217,6 +227,8 @@ void do_exmode(void) emsg(_(e_emptybuf)); } else { if (ex_pressedreturn) { + // Make sure the message overwrites the right line and isn't throttled. + msg_scroll_flush(); // go up one line, to overwrite the ":<CR>" line, so the // output doesn't contain empty lines. msg_row = prev_msg_row; @@ -245,9 +257,10 @@ void do_exmode(void) msg_scroll = save_msg_scroll; } -// Print the executed command for when 'verbose' is set. -// When "lnum" is 0 only print the command. -static void msg_verbose_cmd(linenr_T lnum, char_u *cmd) +/// Print the executed command for when 'verbose' is set. +/// +/// @param lnum if 0, only print the command. +static void msg_verbose_cmd(linenr_T lnum, char *cmd) FUNC_ATTR_NONNULL_ALL { no_wait_return++; @@ -266,13 +279,10 @@ static void msg_verbose_cmd(linenr_T lnum, char_u *cmd) no_wait_return--; } -/* - * Execute a simple command line. Used for translated commands like "*". - */ +/// Execute a simple command line. Used for translated commands like "*". int do_cmdline_cmd(const char *cmd) { - return do_cmdline((char_u *)cmd, NULL, NULL, - DOCMD_NOWAIT|DOCMD_KEYTYPED); + return do_cmdline((char *)cmd, NULL, NULL, DOCMD_NOWAIT|DOCMD_KEYTYPED); } /// do_cmdline(): execute one Ex command line @@ -290,15 +300,14 @@ int do_cmdline_cmd(const char *cmd) /// DOCMD_KEYTYPED - Don't reset KeyTyped. /// DOCMD_EXCRESET - Reset the exception environment (used for debugging). /// DOCMD_KEEPLINE - Store first typed line (for repeating with "."). -/// DOCMD_PREVIEW - During 'inccommand' preview. /// /// @param cookie argument for fgetline() /// /// @return FAIL if cmdline could not be executed, OK otherwise -int do_cmdline(char_u *cmdline, LineGetter fgetline, void *cookie, int flags) +int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags) { - char_u *next_cmdline; // next cmd to execute - char_u *cmdline_copy = NULL; // copy of cmd line + char *next_cmdline; // next cmd to execute + char *cmdline_copy = NULL; // copy of cmd line bool used_getline = false; // used "fgetline" to obtain command static int recursive = 0; // recursive depth bool msg_didout_before_start = false; @@ -310,7 +319,7 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, void *cookie, int flags) }; garray_T lines_ga; // keep lines for ":while"/":for" int current_line = 0; // active line in lines_ga - char_u *fname = NULL; // function or script name + char *fname = NULL; // function or script name 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 @@ -319,7 +328,7 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, void *cookie, int flags) struct msglist *private_msg_list; // "fgetline" and "cookie" passed to do_one_cmd() - char_u *(*cmd_getline)(int, void *, int, bool); + char *(*cmd_getline)(int, void *, int, bool); void *cmd_cookie; struct loop_cookie cmd_loop_cookie; void *real_cookie; @@ -340,10 +349,10 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, void *cookie, int flags) // here. The value of 200 allows nested function calls, ":source", etc. // Allow 200 or 'maxfuncdepth', whatever is larger. if (call_depth >= 200 && call_depth >= p_mfd) { - emsg(_("E169: Command too recursive")); + emsg(_(e_command_too_recursive)); // When converting to an exception, we do not include the command name // since this is not an error of the specific command. - do_errthrow((cstack_T *)NULL, (char_u *)NULL); + do_errthrow((cstack_T *)NULL, NULL); msg_list = saved_msg_list; return FAIL; } @@ -363,7 +372,7 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, void *cookie, int flags) // Get the function or script name and the address where the next breakpoint // line and the debug tick for a function or script are stored. if (getline_is_func) { - fname = func_name(real_cookie); + fname = (char *)func_name(real_cookie); breakpoint = func_breakpoint(real_cookie); dbg_tick = func_dbg_tick(real_cookie); } else if (getline_equal(fgetline, cookie, getsourceline)) { @@ -460,7 +469,7 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, void *cookie, int flags) if (breakpoint != NULL && dbg_tick != NULL && *dbg_tick != debug_tick) { *breakpoint = dbg_find_breakpoint(getline_equal(fgetline, cookie, getsourceline), - fname, sourcing_lnum); + (char_u *)fname, sourcing_lnum); *dbg_tick = debug_tick; } @@ -470,10 +479,10 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, void *cookie, int flags) // Did we encounter a breakpoint? if (breakpoint != NULL && *breakpoint != 0 && *breakpoint <= sourcing_lnum) { - dbg_breakpoint(fname, sourcing_lnum); + dbg_breakpoint((char_u *)fname, sourcing_lnum); // Find next breakpoint. *breakpoint = dbg_find_breakpoint(getline_equal(fgetline, cookie, getsourceline), - fname, sourcing_lnum); + (char_u *)fname, sourcing_lnum); *dbg_tick = debug_tick; } if (do_profiling == PROF_YES) { @@ -534,15 +543,14 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, void *cookie, int flags) if (flags & DOCMD_KEEPLINE) { xfree(repeat_cmdline); if (count == 0) { - repeat_cmdline = vim_strsave(next_cmdline); + repeat_cmdline = vim_strsave((char_u *)next_cmdline); } else { repeat_cmdline = NULL; } } - } - // 3. Make a copy of the command so we can mess with it. - else if (cmdline_copy == NULL) { - next_cmdline = vim_strsave(next_cmdline); + } else if (cmdline_copy == NULL) { + // 3. Make a copy of the command so we can mess with it. + next_cmdline = xstrdup(next_cmdline); } cmdline_copy = next_cmdline; @@ -587,16 +595,9 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, void *cookie, int flags) * "cmdline_copy" can change, e.g. for '%' and '#' expansion. */ recursive++; - next_cmdline = do_one_cmd(&cmdline_copy, flags, - &cstack, - cmd_getline, cmd_cookie); + next_cmdline = do_one_cmd(&cmdline_copy, flags, &cstack, cmd_getline, cmd_cookie); recursive--; - // Ignore trailing '|'-separated commands in preview-mode ('inccommand'). - if ((State & CMDPREVIEW) && (flags & DOCMD_PREVIEW)) { - next_cmdline = NULL; - } - if (cmd_cookie == (void *)&cmd_loop_cookie) { // Use "current_line" from "cmd_loop_cookie", it may have been // incremented when defining a function. @@ -622,7 +623,6 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, void *cookie, int flags) next_cmdline = cmdline_copy; } - // reset did_emsg for a function that is not aborted by an error if (did_emsg && !force_abort && getline_equal(fgetline, cookie, get_func_line) @@ -661,8 +661,8 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, void *cookie, int flags) // or ":for". if (breakpoint != NULL) { *breakpoint = dbg_find_breakpoint(getline_equal(fgetline, cookie, getsourceline), - fname, - ((wcmd_T *)lines_ga.ga_data)[current_line].lnum-1); + (char_u *)fname, + ((wcmd_T *)lines_ga.ga_data)[current_line].lnum - 1); *dbg_tick = debug_tick; } } else { @@ -795,8 +795,7 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, void *cookie, int flags) // If a missing ":endtry", ":endwhile", ":endfor", or ":endif" or a memory // lack was reported above and the error message is to be converted to an // exception, do this now after rewinding the cstack. - do_errthrow(&cstack, getline_equal(fgetline, cookie, get_func_line) - ? (char_u *)"endfunction" : (char_u *)NULL); + do_errthrow(&cstack, getline_equal(fgetline, cookie, get_func_line) ? "endfunction" : NULL); if (trylevel == 0) { // When an exception is being thrown out of the outermost try @@ -805,8 +804,8 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, void *cookie, int flags) // commands are executed. if (current_exception) { char *p = NULL; - char_u *saved_sourcing_name; - int saved_sourcing_lnum; + char *saved_sourcing_name; + linenr_T saved_sourcing_lnum; struct msglist *messages = NULL; struct msglist *next; @@ -911,6 +910,15 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, void *cookie, int flags) msg_list = saved_msg_list; + // 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) { + temp = elem->next; + xfree(elem); + } + } + /* * If there was too much output to fit on the command line, ask the user to * hit return before redrawing the screen. With the ":global" command we do @@ -947,14 +955,12 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, void *cookie, int flags) return retval; } -/* - * Obtain a line when inside a ":while" or ":for" loop. - */ -static char_u *get_loop_line(int c, void *cookie, int indent, bool do_concat) +/// Obtain a line when inside a ":while" or ":for" loop. +static char *get_loop_line(int c, void *cookie, int indent, bool do_concat) { struct loop_cookie *cp = (struct loop_cookie *)cookie; wcmd_T *wp; - char_u *line; + char *line; if (cp->current_line + 1 >= cp->lines_gap->ga_len) { if (cp->repeating) { @@ -962,7 +968,7 @@ static char_u *get_loop_line(int c, void *cookie, int indent, bool do_concat) } // First time inside the ":while"/":for": get line normally. if (cp->getline == NULL) { - line = getcmdline(c, 0L, indent, do_concat); + line = (char *)getcmdline(c, 0L, indent, do_concat); } else { line = cp->getline(c, cp->cookie, indent, do_concat); } @@ -978,16 +984,14 @@ static char_u *get_loop_line(int c, void *cookie, int indent, bool do_concat) cp->current_line++; wp = (wcmd_T *)(cp->lines_gap->ga_data) + cp->current_line; sourcing_lnum = wp->lnum; - return vim_strsave(wp->line); + return xstrdup(wp->line); } -/* - * Store a line in "gap" so that a ":while" loop can execute it again. - */ -static void store_loop_line(garray_T *gap, char_u *line) +/// Store a line in "gap" so that a ":while" loop can execute it again. +static void store_loop_line(garray_T *gap, char *line) { wcmd_T *p = GA_APPEND_VIA_PTR(wcmd_T, gap); - p->line = vim_strsave(line); + p->line = xstrdup(line); p->lnum = sourcing_lnum; } @@ -1033,16 +1037,15 @@ void *getline_cookie(LineGetter fgetline, void *cookie) return cp; } -/* - * Helper function to apply an offset for buffer commands, i.e. ":bdelete", - * ":bwipeout", etc. - * Returns the buffer number. - */ -static int compute_buffer_local_count(int addr_type, int lnum, int offset) +/// Helper function to apply an offset for buffer commands, i.e. ":bdelete", +/// ":bwipeout", etc. +/// +/// @return the buffer number. +static int compute_buffer_local_count(cmd_addr_T addr_type, linenr_T lnum, long offset) { buf_T *buf; buf_T *nextbuf; - int count = offset; + long count = offset; buf = firstbuf; while (buf->b_next != NULL && buf->b_fnum < lnum) { @@ -1079,8 +1082,8 @@ static int compute_buffer_local_count(int addr_type, int lnum, int offset) return buf->b_fnum; } -// Return the window number of "win". -// When "win" is NULL return the number of windows. +/// @return the window number of "win" or, +/// the number of windows if "win" is NULL static int current_win_nr(const win_T *win) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { @@ -1113,9 +1116,8 @@ static int current_tab_nr(tabpage_T *tab) #define CURRENT_TAB_NR current_tab_nr(curtab) #define LAST_TAB_NR current_tab_nr(NULL) - /// Figure out the address type for ":wincmd". -static void get_wincmd_addr_type(char_u *arg, exarg_T *eap) +static void get_wincmd_addr_type(char *arg, exarg_T *eap) { switch (*arg) { case 'S': @@ -1206,7 +1208,7 @@ static void get_wincmd_addr_type(char_u *arg, exarg_T *eap) /// non-colon, non-whitespace character. // /// @param skipleadingwhite Skip leading whitespace too -static char_u *skip_colon_white(const char_u *p, bool skipleadingwhite) +static char *skip_colon_white(const char *p, bool skipleadingwhite) { if (skipleadingwhite) { p = skipwhite(p); @@ -1216,7 +1218,489 @@ static char_u *skip_colon_white(const char_u *p, bool skipleadingwhite) p = skipwhite(p + 1); } - return (char_u *)p; + return (char *)p; +} + +/// Set the addr type for command +/// +/// @param p pointer to character after command name in cmdline +void set_cmd_addr_type(exarg_T *eap, char *p) +{ + // ea.addr_type for user commands is set by find_ucmd + if (IS_USER_CMDIDX(eap->cmdidx)) { + return; + } + if (eap->cmdidx != CMD_SIZE) { + eap->addr_type = cmdnames[(int)eap->cmdidx].cmd_addr_type; + } else { + eap->addr_type = ADDR_LINES; + } + // :wincmd range depends on the argument + if (eap->cmdidx == CMD_wincmd && p != NULL) { + get_wincmd_addr_type(skipwhite(p), eap); + } + // :.cc in quickfix window uses line number + if ((eap->cmdidx == CMD_cc || eap->cmdidx == CMD_ll) && bt_quickfix(curbuf)) { + eap->addr_type = ADDR_OTHER; + } +} + +/// Get default range number for command based on its address type +linenr_T get_cmd_default_range(exarg_T *eap) +{ + switch (eap->addr_type) { + case ADDR_LINES: + case ADDR_OTHER: + // Default is the cursor line number. Avoid using an invalid + // line number though. + return MIN(curwin->w_cursor.lnum, curbuf->b_ml.ml_line_count); + break; + case ADDR_WINDOWS: + return CURRENT_WIN_NR; + break; + case ADDR_ARGUMENTS: + return MIN(curwin->w_arg_idx + 1, ARGCOUNT); + break; + case ADDR_LOADED_BUFFERS: + case ADDR_BUFFERS: + return curbuf->b_fnum; + break; + case ADDR_TABS: + return CURRENT_TAB_NR; + break; + case ADDR_TABS_RELATIVE: + case ADDR_UNSIGNED: + return 1; + break; + case ADDR_QUICKFIX: + return (linenr_T)qf_get_cur_idx(eap); + break; + case ADDR_QUICKFIX_VALID: + return qf_get_cur_valid_idx(eap); + break; + default: + return 0; + // Will give an error later if a range is found. + break; + } +} + +/// Set default command range for -range=% based on the addr type of the command +void set_cmd_dflall_range(exarg_T *eap) +{ + buf_T *buf; + + eap->line1 = 1; + switch (eap->addr_type) { + case ADDR_LINES: + case ADDR_OTHER: + eap->line2 = curbuf->b_ml.ml_line_count; + break; + case ADDR_LOADED_BUFFERS: + buf = firstbuf; + while (buf->b_next != NULL && buf->b_ml.ml_mfp == NULL) { + buf = buf->b_next; + } + eap->line1 = buf->b_fnum; + buf = lastbuf; + while (buf->b_prev != NULL && buf->b_ml.ml_mfp == NULL) { + buf = buf->b_prev; + } + eap->line2 = buf->b_fnum; + break; + case ADDR_BUFFERS: + eap->line1 = firstbuf->b_fnum; + eap->line2 = lastbuf->b_fnum; + break; + case ADDR_WINDOWS: + eap->line2 = LAST_WIN_NR; + break; + case ADDR_TABS: + eap->line2 = LAST_TAB_NR; + break; + case ADDR_TABS_RELATIVE: + eap->line2 = 1; + break; + case ADDR_ARGUMENTS: + if (ARGCOUNT == 0) { + eap->line1 = eap->line2 = 0; + } else { + eap->line2 = ARGCOUNT; + } + break; + case ADDR_QUICKFIX_VALID: + eap->line2 = (linenr_T)qf_get_valid_size(eap); + if (eap->line2 == 0) { + eap->line2 = 1; + } + break; + case ADDR_NONE: + case ADDR_UNSIGNED: + case ADDR_QUICKFIX: + iemsg(_("INTERNAL: Cannot use EX_DFLALL " + "with ADDR_NONE, ADDR_UNSIGNED or ADDR_QUICKFIX")); + break; + } +} + +static void parse_register(exarg_T *eap) +{ + // Accept numbered register only when no count allowed (:put) + if ((eap->argt & EX_REGSTR) + && *eap->arg != NUL + // Do not allow register = for user commands + && (!IS_USER_CMDIDX(eap->cmdidx) || *eap->arg != '=') + && !((eap->argt & EX_COUNT) && ascii_isdigit(*eap->arg))) { + if (valid_yank_reg(*eap->arg, (eap->cmdidx != CMD_put + && !IS_USER_CMDIDX(eap->cmdidx)))) { + eap->regname = (uint8_t)(*eap->arg++); + // for '=' register: accept the rest of the line as an expression + if (eap->arg[-1] == '=' && eap->arg[0] != NUL) { + if (!eap->skip) { + set_expr_line(vim_strsave((char_u *)eap->arg)); + } + eap->arg += STRLEN(eap->arg); + } + eap->arg = skipwhite(eap->arg); + } + } +} + +// Change line1 and line2 of Ex command to use count +void set_cmd_count(exarg_T *eap, long count, bool validate) +{ + if (eap->addr_type != ADDR_LINES) { // e.g. :buffer 2, :sleep 3 + eap->line2 = (linenr_T)count; + if (eap->addr_count == 0) { + eap->addr_count = 1; + } + } else { + eap->line1 = eap->line2; + eap->line2 += (linenr_T)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) { + eap->line2 = curbuf->b_ml.ml_line_count; + } + } +} + +static int parse_count(exarg_T *eap, 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. + char *p; + long n; + + if ((eap->argt & EX_COUNT) && ascii_isdigit(*eap->arg) + && (!(eap->argt & EX_BUFNAME) || *(p = skipdigits(eap->arg + 1)) == NUL + || ascii_iswhite(*p))) { + n = getdigits_long((char_u **)&eap->arg, false, -1); + eap->arg = skipwhite(eap->arg); + if (n <= 0 && (eap->argt & EX_ZEROR) == 0) { + if (errormsg != NULL) { + *errormsg = _(e_zerocount); + } + return FAIL; + } + set_cmd_count(eap, n, validate); + } + + return OK; +} + +/// Check if command is not implemented +bool is_cmd_ni(cmdidx_T cmdidx) +{ + return !IS_USER_CMDIDX(cmdidx) && (cmdnames[cmdidx].cmd_func == ex_ni + || cmdnames[cmdidx].cmd_func == ex_script_ni); +} + +/// Parse command line and return information about the first command. +/// If parsing is done successfully, need to free cmod_filter_pat and 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 +/// @param[out] cmdinfo Command parse information +/// @param[out] errormsg Error message, if any +/// +/// @return Success or failure +bool parse_cmdline(char *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo, char **errormsg) +{ + char *cmd; + char *p; + char *after_modifier = NULL; + + // Initialize cmdinfo + memset(cmdinfo, 0, sizeof(*cmdinfo)); + + // Initialize eap + memset(eap, 0, sizeof(*eap)); + eap->line1 = 1; + eap->line2 = 1; + eap->cmd = cmdline; + eap->cmdlinep = &cmdline; + eap->getline = NULL; + eap->cookie = NULL; + + const bool save_ex_pressedreturn = ex_pressedreturn; + // Parse command modifiers + if (parse_command_modifiers(eap, errormsg, &cmdinfo->cmdmod, false) == FAIL) { + ex_pressedreturn = save_ex_pressedreturn; + goto err; + } + ex_pressedreturn = save_ex_pressedreturn; + after_modifier = eap->cmd; + + // Save location after command modifiers + cmd = eap->cmd; + // Skip ranges to find command name since we need the command to know what kind of range it uses + eap->cmd = skip_range(eap->cmd, NULL); + if (*eap->cmd == '*') { + eap->cmd = skipwhite(eap->cmd + 1); + } + p = find_ex_command(eap, NULL); + if (p == NULL) { + *errormsg = _(e_ambiguous_use_of_user_defined_command); + goto err; + } + + // Set command address type and parse command range + set_cmd_addr_type(eap, p); + eap->cmd = cmd; + if (parse_cmd_address(eap, errormsg, false) == FAIL) { + goto err; + } + + // Skip colon and whitespace + eap->cmd = skip_colon_white(eap->cmd, true); + // Fail if command is a comment or if command doesn't exist + if (*eap->cmd == NUL || *eap->cmd == '"') { + goto err; + } + // Fail if command is invalid + if (eap->cmdidx == CMD_SIZE) { + STRCPY(IObuff, _(e_not_an_editor_command)); + // If the modifier was parsed OK the error must be in the following command + char *cmdname = after_modifier ? after_modifier : cmdline; + append_command(cmdname); + *errormsg = (char *)IObuff; + goto err; + } + + // Correctly set 'forceit' for commands + if (*p == '!' && eap->cmdidx != CMD_substitute + && eap->cmdidx != CMD_smagic && eap->cmdidx != CMD_snomagic) { + p++; + eap->forceit = true; + } else { + eap->forceit = false; + } + + // Parse arguments. + if (!IS_USER_CMDIDX(eap->cmdidx)) { + eap->argt = cmdnames[(int)eap->cmdidx].cmd_argt; + } + // Skip to start of argument. + // Don't do this for the ":!" command, because ":!! -l" needs the space. + if (eap->cmdidx == CMD_bang) { + eap->arg = p; + } else { + eap->arg = skipwhite(p); + } + + // Don't treat ":r! filter" like a bang + if (eap->cmdidx == CMD_read) { + if (eap->forceit) { + eap->forceit = false; // :r! filter + } + } + + // Check for '|' to separate commands and '"' to start comments. + // Don't do this for ":read !cmd" and ":write !cmd". + if ((eap->argt & EX_TRLBAR)) { + separate_nextcmd(eap); + } + // Fail if command doesn't support bang but is used with a bang + if (!(eap->argt & EX_BANG) && eap->forceit) { + *errormsg = _(e_nobang); + goto err; + } + // Fail if command doesn't support a range but it is given a range + if (!(eap->argt & EX_RANGE) && eap->addr_count > 0) { + *errormsg = _(e_norange); + goto err; + } + // Set default range for command if required + if ((eap->argt & EX_DFLALL) && eap->addr_count == 0) { + set_cmd_dflall_range(eap); + } + + // Parse register and count + parse_register(eap); + if (parse_count(eap, errormsg, false) == FAIL) { + goto err; + } + + // Remove leading whitespace and colon from next command + if (eap->nextcmd) { + eap->nextcmd = skip_colon_white(eap->nextcmd, true); + } + + // Set the "magic" values (characters that get treated specially) + if (eap->argt & EX_XFILE) { + cmdinfo->magic.file = true; + } + if (eap->argt & EX_TRLBAR) { + cmdinfo->magic.bar = true; + } + + return true; +err: + undo_cmdmod(&cmdinfo->cmdmod); + return false; +} + +/// Execute an Ex command using parsed command line information. +/// Does not do any validation of the Ex command arguments. +/// +/// @param eap Ex-command arguments +/// @param cmdinfo Command parse information +/// @param preview Execute command preview callback instead of actual command +int execute_cmd(exarg_T *eap, CmdParseInfo *cmdinfo, bool preview) +{ + char *errormsg = NULL; + int retv = 0; + +#define ERROR(msg) \ + do { \ + errormsg = msg; \ + goto end; \ + } while (0) + + cmdmod_T save_cmdmod = cmdmod; + cmdmod = cmdinfo->cmdmod; + + // Apply command modifiers + apply_cmdmod(&cmdmod); + + if (!MODIFIABLE(curbuf) && (eap->argt & EX_MODIFY) + // allow :put in terminals + && !(curbuf->terminal && eap->cmdidx == CMD_put)) { + ERROR(_(e_modifiable)); + } + if (!IS_USER_CMDIDX(eap->cmdidx)) { + if (cmdwin_type != 0 && !(eap->argt & EX_CMDWIN)) { + // Command not allowed in the command line window + ERROR(_(e_cmdwin)); + } + if (text_locked() && !(eap->argt & EX_LOCK_OK)) { + // Command not allowed when text is locked + ERROR(_(get_text_locked_msg())); + } + } + // Disallow editing another buffer when "curbuf->b_ro_locked" is set. + // Do allow ":checktime" (it is postponed). + // Do allow ":edit" (check for an argument later). + // Do allow ":file" with no arguments + if (!(eap->argt & EX_CMDWIN) + && eap->cmdidx != CMD_checktime + && eap->cmdidx != CMD_edit + && !(eap->cmdidx == CMD_file && *eap->arg == NUL) + && !IS_USER_CMDIDX(eap->cmdidx) + && curbuf_locked()) { + ERROR(_(e_cannot_edit_other_buf)); + } + + if (((eap->argt & EX_WHOLEFOLD) || eap->addr_count >= 2) && !global_busy + && eap->addr_type == ADDR_LINES) { + // Put the first line at the start of a closed fold, put the last line + // at the end of a closed fold. + (void)hasFolding(eap->line1, &eap->line1, NULL); + (void)hasFolding(eap->line2, NULL, &eap->line2); + } + + // If filename expansion is enabled, expand filenames + if (cmdinfo->magic.file) { + if (expand_filename(eap, (char_u **)eap->cmdlinep, &errormsg) == FAIL) { + goto end; + } + } + + // Accept buffer name. Cannot be used at the same time with a buffer + // number. Don't do this for a user command. + if ((eap->argt & EX_BUFNAME) && *eap->arg != NUL && eap->addr_count == 0 + && !IS_USER_CMDIDX(eap->cmdidx)) { + if (eap->args == NULL) { + // If argument positions are not specified, search the argument for the buffer name. + // :bdelete, :bwipeout and :bunload take several arguments, separated by spaces: + // find next space (skipping over escaped characters). + // The others take one argument: ignore trailing spaces. + char *p; + + if (eap->cmdidx == CMD_bdelete || eap->cmdidx == CMD_bwipeout + || eap->cmdidx == CMD_bunload) { + p = skiptowhite_esc(eap->arg); + } else { + p = eap->arg + STRLEN(eap->arg); + while (p > eap->arg && ascii_iswhite(p[-1])) { + p--; + } + } + eap->line2 = buflist_findpat(eap->arg, p, (eap->argt & EX_BUFUNL) != 0, + false, false); + eap->addr_count = 1; + eap->arg = skipwhite(p); + } else { + // If argument positions are specified, just use the first argument + eap->line2 = buflist_findpat(eap->args[0], + eap->args[0] + eap->arglens[0], + (eap->argt & EX_BUFUNL) != 0, false, false); + eap->addr_count = 1; + // Shift each argument by 1 + for (size_t i = 0; i < eap->argc - 1; i++) { + eap->args[i] = eap->args[i + 1]; + } + // Make the last argument point to the NUL terminator at the end of string + eap->args[eap->argc - 1] = eap->args[eap->argc - 1] + eap->arglens[eap->argc - 1]; + eap->argc -= 1; + + eap->arg = eap->args[0]; + } + if (eap->line2 < 0) { // failed + goto end; + } + } + + // Execute the command + if (IS_USER_CMDIDX(eap->cmdidx)) { + // Execute a user-defined command. + retv = do_ucmd(eap, preview); + } else { + // Call the function to execute the command or the preview callback. + eap->errmsg = NULL; + + if (preview) { + retv = (cmdnames[eap->cmdidx].cmd_preview_func)(eap, cmdpreview_get_ns(), + cmdpreview_get_bufnr()); + } else { + (cmdnames[eap->cmdidx].cmd_func)(eap); + } + if (eap->errmsg != NULL) { + errormsg = _(eap->errmsg); + } + } + +end: + if (errormsg != NULL && *errormsg != NUL) { + emsg(errormsg); + } + // Undo command modifiers + undo_cmdmod(&cmdmod); + cmdmod = save_cmdmod; + return retv; +#undef ERROR } /// Execute one Ex command. @@ -1236,19 +1720,18 @@ static char_u *skip_colon_white(const char_u *p, bool skipleadingwhite) /// This function may be called recursively! /// /// @param cookie argument for fgetline() -static char_u *do_one_cmd(char_u **cmdlinep, int flags, cstack_T *cstack, LineGetter fgetline, - void *cookie) +static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter fgetline, + void *cookie) { - char_u *p; + char *p; linenr_T lnum; - long n; char *errormsg = NULL; // error message - char_u *after_modifier = NULL; + char *after_modifier = NULL; exarg_T ea; - const int save_msg_scroll = msg_scroll; cmdmod_T save_cmdmod; const int save_reg_executing = reg_executing; - char_u *cmd; + const bool save_pending_end_reg_executing = pending_end_reg_executing; + char *cmd; memset(&ea, 0, sizeof(ea)); ea.line1 = 1; @@ -1286,9 +1769,10 @@ static char_u *do_one_cmd(char_u **cmdlinep, int flags, cstack_T *cstack, LineGe ea.cookie = cookie; ea.cstack = cstack; - if (parse_command_modifiers(&ea, &errormsg, false) == FAIL) { + if (parse_command_modifiers(&ea, &errormsg, &cmdmod, false) == FAIL) { goto doend; } + apply_cmdmod(&cmdmod); after_modifier = ea.cmd; @@ -1306,7 +1790,7 @@ static char_u *do_one_cmd(char_u **cmdlinep, int flags, cstack_T *cstack, LineGe if (*ea.cmd == '*') { ea.cmd = skipwhite(ea.cmd + 1); } - p = find_command(&ea, NULL); + p = find_ex_command(&ea, NULL); // Count this line for profiling if skip is TRUE. if (do_profiling == PROF_YES @@ -1363,23 +1847,7 @@ static char_u *do_one_cmd(char_u **cmdlinep, int flags, cstack_T *cstack, LineGe // The ea.cmd pointer is updated to point to the first character following the // range spec. If an initial address is found, but no second, the upper bound // is equal to the lower. - - // ea.addr_type for user commands is set by find_ucmd - if (!IS_USER_CMDIDX(ea.cmdidx)) { - if (ea.cmdidx != CMD_SIZE) { - ea.addr_type = cmdnames[(int)ea.cmdidx].cmd_addr_type; - } else { - ea.addr_type = ADDR_LINES; - } - // :wincmd range depends on the argument - if (ea.cmdidx == CMD_wincmd && p != NULL) { - get_wincmd_addr_type(skipwhite(p), &ea); - } - // :.cc in quickfix window uses line number - if ((ea.cmdidx == CMD_cc || ea.cmdidx == CMD_ll) && bt_quickfix(curbuf)) { - ea.addr_type = ADDR_OTHER; - } - } + set_cmd_addr_type(&ea, p); ea.cmd = cmd; if (parse_cmd_address(&ea, &errormsg, false) == FAIL) { @@ -1400,7 +1868,7 @@ static char_u *do_one_cmd(char_u **cmdlinep, int flags, cstack_T *cstack, LineGe * If we find a '|' or '\n' we set ea.nextcmd. */ if (*ea.cmd == NUL || *ea.cmd == '"' - || (ea.nextcmd = check_nextcmd(ea.cmd)) != NULL) { + || (ea.nextcmd = (char *)check_nextcmd((char_u *)ea.cmd)) != NULL) { // strange vi behaviour: // ":3" jumps to line 3 // ":3|..." prints line 3 @@ -1443,27 +1911,27 @@ static char_u *do_one_cmd(char_u **cmdlinep, int flags, cstack_T *cstack, LineGe while (ASCII_ISALNUM(*p)) { ++p; } - p = vim_strnsave(ea.cmd, p - ea.cmd); + p = xstrnsave(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 // finding the command again. - p = (ret && !aborting()) ? find_command(&ea, NULL) : ea.cmd; + p = (ret && !aborting()) ? find_ex_command(&ea, NULL) : ea.cmd; } if (p == NULL) { if (!ea.skip) { - errormsg = _("E464: Ambiguous use of user-defined command"); + errormsg = _(e_ambiguous_use_of_user_defined_command); } goto doend; } // Check for wrong commands. if (ea.cmdidx == CMD_SIZE) { if (!ea.skip) { - STRCPY(IObuff, _("E492: Not an editor command")); + STRCPY(IObuff, _(e_not_an_editor_command)); // If the modifier was parsed OK the error must be in the following // command - char_u *cmdname = after_modifier ? after_modifier : *cmdlinep; + char *cmdname = after_modifier ? after_modifier : *cmdlinep; if (!(flags & DOCMD_VERBOSE)) { append_command(cmdname); } @@ -1475,10 +1943,7 @@ static char_u *do_one_cmd(char_u **cmdlinep, int flags, cstack_T *cstack, LineGe } // set when Not Implemented - const int ni = !IS_USER_CMDIDX(ea.cmdidx) - && (cmdnames[ea.cmdidx].cmd_func == ex_ni - || cmdnames[ea.cmdidx].cmd_func == ex_script_ni); - + const int ni = is_cmd_ni(ea.cmdidx); // Forced commands. if (*p == '!' && ea.cmdidx != CMD_substitute @@ -1508,11 +1973,17 @@ static char_u *do_one_cmd(char_u **cmdlinep, int flags, cstack_T *cstack, LineGe goto doend; } - if (text_locked() && !(ea.argt & EX_CMDWIN) - && !IS_USER_CMDIDX(ea.cmdidx)) { - // Command not allowed when editing the command line. - errormsg = _(get_text_locked_msg()); - goto doend; + if (!IS_USER_CMDIDX(ea.cmdidx)) { + if (cmdwin_type != 0 && !(ea.argt & EX_CMDWIN)) { + // Command not allowed in the command line window + errormsg = _(e_cmdwin); + goto doend; + } + if (text_locked() && !(ea.argt & EX_LOCK_OK)) { + // Command not allowed when text is locked + errormsg = _(get_text_locked_msg()); + goto doend; + } } // Disallow editing another buffer when "curbuf->b_ro_locked" is set. @@ -1626,7 +2097,7 @@ static char_u *do_one_cmd(char_u **cmdlinep, int flags, cstack_T *cstack, LineGe goto doend; } ea.arg = skipwhite(ea.arg + 1); - ea.append = TRUE; + ea.append = true; } else if (*ea.arg == '!' && ea.cmdidx == CMD_write) { // :w !filter ++ea.arg; ea.usefilter = TRUE; @@ -1646,8 +2117,8 @@ static char_u *do_one_cmd(char_u **cmdlinep, int flags, cstack_T *cstack, LineGe if (ea.cmdidx == CMD_lshift || ea.cmdidx == CMD_rshift) { ea.amount = 1; while (*ea.arg == *ea.cmd) { // count number of '>' or '<' - ++ea.arg; - ++ea.amount; + ea.arg++; + ea.amount++; } ea.arg = skipwhite(ea.arg); } @@ -1692,106 +2163,13 @@ static char_u *do_one_cmd(char_u **cmdlinep, int flags, cstack_T *cstack, LineGe } if ((ea.argt & EX_DFLALL) && ea.addr_count == 0) { - buf_T *buf; - - ea.line1 = 1; - switch (ea.addr_type) { - case ADDR_LINES: - case ADDR_OTHER: - ea.line2 = curbuf->b_ml.ml_line_count; - break; - case ADDR_LOADED_BUFFERS: - buf = firstbuf; - while (buf->b_next != NULL && buf->b_ml.ml_mfp == NULL) { - buf = buf->b_next; - } - ea.line1 = buf->b_fnum; - buf = lastbuf; - while (buf->b_prev != NULL && buf->b_ml.ml_mfp == NULL) { - buf = buf->b_prev; - } - ea.line2 = buf->b_fnum; - break; - case ADDR_BUFFERS: - ea.line1 = firstbuf->b_fnum; - ea.line2 = lastbuf->b_fnum; - break; - case ADDR_WINDOWS: - ea.line2 = LAST_WIN_NR; - break; - case ADDR_TABS: - ea.line2 = LAST_TAB_NR; - break; - case ADDR_TABS_RELATIVE: - ea.line2 = 1; - break; - case ADDR_ARGUMENTS: - if (ARGCOUNT == 0) { - ea.line1 = ea.line2 = 0; - } else { - ea.line2 = ARGCOUNT; - } - break; - case ADDR_QUICKFIX_VALID: - ea.line2 = qf_get_valid_size(&ea); - if (ea.line2 == 0) { - ea.line2 = 1; - } - break; - case ADDR_NONE: - case ADDR_UNSIGNED: - case ADDR_QUICKFIX: - iemsg(_("INTERNAL: Cannot use EX_DFLALL " - "with ADDR_NONE, ADDR_UNSIGNED or ADDR_QUICKFIX")); - break; - } - } - - // accept numbered register only when no count allowed (:put) - if ((ea.argt & EX_REGSTR) - && *ea.arg != NUL - // Do not allow register = for user commands - && (!IS_USER_CMDIDX(ea.cmdidx) || *ea.arg != '=') - && !((ea.argt & EX_COUNT) && ascii_isdigit(*ea.arg))) { - if (valid_yank_reg(*ea.arg, (ea.cmdidx != CMD_put - && !IS_USER_CMDIDX(ea.cmdidx)))) { - ea.regname = *ea.arg++; - // for '=' register: accept the rest of the line as an expression - if (ea.arg[-1] == '=' && ea.arg[0] != NUL) { - set_expr_line(vim_strsave(ea.arg)); - ea.arg += STRLEN(ea.arg); - } - ea.arg = skipwhite(ea.arg); - } + set_cmd_dflall_range(&ea); } - // - // Check for a count. When accepting a EX_BUFNAME, don't use "123foo" as a - // count, it's a buffer name. - /// - if ((ea.argt & EX_COUNT) && ascii_isdigit(*ea.arg) - && (!(ea.argt & EX_BUFNAME) || *(p = skipdigits(ea.arg + 1)) == NUL - || ascii_iswhite(*p))) { - n = getdigits_long(&ea.arg, false, -1); - ea.arg = skipwhite(ea.arg); - if (n <= 0 && !ni && (ea.argt & EX_ZEROR) == 0) { - errormsg = _(e_zerocount); - goto doend; - } - if (ea.addr_type != ADDR_LINES) { // e.g. :buffer 2, :sleep 3 - ea.line2 = n; - if (ea.addr_count == 0) { - ea.addr_count = 1; - } - } else { - ea.line1 = ea.line2; - ea.line2 += n - 1; - ++ea.addr_count; - // Be vi compatible: no error message for out of range. - if (ea.line2 > curbuf->b_ml.ml_line_count) { - ea.line2 = curbuf->b_ml.ml_line_count; - } - } + // Parse register and count + parse_register(&ea); + if (parse_count(&ea, &errormsg, true) == FAIL) { + goto doend; } /* @@ -1911,7 +2289,7 @@ static char_u *do_one_cmd(char_u **cmdlinep, int flags, cstack_T *cstack, LineGe } if (ea.argt & EX_XFILE) { - if (expand_filename(&ea, cmdlinep, &errormsg) == FAIL) { + if (expand_filename(&ea, (char_u **)cmdlinep, &errormsg) == FAIL) { goto doend; } } @@ -1933,7 +2311,7 @@ static char_u *do_one_cmd(char_u **cmdlinep, int flags, cstack_T *cstack, LineGe } else { p = ea.arg + STRLEN(ea.arg); while (p > ea.arg && ascii_iswhite(p[-1])) { - --p; + p--; } } ea.line2 = buflist_findpat(ea.arg, p, (ea.argt & EX_BUFUNL) != 0, @@ -1947,12 +2325,12 @@ static char_u *do_one_cmd(char_u **cmdlinep, int flags, cstack_T *cstack, LineGe // The :try command saves the emsg_silent flag, reset it here when // ":silent! try" was used, it should only apply to :try itself. - if (ea.cmdidx == CMD_try && ea.did_esilent > 0) { - emsg_silent -= ea.did_esilent; + if (ea.cmdidx == CMD_try && cmdmod.cmod_did_esilent > 0) { + emsg_silent -= cmdmod.cmod_did_esilent; if (emsg_silent < 0) { emsg_silent = 0; } - ea.did_esilent = 0; + cmdmod.cmod_did_esilent = 0; } // 7. Execute the command. @@ -1960,7 +2338,7 @@ static char_u *do_one_cmd(char_u **cmdlinep, int flags, cstack_T *cstack, LineGe /* * Execute a user-defined command. */ - do_ucmd(&ea); + do_ucmd(&ea, false); } else { /* * Call the function to execute the command. @@ -1989,7 +2367,7 @@ static char_u *do_one_cmd(char_u **cmdlinep, int flags, cstack_T *cstack, LineGe do_return(&ea, TRUE, FALSE, NULL); } } - need_rethrow = check_cstack = FALSE; + need_rethrow = check_cstack = false; doend: // can happen with zero line number @@ -2009,17 +2387,13 @@ doend: emsg(errormsg); } do_errthrow(cstack, - (ea.cmdidx != CMD_SIZE && !IS_USER_CMDIDX(ea.cmdidx)) - ? cmdnames[(int)ea.cmdidx].cmd_name - : (char_u *)NULL); + (ea.cmdidx != CMD_SIZE + && !IS_USER_CMDIDX(ea.cmdidx)) ? cmdnames[(int)ea.cmdidx].cmd_name : NULL); - undo_cmdmod(&ea, save_msg_scroll); + undo_cmdmod(&cmdmod); cmdmod = save_cmdmod; reg_executing = save_reg_executing; - - if (ea.did_sandbox) { - sandbox--; - } + pending_end_reg_executing = save_pending_end_reg_executing; if (ea.nextcmd && *ea.nextcmd == NUL) { // not really a next command ea.nextcmd = NULL; @@ -2030,25 +2404,40 @@ doend: return ea.nextcmd; } -// Parse and skip over command modifiers: -// - update eap->cmd -// - store flags in "cmdmod". -// - Set ex_pressedreturn for an empty command line. -// - set msg_silent for ":silent" -// - set 'eventignore' to "all" for ":noautocmd" -// - set p_verbose for ":verbose" -// - Increment "sandbox" for ":sandbox" -// When "skip_only" is true the global variables are not changed, except for -// "cmdmod". -// Return FAIL when the command is not to be executed. -// May set "errormsg" to an error message. -int parse_command_modifiers(exarg_T *eap, char **errormsg, bool skip_only) +static char ex_error_buf[MSG_BUF_LEN]; + +/// @return an error message with argument included. +/// Uses a static buffer, only the last error will be kept. +/// "msg" will be translated, caller should use N_(). +char *ex_errmsg(const char *const msg, const char *const arg) + FUNC_ATTR_NONNULL_ALL +{ + vim_snprintf(ex_error_buf, MSG_BUF_LEN, _(msg), arg); + return ex_error_buf; +} + +/// Parse and skip over command modifiers: +/// - update eap->cmd +/// - store flags in "cmod". +/// - Set ex_pressedreturn for an empty command line. +/// +/// @param skip_only if false, undo_cmdmod() must be called later to free +/// any cmod_filter_pat and cmod_filter_regmatch.regprog, +/// and ex_pressedreturn may be set. +/// @param[out] errormsg potential error message. +/// +/// Call apply_cmdmod() to get the side effects of the modifiers: +/// - Increment "sandbox" for ":sandbox" +/// - set p_verbose for ":verbose" +/// - set msg_silent for ":silent" +/// - 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) { - char_u *p; + char *p; - memset(&cmdmod, 0, sizeof(cmdmod)); - eap->verbose_save = -1; - eap->save_msg_silent = -1; + CLEAR_POINTER(cmod); // Repeat until no more command modifiers are found. for (;;) { @@ -2062,7 +2451,7 @@ int parse_command_modifiers(exarg_T *eap, char **errormsg, bool skip_only) if (*eap->cmd == NUL && exmode_active && getline_equal(eap->getline, eap->cookie, getexline) && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) { - eap->cmd = (char_u *)"+"; + eap->cmd = "+"; if (!skip_only) { ex_pressedreturn = true; } @@ -2086,58 +2475,58 @@ int parse_command_modifiers(exarg_T *eap, char **errormsg, bool skip_only) if (!checkforcmd(&eap->cmd, "aboveleft", 3)) { break; } - cmdmod.split |= WSP_ABOVE; + cmod->cmod_split |= WSP_ABOVE; continue; case 'b': if (checkforcmd(&eap->cmd, "belowright", 3)) { - cmdmod.split |= WSP_BELOW; + cmod->cmod_split |= WSP_BELOW; continue; } if (checkforcmd(&eap->cmd, "browse", 3)) { - cmdmod.browse = true; + cmod->cmod_flags |= CMOD_BROWSE; continue; } if (!checkforcmd(&eap->cmd, "botright", 2)) { break; } - cmdmod.split |= WSP_BOT; + cmod->cmod_split |= WSP_BOT; continue; case 'c': if (!checkforcmd(&eap->cmd, "confirm", 4)) { break; } - cmdmod.confirm = true; + cmod->cmod_flags |= CMOD_CONFIRM; continue; case 'k': if (checkforcmd(&eap->cmd, "keepmarks", 3)) { - cmdmod.keepmarks = true; + cmod->cmod_flags |= CMOD_KEEPMARKS; continue; } if (checkforcmd(&eap->cmd, "keepalt", 5)) { - cmdmod.keepalt = true; + cmod->cmod_flags |= CMOD_KEEPALT; continue; } if (checkforcmd(&eap->cmd, "keeppatterns", 5)) { - cmdmod.keeppatterns = true; + cmod->cmod_flags |= CMOD_KEEPPATTERNS; continue; } if (!checkforcmd(&eap->cmd, "keepjumps", 5)) { break; } - cmdmod.keepjumps = true; + cmod->cmod_flags |= CMOD_KEEPJUMPS; continue; case 'f': { // only accept ":filter {pat} cmd" - char_u *reg_pat; + char *reg_pat; if (!checkforcmd(&p, "filter", 4) || *p == NUL || ends_excmd(*p)) { break; } if (*p == '!') { - cmdmod.filter_force = true; + cmod->cmod_filter_force = true; p = skipwhite(p + 1); if (*p == NUL || ends_excmd(*p)) { break; @@ -2153,8 +2542,9 @@ int parse_command_modifiers(exarg_T *eap, char **errormsg, bool skip_only) break; } if (!skip_only) { - cmdmod.filter_regmatch.regprog = vim_regcomp(reg_pat, RE_MAGIC); - if (cmdmod.filter_regmatch.regprog == NULL) { + cmod->cmod_filter_pat = xstrdup(reg_pat); + cmod->cmod_filter_regmatch.regprog = vim_regcomp(reg_pat, RE_MAGIC); + if (cmod->cmod_filter_regmatch.regprog == NULL) { break; } } @@ -2169,87 +2559,69 @@ int parse_command_modifiers(exarg_T *eap, char **errormsg, bool skip_only) break; } eap->cmd = p; - cmdmod.hide = true; + cmod->cmod_flags |= CMOD_HIDE; continue; case 'l': if (checkforcmd(&eap->cmd, "lockmarks", 3)) { - cmdmod.lockmarks = true; + cmod->cmod_flags |= CMOD_LOCKMARKS; continue; } if (!checkforcmd(&eap->cmd, "leftabove", 5)) { break; } - cmdmod.split |= WSP_ABOVE; + cmod->cmod_split |= WSP_ABOVE; continue; case 'n': if (checkforcmd(&eap->cmd, "noautocmd", 3)) { - if (cmdmod.save_ei == NULL && !skip_only) { - // Set 'eventignore' to "all". Restore the - // existing option value later. - cmdmod.save_ei = vim_strsave(p_ei); - set_string_option_direct("ei", -1, - (char_u *)"all", OPT_FREE, SID_NONE); - } + cmod->cmod_flags |= CMOD_NOAUTOCMD; continue; } if (!checkforcmd(&eap->cmd, "noswapfile", 3)) { break; } - cmdmod.noswapfile = true; + cmod->cmod_flags |= CMOD_NOSWAPFILE; continue; case 'r': if (!checkforcmd(&eap->cmd, "rightbelow", 6)) { break; } - cmdmod.split |= WSP_BELOW; + cmod->cmod_split |= WSP_BELOW; continue; case 's': if (checkforcmd(&eap->cmd, "sandbox", 3)) { - if (!skip_only) { - if (!eap->did_sandbox) { - sandbox++; - } - eap->did_sandbox = true; - } + cmod->cmod_flags |= CMOD_SANDBOX; continue; } if (!checkforcmd(&eap->cmd, "silent", 3)) { break; } - if (!skip_only) { - if (eap->save_msg_silent == -1) { - eap->save_msg_silent = msg_silent; - } - msg_silent++; - } + cmod->cmod_flags |= CMOD_SILENT; if (*eap->cmd == '!' && !ascii_iswhite(eap->cmd[-1])) { // ":silent!", but not "silent !cmd" eap->cmd = skipwhite(eap->cmd + 1); - if (!skip_only) { - emsg_silent++; - eap->did_esilent++; - } + cmod->cmod_flags |= CMOD_ERRSILENT; } continue; case 't': if (checkforcmd(&p, "tab", 3)) { if (!skip_only) { - long tabnr = get_address(eap, &eap->cmd, ADDR_TABS, eap->skip, skip_only, false, 1); + int tabnr = (int)get_address(eap, &eap->cmd, ADDR_TABS, eap->skip, skip_only, + false, 1); if (tabnr == MAXLNUM) { - cmdmod.tab = tabpage_index(curtab) + 1; + cmod->cmod_tab = tabpage_index(curtab) + 1; } else { if (tabnr < 0 || tabnr > LAST_TAB_NR) { *errormsg = _(e_invrange); return false; } - cmdmod.tab = tabnr + 1; + cmod->cmod_tab = tabnr + 1; } } eap->cmd = p; @@ -2258,38 +2630,29 @@ int parse_command_modifiers(exarg_T *eap, char **errormsg, bool skip_only) if (!checkforcmd(&eap->cmd, "topleft", 2)) { break; } - cmdmod.split |= WSP_TOP; + cmod->cmod_split |= WSP_TOP; continue; case 'u': if (!checkforcmd(&eap->cmd, "unsilent", 3)) { break; } - if (!skip_only) { - if (eap->save_msg_silent == -1) { - eap->save_msg_silent = msg_silent; - } - msg_silent = 0; - } + cmod->cmod_flags |= CMOD_UNSILENT; continue; case 'v': if (checkforcmd(&eap->cmd, "vertical", 4)) { - cmdmod.split |= WSP_VERT; + cmod->cmod_split |= WSP_VERT; continue; } if (!checkforcmd(&p, "verbose", 4)) { break; } - if (!skip_only) { - if (eap->verbose_save < 0) { - eap->verbose_save = p_verbose; - } - if (ascii_isdigit(*eap->cmd)) { - p_verbose = atoi((char *)eap->cmd); - } else { - p_verbose = 1; - } + if (ascii_isdigit(*eap->cmd)) { + // zero means not set, one is verbose == 0, etc. + cmod->cmod_verbose = atoi(eap->cmd) + 1; + } else { + cmod->cmod_verbose = 2; // default: verbose == 1 } eap->cmd = p; continue; @@ -2300,98 +2663,116 @@ int parse_command_modifiers(exarg_T *eap, char **errormsg, bool skip_only) return OK; } -// Undo and free contents of "cmdmod". -static void undo_cmdmod(const exarg_T *eap, int save_msg_scroll) +/// Apply the command modifiers. Saves current state in "cmdmod", call +/// undo_cmdmod() later. +static void apply_cmdmod(cmdmod_T *cmod) +{ + if ((cmod->cmod_flags & CMOD_SANDBOX) && !cmod->cmod_did_sandbox) { + sandbox++; + cmod->cmod_did_sandbox = true; + } + if (cmod->cmod_verbose > 0) { + if (cmod->cmod_verbose_save == 0) { + cmod->cmod_verbose_save = p_verbose + 1; + } + p_verbose = cmod->cmod_verbose - 1; + } + + if ((cmod->cmod_flags & (CMOD_SILENT | CMOD_UNSILENT)) + && cmod->cmod_save_msg_silent == 0) { + cmod->cmod_save_msg_silent = msg_silent + 1; + cmod->cmod_save_msg_scroll = msg_scroll; + } + if (cmod->cmod_flags & CMOD_SILENT) { + msg_silent++; + } + if (cmod->cmod_flags & CMOD_UNSILENT) { + msg_silent = 0; + } + + if (cmod->cmod_flags & CMOD_ERRSILENT) { + emsg_silent++; + cmod->cmod_did_esilent++; + } + + if ((cmod->cmod_flags & CMOD_NOAUTOCMD) && cmod->cmod_save_ei == NULL) { + // Set 'eventignore' to "all". + // First save the existing option value for restoring it later. + cmod->cmod_save_ei = (char *)vim_strsave(p_ei); + set_string_option_direct("ei", -1, "all", OPT_FREE, SID_NONE); + } +} + +/// Undo and free contents of "cmod". +void undo_cmdmod(cmdmod_T *cmod) FUNC_ATTR_NONNULL_ALL { - if (eap->verbose_save >= 0) { - p_verbose = eap->verbose_save; + if (cmod->cmod_verbose_save > 0) { + p_verbose = cmod->cmod_verbose_save - 1; + cmod->cmod_verbose_save = 0; + } + + if (cmod->cmod_did_sandbox) { + sandbox--; + cmod->cmod_did_sandbox = false; } - if (cmdmod.save_ei != NULL) { + if (cmod->cmod_save_ei != NULL) { // Restore 'eventignore' to the value before ":noautocmd". - set_string_option_direct("ei", -1, cmdmod.save_ei, OPT_FREE, SID_NONE); - free_string_option(cmdmod.save_ei); + set_string_option_direct("ei", -1, cmod->cmod_save_ei, OPT_FREE, SID_NONE); + free_string_option((char_u *)cmod->cmod_save_ei); + cmod->cmod_save_ei = NULL; } - vim_regfree(cmdmod.filter_regmatch.regprog); + xfree(cmod->cmod_filter_pat); + vim_regfree(cmod->cmod_filter_regmatch.regprog); - if (eap->save_msg_silent != -1) { + if (cmod->cmod_save_msg_silent > 0) { // messages could be enabled for a serious error, need to check if the // counters don't become negative - if (!did_emsg || msg_silent > eap->save_msg_silent) { - msg_silent = eap->save_msg_silent; + if (!did_emsg || msg_silent > cmod->cmod_save_msg_silent - 1) { + msg_silent = cmod->cmod_save_msg_silent - 1; } - emsg_silent -= eap->did_esilent; + emsg_silent -= cmod->cmod_did_esilent; if (emsg_silent < 0) { emsg_silent = 0; } // Restore msg_scroll, it's set by file I/O commands, even when no // message is actually displayed. - msg_scroll = save_msg_scroll; + msg_scroll = cmod->cmod_save_msg_scroll; // "silent reg" or "silent echo x" inside "redir" leaves msg_col // somewhere in the line. Put it back in the first column. if (redirecting()) { msg_col = 0; } + + cmod->cmod_save_msg_silent = 0; + cmod->cmod_did_esilent = 0; } } - -// Parse the address range, if any, in "eap". -// May set the last search pattern, unless "silent" is true. -// Return FAIL and set "errormsg" or return OK. +/// Parse the address range, if any, in "eap". +/// 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) FUNC_ATTR_NONNULL_ALL { int address_count = 1; linenr_T lnum; + bool need_check_cursor = false; + int ret = FAIL; // Repeat for all ',' or ';' separated addresses. for (;;) { eap->line1 = eap->line2; - switch (eap->addr_type) { - case ADDR_LINES: - case ADDR_OTHER: - // default is current line number - eap->line2 = curwin->w_cursor.lnum; - break; - case ADDR_WINDOWS: - eap->line2 = CURRENT_WIN_NR; - break; - case ADDR_ARGUMENTS: - eap->line2 = curwin->w_arg_idx + 1; - if (eap->line2 > ARGCOUNT) { - eap->line2 = ARGCOUNT; - } - break; - case ADDR_LOADED_BUFFERS: - case ADDR_BUFFERS: - eap->line2 = curbuf->b_fnum; - break; - case ADDR_TABS: - eap->line2 = CURRENT_TAB_NR; - break; - case ADDR_TABS_RELATIVE: - case ADDR_UNSIGNED: - eap->line2 = 1; - break; - case ADDR_QUICKFIX: - eap->line2 = qf_get_cur_idx(eap); - break; - case ADDR_QUICKFIX_VALID: - eap->line2 = qf_get_cur_valid_idx(eap); - break; - case ADDR_NONE: - // Will give an error later if a range is found. - break; - } + 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++); if (eap->cmd == NULL) { // error detected - return FAIL; + goto theend; } if (lnum == MAXLNUM) { if (*eap->cmd == '%') { // '%' - all lines @@ -2430,14 +2811,14 @@ int parse_cmd_address(exarg_T *eap, char **errormsg, bool silent) // there is no Vim command which uses '%' and // ADDR_WINDOWS or ADDR_TABS *errormsg = _(e_invrange); - return FAIL; + goto theend; } break; case ADDR_TABS_RELATIVE: case ADDR_UNSIGNED: case ADDR_QUICKFIX: *errormsg = _(e_invrange); - return FAIL; + goto theend; case ADDR_ARGUMENTS: if (ARGCOUNT == 0) { eap->line1 = eap->line2 = 0; @@ -2448,7 +2829,7 @@ int parse_cmd_address(exarg_T *eap, char **errormsg, bool silent) break; case ADDR_QUICKFIX_VALID: eap->line1 = 1; - eap->line2 = qf_get_valid_size(eap); + eap->line2 = (linenr_T)qf_get_valid_size(eap); if (eap->line2 == 0) { eap->line2 = 1; } @@ -2462,21 +2843,21 @@ int parse_cmd_address(exarg_T *eap, char **errormsg, bool silent) // '*' - visual area if (eap->addr_type != ADDR_LINES) { *errormsg = _(e_invrange); - return FAIL; + goto theend; } eap->cmd++; if (!eap->skip) { - pos_T *fp = getmark('<', false); - if (check_mark(fp) == FAIL) { - return FAIL; + fmark_T *fm = mark_get_visual(curbuf, '<'); + if (!mark_check(fm)) { + goto theend; } - eap->line1 = fp->lnum; - fp = getmark('>', false); - if (check_mark(fp) == FAIL) { - return FAIL; + eap->line1 = fm->mark.lnum; + fm = mark_get_visual(curbuf, '>'); + if (!mark_check(fm)) { + goto theend; } - eap->line2 = fp->lnum; + eap->line2 = fm->mark.lnum; eap->addr_count++; } } @@ -2488,11 +2869,17 @@ int parse_cmd_address(exarg_T *eap, char **errormsg, bool silent) if (*eap->cmd == ';') { if (!eap->skip) { curwin->w_cursor.lnum = eap->line2; + // Don't leave the cursor on an illegal line or column, but do - // accept zero as address, so 0;/PATTERN/ works correctly. + // accept zero as address, so 0;/PATTERN/ works correctly + // (where zero usually means to use the first line). + // Check the cursor position before returning. if (eap->line2 > 0) { check_cursor(); + } else { + check_cursor_col(); } + need_check_cursor = true; } } else if (*eap->cmd != ',') { break; @@ -2508,7 +2895,13 @@ int parse_cmd_address(exarg_T *eap, char **errormsg, bool silent) eap->addr_count = 0; } } - return OK; + ret = OK; + +theend: + if (need_check_cursor) { + check_cursor(); + } + return ret; } /// Check for an Ex command with optional tail. @@ -2517,56 +2910,64 @@ int parse_cmd_address(exarg_T *eap, char **errormsg, bool silent) /// @param pp start of command /// @param cmd name of command /// @param len required length -int checkforcmd(char_u **pp, char *cmd, int len) +int checkforcmd(char **pp, char *cmd, int len) { int i; - for (i = 0; cmd[i] != NUL; ++i) { - if (((char_u *)cmd)[i] != (*pp)[i]) { + for (i = 0; cmd[i] != NUL; i++) { + if ((cmd)[i] != (*pp)[i]) { break; } } if (i >= len && !isalpha((*pp)[i])) { *pp = skipwhite(*pp + i); - return TRUE; + return true; } return FALSE; } -/* - * 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_u *cmd) +/// 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) { - char_u *s = cmd; - char_u *d; + size_t len = STRLEN(IObuff); + char *s = cmd; + char *d; + if (len > IOSIZE - 100) { + // Not enough space, truncate and put in "...". + d = (char *)IObuff + IOSIZE - 100; + d -= utf_head_off(IObuff, (const char_u *)d); + STRCPY(d, "..."); + } STRCAT(IObuff, ": "); - d = IObuff + STRLEN(IObuff); - while (*s != NUL && d - IObuff < IOSIZE - 7) { - if (s[0] == 0xc2 && s[1] == 0xa0) { + d = (char *)IObuff + STRLEN(IObuff); + while (*s != NUL && (char_u *)d - IObuff + 5 < IOSIZE) { + if ((char_u)s[0] == 0xc2 && (char_u)s[1] == 0xa0) { s += 2; STRCPY(d, "<a0>"); d += 4; + } else if ((char_u *)d - IObuff + utfc_ptr2len(s) + 1 >= IOSIZE) { + break; } else { - mb_copy_char((const char_u **)&s, &d); + mb_copy_char((const char_u **)&s, (char_u **)&d); } } *d = NUL; } -// Find an Ex command by its name, either built-in or user. -// Start of the name can be found at eap->cmd. -// Sets eap->cmdidx and returns a pointer to char after the command name. -// "full" is set to TRUE if the whole command name matched. -// Returns NULL for an ambiguous user command. -static char_u *find_command(exarg_T *eap, int *full) +/// Find an Ex command by its name, either built-in or user. +/// Start of the name can be found at eap->cmd. +/// Sets eap->cmdidx and returns a pointer to char after the command name. +/// "full" is set to TRUE if the whole command name matched. +/// +/// @return NULL for an ambiguous user command. +char *find_ex_command(exarg_T *eap, int *full) FUNC_ATTR_NONNULL_ARG(1) { int len; - char_u *p; + char *p; int i; /* @@ -2606,15 +3007,15 @@ static char_u *find_command(exarg_T *eap, int *full) } // check for non-alpha command - if (p == eap->cmd && vim_strchr((char_u *)"@!=><&~#", *p) != NULL) { - ++p; + if (p == eap->cmd && vim_strchr("@!=><&~#", *p) != NULL) { + p++; } len = (int)(p - eap->cmd); if (*eap->cmd == 'd' && (p[-1] == 'l' || p[-1] == 'p')) { // Check for ":dl", ":dell", etc. to ":deletel": that's // :delete with the 'l' flag. Same for 'p'. for (i = 0; i < len; i++) { - if (eap->cmd[i] != ((char_u *)"delete")[i]) { + if (eap->cmd[i] != ("delete")[i]) { break; } } @@ -2629,7 +3030,7 @@ static char_u *find_command(exarg_T *eap, int *full) } if (ASCII_ISLOWER(eap->cmd[0])) { - const int c1 = eap->cmd[0]; + const int c1 = (char_u)eap->cmd[0]; const int c2 = len == 1 ? NUL : eap->cmd[1]; if (command_count != CMD_SIZE) { @@ -2639,17 +3040,19 @@ static char_u *find_command(exarg_T *eap, int *full) // Use a precomputed index for fast look-up in cmdnames[] // taking into account the first 2 letters of eap->cmd. - eap->cmdidx = cmdidxs1[CharOrdLow(c1)]; + eap->cmdidx = cmdidxs1[CHAR_ORD_LOW(c1)]; if (ASCII_ISLOWER(c2)) { - eap->cmdidx += cmdidxs2[CharOrdLow(c1)][CharOrdLow(c2)]; + eap->cmdidx += cmdidxs2[CHAR_ORD_LOW(c1)][CHAR_ORD_LOW(c2)]; } + } else if (ASCII_ISUPPER(eap->cmd[0])) { + eap->cmdidx = CMD_Next; } else { eap->cmdidx = CMD_bang; } for (; (int)eap->cmdidx < CMD_SIZE; eap->cmdidx = (cmdidx_T)((int)eap->cmdidx + 1)) { - if (STRNCMP(cmdnames[(int)eap->cmdidx].cmd_name, (char *)eap->cmd, + if (STRNCMP(cmdnames[(int)eap->cmdidx].cmd_name, eap->cmd, (size_t)len) == 0) { if (full != NULL && cmdnames[(int)eap->cmdidx].cmd_name[len] == NUL) { @@ -2685,27 +3088,25 @@ static char_u *find_command(exarg_T *eap, int *full) /// @param full set to TRUE for a full match /// @param xp used for completion, NULL otherwise /// @param complp completion flags or NULL -static char_u *find_ucmd(exarg_T *eap, char_u *p, int *full, expand_T *xp, int *complp) +static char *find_ucmd(exarg_T *eap, char *p, int *full, expand_T *xp, int *complp) { int len = (int)(p - eap->cmd); int j, k, matchlen = 0; ucmd_T *uc; bool found = false; bool possible = false; - char_u *cp, *np; // Point into typed cmd and test name + char *cp, *np; // Point into typed cmd and test name garray_T *gap; bool amb_local = false; // Found ambiguous buffer-local command, // only full match global is accepted. - /* - * Look for buffer-local user commands first, then global ones. - */ - gap = &curbuf->b_ucmds; + // Look for buffer-local user commands first, then global ones. + gap = &prevwin_curwin()->w_buffer->b_ucmds; for (;;) { for (j = 0; j < gap->ga_len; j++) { uc = USER_CMD_GA(gap, j); cp = eap->cmd; - np = uc->uc_name; + np = (char *)uc->uc_name; k = 0; while (k < len && *np != NUL && *cp++ == *np++) { k++; @@ -2746,7 +3147,7 @@ static char_u *find_ucmd(exarg_T *eap, char_u *p, int *full, expand_T *xp, int * } if (xp != NULL) { xp->xp_luaref = uc->uc_compl_luaref; - xp->xp_arg = uc->uc_compl_arg; + xp->xp_arg = (char *)uc->uc_compl_arg; xp->xp_script_ctx = uc->uc_script_ctx; xp->xp_script_ctx.sc_lnum += sourcing_lnum; } @@ -2817,13 +3218,11 @@ static struct cmdmod { { "vertical", 4, false }, }; -/* - * Return length of a command modifier (including optional count). - * Return zero when it's not a modifier. - */ -int modifier_len(char_u *cmd) +/// @return length of a command modifier (including optional count) or, +/// zero when it's not a modifier. +int modifier_len(char *cmd) { - char_u *p = cmd; + char *p = cmd; if (ascii_isdigit(*cmd)) { p = skipwhite(skipdigits(cmd + 1)); @@ -2844,15 +3243,13 @@ int modifier_len(char_u *cmd) return 0; } -/* - * Return > 0 if an Ex command "name" exists. - * Return 2 if there is an exact match. - * Return 3 if there is an ambiguous match. - */ +/// @return > 0 if an Ex command "name" exists or, +/// 2 if there is an exact match or, +/// 3 if there is an ambiguous match. int cmd_exists(const char *const name) { exarg_T ea; - char_u *p; + char *p; // Check command modifiers. for (int i = 0; i < (int)ARRAY_SIZE(cmdmods); i++) { @@ -2869,10 +3266,10 @@ int cmd_exists(const char *const name) // Check built-in commands and user defined commands. // For ":2match" and ":3match" we need to skip the number. - ea.cmd = (char_u *)((*name == '2' || *name == '3') ? name + 1 : name); + ea.cmd = (char *)((*name == '2' || *name == '3') ? name + 1 : name); ea.cmdidx = (cmdidx_T)0; int full = false; - p = find_command(&ea, &full); + p = find_ex_command(&ea, &full); if (p == NULL) { return 3; } @@ -2885,29 +3282,34 @@ int cmd_exists(const char *const name) return ea.cmdidx == CMD_SIZE ? 0 : (full ? 2 : 1); } -// "fullcommand" function +/// "fullcommand" function void f_fullcommand(typval_T *argvars, typval_T *rettv, FunPtr fptr) { exarg_T ea; - char_u *name = argvars[0].vval.v_string; + char *name = argvars[0].vval.v_string; + + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + if (name == NULL) { + return; + } - while (name[0] != NUL && name[0] == ':') { + while (*name == ':') { name++; } name = skip_range(name, NULL); - rettv->v_type = VAR_STRING; - ea.cmd = (*name == '2' || *name == '3') ? name + 1 : name; ea.cmdidx = (cmdidx_T)0; - char_u *p = find_command(&ea, NULL); + char *p = find_ex_command(&ea, NULL); if (p == NULL || ea.cmdidx == CMD_SIZE) { return; } - rettv->vval.v_string = vim_strsave(IS_USER_CMDIDX(ea.cmdidx) - ? get_user_commands(NULL, ea.useridx) - : cmdnames[ea.cmdidx].cmd_name); + rettv->vval.v_string = (char *)vim_strsave(IS_USER_CMDIDX(ea.cmdidx) + ? (char_u *)get_user_command_name(ea.useridx, + ea.cmdidx) + : (char_u *)cmdnames[ea.cmdidx].cmd_name); } /// This is all pretty much copied from do_one_cmd(), with all the extra stuff @@ -2926,16 +3328,15 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff) bool usefilter = false; // Filter instead of file name. ExpandInit(xp); - xp->xp_pattern = (char_u *)buff; - xp->xp_line = (char_u *)buff; + xp->xp_pattern = (char *)buff; + xp->xp_line = (char *)buff; xp->xp_context = EXPAND_COMMANDS; // Default until we get past command ea.argt = 0; // 2. skip comment lines and leading space, colons or bars const char *cmd; - for (cmd = buff; vim_strchr((const char_u *)" \t:|", *cmd) != NULL; cmd++) { - } - xp->xp_pattern = (char_u *)cmd; + for (cmd = buff; vim_strchr(" \t:|", *cmd) != NULL; cmd++) {} + xp->xp_pattern = (char *)cmd; if (*cmd == NUL) { return NULL; @@ -2948,12 +3349,12 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff) /* * 3. parse a range specifier of the form: addr [,addr] [;addr] .. */ - cmd = (const char *)skip_range((const char_u *)cmd, &xp->xp_context); + cmd = (const char *)skip_range(cmd, &xp->xp_context); /* * 4. parse command */ - xp->xp_pattern = (char_u *)cmd; + xp->xp_pattern = (char *)cmd; if (*cmd == NUL) { return NULL; } @@ -2995,7 +3396,7 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff) } } // check for non-alpha command - if (p == cmd && vim_strchr((const char_u *)"@*!=><&~#", *p) != NULL) { + if (p == cmd && vim_strchr("@*!=><&~#", *p) != NULL) { p++; } len = (size_t)(p - cmd); @@ -3027,12 +3428,12 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff) } if (ea.cmdidx == CMD_SIZE) { - if (*cmd == 's' && vim_strchr((const char_u *)"cgriI", cmd[1]) != NULL) { + if (*cmd == 's' && vim_strchr("cgriI", cmd[1]) != NULL) { ea.cmdidx = CMD_substitute; p = cmd + 1; } else if (cmd[0] >= 'A' && cmd[0] <= 'Z') { - ea.cmd = (char_u *)cmd; - p = (const char *)find_ucmd(&ea, (char_u *)p, NULL, xp, &context); + ea.cmd = (char *)cmd; + p = (const char *)find_ucmd(&ea, (char *)p, NULL, xp, &context); if (p == NULL) { ea.cmdidx = CMD_SIZE; // Ambiguous user command. } @@ -3058,7 +3459,7 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff) ea.argt = cmdnames[(int)ea.cmdidx].cmd_argt; } - const char *arg = (const char *)skipwhite((const char_u *)p); + const char *arg = (const char *)skipwhite(p); // Skip over ++argopt argument if ((ea.argt & EX_ARGOPT) && *arg != NUL && strncmp(arg, "++", 2) == 0) { @@ -3066,7 +3467,7 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff) while (*p && !ascii_isspace(*p)) { MB_PTR_ADV(p); } - arg = (const char *)skipwhite((const char_u *)p); + arg = (const char *)skipwhite(p); } if (ea.cmdidx == CMD_write || ea.cmdidx == CMD_update) { @@ -3074,7 +3475,7 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff) if (*++arg == '>') { arg++; } - arg = (const char *)skipwhite((const char_u *)arg); + arg = (const char *)skipwhite(arg); } else if (*arg == '!' && ea.cmdidx == CMD_write) { // :w !filter arg++; usefilter = true; @@ -3093,14 +3494,14 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff) while (*arg == *cmd) { // allow any number of '>' or '<' arg++; } - arg = (const char *)skipwhite((const char_u *)arg); + arg = (const char *)skipwhite(arg); } // Does command allow "+command"? if ((ea.argt & EX_CMDARG) && !usefilter && *arg == '+') { // Check if we're in the +command p = arg + 1; - arg = (const char *)skip_cmd_arg((char_u *)arg, false); + arg = (const char *)skip_cmd_arg((char *)arg, false); // Still touching the command after '+'? if (*arg == NUL) { @@ -3108,7 +3509,7 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff) } // Skip space(s) after +command to get to the real argument. - arg = (const char *)skipwhite((const char_u *)arg); + arg = (const char *)skipwhite(arg); } /* @@ -3147,12 +3548,12 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff) // Find start of last argument (argument just before cursor): p = buff; - xp->xp_pattern = (char_u *)p; + xp->xp_pattern = (char *)p; len = strlen(buff); while (*p && p < buff + len) { if (*p == ' ' || *p == TAB) { // Argument starts after a space. - xp->xp_pattern = (char_u *)++p; + xp->xp_pattern = (char *)++p; } else { if (*p == '\\' && *(p + 1) != NUL) { p++; // skip over escaped character @@ -3170,15 +3571,15 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff) * Allow spaces within back-quotes to count as part of the argument * being expanded. */ - xp->xp_pattern = skipwhite((const char_u *)arg); + xp->xp_pattern = skipwhite(arg); p = (const char *)xp->xp_pattern; while (*p != NUL) { - c = utf_ptr2char((const char_u *)p); + c = utf_ptr2char(p); if (c == '\\' && p[1] != NUL) { p++; } else if (c == '`') { if (!in_quote) { - xp->xp_pattern = (char_u *)p; + xp->xp_pattern = (char *)p; bow = p + 1; } in_quote = !in_quote; @@ -3191,17 +3592,17 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff) || ascii_iswhite(c)) { len = 0; // avoid getting stuck when space is in 'isfname' while (*p != NUL) { - c = utf_ptr2char((const char_u *)p); + c = utf_ptr2char(p); if (c == '`' || vim_isfilec_or_wc(c)) { break; } - len = (size_t)utfc_ptr2len((const char_u *)p); + len = (size_t)utfc_ptr2len(p); MB_PTR_ADV(p); } if (in_quote) { bow = p; } else { - xp->xp_pattern = (char_u *)p; + xp->xp_pattern = (char *)p; } p -= len; } @@ -3213,7 +3614,7 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff) * expand from there. */ if (bow != NULL && in_quote) { - xp->xp_pattern = (char_u *)bow; + xp->xp_pattern = (char *)bow; } xp->xp_context = EXPAND_FILES; @@ -3223,7 +3624,7 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff) xp->xp_shell = TRUE; #endif // When still after the command name expand executables. - if (xp->xp_pattern == skipwhite((const char_u *)arg)) { + if (xp->xp_pattern == skipwhite(arg)) { xp->xp_context = EXPAND_SHELLCMD; } } @@ -3246,13 +3647,12 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff) } // Check for user names. if (*xp->xp_pattern == '~') { - for (p = (const char *)xp->xp_pattern + 1; *p != NUL && *p != '/'; p++) { - } + for (p = (const char *)xp->xp_pattern + 1; *p != NUL && *p != '/'; p++) {} // Complete ~user only if it partially matches a user name. // A full match ~user<Tab> will be replaced by user's home // directory i.e. something like ~user<Tab> -> /home/user/ if (*p == NUL && p > (const char *)xp->xp_pattern + 1 - && match_user(xp->xp_pattern + 1) >= 1) { + && match_user((char_u *)xp->xp_pattern + 1) >= 1) { xp->xp_context = EXPAND_USER; ++xp->xp_pattern; } @@ -3282,7 +3682,7 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff) break; case CMD_help: xp->xp_context = EXPAND_HELP; - xp->xp_pattern = (char_u *)arg; + xp->xp_pattern = (char *)arg; break; /* Command modifiers: return the argument. @@ -3323,19 +3723,19 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff) case CMD_filter: if (*arg != NUL) { - arg = (const char *)skip_vimgrep_pat((char_u *)arg, NULL, NULL); + arg = (const char *)skip_vimgrep_pat((char *)arg, NULL, NULL); } if (arg == NULL || *arg == NUL) { xp->xp_context = EXPAND_NOTHING; return NULL; } - return (const char *)skipwhite((const char_u *)arg); + return (const char *)skipwhite(arg); case CMD_match: if (*arg == NUL || !ends_excmd(*arg)) { // also complete "None" set_context_in_echohl_cmd(xp, arg); - arg = (const char *)skipwhite(skiptowhite((const char_u *)arg)); + arg = (const char *)skipwhite((char *)skiptowhite((const char_u *)arg)); if (*arg != NUL) { xp->xp_context = EXPAND_NOTHING; arg = (const char *)skip_regexp((char_u *)arg + 1, (uint8_t)(*arg), @@ -3359,7 +3759,7 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff) if (p == NULL) { // No "=", so complete attribute names. xp->xp_context = EXPAND_USER_CMD_FLAGS; - xp->xp_pattern = (char_u *)arg; + xp->xp_pattern = (char *)arg; return NULL; } @@ -3367,36 +3767,36 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff) // their arguments as well. if (STRNICMP(arg, "complete", p - arg) == 0) { xp->xp_context = EXPAND_USER_COMPLETE; - xp->xp_pattern = (char_u *)p + 1; + xp->xp_pattern = (char *)p + 1; return NULL; } else if (STRNICMP(arg, "nargs", p - arg) == 0) { xp->xp_context = EXPAND_USER_NARGS; - xp->xp_pattern = (char_u *)p + 1; + xp->xp_pattern = (char *)p + 1; return NULL; } else if (STRNICMP(arg, "addr", p - arg) == 0) { xp->xp_context = EXPAND_USER_ADDR_TYPE; - xp->xp_pattern = (char_u *)p + 1; + xp->xp_pattern = (char *)p + 1; return NULL; } return NULL; } - arg = (const char *)skipwhite((char_u *)p); + arg = (const char *)skipwhite(p); } // After the attributes comes the new command name. p = (const char *)skiptowhite((const char_u *)arg); if (*p == NUL) { xp->xp_context = EXPAND_USER_COMMANDS; - xp->xp_pattern = (char_u *)arg; + xp->xp_pattern = (char *)arg; break; } // And finally comes a normal command. - return (const char *)skipwhite((const char_u *)p); + return (const char *)skipwhite(p); case CMD_delcommand: xp->xp_context = EXPAND_USER_COMMANDS; - xp->xp_pattern = (char_u *)arg; + xp->xp_pattern = (char *)arg; break; case CMD_global: @@ -3453,7 +3853,7 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff) case CMD_isplit: case CMD_dsplit: // Skip count. - arg = (const char *)skipwhite(skipdigits((const char_u *)arg)); + arg = (const char *)skipwhite(skipdigits(arg)); if (*arg == '/') { // Match regexp, not just whole words. for (++arg; *arg && *arg != '/'; arg++) { if (*arg == '\\' && arg[1] != NUL) { @@ -3461,7 +3861,7 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff) } } if (*arg) { - arg = (const char *)skipwhite((const char_u *)arg + 1); + arg = (const char *)skipwhite(arg + 1); // Check for trailing illegal characters. if (*arg && strchr("|\"\n", *arg) == NULL) { @@ -3473,11 +3873,11 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff) } break; case CMD_autocmd: - return (const char *)set_context_in_autocmd(xp, (char_u *)arg, false); + return (const char *)set_context_in_autocmd(xp, (char *)arg, false); case CMD_doautocmd: case CMD_doautoall: - return (const char *)set_context_in_autocmd(xp, (char_u *)arg, true); + return (const char *)set_context_in_autocmd(xp, (char *)arg, true); case CMD_set: set_context_in_set_cmd(xp, (char_u *)arg, 0); break; @@ -3502,11 +3902,11 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff) } else { xp->xp_context = EXPAND_TAGS; } - xp->xp_pattern = (char_u *)arg; + xp->xp_pattern = (char *)arg; break; case CMD_augroup: xp->xp_context = EXPAND_AUGROUP; - xp->xp_pattern = (char_u *)arg; + xp->xp_pattern = (char *)arg; break; case CMD_syntax: set_context_in_syntax_cmd(xp, arg); @@ -3530,16 +3930,16 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff) case CMD_lexpr: case CMD_laddexpr: case CMD_lgetexpr: - set_context_for_expression(xp, (char_u *)arg, ea.cmdidx); + set_context_for_expression(xp, (char *)arg, ea.cmdidx); break; case CMD_unlet: - while ((xp->xp_pattern = (char_u *)strchr(arg, ' ')) != NULL) { + while ((xp->xp_pattern = strchr(arg, ' ')) != NULL) { arg = (const char *)xp->xp_pattern + 1; } xp->xp_context = EXPAND_USER_VARS; - xp->xp_pattern = (char_u *)arg; + xp->xp_pattern = (char *)arg; if (*xp->xp_pattern == '$') { xp->xp_context = EXPAND_ENV_VARS; @@ -3551,7 +3951,7 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff) case CMD_function: case CMD_delfunction: xp->xp_context = EXPAND_USER_FUNC; - xp->xp_pattern = (char_u *)arg; + xp->xp_pattern = (char *)arg; break; case CMD_echohl: @@ -3571,7 +3971,7 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff) case CMD_bdelete: case CMD_bwipeout: case CMD_bunload: - while ((xp->xp_pattern = (char_u *)strchr(arg, ' ')) != NULL) { + while ((xp->xp_pattern = strchr(arg, ' ')) != NULL) { arg = (const char *)xp->xp_pattern + 1; } FALLTHROUGH; @@ -3579,14 +3979,14 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff) case CMD_sbuffer: case CMD_checktime: xp->xp_context = EXPAND_BUFFERS; - xp->xp_pattern = (char_u *)arg; + xp->xp_pattern = (char *)arg; break; case CMD_diffget: case CMD_diffput: // If current buffer is in diff mode, complete buffer names // which are in diff mode, and different than current buffer. xp->xp_context = EXPAND_DIFF_BUFFERS; - xp->xp_pattern = (char_u *)arg; + xp->xp_pattern = (char *)arg; break; case CMD_USER: case CMD_USER_BUF: @@ -3594,8 +3994,7 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff) // EX_XFILE: file names are handled above. if (!(ea.argt & EX_XFILE)) { if (context == EXPAND_MENUS) { - return (const char *)set_context_in_menu_cmd(xp, cmd, - (char_u *)arg, forceit); + return (const char *)set_context_in_menu_cmd(xp, cmd, (char *)arg, forceit); } else if (context == EXPAND_COMMANDS) { return arg; } else if (context == EXPAND_MAPPINGS) { @@ -3614,7 +4013,7 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff) } MB_PTR_ADV(p); } - xp->xp_pattern = (char_u *)arg; + xp->xp_pattern = (char *)arg; } xp->xp_context = context; } @@ -3660,7 +4059,7 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff) case CMD_smapclear: case CMD_xmapclear: xp->xp_context = EXPAND_MAPCLEAR; - xp->xp_pattern = (char_u *)arg; + xp->xp_pattern = (char *)arg; break; case CMD_abbreviate: @@ -3697,35 +4096,38 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff) case CMD_cmenu: case CMD_cnoremenu: case CMD_cunmenu: + case CMD_tlmenu: + case CMD_tlnoremenu: + case CMD_tlunmenu: case CMD_tmenu: case CMD_tunmenu: case CMD_popup: case CMD_emenu: - return (const char *)set_context_in_menu_cmd(xp, cmd, (char_u *)arg, forceit); + return (const char *)set_context_in_menu_cmd(xp, cmd, (char *)arg, forceit); case CMD_colorscheme: xp->xp_context = EXPAND_COLORS; - xp->xp_pattern = (char_u *)arg; + xp->xp_pattern = (char *)arg; break; case CMD_compiler: xp->xp_context = EXPAND_COMPILER; - xp->xp_pattern = (char_u *)arg; + xp->xp_pattern = (char *)arg; break; case CMD_ownsyntax: xp->xp_context = EXPAND_OWNSYNTAX; - xp->xp_pattern = (char_u *)arg; + xp->xp_pattern = (char *)arg; break; case CMD_setfiletype: xp->xp_context = EXPAND_FILETYPE; - xp->xp_pattern = (char_u *)arg; + xp->xp_pattern = (char *)arg; break; case CMD_packadd: xp->xp_context = EXPAND_PACKADD; - xp->xp_pattern = (char_u *)arg; + xp->xp_pattern = (char *)arg; break; #ifdef HAVE_WORKING_LIBINTL @@ -3733,14 +4135,14 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff) p = (const char *)skiptowhite((const char_u *)arg); if (*p == NUL) { xp->xp_context = EXPAND_LANGUAGE; - xp->xp_pattern = (char_u *)arg; + xp->xp_pattern = (char *)arg; } else { - if (strncmp(arg, "messages", p - arg) == 0 - || strncmp(arg, "ctype", p - arg) == 0 - || strncmp(arg, "time", p - arg) == 0 - || strncmp(arg, "collate", p - arg) == 0) { + if (strncmp(arg, "messages", (size_t)(p - arg)) == 0 + || strncmp(arg, "ctype", (size_t)(p - arg)) == 0 + || strncmp(arg, "time", (size_t)(p - arg)) == 0 + || strncmp(arg, "collate", (size_t)(p - arg)) == 0) { xp->xp_context = EXPAND_LOCALES; - xp->xp_pattern = skipwhite((const char_u *)p); + xp->xp_pattern = skipwhite(p); } else { xp->xp_context = EXPAND_NOTHING; } @@ -3752,33 +4154,33 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff) break; case CMD_checkhealth: xp->xp_context = EXPAND_CHECKHEALTH; - xp->xp_pattern = (char_u *)arg; + xp->xp_pattern = (char *)arg; break; case CMD_behave: xp->xp_context = EXPAND_BEHAVE; - xp->xp_pattern = (char_u *)arg; + xp->xp_pattern = (char *)arg; break; case CMD_messages: xp->xp_context = EXPAND_MESSAGES; - xp->xp_pattern = (char_u *)arg; + xp->xp_pattern = (char *)arg; break; case CMD_history: xp->xp_context = EXPAND_HISTORY; - xp->xp_pattern = (char_u *)arg; + xp->xp_pattern = (char *)arg; break; case CMD_syntime: xp->xp_context = EXPAND_SYNTIME; - xp->xp_pattern = (char_u *)arg; + xp->xp_pattern = (char *)arg; break; case CMD_argdelete: - while ((xp->xp_pattern = vim_strchr((const char_u *)arg, ' ')) != NULL) { + while ((xp->xp_pattern = vim_strchr(arg, ' ')) != NULL) { arg = (const char *)(xp->xp_pattern + 1); } xp->xp_context = EXPAND_ARGLIST; - xp->xp_pattern = (char_u *)arg; + xp->xp_pattern = (char *)arg; break; case CMD_lua: @@ -3801,11 +4203,11 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff) /// @param ctx pointer to xp_context or NULL /// /// @return the "cmd" pointer advanced to beyond the range. -char_u *skip_range(const char_u *cmd, int *ctx) +char *skip_range(const char *cmd, int *ctx) { unsigned delim; - while (vim_strchr((char_u *)" \t0123456789.$%'/?-+,;\\", *cmd) != NULL) { + while (vim_strchr(" \t0123456789.$%'/?-+,;\\", *cmd) != NULL) { if (*cmd == '\\') { if (cmd[1] == '?' || cmd[1] == '/' || cmd[1] == '&') { cmd++; @@ -3817,8 +4219,8 @@ char_u *skip_range(const char_u *cmd, int *ctx) *ctx = EXPAND_NOTHING; } } else if (*cmd == '/' || *cmd == '?') { - delim = *cmd++; - while (*cmd != NUL && *cmd != delim) { + delim = (unsigned)(*cmd++); + while (*cmd != NUL && *cmd != (char)delim) { if (*cmd++ == '\\' && *cmd != NUL) { ++cmd; } @@ -3833,9 +4235,9 @@ char_u *skip_range(const char_u *cmd, int *ctx) } // Skip ":" and white space. - cmd = skip_colon_white(cmd, false); + cmd = skip_colon_white((char *)cmd, false); - return (char_u *)cmd; + return (char *)cmd; } static void addr_error(cmd_addr_T addr_type) @@ -3859,16 +4261,15 @@ static void addr_error(cmd_addr_T addr_type) /// @param address_count 1 for first, >1 after comma /// /// @return MAXLNUM when no Ex address was found. -static linenr_T get_address(exarg_T *eap, char_u **ptr, cmd_addr_T addr_type, int skip, bool silent, +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) FUNC_ATTR_NONNULL_ALL { int c; int i; - long n; - char_u *cmd; + linenr_T n; + char *cmd; pos_T pos; - pos_T *fp; linenr_T lnum; buf_T *buf; @@ -3904,7 +4305,7 @@ static linenr_T get_address(exarg_T *eap, char_u **ptr, cmd_addr_T addr_type, in goto error; break; case ADDR_QUICKFIX: - lnum = qf_get_cur_idx(eap); + lnum = (linenr_T)qf_get_cur_idx(eap); break; case ADDR_QUICKFIX_VALID: lnum = qf_get_cur_valid_idx(eap); @@ -3949,13 +4350,13 @@ static linenr_T get_address(exarg_T *eap, char_u **ptr, cmd_addr_T addr_type, in goto error; break; case ADDR_QUICKFIX: - lnum = qf_get_size(eap); + lnum = (linenr_T)qf_get_size(eap); if (lnum == 0) { lnum = 1; } break; case ADDR_QUICKFIX_VALID: - lnum = qf_get_valid_size(eap); + lnum = (linenr_T)qf_get_valid_size(eap); if (lnum == 0) { lnum = 1; } @@ -3978,31 +4379,32 @@ static linenr_T get_address(exarg_T *eap, char_u **ptr, cmd_addr_T addr_type, in } else { // Only accept a mark in another file when it is // used by itself: ":'M". - fp = getmark(*cmd, to_other_file && cmd[1] == NUL); - ++cmd; - if (fp == (pos_T *)-1) { + MarkGet flag = to_other_file && cmd[1] == NUL ? kMarkAll : kMarkBufLocal; + fmark_T *fm = mark_get(curbuf, curwin, NULL, flag, *cmd); + cmd++; + if (fm != NULL && fm->fnum != curbuf->handle) { // Jumped to another file. lnum = curwin->w_cursor.lnum; } else { - if (check_mark(fp) == FAIL) { + if (!mark_check(fm)) { cmd = NULL; goto error; } - lnum = fp->lnum; + lnum = fm->mark.lnum; } } break; case '/': case '?': // '/' or '?' - search - c = *cmd++; + c = (char_u)(*cmd++); if (addr_type != ADDR_LINES) { addr_error(addr_type); cmd = NULL; goto error; } if (skip) { // skip "/pat/" - cmd = skip_regexp(cmd, c, p_magic, NULL); + cmd = (char *)skip_regexp((char_u *)cmd, c, p_magic, NULL); if (*cmd == c) { ++cmd; } @@ -4013,8 +4415,9 @@ static linenr_T get_address(exarg_T *eap, char_u **ptr, cmd_addr_T addr_type, in // When '/' or '?' follows another address, start from // there. - if (lnum != MAXLNUM) { - curwin->w_cursor.lnum = lnum; + if (lnum > 0 && lnum != MAXLNUM) { + curwin->w_cursor.lnum + = lnum > curbuf->b_ml.ml_line_count ? curbuf->b_ml.ml_line_count : lnum; } // Start a forward search at the end of the line (unless @@ -4030,7 +4433,7 @@ static linenr_T get_address(exarg_T *eap, char_u **ptr, cmd_addr_T addr_type, in } 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, (char_u *)cmd, 1L, flags, NULL)) { curwin->w_cursor = pos; cmd = NULL; goto error; @@ -4079,7 +4482,7 @@ static linenr_T get_address(exarg_T *eap, char_u **ptr, cmd_addr_T addr_type, in default: if (ascii_isdigit(*cmd)) { // absolute line number - lnum = getdigits_long(&cmd, false, 0); + lnum = (linenr_T)getdigits((char_u **)&cmd, false, 0); } } @@ -4113,7 +4516,7 @@ static linenr_T get_address(exarg_T *eap, char_u **ptr, cmd_addr_T addr_type, in lnum = 1; break; case ADDR_QUICKFIX: - lnum = qf_get_cur_idx(eap); + lnum = (linenr_T)qf_get_cur_idx(eap); break; case ADDR_QUICKFIX_VALID: lnum = qf_get_cur_valid_idx(eap); @@ -4128,12 +4531,16 @@ static linenr_T get_address(exarg_T *eap, char_u **ptr, cmd_addr_T addr_type, in if (ascii_isdigit(*cmd)) { i = '+'; // "number" is same as "+number" } else { - i = *cmd++; + i = (char_u)(*cmd++); } if (!ascii_isdigit(*cmd)) { // '+' is '+1', but '+0' is not '+1' n = 1; } else { - n = getdigits(&cmd, true, 0); + n = getdigits_int32(&cmd, false, MAXLNUM); + if (n == MAXLNUM) { + emsg(_(e_line_number_out_of_range)); + goto error; + } } if (addr_type == ADDR_TABS_RELATIVE) { @@ -4152,6 +4559,10 @@ static linenr_T get_address(exarg_T *eap, char_u **ptr, cmd_addr_T addr_type, in if (i == '-') { lnum -= n; } else { + if (n >= INT32_MAX - lnum) { + emsg(_(e_line_number_out_of_range)); + goto error; + } lnum += n; } } @@ -4163,12 +4574,10 @@ error: return lnum; } -/* - * Get flags from an Ex command argument. - */ +/// Get flags from an Ex command argument. static void get_flags(exarg_T *eap) { - while (vim_strchr((char_u *)"lp#", *eap->arg) != NULL) { + while (vim_strchr("lp#", *eap->arg) != NULL) { if (*eap->arg == 'l') { eap->flags |= EXFLAG_LIST; } else if (*eap->arg == 'p') { @@ -4200,11 +4609,10 @@ static void ex_script_ni(exarg_T *eap) } } -/* - * Check range in Ex command for validity. - * Return NULL when valid, error message when invalid. - */ -static char *invalid_range(exarg_T *eap) +/// Check range in Ex command for validity. +/// +/// @return NULL when valid, error message when invalid. +char *invalid_range(exarg_T *eap) { buf_T *buf; if (eap->line1 < 0 || eap->line2 < 0 || eap->line1 > eap->line2) { @@ -4226,8 +4634,9 @@ static char *invalid_range(exarg_T *eap) } break; case ADDR_BUFFERS: - if (eap->line1 < firstbuf->b_fnum - || eap->line2 > lastbuf->b_fnum) { + // Only a boundary check, not whether the buffers actually + // exist. + if (eap->line1 < 1 || eap->line2 > get_highest_fnum()) { return _(e_invrange); } break; @@ -4289,9 +4698,7 @@ static char *invalid_range(exarg_T *eap) return NULL; } -/* - * Correct the range for zero line number, if required. - */ +/// Correct the range for zero line number, if required. static void correct_range(exarg_T *eap) { if (!(eap->argt & EX_ZEROR)) { // zero in range not allowed @@ -4304,14 +4711,11 @@ static void correct_range(exarg_T *eap) } } - -/* - * For a ":vimgrep" or ":vimgrepadd" command return a pointer past the - * pattern. Otherwise return eap->arg. - */ -static char_u *skip_grep_pat(exarg_T *eap) +/// For a ":vimgrep" or ":vimgrepadd" command return a pointer past the +/// pattern. Otherwise return eap->arg. +static char *skip_grep_pat(exarg_T *eap) { - char_u *p = eap->arg; + char *p = eap->arg; if (*p != NUL && (eap->cmdidx == CMD_vimgrep || eap->cmdidx == CMD_lvimgrep || eap->cmdidx == CMD_vimgrepadd @@ -4325,18 +4729,16 @@ static char_u *skip_grep_pat(exarg_T *eap) return p; } -/* - * For the ":make" and ":grep" commands insert the 'makeprg'/'grepprg' option - * in the command line, so that things like % get expanded. - */ -static char_u *replace_makeprg(exarg_T *eap, char_u *p, char_u **cmdlinep) +/// For the ":make" and ":grep" commands insert the 'makeprg'/'grepprg' option +/// in the command line, so that things like % get expanded. +char *replace_makeprg(exarg_T *eap, char *p, char **cmdlinep) { - char_u *new_cmdline; - char_u *program; - char_u *pos; - char_u *ptr; + char *new_cmdline; + char *program; + char *pos; + char *ptr; int len; - int i; + size_t i; /* * Don't do it when ":vimgrep" is used for ":grep". @@ -4349,31 +4751,31 @@ static char_u *replace_makeprg(exarg_T *eap, char_u *p, char_u **cmdlinep) if (eap->cmdidx == CMD_grep || eap->cmdidx == CMD_lgrep || eap->cmdidx == CMD_grepadd || eap->cmdidx == CMD_lgrepadd) { if (*curbuf->b_p_gp == NUL) { - program = p_gp; + program = (char *)p_gp; } else { - program = curbuf->b_p_gp; + program = (char *)curbuf->b_p_gp; } } else { if (*curbuf->b_p_mp == NUL) { - program = p_mp; + program = (char *)p_mp; } else { - program = curbuf->b_p_mp; + program = (char *)curbuf->b_p_mp; } } p = skipwhite(p); - if ((pos = (char_u *)strstr((char *)program, "$*")) != NULL) { + if ((pos = strstr(program, "$*")) != NULL) { // replace $* by given arguments i = 1; - while ((pos = (char_u *)strstr((char *)pos + 2, "$*")) != NULL) { - ++i; + while ((pos = strstr(pos + 2, "$*")) != NULL) { + i++; } len = (int)STRLEN(p); - new_cmdline = xmalloc(STRLEN(program) + (size_t)i * (len - 2) + 1); + new_cmdline = xmalloc(STRLEN(program) + i * (size_t)(len - 2) + 1); ptr = new_cmdline; - while ((pos = (char_u *)strstr((char *)program, "$*")) != NULL) { - i = (int)(pos - program); + while ((pos = strstr(program, "$*")) != NULL) { + i = (size_t)(pos - program); memcpy(ptr, program, i); STRCPY(ptr += i, p); ptr += len; @@ -4386,7 +4788,7 @@ static char_u *replace_makeprg(exarg_T *eap, char_u *p, char_u **cmdlinep) STRCAT(new_cmdline, " "); STRCAT(new_cmdline, p); } - msg_make(p); + msg_make((char_u *)p); // 'eap->cmd' is not set here, because it is not used at CMD_make xfree(*cmdlinep); @@ -4396,15 +4798,16 @@ static char_u *replace_makeprg(exarg_T *eap, char_u *p, char_u **cmdlinep) return p; } -// Expand file name in Ex command argument. -// When an error is detected, "errormsgp" is set to a non-NULL pointer. -// Return FAIL for failure, OK otherwise. +/// Expand file name in Ex command argument. +/// 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_u **cmdlinep, char **errormsgp) { int has_wildcards; // need to expand wildcards - char_u *repl; + char *repl; size_t srclen; - char_u *p; + char *p; int escaped; // Skip a regexp pattern for ":vimgrep[add] pat file..." @@ -4415,7 +4818,7 @@ int expand_filename(exarg_T *eap, char_u **cmdlinep, char **errormsgp) * the file name contains a wildcard it should not cause expanding. * (it will be expanded anyway if there is a wildcard before replacing). */ - has_wildcards = path_has_wildcard(p); + has_wildcards = path_has_wildcard((char_u *)p); while (*p != NUL) { // Skip over `=expr`, wildcards in it are not expanded. if (p[0] == '`' && p[1] == '=') { @@ -4430,16 +4833,16 @@ int expand_filename(exarg_T *eap, char_u **cmdlinep, char **errormsgp) * Quick check if this cannot be the start of a special string. * Also removes backslash before '%', '#' and '<'. */ - if (vim_strchr((char_u *)"%#<", *p) == NULL) { - ++p; + if (vim_strchr("%#<", *p) == NULL) { + p++; continue; } /* * Try to find a match at this position. */ - repl = eval_vars(p, eap->arg, &srclen, &(eap->do_ecmd_lnum), - errormsgp, &escaped); + repl = (char *)eval_vars((char_u *)p, (char_u *)eap->arg, &srclen, &(eap->do_ecmd_lnum), + errormsgp, &escaped); if (*errormsgp != NULL) { // error detected return FAIL; } @@ -4451,7 +4854,7 @@ int expand_filename(exarg_T *eap, char_u **cmdlinep, char **errormsgp) // Wildcards won't be expanded below, the replacement is taken // literally. But do expand "~/file", "~user/file" and "$HOME/file". if (vim_strchr(repl, '$') != NULL || vim_strchr(repl, '~') != NULL) { - char_u *l = repl; + char *l = repl; repl = expand_env_save(repl); xfree(l); @@ -4473,19 +4876,19 @@ int expand_filename(exarg_T *eap, char_u **cmdlinep, char **errormsgp) && eap->cmdidx != CMD_make && eap->cmdidx != CMD_terminal && !(eap->argt & EX_NOSPC)) { - char_u *l; + char *l; #ifdef BACKSLASH_IN_FILENAME // Don't escape a backslash here, because rem_backslash() doesn't // remove it later. - static char_u *nobslash = (char_u *)" \t\"|"; + static char *nobslash = " \t\"|"; # define ESCAPE_CHARS nobslash #else # define ESCAPE_CHARS escape_chars #endif - for (l = repl; *l; ++l) { - if (vim_strchr(ESCAPE_CHARS, *l) != NULL) { - l = vim_strsave_escaped(repl, ESCAPE_CHARS); + for (l = repl; *l; l++) { + if (vim_strchr((char *)ESCAPE_CHARS, *l) != NULL) { + l = (char *)vim_strsave_escaped((char_u *)repl, ESCAPE_CHARS); xfree(repl); repl = l; break; @@ -4497,15 +4900,15 @@ int expand_filename(exarg_T *eap, char_u **cmdlinep, char **errormsgp) if ((eap->usefilter || eap->cmdidx == CMD_bang || eap->cmdidx == CMD_terminal) - && vim_strpbrk(repl, (char_u *)"!") != NULL) { - char_u *l; + && strpbrk(repl, "!") != NULL) { + char *l; - l = vim_strsave_escaped(repl, (char_u *)"!"); + l = (char *)vim_strsave_escaped((char_u *)repl, (char_u *)"!"); xfree(repl); repl = l; } - p = repl_cmdline(eap, p, srclen, repl, cmdlinep); + p = repl_cmdline(eap, p, srclen, repl, (char **)cmdlinep); xfree(repl); } @@ -4525,14 +4928,14 @@ int expand_filename(exarg_T *eap, char_u **cmdlinep, char **errormsgp) */ if (vim_strchr(eap->arg, '$') != NULL || vim_strchr(eap->arg, '~') != NULL) { - expand_env_esc(eap->arg, NameBuff, MAXPATHL, true, true, NULL); + expand_env_esc((char_u *)eap->arg, NameBuff, MAXPATHL, true, true, NULL); has_wildcards = path_has_wildcard(NameBuff); - p = NameBuff; + p = (char *)NameBuff; } else { p = NULL; } if (p != NULL) { - (void)repl_cmdline(eap, eap->arg, STRLEN(eap->arg), p, cmdlinep); + (void)repl_cmdline(eap, eap->arg, STRLEN(eap->arg), p, (char **)cmdlinep); } } @@ -4544,7 +4947,7 @@ int expand_filename(exarg_T *eap, char_u **cmdlinep, char **errormsgp) #ifdef UNIX if (!has_wildcards) #endif - backslash_halve(eap->arg); + backslash_halve((char_u *)eap->arg); if (has_wildcards) { expand_T xpc; @@ -4555,26 +4958,24 @@ int expand_filename(exarg_T *eap, char_u **cmdlinep, char **errormsgp) if (p_wic) { options += WILD_ICASE; } - p = ExpandOne(&xpc, eap->arg, NULL, options, WILD_EXPAND_FREE); + p = (char *)ExpandOne(&xpc, (char_u *)eap->arg, NULL, options, WILD_EXPAND_FREE); if (p == NULL) { return FAIL; } - (void)repl_cmdline(eap, eap->arg, STRLEN(eap->arg), p, cmdlinep); + (void)repl_cmdline(eap, eap->arg, STRLEN(eap->arg), p, (char **)cmdlinep); xfree(p); } } return OK; } -/* - * Replace part of the command line, keeping eap->cmd, eap->arg and - * eap->nextcmd correct. - * "src" points to the part that is to be replaced, of length "srclen". - * "repl" is the replacement string. - * Returns a pointer to the character after the replaced string. - */ -static char_u *repl_cmdline(exarg_T *eap, char_u *src, size_t srclen, char_u *repl, - char_u **cmdlinep) +/// Replace part of the command line, keeping eap->cmd, eap->arg, eap->args and +/// eap->nextcmd correct. +/// "src" points to the part that is to be replaced, of length "srclen". +/// "repl" is the replacement string. +/// +/// @return a pointer to the character after the replaced string. +static char *repl_cmdline(exarg_T *eap, char *src, size_t srclen, char *repl, char **cmdlinep) { /* * The new command line is build in new_cmdline[]. @@ -4586,7 +4987,8 @@ static char_u *repl_cmdline(exarg_T *eap, char_u *src, size_t srclen, char_u *re if (eap->nextcmd != NULL) { i += STRLEN(eap->nextcmd); // add space for next command } - char_u *new_cmdline = xmalloc(i); + char *new_cmdline = xmalloc(i); + size_t offset = (size_t)(src - *cmdlinep); /* * Copy the stuff before the expanded part. @@ -4594,7 +4996,7 @@ static char_u *repl_cmdline(exarg_T *eap, char_u *src, size_t srclen, char_u *re * Copy what came after the expanded part. * Copy the next commands, if there are any. */ - i = (size_t)(src - *cmdlinep); // length of part before match + i = offset; // length of part before match memmove(new_cmdline, *cmdlinep, i); memmove(new_cmdline + i, repl, len); @@ -4609,6 +5011,19 @@ static char_u *repl_cmdline(exarg_T *eap, char_u *src, size_t srclen, char_u *re } eap->cmd = new_cmdline + (eap->cmd - *cmdlinep); eap->arg = new_cmdline + (eap->arg - *cmdlinep); + + for (size_t j = 0; j < eap->argc; j++) { + if (offset >= (size_t)(eap->args[j] - *cmdlinep)) { + // If replaced text is after or in the same position as the argument, + // the argument's position relative to the beginning of the cmdline stays the same. + eap->args[j] = new_cmdline + (eap->args[j] - *cmdlinep); + } else { + // Otherwise, argument gets shifted alongside the replaced text. + // The amount of the shift is equal to the difference of the old and new string length. + eap->args[j] = new_cmdline + (eap->args[j] - *cmdlinep) + (len - srclen); + } + } + if (eap->do_ecmd_cmd != NULL && eap->do_ecmd_cmd != dollar_command) { eap->do_ecmd_cmd = new_cmdline + (eap->do_ecmd_cmd - *cmdlinep); } @@ -4618,14 +5033,10 @@ static char_u *repl_cmdline(exarg_T *eap, char_u *src, size_t srclen, char_u *re return src; } -/* - * Check for '|' to separate commands and '"' to start comments. - */ +/// Check for '|' to separate commands and '"' to start comments. void separate_nextcmd(exarg_T *eap) { - char_u *p; - - p = skip_grep_pat(eap); + char *p = skip_grep_pat(eap); for (; *p; MB_PTR_ADV(p)) { if (*p == Ctrl_V) { @@ -4653,9 +5064,7 @@ void separate_nextcmd(exarg_T *eap) && !(eap->argt & EX_NOTRLCOM) && (eap->cmdidx != CMD_at || p != eap->arg) && (eap->cmdidx != CMD_redir - || p != eap->arg + 1 || p[-1] != '@')) - || *p == '|' - || *p == '\n') { + || p != eap->arg + 1 || p[-1] != '@')) || *p == '|' || *p == '\n') { // We remove the '\' before the '|', unless EX_CTRLV is used // AND 'b' is present in 'cpoptions'. if ((vim_strchr(p_cpo, CPO_BAR) == NULL @@ -4663,7 +5072,7 @@ void separate_nextcmd(exarg_T *eap) STRMOVE(p - 1, p); // remove the '\' p--; } else { - eap->nextcmd = check_nextcmd(p); + eap->nextcmd = (char *)check_nextcmd((char_u *)p); *p = NUL; break; } @@ -4671,22 +5080,20 @@ void separate_nextcmd(exarg_T *eap) } if (!(eap->argt & EX_NOTRLCOM)) { // remove trailing spaces - del_trailing_spaces(eap->arg); + del_trailing_spaces((char_u *)eap->arg); } } -/* - * get + command from ex argument - */ -static char_u *getargcmd(char_u **argp) +/// get + command from ex argument +static char *getargcmd(char **argp) { - char_u *arg = *argp; - char_u *command = NULL; + char *arg = *argp; + char *command = NULL; if (*arg == '+') { // +[command] ++arg; if (ascii_isspace(*arg) || *arg == '\0') { - command = dollar_command; + command = (char *)dollar_command; } else { command = arg; arg = skip_cmd_arg(command, TRUE); @@ -4704,7 +5111,7 @@ static char_u *getargcmd(char_u **argp) /// Find end of "+command" argument. Skip over "\ " and "\\". /// /// @param rembs TRUE to halve the number of backslashes -static char_u *skip_cmd_arg(char_u *p, int rembs) +static char *skip_cmd_arg(char *p, int rembs) { while (*p && !ascii_isspace(*p)) { if (*p == '\\' && p[1] != NUL) { @@ -4734,16 +5141,15 @@ int get_bad_opt(const char_u *p, exarg_T *eap) return OK; } -/* - * Get "++opt=arg" argument. - * Return FAIL or OK. - */ +/// Get "++opt=arg" argument. +/// +/// @return FAIL or OK. static int getargopt(exarg_T *eap) { - char_u *arg = eap->arg + 2; + char *arg = eap->arg + 2; int *pp = NULL; int bad_char_idx; - char_u *p; + char *p; // ":edit ++[no]bin[ary] file" if (STRNCMP(arg, "bin", 3) == 0 || STRNCMP(arg, "nobin", 5) == 0) { @@ -4762,7 +5168,7 @@ static int getargopt(exarg_T *eap) // ":read ++edit file" if (STRNCMP(arg, "edit", 4) == 0) { - eap->read_edit = TRUE; + eap->read_edit = true; eap->arg = skipwhite(arg + 4); return OK; } @@ -4789,26 +5195,26 @@ static int getargopt(exarg_T *eap) return FAIL; } - ++arg; + arg++; *pp = (int)(arg - eap->cmd); - arg = skip_cmd_arg(arg, FALSE); + arg = skip_cmd_arg(arg, false); eap->arg = skipwhite(arg); *arg = NUL; if (pp == &eap->force_ff) { - if (check_ff_value(eap->cmd + eap->force_ff) == FAIL) { + if (check_ff_value((char_u *)eap->cmd + eap->force_ff) == FAIL) { return FAIL; } - eap->force_ff = eap->cmd[eap->force_ff]; + eap->force_ff = (char_u)eap->cmd[eap->force_ff]; } else if (pp == &eap->force_enc) { // Make 'fileencoding' lower case. - for (p = eap->cmd + eap->force_enc; *p != NUL; ++p) { - *p = TOLOWER_ASC(*p); + for (p = eap->cmd + eap->force_enc; *p != NUL; p++) { + *p = (char)TOLOWER_ASC(*p); } } else { // Check ++bad= argument. Must be a single-byte character, "keep" or // "drop". - if (get_bad_opt(eap->cmd + bad_char_idx, eap) == FAIL) { + if (get_bad_opt((char_u *)eap->cmd + bad_char_idx, eap) == FAIL) { return FAIL; } } @@ -4817,16 +5223,17 @@ static int getargopt(exarg_T *eap) } /// Handle the argument for a tabpage related ex command. -/// Returns a tabpage number. /// When an error is encountered then eap->errmsg is set. +/// +/// @return a tabpage number. static int get_tabpage_arg(exarg_T *eap) { int tab_number = 0; int unaccept_arg0 = (eap->cmdidx == CMD_tabmove) ? 0 : 1; if (eap->arg && *eap->arg != NUL) { - char_u *p = eap->arg; - char_u *p_save; + char *p = eap->arg; + char *p_save; int relative = 0; // argument +N/-N means: go to N places to the // right/left relative to the current position. @@ -4839,13 +5246,19 @@ static int get_tabpage_arg(exarg_T *eap) } p_save = p; - tab_number = getdigits(&p, false, tab_number); + tab_number = (int)getdigits((char_u **)&p, false, tab_number); if (relative == 0) { if (STRCMP(p, "$") == 0) { tab_number = LAST_TAB_NR; } else if (STRCMP(p, "#") == 0) { - tab_number = tabpage_index(lastused_tabpage); + if (valid_tabpage(lastused_tabpage)) { + tab_number = tabpage_index(lastused_tabpage); + } else { + eap->errmsg = ex_errmsg(e_invargval, eap->arg); + tab_number = 0; + goto theend; + } } else if (p == p_save || *p_save == '-' || *p != NUL || tab_number > LAST_TAB_NR) { // No numbers as argument. @@ -4873,8 +5286,10 @@ static int get_tabpage_arg(exarg_T *eap) eap->errmsg = e_invrange; tab_number = 0; } else { - tab_number = eap->line2; - if (!unaccept_arg0 && *skipwhite(*eap->cmdlinep) == '-') { + tab_number = (int)eap->line2; + char *cmdp = eap->cmd; + while (--cmdp > *eap->cmdlinep && (*cmdp == ' ' || ascii_isdigit(*cmdp))) {} + if (!unaccept_arg0 && *cmdp == '-') { tab_number--; if (tab_number < unaccept_arg0) { eap->errmsg = e_invarg; @@ -4901,55 +5316,6 @@ theend: return tab_number; } -/* - * ":abbreviate" and friends. - */ -static void ex_abbreviate(exarg_T *eap) -{ - do_exmap(eap, TRUE); // almost the same as mapping -} - -/* - * ":map" and friends. - */ -static void ex_map(exarg_T *eap) -{ - /* - * If we are sourcing .exrc or .vimrc in current directory we - * print the mappings for security reasons. - */ - if (secure) { - secure = 2; - msg_outtrans(eap->cmd); - msg_putchar('\n'); - } - do_exmap(eap, FALSE); -} - -/* - * ":unmap" and friends. - */ -static void ex_unmap(exarg_T *eap) -{ - do_exmap(eap, FALSE); -} - -/* - * ":mapclear" and friends. - */ -static void ex_mapclear(exarg_T *eap) -{ - map_clear_mode(eap->cmd, eap->arg, eap->forceit, false); -} - -/* - * ":abclear" and friends. - */ -static void ex_abclear(exarg_T *eap) -{ - map_clear_mode(eap->cmd, eap->arg, true, true); -} - static void ex_autocmd(exarg_T *eap) { // Disallow autocommands from .exrc and .vimrc in current @@ -4964,12 +5330,10 @@ static void ex_autocmd(exarg_T *eap) } } -/* - * ":doautocmd": Apply the automatic commands to the current buffer. - */ +/// ":doautocmd": Apply the automatic commands to the current buffer. static void ex_doautocmd(exarg_T *eap) { - char_u *arg = eap->arg; + char *arg = eap->arg; int call_do_modelines = check_nomodeline(&arg); bool did_aucmd; @@ -4980,24 +5344,22 @@ static void ex_doautocmd(exarg_T *eap) } } -/* - * :[N]bunload[!] [N] [bufname] unload buffer - * :[N]bdelete[!] [N] [bufname] delete buffer from buffer list - * :[N]bwipeout[!] [N] [bufname] delete buffer really - */ +/// :[N]bunload[!] [N] [bufname] unload buffer +/// :[N]bdelete[!] [N] [bufname] delete buffer from buffer list +/// :[N]bwipeout[!] [N] [bufname] delete buffer really static void ex_bunload(exarg_T *eap) { - eap->errmsg = do_bufdel(eap->cmdidx == CMD_bdelete ? DOBUF_DEL - : eap->cmdidx == CMD_bwipeout ? DOBUF_WIPE - : DOBUF_UNLOAD, - eap->arg, - eap->addr_count, (int)eap->line1, (int)eap->line2, eap->forceit); + eap->errmsg = do_bufdel(eap->cmdidx == CMD_bdelete + ? DOBUF_DEL + : eap->cmdidx == CMD_bwipeout + ? DOBUF_WIPE + : DOBUF_UNLOAD, + eap->arg, eap->addr_count, (int)eap->line1, (int)eap->line2, + eap->forceit); } -/* - * :[N]buffer [N] to buffer N - * :[N]sbuffer [N] to buffer N - */ +/// :[N]buffer [N] to buffer N +/// :[N]sbuffer [N] to buffer N static void ex_buffer(exarg_T *eap) { if (*eap->arg) { @@ -5009,72 +5371,62 @@ static void ex_buffer(exarg_T *eap) goto_buffer(eap, DOBUF_FIRST, FORWARD, (int)eap->line2); } if (eap->do_ecmd_cmd != NULL) { - do_cmdline_cmd((char *)eap->do_ecmd_cmd); + do_cmdline_cmd(eap->do_ecmd_cmd); } } } -/* - * :[N]bmodified [N] to next mod. buffer - * :[N]sbmodified [N] to next mod. buffer - */ +/// :[N]bmodified [N] to next mod. buffer +/// :[N]sbmodified [N] to next mod. buffer static void ex_bmodified(exarg_T *eap) { goto_buffer(eap, DOBUF_MOD, FORWARD, (int)eap->line2); if (eap->do_ecmd_cmd != NULL) { - do_cmdline_cmd((char *)eap->do_ecmd_cmd); + do_cmdline_cmd(eap->do_ecmd_cmd); } } -/* - * :[N]bnext [N] to next buffer - * :[N]sbnext [N] split and to next buffer - */ +/// :[N]bnext [N] to next buffer +/// :[N]sbnext [N] split and to next buffer static void ex_bnext(exarg_T *eap) { goto_buffer(eap, DOBUF_CURRENT, FORWARD, (int)eap->line2); if (eap->do_ecmd_cmd != NULL) { - do_cmdline_cmd((char *)eap->do_ecmd_cmd); + do_cmdline_cmd(eap->do_ecmd_cmd); } } -/* - * :[N]bNext [N] to previous buffer - * :[N]bprevious [N] to previous buffer - * :[N]sbNext [N] split and to previous buffer - * :[N]sbprevious [N] split and to previous buffer - */ +/// :[N]bNext [N] to previous buffer +/// :[N]bprevious [N] to previous buffer +/// :[N]sbNext [N] split and to previous buffer +/// :[N]sbprevious [N] split and to previous buffer static void ex_bprevious(exarg_T *eap) { goto_buffer(eap, DOBUF_CURRENT, BACKWARD, (int)eap->line2); if (eap->do_ecmd_cmd != NULL) { - do_cmdline_cmd((char *)eap->do_ecmd_cmd); + do_cmdline_cmd(eap->do_ecmd_cmd); } } -/* - * :brewind to first buffer - * :bfirst to first buffer - * :sbrewind split and to first buffer - * :sbfirst split and to first buffer - */ +/// :brewind to first buffer +/// :bfirst to first buffer +/// :sbrewind split and to first buffer +/// :sbfirst split and to first buffer static void ex_brewind(exarg_T *eap) { goto_buffer(eap, DOBUF_FIRST, FORWARD, 0); if (eap->do_ecmd_cmd != NULL) { - do_cmdline_cmd((char *)eap->do_ecmd_cmd); + do_cmdline_cmd(eap->do_ecmd_cmd); } } -/* - * :blast to last buffer - * :sblast split and to last buffer - */ +/// :blast to last buffer +/// :sblast split and to last buffer static void ex_blast(exarg_T *eap) { goto_buffer(eap, DOBUF_LAST, BACKWARD, 0); if (eap->do_ecmd_cmd != NULL) { - do_cmdline_cmd((char *)eap->do_ecmd_cmd); + do_cmdline_cmd(eap->do_ecmd_cmd); } } @@ -5083,10 +5435,8 @@ int ends_excmd(int c) FUNC_ATTR_CONST return c == NUL || c == '|' || c == '"' || c == '\n'; } -/* - * Return the next command, after the first '|' or '\n'. - * Return NULL if not found. - */ +/// @return the next command, after the first '|' or '\n' or, +/// NULL if not found. char_u *find_nextcmd(const char_u *p) { while (*p != '|' && *p != '\n') { @@ -5099,13 +5449,14 @@ char_u *find_nextcmd(const char_u *p) } /// Check if *p is a separator between Ex commands, skipping over white space. -/// Return NULL if it isn't, the following character if it is. +/// +/// @return NULL if it isn't, the following character if it is. char_u *check_nextcmd(char_u *p) { - char_u *s = skipwhite(p); + char *s = skipwhite((char *)p); if (*s == '|' || *s == '\n') { - return (s + 1); + return (char_u *)(s + 1); } else { return NULL; } @@ -5115,9 +5466,10 @@ char_u *check_nextcmd(char_u *p) /// - and this is the last window /// - and forceit not used /// - and not repeated twice on a row -/// @return FAIL and give error message if 'message' TRUE, return OK otherwise /// /// @param message when FALSE check only, no messages +/// +/// @return FAIL and give error message if 'message' TRUE, return OK otherwise static int check_more(int message, bool forceit) { int n = ARGCOUNT - curwin->w_arg_idx - 1; @@ -5125,13 +5477,13 @@ static int check_more(int message, bool forceit) if (!forceit && only_one_window() && ARGCOUNT > 1 && !arg_had_last && n > 0 && quitmore == 0) { if (message) { - if ((p_confirm || cmdmod.confirm) && curbuf->b_fname != NULL) { - char_u buff[DIALOG_MSG_SIZE]; + if ((p_confirm || (cmdmod.cmod_flags & CMOD_CONFIRM)) && curbuf->b_fname != NULL) { + char buff[DIALOG_MSG_SIZE]; vim_snprintf((char *)buff, DIALOG_MSG_SIZE, NGETTEXT("%d more file to edit. Quit anyway?", "%d more files to edit. Quit anyway?", (unsigned long)n), n); - if (vim_dialog_yesno(VIM_QUESTION, NULL, buff, 1) == VIM_YES) { + if (vim_dialog_yesno(VIM_QUESTION, NULL, (char_u *)buff, 1) == VIM_YES) { return OK; } return FAIL; @@ -5145,33 +5497,53 @@ static int check_more(int message, bool forceit) return OK; } -/* - * Function given to ExpandGeneric() to obtain the list of command names. - */ -char_u *get_command_name(expand_T *xp, int idx) +/// Function given to ExpandGeneric() to obtain the list of command names. +char *get_command_name(expand_T *xp, int idx) { if (idx >= CMD_SIZE) { - return get_user_command_name(idx); + return expand_user_command_name(idx); } return cmdnames[idx].cmd_name; } -int uc_add_command(char_u *name, size_t name_len, char_u *rep, uint32_t argt, long def, int flags, - int compl, char_u *compl_arg, LuaRef compl_luaref, cmd_addr_T addr_type, - LuaRef luaref, bool force) +/// Check for a valid user command name +/// +/// If the given {name} is valid, then a pointer to the end of the valid name is returned. +/// Otherwise, returns NULL. +char *uc_validate_name(char *name) +{ + if (ASCII_ISALPHA(*name)) { + while (ASCII_ISALNUM(*name)) { + name++; + } + } + if (!ends_excmd(*name) && !ascii_iswhite(*name)) { + return NULL; + } + + return name; +} + +/// Create a new user command {name}, if one doesn't already exist. +/// +/// This function takes ownership of compl_arg, compl_luaref, and luaref. +/// +/// @return OK if the command is created, FAIL otherwise. +int uc_add_command(char *name, size_t name_len, char *rep, uint32_t argt, long def, int flags, + int compl, char *compl_arg, LuaRef compl_luaref, LuaRef preview_luaref, + cmd_addr_T addr_type, LuaRef luaref, bool force) FUNC_ATTR_NONNULL_ARG(1, 3) { ucmd_T *cmd = NULL; int i; int cmp = 1; - char_u *rep_buf = NULL; + char *rep_buf = NULL; garray_T *gap; - replace_termcodes(rep, STRLEN(rep), &rep_buf, false, false, true, - CPO_TO_CPO_FLAGS); + replace_termcodes(rep, STRLEN(rep), &rep_buf, 0, NULL, CPO_TO_CPO_FLAGS); if (rep_buf == NULL) { // Can't replace termcodes - try using the string as is - rep_buf = vim_strsave(rep); + rep_buf = xstrdup(rep); } // get address of growarray: global or in curbuf @@ -5214,6 +5586,7 @@ int uc_add_command(char_u *name, size_t name_len, char_u *rep, uint32_t argt, lo XFREE_CLEAR(cmd->uc_compl_arg); NLUA_CLEAR_REF(cmd->uc_luaref); NLUA_CLEAR_REF(cmd->uc_compl_luaref); + NLUA_CLEAR_REF(cmd->uc_preview_luaref); break; } @@ -5227,24 +5600,26 @@ int uc_add_command(char_u *name, size_t name_len, char_u *rep, uint32_t argt, lo if (cmp != 0) { ga_grow(gap, 1); - char_u *const p = vim_strnsave(name, name_len); + char *const p = xstrnsave(name, name_len); cmd = USER_CMD_GA(gap, i); - memmove(cmd + 1, cmd, (gap->ga_len - i) * sizeof(ucmd_T)); + memmove(cmd + 1, cmd, (size_t)(gap->ga_len - i) * sizeof(ucmd_T)); ++gap->ga_len; - cmd->uc_name = p; + cmd->uc_name = (char_u *)p; } - cmd->uc_rep = rep_buf; + cmd->uc_rep = (char_u *)rep_buf; cmd->uc_argt = argt; cmd->uc_def = def; cmd->uc_compl = compl; cmd->uc_script_ctx = current_sctx; cmd->uc_script_ctx.sc_lnum += sourcing_lnum; - cmd->uc_compl_arg = compl_arg; + nlua_set_sctx(&cmd->uc_script_ctx); + cmd->uc_compl_arg = (char_u *)compl_arg; cmd->uc_compl_luaref = compl_luaref; + cmd->uc_preview_luaref = preview_luaref; cmd->uc_addr_type = addr_type; cmd->uc_luaref = luaref; @@ -5255,10 +5630,10 @@ fail: xfree(compl_arg); NLUA_CLEAR_REF(luaref); NLUA_CLEAR_REF(compl_luaref); + NLUA_CLEAR_REF(preview_luaref); return FAIL; } - static struct { cmd_addr_T expand; char *name; @@ -5335,7 +5710,7 @@ static char *get_command_complete(int arg) } } -static void uc_list(char_u *name, size_t name_len) +static void uc_list(char *name, size_t name_len) { int i, j; bool found = false; @@ -5343,9 +5718,7 @@ static void uc_list(char_u *name, size_t name_len) uint32_t a; // In cmdwin, the alternative buffer should be used. - garray_T *gap = (cmdwin_type != 0 && get_cmdline_type() == NUL) - ? &prevwin->w_buffer->b_ucmds - : &curbuf->b_ucmds; + const garray_T *gap = &prevwin_curwin()->w_buffer->b_ucmds; for (;;) { for (i = 0; i < gap->ga_len; i++) { cmd = USER_CMD_GA(gap, i); @@ -5476,7 +5849,7 @@ static void uc_list(char_u *name, size_t name_len) } while (len < 25 - over); IObuff[len] = '\0'; - msg_outtrans(IObuff); + msg_outtrans((char *)IObuff); msg_outtrans_special(cmd->uc_rep, false, name_len == 0 ? Columns - 47 : 0); @@ -5499,11 +5872,11 @@ static void uc_list(char_u *name, size_t name_len) } } -static int uc_scan_attr(char_u *attr, size_t len, uint32_t *argt, long *def, int *flags, - int *complp, char_u **compl_arg, cmd_addr_T *addr_type_arg) +static int uc_scan_attr(char *attr, size_t len, uint32_t *argt, long *def, int *flags, int *complp, + char_u **compl_arg, cmd_addr_T *addr_type_arg) FUNC_ATTR_NONNULL_ALL { - char_u *p; + char *p; if (len == 0) { emsg(_("E175: No attribute specified")); @@ -5517,11 +5890,13 @@ static int uc_scan_attr(char_u *attr, size_t len, uint32_t *argt, long *def, int *flags |= UC_BUFFER; } else if (STRNICMP(attr, "register", len) == 0) { *argt |= EX_REGSTR; + } else if (STRNICMP(attr, "keepscript", len) == 0) { + *argt |= EX_KEEPSCRIPT; } else if (STRNICMP(attr, "bar", len) == 0) { *argt |= EX_TRLBAR; } else { int i; - char_u *val = NULL; + char *val = NULL; size_t vallen = 0; size_t attrlen = len; @@ -5529,8 +5904,8 @@ static int uc_scan_attr(char_u *attr, size_t len, uint32_t *argt, long *def, int for (i = 0; i < (int)len; i++) { if (attr[i] == '=') { val = &attr[i + 1]; - vallen = len - i - 1; - attrlen = i; + vallen = len - (size_t)i - 1; + attrlen = (size_t)i; break; } } @@ -5567,7 +5942,7 @@ two_count: return FAIL; } - *def = getdigits_long(&p, true, 0); + *def = getdigits_long((char_u **)&p, true, 0); *argt |= EX_ZEROR; if (p != val + vallen || vallen == 0) { @@ -5593,7 +5968,7 @@ invalid_count: goto two_count; } - *def = getdigits_long(&p, true, 0); + *def = getdigits_long((char_u **)&p, true, 0); if (p != val + vallen) { goto invalid_count; @@ -5609,7 +5984,7 @@ invalid_count: return FAIL; } - if (parse_compl_arg(val, (int)vallen, complp, argt, compl_arg) + if (parse_compl_arg(val, (int)vallen, complp, argt, (char **)compl_arg) == FAIL) { return FAIL; } @@ -5626,7 +6001,7 @@ invalid_count: *argt |= EX_ZEROR; } } else { - char_u ch = attr[len]; + char ch = attr[len]; attr[len] = '\0'; semsg(_("E181: Invalid attribute: %s"), attr); attr[len] = ch; @@ -5639,30 +6014,28 @@ invalid_count: static char e_complete_used_without_nargs[] = N_("E1208: -complete used without -nargs"); -/* - * ":command ..." - */ +/// ":command ..." static void ex_command(exarg_T *eap) { - char_u *name; - char_u *end; - char_u *p; + char *name; + char *end; + char *p; uint32_t argt = 0; long def = -1; int flags = 0; int compl = EXPAND_NOTHING; - char_u *compl_arg = NULL; + char *compl_arg = NULL; cmd_addr_T addr_type_arg = ADDR_NONE; int has_attr = (eap->arg[0] == '-'); - int name_len; + size_t name_len; p = eap->arg; // Check for attributes while (*p == '-') { - ++p; - end = skiptowhite(p); - if (uc_scan_attr(p, end - p, &argt, &def, &flags, &compl, &compl_arg, + p++; + end = (char *)skiptowhite((char_u *)p); + if (uc_scan_attr(p, (size_t)(end - p), &argt, &def, &flags, &compl, (char_u **)&compl_arg, &addr_type_arg) == FAIL) { return; } @@ -5671,23 +6044,18 @@ static void ex_command(exarg_T *eap) // Get the name (if any) and skip to the following argument. name = p; - if (ASCII_ISALPHA(*p)) { - while (ASCII_ISALNUM(*p)) { - p++; - } - } - if (!ends_excmd(*p) && !ascii_iswhite(*p)) { + end = uc_validate_name(name); + if (!end) { emsg(_("E182: Invalid command name")); return; } - end = p; - name_len = (int)(end - name); + name_len = (size_t)(end - name); // If there is nothing after the name, and no attributes were specified, // we are listing commands p = skipwhite(end); if (!has_attr && ends_excmd(*p)) { - uc_list(name, end - name); + uc_list(name, name_len); } else if (!ASCII_ISUPPER(*name)) { emsg(_("E183: User defined commands must start with an uppercase letter")); } else if (name_len <= 4 && STRNCMP(name, "Next", name_len) == 0) { @@ -5695,15 +6063,13 @@ static void ex_command(exarg_T *eap) } else if (compl > 0 && (argt & EX_EXTRA) == 0) { emsg(_(e_complete_used_without_nargs)); } else { - uc_add_command(name, end - name, p, argt, def, flags, compl, compl_arg, LUA_NOREF, + uc_add_command(name, name_len, p, argt, def, flags, compl, compl_arg, LUA_NOREF, LUA_NOREF, addr_type_arg, LUA_NOREF, eap->forceit); } } -/* - * ":comclear" - * Clear all user commands, global and for current buffer. - */ +/// ":comclear" +/// Clear all user commands, global and for current buffer. void ex_comclear(exarg_T *eap) { uc_clear(&ucmds); @@ -5717,11 +6083,10 @@ void free_ucmd(ucmd_T *cmd) xfree(cmd->uc_compl_arg); NLUA_CLEAR_REF(cmd->uc_compl_luaref); NLUA_CLEAR_REF(cmd->uc_luaref); + NLUA_CLEAR_REF(cmd->uc_preview_luaref); } -/* - * Clear all user commands for "gap". - */ +/// Clear all user commands for "gap". void uc_clear(garray_T *gap) { GA_DEEP_CLEAR(gap, ucmd_T, free_ucmd); @@ -5731,26 +6096,36 @@ static void ex_delcommand(exarg_T *eap) { int i = 0; ucmd_T *cmd = NULL; - int cmp = -1; + int res = -1; garray_T *gap; + const char *arg = eap->arg; + bool buffer_only = false; + + if (STRNCMP(arg, "-buffer", 7) == 0 && ascii_iswhite(arg[7])) { + buffer_only = true; + arg = skipwhite(arg + 7); + } gap = &curbuf->b_ucmds; for (;;) { for (i = 0; i < gap->ga_len; i++) { cmd = USER_CMD_GA(gap, i); - cmp = STRCMP(eap->arg, cmd->uc_name); - if (cmp <= 0) { + res = STRCMP(arg, cmd->uc_name); + if (res <= 0) { break; } } - if (gap == &ucmds || cmp == 0) { + if (gap == &ucmds || res == 0 || buffer_only) { break; } gap = &ucmds; } - if (cmp != 0) { - semsg(_("E184: No such user-defined command: %s"), eap->arg); + if (res != 0) { + semsg(_(buffer_only + ? e_no_such_user_defined_command_in_current_buffer_str + : e_no_such_user_defined_command_str), + arg); return; } @@ -5759,80 +6134,167 @@ static void ex_delcommand(exarg_T *eap) --gap->ga_len; if (i < gap->ga_len) { - memmove(cmd, cmd + 1, (gap->ga_len - i) * sizeof(ucmd_T)); + memmove(cmd, cmd + 1, (size_t)(gap->ga_len - i) * sizeof(ucmd_T)); } } -/* - * split and quote args for <f-args> - */ -static char_u *uc_split_args(char_u *arg, size_t *lenp) +/// Split a string by unescaped whitespace (space & tab), used for f-args on Lua commands callback. +/// Similar to uc_split_args(), but does not allocate, add quotes, add commas and is an iterator. +/// +/// @param[in] arg String to split +/// @param[in] arglen Length of {arg} +/// @param[inout] end Index of last character of previous iteration +/// @param[out] buf Buffer to copy string into +/// @param[out] len Length of string in {buf} +/// +/// @return true if iteration is complete, else false +bool uc_split_args_iter(const char *arg, size_t arglen, size_t *end, char *buf, size_t *len) +{ + if (!arglen) { + return true; + } + + size_t pos = *end; + while (pos < arglen && ascii_iswhite(arg[pos])) { + pos++; + } + + size_t l = 0; + for (; pos < arglen - 1; pos++) { + if (arg[pos] == '\\' && (arg[pos + 1] == '\\' || ascii_iswhite(arg[pos + 1]))) { + buf[l++] = arg[++pos]; + } else { + buf[l++] = arg[pos]; + if (ascii_iswhite(arg[pos + 1])) { + *end = pos + 1; + *len = l; + return false; + } + } + } + + if (pos < arglen && !ascii_iswhite(arg[pos])) { + buf[l++] = arg[pos]; + } + + *len = l; + return true; +} + +/// split and quote args for <f-args> +static char *uc_split_args(char *arg, char **args, size_t *arglens, size_t argc, size_t *lenp) { - char_u *buf; - char_u *p; - char_u *q; + char *buf; + char *p; + char *q; int len; // Precalculate length - p = arg; len = 2; // Initial and final quotes + if (args == NULL) { + p = arg; - while (*p) { - if (p[0] == '\\' && p[1] == '\\') { - len += 2; - p += 2; - } else if (p[0] == '\\' && ascii_iswhite(p[1])) { - len += 1; - p += 2; - } else if (*p == '\\' || *p == '"') { - len += 2; - p += 1; - } else if (ascii_iswhite(*p)) { - p = skipwhite(p); - if (*p == NUL) { - break; + while (*p) { + if (p[0] == '\\' && p[1] == '\\') { + len += 2; + p += 2; + } else if (p[0] == '\\' && ascii_iswhite(p[1])) { + len += 1; + p += 2; + } else if (*p == '\\' || *p == '"') { + len += 2; + p += 1; + } else if (ascii_iswhite(*p)) { + p = skipwhite(p); + if (*p == NUL) { + break; + } + len += 3; // "," + } else { + const int charlen = utfc_ptr2len(p); + + len += charlen; + p += charlen; } - len += 3; // "," - } else { - const int charlen = utfc_ptr2len(p); + } + } else { + for (size_t i = 0; i < argc; i++) { + p = args[i]; + const char *arg_end = args[i] + arglens[i]; + + while (p < arg_end) { + if (*p == '\\' || *p == '"') { + len += 2; + p += 1; + } else { + const int charlen = utfc_ptr2len(p); - len += charlen; - p += charlen; + len += charlen; + p += charlen; + } + } + + if (i != argc - 1) { + len += 3; // "," + } } } - buf = xmalloc(len + 1); + buf = xmalloc((size_t)len + 1); - p = arg; q = buf; *q++ = '"'; - while (*p) { - if (p[0] == '\\' && p[1] == '\\') { - *q++ = '\\'; - *q++ = '\\'; - p += 2; - } else if (p[0] == '\\' && ascii_iswhite(p[1])) { - *q++ = p[1]; - p += 2; - } else if (*p == '\\' || *p == '"') { - *q++ = '\\'; - *q++ = *p++; - } else if (ascii_iswhite(*p)) { - p = skipwhite(p); - if (*p == NUL) { - break; + + if (args == NULL) { + p = arg; + while (*p) { + if (p[0] == '\\' && p[1] == '\\') { + *q++ = '\\'; + *q++ = '\\'; + p += 2; + } else if (p[0] == '\\' && ascii_iswhite(p[1])) { + *q++ = p[1]; + p += 2; + } else if (*p == '\\' || *p == '"') { + *q++ = '\\'; + *q++ = *p++; + } else if (ascii_iswhite(*p)) { + p = skipwhite(p); + if (*p == NUL) { + break; + } + *q++ = '"'; + *q++ = ','; + *q++ = '"'; + } else { + mb_copy_char((const char_u **)&p, (char_u **)&q); + } + } + } else { + for (size_t i = 0; i < argc; i++) { + p = args[i]; + const char *arg_end = args[i] + arglens[i]; + + while (p < arg_end) { + if (*p == '\\' || *p == '"') { + *q++ = '\\'; + *q++ = *p++; + } else { + mb_copy_char((const char_u **)&p, (char_u **)&q); + } + } + if (i != argc - 1) { + *q++ = '"'; + *q++ = ','; + *q++ = '"'; } - *q++ = '"'; - *q++ = ','; - *q++ = '"'; - } else { - mb_copy_char((const char_u **)&p, &q); } } + *q++ = '"'; *q = 0; - *lenp = len; + *lenp = (size_t)len; return buf; } @@ -5865,11 +6327,11 @@ static size_t add_cmd_modifier(char *buf, char *mod_str, bool *multi_mods) /// /// @return the length of the replacement, which has been added to "buf". /// Return -1 if there was no match, and only the "<" has been copied. -static size_t uc_check_code(char_u *code, size_t len, char_u *buf, ucmd_T *cmd, exarg_T *eap, - char_u **split_buf, size_t *split_len) +static size_t uc_check_code(char *code, size_t len, char *buf, ucmd_T *cmd, exarg_T *eap, + char **split_buf, size_t *split_len) { size_t result = 0; - char_u *p = code + 1; + char *p = code + 1; size_t l = len - 2; int quote = 0; enum { @@ -5885,7 +6347,7 @@ static size_t uc_check_code(char_u *code, size_t len, char_u *buf, ucmd_T *cmd, ct_NONE, } type = ct_NONE; - if ((vim_strchr((char_u *)"qQfF", *p) != NULL) && p[1] == '-') { + if ((vim_strchr("qQfF", *p) != NULL) && p[1] == '-') { quote = (*p == 'q' || *p == 'Q') ? 1 : 2; p += 2; l -= 2; @@ -5965,7 +6427,7 @@ static size_t uc_check_code(char_u *code, size_t len, char_u *buf, ucmd_T *cmd, case 2: // Quote and split (<f-args>) // This is hard, so only do it once, and cache the result if (*split_buf == NULL) { - *split_buf = uc_split_args(eap->arg, split_len); + *split_buf = uc_split_args(eap->arg, eap->args, eap->arglens, eap->argc, split_len); } result = *split_len; @@ -6029,20 +6491,7 @@ static size_t uc_check_code(char_u *code, size_t len, char_u *buf, ucmd_T *cmd, } case ct_MODS: - result = quote ? 2 : 0; - if (buf != NULL) { - if (quote) { - *buf++ = '"'; - } - *buf = '\0'; - } - - result += uc_mods((char *)buf); - - if (quote && buf != NULL) { - buf += result - 2; - *buf = '"'; - } + result = uc_mods(buf, &cmdmod, quote); break; case ct_REGISTER: @@ -6055,7 +6504,7 @@ static size_t uc_check_code(char_u *code, size_t len, char_u *buf, ucmd_T *cmd, *buf++ = '\''; } if (eap->regname) { - *buf++ = eap->regname; + *buf++ = (char)eap->regname; } if (quote) { *buf = '\''; @@ -6082,91 +6531,124 @@ static size_t uc_check_code(char_u *code, size_t len, char_u *buf, ucmd_T *cmd, return result; } -size_t uc_mods(char *buf) +/// Add modifiers from "cmod->cmod_split" to "buf". Set "multi_mods" when one +/// was added. +/// +/// @return the number of bytes added +size_t add_win_cmd_modifers(char *buf, const cmdmod_T *cmod, bool *multi_mods) { size_t result = 0; - bool multi_mods = false; // :aboveleft and :leftabove - if (cmdmod.split & WSP_ABOVE) { - result += add_cmd_modifier(buf, "aboveleft", &multi_mods); + if (cmod->cmod_split & WSP_ABOVE) { + result += add_cmd_modifier(buf, "aboveleft", multi_mods); } // :belowright and :rightbelow - if (cmdmod.split & WSP_BELOW) { - result += add_cmd_modifier(buf, "belowright", &multi_mods); + if (cmod->cmod_split & WSP_BELOW) { + result += add_cmd_modifier(buf, "belowright", multi_mods); } // :botright - if (cmdmod.split & WSP_BOT) { - result += add_cmd_modifier(buf, "botright", &multi_mods); + if (cmod->cmod_split & WSP_BOT) { + result += add_cmd_modifier(buf, "botright", multi_mods); + } + + // :tab + if (cmod->cmod_tab > 0) { + result += add_cmd_modifier(buf, "tab", multi_mods); } + // :topleft + if (cmod->cmod_split & WSP_TOP) { + result += add_cmd_modifier(buf, "topleft", multi_mods); + } + // :vertical + if (cmod->cmod_split & WSP_VERT) { + result += add_cmd_modifier(buf, "vertical", multi_mods); + } + return result; +} + +/// Generate text for the "cmod" command modifiers. +/// If "buf" is NULL just return the length. +size_t uc_mods(char *buf, const cmdmod_T *cmod, bool quote) +{ + size_t result = 0; + bool multi_mods = false; typedef struct { - bool *set; + int flag; char *name; } mod_entry_T; static mod_entry_T mod_entries[] = { - { &cmdmod.browse, "browse" }, - { &cmdmod.confirm, "confirm" }, - { &cmdmod.hide, "hide" }, - { &cmdmod.keepalt, "keepalt" }, - { &cmdmod.keepjumps, "keepjumps" }, - { &cmdmod.keepmarks, "keepmarks" }, - { &cmdmod.keeppatterns, "keeppatterns" }, - { &cmdmod.lockmarks, "lockmarks" }, - { &cmdmod.noswapfile, "noswapfile" } + { CMOD_BROWSE, "browse" }, + { CMOD_CONFIRM, "confirm" }, + { CMOD_HIDE, "hide" }, + { CMOD_KEEPALT, "keepalt" }, + { CMOD_KEEPJUMPS, "keepjumps" }, + { CMOD_KEEPMARKS, "keepmarks" }, + { CMOD_KEEPPATTERNS, "keeppatterns" }, + { CMOD_LOCKMARKS, "lockmarks" }, + { CMOD_NOSWAPFILE, "noswapfile" }, + { CMOD_UNSILENT, "unsilent" }, + { CMOD_NOAUTOCMD, "noautocmd" }, + { CMOD_SANDBOX, "sandbox" }, }; + + result = quote ? 2 : 0; + if (buf != NULL) { + if (quote) { + *buf++ = '"'; + } + *buf = '\0'; + } + // the modifiers that are simple flags for (size_t i = 0; i < ARRAY_SIZE(mod_entries); i++) { - if (*mod_entries[i].set) { + if (cmod->cmod_flags & mod_entries[i].flag) { result += add_cmd_modifier(buf, mod_entries[i].name, &multi_mods); } } - // TODO(vim): How to support :noautocmd? - // TODO(vim): How to support :sandbox? - // :silent - if (msg_silent > 0) { - result += add_cmd_modifier(buf, emsg_silent > 0 ? "silent!" : "silent", &multi_mods); - } - // :tab - if (cmdmod.tab > 0) { - result += add_cmd_modifier(buf, "tab", &multi_mods); - } - // :topleft - if (cmdmod.split & WSP_TOP) { - result += add_cmd_modifier(buf, "topleft", &multi_mods); + if (cmod->cmod_flags & CMOD_SILENT) { + result += add_cmd_modifier(buf, + (cmod->cmod_flags & CMOD_ERRSILENT) ? "silent!" : "silent", + &multi_mods); } - - // TODO(vim): How to support :unsilent? - // :verbose - if (p_verbose > 0) { - result += add_cmd_modifier(buf, "verbose", &multi_mods); - } - // :vertical - if (cmdmod.split & WSP_VERT) { - result += add_cmd_modifier(buf, "vertical", &multi_mods); + if (cmod->cmod_verbose > 0) { + int verbose_value = cmod->cmod_verbose - 1; + if (verbose_value == 1) { + result += add_cmd_modifier(buf, "verbose", &multi_mods); + } else { + char verbose_buf[NUMBUFLEN]; + snprintf(verbose_buf, NUMBUFLEN, "%dverbose", verbose_value); + result += add_cmd_modifier(buf, verbose_buf, &multi_mods); + } } + // flags from cmod->cmod_split + result += add_win_cmd_modifers(buf, cmod, &multi_mods); + if (quote && buf != NULL) { + buf += result - 2; + *buf = '"'; + } return result; } -static void do_ucmd(exarg_T *eap) +static int do_ucmd(exarg_T *eap, bool preview) { - char_u *buf; - char_u *p; - char_u *q; + char *buf; + char *p; + char *q; - char_u *start; - char_u *end = NULL; - char_u *ksp; + char *start; + char *end = NULL; + char *ksp; size_t len, totlen; size_t split_len = 0; - char_u *split_buf = NULL; + char *split_buf = NULL; ucmd_T *cmd; - const sctx_T save_current_sctx = current_sctx; if (eap->cmdidx == CMD_USER) { cmd = USER_CMD(eap->useridx); @@ -6174,9 +6656,14 @@ static void do_ucmd(exarg_T *eap) cmd = USER_CMD_GA(&curbuf->b_ucmds, eap->useridx); } + if (preview) { + assert(cmd->uc_preview_luaref > 0); + return nlua_do_ucmd(cmd, eap, true); + } + if (cmd->uc_luaref > 0) { - nlua_do_ucmd(cmd, eap); - return; + nlua_do_ucmd(cmd, eap, false); + return 0; } /* @@ -6186,7 +6673,7 @@ static void do_ucmd(exarg_T *eap) */ buf = NULL; for (;;) { - p = cmd->uc_rep; // source + p = (char *)cmd->uc_rep; // source q = buf; // destination totlen = 0; @@ -6196,21 +6683,19 @@ static void do_ucmd(exarg_T *eap) end = vim_strchr(start + 1, '>'); } if (buf != NULL) { - for (ksp = p; *ksp != NUL && *ksp != K_SPECIAL; ksp++) { - } - if (*ksp == K_SPECIAL + for (ksp = p; *ksp != NUL && (char_u)(*ksp) != K_SPECIAL; ksp++) {} + if ((char_u)(*ksp) == K_SPECIAL && (start == NULL || ksp < start || end == NULL) - && (ksp[1] == KS_SPECIAL && ksp[2] == KE_FILLER)) { + && ((char_u)ksp[1] == KS_SPECIAL && ksp[2] == KE_FILLER)) { // K_SPECIAL has been put in the buffer as K_SPECIAL // KS_SPECIAL KE_FILLER, like for mappings, but // do_cmdline() doesn't handle that, so convert it back. - // Also change K_SPECIAL KS_EXTRA KE_CSI into CSI. - len = ksp - p; + len = (size_t)(ksp - p); if (len > 0) { memmove(q, p, len); q += len; } - *q++ = K_SPECIAL; + *q++ = (char)K_SPECIAL; p = ksp + 3; continue; } @@ -6225,7 +6710,7 @@ static void do_ucmd(exarg_T *eap) ++end; // Take everything up to the '<' - len = start - p; + len = (size_t)(start - p); if (buf == NULL) { totlen += len; } else { @@ -6233,8 +6718,7 @@ static void do_ucmd(exarg_T *eap) q += len; } - len = uc_check_code(start, end - start, q, cmd, eap, - &split_buf, &split_len); + len = uc_check_code(start, (size_t)(end - start), q, cmd, eap, &split_buf, &split_len); if (len == (size_t)-1) { // no match, continue after '<' p = start + 1; @@ -6257,96 +6741,117 @@ static void do_ucmd(exarg_T *eap) buf = xmalloc(totlen + 1); } - current_sctx.sc_sid = cmd->uc_script_ctx.sc_sid; + sctx_T save_current_sctx; + bool restore_current_sctx = false; + if ((cmd->uc_argt & EX_KEEPSCRIPT) == 0) { + restore_current_sctx = true; + save_current_sctx = current_sctx; + current_sctx.sc_sid = cmd->uc_script_ctx.sc_sid; + } (void)do_cmdline(buf, eap->getline, eap->cookie, DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_KEYTYPED); - current_sctx = save_current_sctx; + + // Careful: Do not use "cmd" here, it may have become invalid if a user + // command was added. + if (restore_current_sctx) { + current_sctx = save_current_sctx; + } xfree(buf); xfree(split_buf); + + return 0; } -static char_u *get_user_command_name(int idx) +static char *expand_user_command_name(int idx) { return get_user_commands(NULL, idx - CMD_SIZE); } -/* - * Function given to ExpandGeneric() to obtain the list of user address type names. - */ -char_u *get_user_cmd_addr_type(expand_T *xp, int idx) + +/// Function given to ExpandGeneric() to obtain the list of user address type names. +char *get_user_cmd_addr_type(expand_T *xp, int idx) { - return (char_u *)addr_type_complete[idx].name; + return addr_type_complete[idx].name; } -/* - * Function given to ExpandGeneric() to obtain the list of user command names. - */ -char_u *get_user_commands(expand_T *xp FUNC_ATTR_UNUSED, int idx) +/// Function given to ExpandGeneric() to obtain the list of user command names. +char *get_user_commands(expand_T *xp FUNC_ATTR_UNUSED, int idx) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { // In cmdwin, the alternative buffer should be used. - const buf_T *const buf = (cmdwin_type != 0 && get_cmdline_type() == NUL) - ? prevwin->w_buffer - : curbuf; + const buf_T *const buf = prevwin_curwin()->w_buffer; if (idx < buf->b_ucmds.ga_len) { - return USER_CMD_GA(&buf->b_ucmds, idx)->uc_name; + return (char *)USER_CMD_GA(&buf->b_ucmds, idx)->uc_name; } idx -= buf->b_ucmds.ga_len; if (idx < ucmds.ga_len) { - return USER_CMD(idx)->uc_name; + return (char *)USER_CMD(idx)->uc_name; } return NULL; } -/* - * Function given to ExpandGeneric() to obtain the list of user command - * attributes. - */ -char_u *get_user_cmd_flags(expand_T *xp, int idx) +/// Get the name of user command "idx". "cmdidx" can be CMD_USER or +/// CMD_USER_BUF. +/// +/// @return NULL if the command is not found. +static char *get_user_command_name(int idx, int cmdidx) +{ + if (cmdidx == CMD_USER && idx < ucmds.ga_len) { + return (char *)USER_CMD(idx)->uc_name; + } + if (cmdidx == CMD_USER_BUF) { + // In cmdwin, the alternative buffer should be used. + const buf_T *const buf = prevwin_curwin()->w_buffer; + + if (idx < buf->b_ucmds.ga_len) { + return (char *)USER_CMD_GA(&buf->b_ucmds, idx)->uc_name; + } + } + return NULL; +} + +/// Function given to ExpandGeneric() to obtain the list of user command +/// attributes. +char *get_user_cmd_flags(expand_T *xp, int idx) { static char *user_cmd_flags[] = { "addr", "bang", "bar", "buffer", "complete", "count", - "nargs", "range", "register" }; + "nargs", "range", "register", + "keepscript" }; if (idx >= (int)ARRAY_SIZE(user_cmd_flags)) { return NULL; } - return (char_u *)user_cmd_flags[idx]; + return user_cmd_flags[idx]; } -/* - * Function given to ExpandGeneric() to obtain the list of values for -nargs. - */ -char_u *get_user_cmd_nargs(expand_T *xp, int idx) +/// Function given to ExpandGeneric() to obtain the list of values for -nargs. +char *get_user_cmd_nargs(expand_T *xp, int idx) { static char *user_cmd_nargs[] = { "0", "1", "*", "?", "+" }; if (idx >= (int)ARRAY_SIZE(user_cmd_nargs)) { return NULL; } - return (char_u *)user_cmd_nargs[idx]; + return user_cmd_nargs[idx]; } -/* - * Function given to ExpandGeneric() to obtain the list of values for -complete. - */ -char_u *get_user_cmd_complete(expand_T *xp, int idx) +/// Function given to ExpandGeneric() to obtain the list of values for -complete. +char *get_user_cmd_complete(expand_T *xp, int idx) { if (idx >= (int)ARRAY_SIZE(command_complete)) { return NULL; } char *cmd_compl = get_command_complete(idx); if (cmd_compl == NULL) { - return (char_u *)""; + return ""; } else { - return (char_u *)cmd_compl; + return cmd_compl; } } -/* - * Parse address type argument - */ -int parse_addr_type_arg(char_u *value, int vallen, cmd_addr_T *addr_type_arg) +/// Parse address type argument +int parse_addr_type_arg(char *value, int vallen, cmd_addr_T *addr_type_arg) FUNC_ATTR_NONNULL_ALL { int i, a, b; @@ -6361,7 +6866,7 @@ int parse_addr_type_arg(char_u *value, int vallen, cmd_addr_T *addr_type_arg) } if (addr_type_complete[i].expand == ADDR_NONE) { - char_u *err = value; + char *err = value; for (i = 0; err[i] != NUL && !ascii_iswhite(err[i]); i++) {} err[i] = NUL; @@ -6372,18 +6877,16 @@ int parse_addr_type_arg(char_u *value, int vallen, cmd_addr_T *addr_type_arg) return OK; } -/* - * Parse a completion argument "value[vallen]". - * The detected completion goes in "*complp", argument type in "*argt". - * When there is an argument, for function and user defined completion, it's - * copied to allocated memory and stored in "*compl_arg". - * Returns FAIL if something is wrong. - */ -int parse_compl_arg(const char_u *value, int vallen, int *complp, uint32_t *argt, - char_u **compl_arg) +/// Parse a completion argument "value[vallen]". +/// The detected completion goes in "*complp", argument type in "*argt". +/// When there is an argument, for function and user defined completion, it's +/// copied to allocated memory and stored in "*compl_arg". +/// +/// @return FAIL if something is wrong. +int parse_compl_arg(const char *value, int vallen, int *complp, uint32_t *argt, char **compl_arg) FUNC_ATTR_NONNULL_ALL { - const char_u *arg = NULL; + const char *arg = NULL; size_t arglen = 0; int i; int valend = vallen; @@ -6391,8 +6894,8 @@ int parse_compl_arg(const char_u *value, int vallen, int *complp, uint32_t *argt // Look for any argument part - which is the part after any ',' for (i = 0; i < vallen; ++i) { if (value[i] == ',') { - arg = &value[i + 1]; - arglen = vallen - i - 1; + arg = (char *)&value[i + 1]; + arglen = (size_t)(vallen - i - 1); valend = i; break; } @@ -6432,7 +6935,7 @@ int parse_compl_arg(const char_u *value, int vallen, int *complp, uint32_t *argt } if (arg != NULL) { - *compl_arg = vim_strnsave(arg, arglen); + *compl_arg = xstrnsave(arg, arglen); } return OK; } @@ -6455,8 +6958,8 @@ int cmdcomplete_str_to_type(const char *complete_str) static void ex_colorscheme(exarg_T *eap) { if (*eap->arg == NUL) { - char_u *expr = vim_strsave((char_u *)"g:colors_name"); - char_u *p = NULL; + char *expr = xstrdup("g:colors_name"); + char *p = NULL; emsg_off++; p = eval_to_string(expr, NULL, false); @@ -6464,12 +6967,12 @@ static void ex_colorscheme(exarg_T *eap) xfree(expr); if (p != NULL) { - msg((char *)p); + msg(p); xfree(p); } else { msg("default"); } - } else if (load_colors(eap->arg) == FAIL) { + } else if (load_colors((char_u *)eap->arg) == FAIL) { semsg(_("E185: Cannot find color scheme '%s'"), eap->arg); } } @@ -6482,11 +6985,8 @@ static void ex_highlight(exarg_T *eap) do_highlight((const char *)eap->arg, eap->forceit, false); } - -/* - * Call this function if we thought we were going to exit, but we won't - * (because of an error). May need to restore the terminal mode. - */ +/// Call this function if we thought we were going to exit, but we won't +/// (because of an error). May need to restore the terminal mode. void not_exiting(void) { exiting = false; @@ -6521,8 +7021,8 @@ bool before_quit_autocmds(win_T *wp, bool quit_all, bool forceit) return false; } -// ":quit": quit current window, quit Vim if the last window is closed. -// ":{nr}quit": quit window {nr} +/// ":quit": quit current window, quit Vim if the last window is closed. +/// ":{nr}quit": quit window {nr} static void ex_quit(exarg_T *eap) { if (cmdwin_type != 0) { @@ -6538,7 +7038,7 @@ static void ex_quit(exarg_T *eap) win_T *wp; if (eap->addr_count > 0) { - int wnr = eap->line2; + linenr_T wnr = eap->line2; for (wp = firstwin; wp->w_next != NULL; wp = wp->w_next) { if (--wnr <= 0) { @@ -6582,12 +7082,13 @@ static void ex_quit(exarg_T *eap) } not_exiting(); // close window; may free buffer - win_close(wp, !buf_hide(wp->w_buffer) || eap->forceit); + win_close(wp, !buf_hide(wp->w_buffer) || eap->forceit, eap->forceit); } } /// ":cquit". static void ex_cquit(exarg_T *eap) + FUNC_ATTR_NORETURN { // this does not always pass on the exit code to the Manx compiler. why? getout(eap->addr_count > 0 ? (int)eap->line2 : EXIT_FAILURE); @@ -6622,9 +7123,7 @@ static void ex_quit_all(exarg_T *eap) not_exiting(); } -/* - * ":close": close current window, unless it is the last one - */ +/// ":close": close current window, unless it is the last one static void ex_close(exarg_T *eap) { win_T *win = NULL; @@ -6650,9 +7149,7 @@ static void ex_close(exarg_T *eap) } } -/* - * ":pclose": Close any preview window. - */ +/// ":pclose": Close any preview window. static void ex_pclose(exarg_T *eap) { FOR_ALL_WINDOWS_IN_TAB(win, curtab) { @@ -6680,7 +7177,7 @@ void ex_win_close(int forceit, win_T *win, tabpage_T *tp) need_hide = (bufIsChanged(buf) && buf->b_nwindows <= 1); if (need_hide && !buf_hide(buf) && !forceit) { - if ((p_confirm || cmdmod.confirm) && p_write) { + if ((p_confirm || (cmdmod.cmod_flags & CMOD_CONFIRM)) && p_write) { bufref_T bufref; set_bufref(&bufref, buf); dialog_changed(buf, false); @@ -6694,19 +7191,16 @@ void ex_win_close(int forceit, win_T *win, tabpage_T *tp) } } - // free buffer when not hiding it or when it's a scratch buffer if (tp == NULL) { - win_close(win, !need_hide && !buf_hide(buf)); + win_close(win, !need_hide && !buf_hide(buf), forceit); } else { win_close_othertab(win, !need_hide && !buf_hide(buf), tp); } } -/* - * ":tabclose": close current tab page, unless it is the last one. - * ":tabclose N": close tab page N. - */ +/// ":tabclose": close current tab page, unless it is the last one. +/// ":tabclose N": close tab page N. static void ex_tabclose(exarg_T *eap) { tabpage_T *tp; @@ -6767,9 +7261,7 @@ static void ex_tabonly(exarg_T *eap) } } -/* - * Close the current tab page. - */ +/// Close the current tab page. void tabpage_close(int forceit) { // First close all the windows but the current one. If that worked then @@ -6785,18 +7277,16 @@ void tabpage_close(int forceit) } } -/* - * Close tab page "tp", which is not the current tab page. - * Note that autocommands may make "tp" invalid. - * Also takes care of the tab pages line disappearing when closing the - * last-but-one tab page. - */ +/// Close tab page "tp", which is not the current tab page. +/// Note that autocommands may make "tp" invalid. +/// Also takes care of the tab pages line disappearing when closing the +/// last-but-one tab page. void tabpage_close_other(tabpage_T *tp, int forceit) { int done = 0; win_T *wp; int h = tabline_height(); - char_u prev_idx[NUMBUFLEN]; + char prev_idx[NUMBUFLEN]; // Limit to 1000 windows, autocommands may add a window while we close // one. OK, so I'm paranoid... @@ -6807,24 +7297,22 @@ void tabpage_close_other(tabpage_T *tp, int forceit) // Autocommands may delete the tab page under our fingers and we may // fail to close a window with a modified buffer. - if (!valid_tabpage(tp) || tp->tp_firstwin == wp) { + if (!valid_tabpage(tp) || tp->tp_lastwin == wp) { break; } } redraw_tabline = true; if (h != tabline_height()) { - shell_new_rows(); + win_new_screen_rows(); } } -/* - * ":only". - */ +/// ":only". static void ex_only(exarg_T *eap) { win_T *wp; - int wnr; + linenr_T wnr; if (eap->addr_count > 0) { wnr = eap->line2; @@ -6844,10 +7332,8 @@ static void ex_only(exarg_T *eap) close_others(TRUE, eap->forceit); } -/* - * ":all" and ":sall". - * Also used for ":tab drop file ..." after setting the argument list. - */ +/// ":all" and ":sall". +/// Also used for ":tab drop file ..." after setting the argument list. void ex_all(exarg_T *eap) { if (eap->addr_count == 0) { @@ -6861,7 +7347,7 @@ static void ex_hide(exarg_T *eap) // ":hide" or ":hide | cmd": hide current window if (!eap->skip) { if (eap->addr_count == 0) { - win_close(curwin, false); // don't free buffer + win_close(curwin, false, eap->forceit); // don't free buffer } else { int winnr = 0; win_T *win = NULL; @@ -6876,7 +7362,7 @@ static void ex_hide(exarg_T *eap) if (win == NULL) { win = lastwin; } - win_close(win, false); + win_close(win, false, eap->forceit); } } } @@ -6902,7 +7388,7 @@ static void ex_stop(exarg_T *eap) apply_autocmds(EVENT_VIMRESUME, NULL, NULL, false, NULL); } -// ":exit", ":xit" and ":wq": Write file and quit the current window. +/// ":exit", ":xit" and ":wq": Write file and quit the current window. static void ex_exit(exarg_T *eap) { if (cmdwin_type != 0) { @@ -6934,13 +7420,11 @@ static void ex_exit(exarg_T *eap) } not_exiting(); // Quit current window, may free the buffer. - win_close(curwin, !buf_hide(curwin->w_buffer)); + win_close(curwin, !buf_hide(curwin->w_buffer), eap->forceit); } } -/* - * ":print", ":list", ":number". - */ +/// ":print", ":list", ":number". static void ex_print(exarg_T *eap) { if (curbuf->b_ml.ml_flags & ML_EMPTY) { @@ -6970,29 +7454,22 @@ static void ex_goto(exarg_T *eap) goto_byte(eap->line2); } -/* - * Clear an argument list: free all file names and reset it to zero entries. - */ +/// Clear an argument list: free all file names and reset it to zero entries. void alist_clear(alist_T *al) { -#define FREE_AENTRY_FNAME(arg) xfree(arg->ae_fname) +#define FREE_AENTRY_FNAME(arg) xfree((arg)->ae_fname) GA_DEEP_CLEAR(&al->al_ga, aentry_T, FREE_AENTRY_FNAME); } -/* - * Init an argument list. - */ +/// Init an argument list. void alist_init(alist_T *al) { ga_init(&al->al_ga, (int)sizeof(aentry_T), 5); } - -/* - * Remove a reference from an argument list. - * Ignored when the argument list is the global one. - * If the argument list is no longer used by any window, free it. - */ +/// Remove a reference from an argument list. +/// Ignored when the argument list is the global one. +/// If the argument list is no longer used by any window, free it. void alist_unlink(alist_T *al) { if (al != &global_alist && --al->al_refcount <= 0) { @@ -7001,9 +7478,7 @@ void alist_unlink(alist_T *al) } } -/* - * Create a new argument list and use it for the current window. - */ +/// Create a new argument list and use it for the current window. void alist_new(void) { curwin->w_alist = xmalloc(sizeof(*curwin->w_alist)); @@ -7013,18 +7488,17 @@ void alist_new(void) } #if !defined(UNIX) -/* - * Expand the file names in the global argument list. - * If "fnum_list" is not NULL, use "fnum_list[fnum_len]" as a list of buffer - * numbers to be re-used. - */ + +/// Expand the file names in the global argument list. +/// If "fnum_list" is not NULL, use "fnum_list[fnum_len]" as a list of buffer +/// numbers to be re-used. void alist_expand(int *fnum_list, int fnum_len) { - char_u **old_arg_files; + char **old_arg_files; int old_arg_count; - char_u **new_arg_files; + char **new_arg_files; int new_arg_file_count; - char_u *save_p_su = p_su; + char *save_p_su = p_su; int i; /* Don't use 'suffixes' here. This should work like the shell did the @@ -7048,11 +7522,9 @@ void alist_expand(int *fnum_list, int fnum_len) } #endif -/* - * Set the argument list for the current window. - * Takes over the allocated files[] and the allocated fnames in it. - */ -void alist_set(alist_T *al, int count, char_u **files, int use_curbuf, int *fnum_list, int fnum_len) +/// Set the argument list for the current window. +/// Takes over the allocated files[] and the allocated fnames in it. +void alist_set(alist_T *al, int count, char **files, int use_curbuf, int *fnum_list, int fnum_len) { int i; static int recursive = 0; @@ -7098,7 +7570,7 @@ void alist_set(alist_T *al, int count, char_u **files, int use_curbuf, int *fnum /// "fname" must have been allocated and "al" must have been checked for room. /// /// @param set_fnum 1: set buffer number; 2: re-use curbuf -void alist_add(alist_T *al, char_u *fname, int set_fnum) +void alist_add(alist_T *al, char *fname, int set_fnum) { if (fname == NULL) { // don't add NULL file names return; @@ -7106,7 +7578,7 @@ void alist_add(alist_T *al, char_u *fname, int set_fnum) #ifdef BACKSLASH_IN_FILENAME slash_adjust(fname); #endif - AARGLIST(al)[al->al_ga.ga_len].ae_fname = fname; + AARGLIST(al)[al->al_ga.ga_len].ae_fname = (char_u *)fname; if (set_fnum > 0) { AARGLIST(al)[al->al_ga.ga_len].ae_fnum = buflist_add(fname, BLN_LISTED | (set_fnum == 2 ? BLN_CURBUF : 0)); @@ -7115,9 +7587,8 @@ void alist_add(alist_T *al, char_u *fname, int set_fnum) } #if defined(BACKSLASH_IN_FILENAME) -/* - * Adjust slashes in file names. Called after 'shellslash' was set. - */ + +/// Adjust slashes in file names. Called after 'shellslash' was set. void alist_slash_adjust(void) { for (int i = 0; i < GARGCOUNT; ++i) { @@ -7142,7 +7613,6 @@ void alist_slash_adjust(void) /// ":preserve". static void ex_preserve(exarg_T *eap) { - curbuf->b_flags |= BF_PRESERVED; ml_preserve(curbuf, true, true); } @@ -7163,38 +7633,34 @@ static void ex_recover(exarg_T *eap) recoverymode = false; } -/* - * Command modifier used in a wrong way. - */ +/// Command modifier used in a wrong way. static void ex_wrongmodifier(exarg_T *eap) { eap->errmsg = e_invcmd; } -/* - * :sview [+command] file split window with new file, read-only - * :split [[+command] file] split window with current or new file - * :vsplit [[+command] file] split window vertically with current or new file - * :new [[+command] file] split window with no or new file - * :vnew [[+command] file] split vertically window with no or new file - * :sfind [+command] file split window with file in 'path' - * - * :tabedit open new Tab page with empty window - * :tabedit [+command] file open new Tab page and edit "file" - * :tabnew [[+command] file] just like :tabedit - * :tabfind [+command] file open new Tab page and find "file" - */ +/// :sview [+command] file split window with new file, read-only +/// :split [[+command] file] split window with current or new file +/// :vsplit [[+command] file] split window vertically with current or new file +/// :new [[+command] file] split window with no or new file +/// :vnew [[+command] file] split vertically window with no or new file +/// :sfind [+command] file split window with file in 'path' +/// +/// :tabedit open new Tab page with empty window +/// :tabedit [+command] file open new Tab page and edit "file" +/// :tabnew [[+command] file] just like :tabedit +/// :tabfind [+command] file open new Tab page and find "file" void ex_splitview(exarg_T *eap) { win_T *old_curwin = curwin; - char_u *fname = NULL; + char *fname = NULL; const bool use_tab = eap->cmdidx == CMD_tabedit || eap->cmdidx == CMD_tabfind || eap->cmdidx == CMD_tabnew; // A ":split" in the quickfix window works like ":new". Don't want two // quickfix windows. But it's OK when doing ":tab split". - if (bt_quickfix(curbuf) && cmdmod.tab == 0) { + if (bt_quickfix(curbuf) && cmdmod.cmod_tab == 0) { if (eap->cmdidx == CMD_split) { eap->cmdidx = CMD_new; } @@ -7204,8 +7670,8 @@ void ex_splitview(exarg_T *eap) } if (eap->cmdidx == CMD_sfind || eap->cmdidx == CMD_tabfind) { - fname = find_file_in_path(eap->arg, STRLEN(eap->arg), - FNAME_MESS, TRUE, curbuf->b_ffname); + fname = (char *)find_file_in_path((char_u *)eap->arg, STRLEN(eap->arg), + FNAME_MESS, true, (char_u *)curbuf->b_ffname); if (fname == NULL) { goto theend; } @@ -7216,8 +7682,8 @@ void ex_splitview(exarg_T *eap) * Either open new tab page or split the window. */ if (use_tab) { - if (win_new_tabpage(cmdmod.tab != 0 ? cmdmod.tab : eap->addr_count == 0 - ? 0 : (int)eap->line2 + 1, eap->arg) != FAIL) { + if (win_new_tabpage(cmdmod.cmod_tab != 0 ? cmdmod.cmod_tab : eap->addr_count == 0 + ? 0 : (int)eap->line2 + 1, (char_u *)eap->arg) != FAIL) { do_exedit(eap, old_curwin); apply_autocmds(EVENT_TABNEWENTERED, NULL, NULL, false, curbuf); @@ -7225,7 +7691,7 @@ void ex_splitview(exarg_T *eap) if (curwin != old_curwin && win_valid(old_curwin) && old_curwin->w_buffer != curbuf - && !cmdmod.keepalt) { + && (cmdmod.cmod_flags & CMOD_KEEPALT) == 0) { old_curwin->w_alt_fnum = curbuf->b_fnum; } } @@ -7241,28 +7707,23 @@ void ex_splitview(exarg_T *eap) do_exedit(eap, old_curwin); } - theend: xfree(fname); } -/* - * Open a new tab page. - */ +/// Open a new tab page. void tabpage_new(void) { exarg_T ea; memset(&ea, 0, sizeof(ea)); ea.cmdidx = CMD_tabnew; - ea.cmd = (char_u *)"tabn"; - ea.arg = (char_u *)""; + ea.cmd = "tabn"; + ea.arg = ""; ex_splitview(&ea); } -/* - * :tabnext command - */ +/// :tabnext command static void ex_tabnext(exarg_T *eap) { int tab_number; @@ -7278,9 +7739,9 @@ static void ex_tabnext(exarg_T *eap) case CMD_tabprevious: case CMD_tabNext: if (eap->arg && *eap->arg != NUL) { - char_u *p = eap->arg; - char_u *p_save = p; - tab_number = getdigits(&p, false, 0); + char *p = eap->arg; + char *p_save = p; + tab_number = (int)getdigits((char_u **)&p, false, 0); if (p == p_save || *p_save == '-' || *p_save == '+' || *p != NUL || tab_number == 0) { // No numbers as argument. @@ -7291,7 +7752,7 @@ static void ex_tabnext(exarg_T *eap) if (eap->addr_count == 0) { tab_number = 1; } else { - tab_number = eap->line2; + tab_number = (int)eap->line2; if (tab_number < 1) { eap->errmsg = e_invrange; return; @@ -7309,9 +7770,7 @@ static void ex_tabnext(exarg_T *eap) } } -/* - * :tabmove command - */ +/// :tabmove command static void ex_tabmove(exarg_T *eap) { int tab_number = get_tabpage_arg(eap); @@ -7320,9 +7779,7 @@ static void ex_tabmove(exarg_T *eap) } } -/* - * :tabs command: List tabs and their contents. - */ +/// :tabs command: List tabs and their contents. static void ex_tabs(exarg_T *eap) { int tabcount = 1; @@ -7358,20 +7815,17 @@ static void ex_tabs(exarg_T *eap) if (buf_spname(wp->w_buffer) != NULL) { STRLCPY(IObuff, buf_spname(wp->w_buffer), IOSIZE); } else { - home_replace(wp->w_buffer, wp->w_buffer->b_fname, IObuff, IOSIZE, true); + home_replace(wp->w_buffer, wp->w_buffer->b_fname, (char *)IObuff, IOSIZE, true); } - msg_outtrans(IObuff); + msg_outtrans((char *)IObuff); ui_flush(); // output one line at a time os_breakcheck(); } } } - -/* - * ":mode": - * If no argument given, get the screen size and redraw. - */ +/// ":mode": +/// If no argument given, get the screen size and redraw. static void ex_mode(exarg_T *eap) { if (*eap->arg == NUL) { @@ -7382,23 +7836,20 @@ static void ex_mode(exarg_T *eap) } } -/* - * ":resize". - * set, increment or decrement current window height - */ +/// ":resize". +/// set, increment or decrement current window height static void ex_resize(exarg_T *eap) { int n; win_T *wp = curwin; if (eap->addr_count > 0) { - n = eap->line2; - for (wp = firstwin; wp->w_next != NULL && --n > 0; wp = wp->w_next) { - } + n = (int)eap->line2; + for (wp = firstwin; wp->w_next != NULL && --n > 0; wp = wp->w_next) {} } - n = atol((char *)eap->arg); - if (cmdmod.split & WSP_VERT) { + n = (int)atol(eap->arg); + if (cmdmod.cmod_split & WSP_VERT) { if (*eap->arg == '-' || *eap->arg == '+') { n += wp->w_width; } else if (n == 0 && eap->arg[0] == NUL) { // default is very wide @@ -7409,29 +7860,27 @@ static void ex_resize(exarg_T *eap) if (*eap->arg == '-' || *eap->arg == '+') { n += wp->w_height; } else if (n == 0 && eap->arg[0] == NUL) { // default is very high - n = Rows-1; + n = Rows - 1; } win_setheight_win(n, wp); } } -/* - * ":find [+command] <file>" command. - */ +/// ":find [+command] <file>" command. static void ex_find(exarg_T *eap) { - char_u *fname; - int count; + char *fname; + linenr_T count; - fname = find_file_in_path(eap->arg, STRLEN(eap->arg), - FNAME_MESS, TRUE, curbuf->b_ffname); + fname = (char *)find_file_in_path((char_u *)eap->arg, STRLEN(eap->arg), + FNAME_MESS, true, (char_u *)curbuf->b_ffname); if (eap->addr_count > 0) { // Repeat finding the file "count" times. This matters when it // appears several times in the path. count = eap->line2; while (fname != NULL && --count > 0) { xfree(fname); - fname = find_file_in_path(NULL, 0, FNAME_MESS, FALSE, curbuf->b_ffname); + fname = (char *)find_file_in_path(NULL, 0, FNAME_MESS, false, (char_u *)curbuf->b_ffname); } } @@ -7448,7 +7897,7 @@ static void ex_edit(exarg_T *eap) do_exedit(eap, NULL); } -/// ":edit <file>" command and alikes. +/// ":edit <file>" command and alike. /// /// @param old_curwin curwin before doing a split or NULL void do_exedit(exarg_T *eap, win_T *old_curwin) @@ -7480,9 +7929,11 @@ void do_exedit(exarg_T *eap, win_T *old_curwin) need_wait_return = false; msg_scroll = 0; redraw_all_later(NOT_VALID); + pending_exmode_active = true; normal_enter(false, true); + pending_exmode_active = false; RedrawingDisabled = rd; no_wait_return = nwr; msg_scroll = ms; @@ -7517,7 +7968,7 @@ void do_exedit(exarg_T *eap, win_T *old_curwin) if (eap->cmdidx != CMD_balt && eap->cmdidx != CMD_badd) { setpcmark(); } - if (do_ecmd(0, (eap->cmdidx == CMD_enew ? NULL : eap->arg), + if (do_ecmd(0, eap->cmdidx == CMD_enew ? NULL : eap->arg, NULL, eap, eap->do_ecmd_lnum, (buf_hide(curbuf) ? ECMD_HIDE : 0) + (eap->forceit ? ECMD_FORCEIT : 0) @@ -7535,7 +7986,7 @@ void do_exedit(exarg_T *eap, win_T *old_curwin) // Reset the error/interrupt/exception state here so that // aborting() returns FALSE when closing a window. enter_cleanup(&cs); - win_close(curwin, !need_hide && !buf_hide(curbuf)); + win_close(curwin, !need_hide && !buf_hide(curbuf), false); // Restore the error/interrupt/exception state if not // discarded by a new aborting error, interrupt, or @@ -7553,7 +8004,7 @@ void do_exedit(exarg_T *eap, win_T *old_curwin) readonlymode = n; } else { if (eap->do_ecmd_cmd != NULL) { - do_cmdline_cmd((char *)eap->do_ecmd_cmd); + do_cmdline_cmd(eap->do_ecmd_cmd); } n = curwin->w_arg_idx_invalid; check_arg_idx(curwin); @@ -7571,7 +8022,7 @@ void do_exedit(exarg_T *eap, win_T *old_curwin) && curwin != old_curwin && win_valid(old_curwin) && old_curwin->w_buffer != curbuf - && !cmdmod.keepalt) { + && (cmdmod.cmod_flags & CMOD_KEEPALT) == 0) { old_curwin->w_alt_fnum = curbuf->b_fnum; } @@ -7584,6 +8035,10 @@ static void ex_nogui(exarg_T *eap) eap->errmsg = N_("E25: Nvim does not have a built-in GUI"); } +static void ex_popup(exarg_T *eap) +{ + pum_make_popup(eap->arg, eap->forceit); +} static void ex_swapname(exarg_T *eap) { @@ -7594,11 +8049,9 @@ static void ex_swapname(exarg_T *eap) } } -/* - * ":syncbind" forces all 'scrollbind' windows to have the same relative - * offset. - * (1998-11-02 16:21:01 R. Edward Ralston <eralston@computer.org>) - */ +/// ":syncbind" forces all 'scrollbind' windows to have the same relative +/// offset. +/// (1998-11-02 16:21:01 R. Edward Ralston <eralston@computer.org>) static void ex_syncbind(exarg_T *eap) { win_T *save_curwin = curwin; @@ -7629,7 +8082,6 @@ static void ex_syncbind(exarg_T *eap) topline = 1; } - /* * Set all scrollbind windows to the same topline. */ @@ -7655,7 +8107,7 @@ static void ex_syncbind(exarg_T *eap) did_syncbind = true; checkpcmark(); if (old_linenr != curwin->w_cursor.lnum) { - char_u ctrl_o[2]; + char ctrl_o[2]; ctrl_o[0] = Ctrl_O; ctrl_o[1] = 0; @@ -7664,7 +8116,6 @@ static void ex_syncbind(exarg_T *eap) } } - static void ex_read(exarg_T *eap) { int i; @@ -7683,13 +8134,13 @@ static void ex_read(exarg_T *eap) return; } i = readfile(curbuf->b_ffname, curbuf->b_fname, - eap->line2, (linenr_T)0, (linenr_T)MAXLNUM, eap, 0); + eap->line2, (linenr_T)0, (linenr_T)MAXLNUM, eap, 0, false); } else { if (vim_strchr(p_cpo, CPO_ALTREAD) != NULL) { (void)setaltfname(eap->arg, eap->arg, (linenr_T)1); } i = readfile(eap->arg, NULL, - eap->line2, (linenr_T)0, (linenr_T)MAXLNUM, eap, 0); + eap->line2, (linenr_T)0, (linenr_T)MAXLNUM, eap, 0, false); } if (i != OK) { if (!aborting()) { @@ -7718,7 +8169,7 @@ static void ex_read(exarg_T *eap) } } -static char_u *prev_dir = NULL; +static char *prev_dir = NULL; #if defined(EXITFREE) void free_cd_dir(void) @@ -7729,8 +8180,8 @@ void free_cd_dir(void) #endif -// Get the previous directory for the given chdir scope. -static char_u *get_prevdir(CdScope scope) +/// Get the previous directory for the given chdir scope. +static char *get_prevdir(CdScope scope) { switch (scope) { case kCdScopeTabpage: @@ -7747,7 +8198,7 @@ static char_u *get_prevdir(CdScope scope) /// Deal with the side effects of changing the current directory. /// /// @param scope Scope of the function call (global, tab or window). -void post_chdir(CdScope scope, bool trigger_dirchanged) +static void post_chdir(CdScope scope, bool trigger_dirchanged) { // Always overwrite the window-local CWD. XFREE_CLEAR(curwin->w_localdir); @@ -7758,10 +8209,10 @@ void post_chdir(CdScope scope, bool trigger_dirchanged) } if (scope < kCdScopeGlobal) { - char_u *pdir = get_prevdir(scope); + char *pdir = get_prevdir(scope); // If still in global directory, set CWD as the global directory. if (globaldir == NULL && pdir != NULL) { - globaldir = vim_strsave(pdir); + globaldir = xstrdup(pdir); } } @@ -7775,10 +8226,10 @@ void post_chdir(CdScope scope, bool trigger_dirchanged) XFREE_CLEAR(globaldir); break; case kCdScopeTabpage: - curtab->tp_localdir = (char_u *)xstrdup(cwd); + curtab->tp_localdir = xstrdup(cwd); break; case kCdScopeWindow: - curwin->w_localdir = (char_u *)xstrdup(cwd); + curwin->w_localdir = xstrdup(cwd); break; case kCdScopeInvalid: abort(); @@ -7788,7 +8239,7 @@ void post_chdir(CdScope scope, bool trigger_dirchanged) shorten_fnames(true); if (trigger_dirchanged) { - do_autocmd_dirchanged(cwd, scope, kCdCauseManual); + do_autocmd_dirchanged(cwd, scope, kCdCauseManual, false); } } @@ -7796,16 +8247,13 @@ void post_chdir(CdScope scope, bool trigger_dirchanged) /// @param new_dir The directory to change to. /// @param scope Scope of the function call (global, tab or window). /// @return true if the directory is successfully changed. -bool changedir_func(char_u *new_dir, CdScope scope) +bool changedir_func(char *new_dir, CdScope scope) { - char_u *tofree; - char_u *pdir = NULL; - bool retval = false; - if (new_dir == NULL || allbuf_locked()) { return false; } + char *pdir = NULL; // ":cd -": Change to previous directory if (STRCMP(new_dir, "-") == 0) { pdir = get_prevdir(scope); @@ -7816,26 +8264,12 @@ bool changedir_func(char_u *new_dir, CdScope scope) new_dir = pdir; } - // Free the previous directory - tofree = get_prevdir(scope); - if (os_dirname(NameBuff, MAXPATHL) == OK) { - pdir = vim_strsave(NameBuff); + pdir = (char *)vim_strsave(NameBuff); } else { pdir = NULL; } - switch (scope) { - case kCdScopeTabpage: - curtab->tp_prevdir = pdir; - break; - case kCdScopeWindow: - curwin->w_prevdir = pdir; - break; - default: - prev_dir = pdir; - } - // For UNIX ":cd" means: go to home directory. // On other systems too if 'cdhome' is set. #if defined(UNIX) @@ -7845,27 +8279,42 @@ bool changedir_func(char_u *new_dir, CdScope scope) #endif // Use NameBuff for home directory name. expand_env((char_u *)"$HOME", NameBuff, MAXPATHL); - new_dir = NameBuff; + new_dir = (char *)NameBuff; } - bool dir_differs = new_dir == NULL || pdir == NULL - || pathcmp((char *)pdir, (char *)new_dir, -1) != 0; - if (new_dir != NULL && (!dir_differs || vim_chdir(new_dir) == 0)) { - post_chdir(scope, dir_differs); - retval = true; - } else { - emsg(_(e_failed)); + bool dir_differs = pdir == NULL || pathcmp(pdir, new_dir, -1) != 0; + if (dir_differs) { + do_autocmd_dirchanged(new_dir, scope, kCdCauseManual, true); + if (vim_chdir((char_u *)new_dir) != 0) { + emsg(_(e_failed)); + xfree(pdir); + return false; + } } - xfree(tofree); - return retval; + char **pp; + switch (scope) { + case kCdScopeTabpage: + pp = &curtab->tp_prevdir; + break; + case kCdScopeWindow: + pp = &curwin->w_prevdir; + break; + default: + pp = &prev_dir; + } + xfree(*pp); + *pp = pdir; + + post_chdir(scope, dir_differs); + + return true; } /// ":cd", ":tcd", ":lcd", ":chdir", "tchdir" and ":lchdir". void ex_cd(exarg_T *eap) { - char_u *new_dir; - new_dir = eap->arg; + char *new_dir = eap->arg; #if !defined(UNIX) // for non-UNIX ":cd" means: print current directory unless 'cdhome' is set if (*new_dir == NUL && !p_cdh) { @@ -7895,9 +8344,7 @@ void ex_cd(exarg_T *eap) } } -/* - * ":pwd". - */ +/// ":pwd". static void ex_pwd(exarg_T *eap) { if (os_dirname(NameBuff, MAXPATHL) == OK) { @@ -7922,9 +8369,7 @@ static void ex_pwd(exarg_T *eap) } } -/* - * ":=". - */ +/// ":=". static void ex_equal(exarg_T *eap) { smsg("%" PRId64, (int64_t)eap->line2); @@ -7955,9 +8400,7 @@ static void ex_sleep(exarg_T *eap) do_sleep(len); } -/* - * Sleep for "msec" milliseconds, but keep checking for a CTRL-C every second. - */ +/// Sleep for "msec" milliseconds, but keep checking for a CTRL-C every second. void do_sleep(long msec) { ui_flush(); // flush before waiting @@ -7974,31 +8417,10 @@ void do_sleep(long msec) } } -static void do_exmap(exarg_T *eap, int isabbrev) -{ - int mode; - char_u *cmdp; - - cmdp = eap->cmd; - mode = get_map_mode(&cmdp, eap->forceit || isabbrev); - - switch (do_map((*cmdp == 'n') ? 2 : (*cmdp == 'u'), - eap->arg, mode, isabbrev)) { - case 1: - emsg(_(e_invarg)); - break; - case 2: - emsg(isabbrev ? _(e_noabbr) : _(e_nomap)); - break; - } -} - -/* - * ":winsize" command (obsolete). - */ +/// ":winsize" command (obsolete). static void ex_winsize(exarg_T *eap) { - char_u *arg = eap->arg; + char *arg = eap->arg; if (!ascii_isdigit(*arg)) { semsg(_(e_invarg2), arg); @@ -8006,7 +8428,7 @@ static void ex_winsize(exarg_T *eap) } int w = getdigits_int(&arg, false, 10); arg = skipwhite(arg); - char_u *p = arg; + char *p = arg; int h = getdigits_int(&arg, false, 10); if (*p != NUL && *arg == NUL) { screen_resize(w, h); @@ -8018,7 +8440,7 @@ static void ex_winsize(exarg_T *eap) static void ex_wincmd(exarg_T *eap) { int xchar = NUL; - char_u *p; + char *p; if (*eap->arg == 'g' || *eap->arg == Ctrl_G) { // CTRL-W g and CTRL-W CTRL-G have an extra command character @@ -8026,29 +8448,27 @@ static void ex_wincmd(exarg_T *eap) emsg(_(e_invarg)); return; } - xchar = eap->arg[1]; + xchar = (uint8_t)eap->arg[1]; p = eap->arg + 2; } else { p = eap->arg + 1; } - eap->nextcmd = check_nextcmd(p); + eap->nextcmd = (char *)check_nextcmd((char_u *)p); p = skipwhite(p); if (*p != NUL && *p != '"' && eap->nextcmd == NULL) { emsg(_(e_invarg)); } else if (!eap->skip) { // Pass flags on for ":vertical wincmd ]". - postponed_split_flags = cmdmod.split; - postponed_split_tab = cmdmod.tab; + postponed_split_flags = cmdmod.cmod_split; + postponed_split_tab = cmdmod.cmod_tab; do_window(*eap->arg, eap->addr_count > 0 ? eap->line2 : 0L, xchar); postponed_split_flags = 0; postponed_split_tab = 0; } } -/* - * Handle command that work like operators: ":delete", ":yank", ":>" and ":<". - */ +/// Handle command that work like operators: ":delete", ":yank", ":>" and ":<". static void ex_operators(exarg_T *eap) { oparg_T oa; @@ -8078,7 +8498,7 @@ static void ex_operators(exarg_T *eap) case CMD_yank: oa.op_type = OP_YANK; - (void)op_yank(&oa, true, false); + (void)op_yank(&oa, true); break; default: // CMD_rshift or CMD_lshift @@ -8095,9 +8515,7 @@ static void ex_operators(exarg_T *eap) ex_may_print(eap); } -/* - * ":put". - */ +/// ":put". static void ex_put(exarg_T *eap) { // ":0put" works like ":1put!". @@ -8106,13 +8524,12 @@ static void ex_put(exarg_T *eap) eap->forceit = TRUE; } curwin->w_cursor.lnum = eap->line2; + check_cursor_col(); do_put(eap->regname, NULL, eap->forceit ? BACKWARD : FORWARD, 1, PUT_LINE|PUT_CURSLINE); } -/* - * Handle ":copy" and ":move". - */ +/// Handle ":copy" and ":move". static void ex_copymove(exarg_T *eap) { long n = get_address(eap, &eap->arg, eap->addr_type, false, false, false, 1); @@ -8131,20 +8548,18 @@ static void ex_copymove(exarg_T *eap) } if (eap->cmdidx == CMD_move) { - if (do_move(eap->line1, eap->line2, n) == FAIL) { + if (do_move(eap->line1, eap->line2, (linenr_T)n) == FAIL) { return; } } else { - ex_copy(eap->line1, eap->line2, n); + ex_copy(eap->line1, eap->line2, (linenr_T)n); } u_clearline(); beginline(BL_SOL | BL_FIX); ex_may_print(eap); } -/* - * Print the current line if flags were given to the Ex command. - */ +/// Print the current line if flags were given to the Ex command. void ex_may_print(exarg_T *eap) { if (eap->flags != 0) { @@ -8164,9 +8579,19 @@ static void ex_submagic(exarg_T *eap) p_magic = magic_save; } -/* - * ":join". - */ +/// ":smagic" and ":snomagic" preview callback. +static int ex_submagic_preview(exarg_T *eap, long cmdpreview_ns, handle_T cmdpreview_bufnr) +{ + int magic_save = p_magic; + + p_magic = (eap->cmdidx == CMD_smagic); + int retv = ex_substitute_preview(eap, cmdpreview_ns, cmdpreview_bufnr); + p_magic = magic_save; + + return retv; +} + +/// ":join". static void ex_join(exarg_T *eap) { curwin->w_cursor.lnum = eap->line1; @@ -8180,14 +8605,12 @@ static void ex_join(exarg_T *eap) } ++eap->line2; } - do_join(eap->line2 - eap->line1 + 1, !eap->forceit, TRUE, TRUE, true); + do_join((size_t)((ssize_t)eap->line2 - eap->line1 + 1), !eap->forceit, true, true, true); beginline(BL_WHITE | BL_FIX); ex_may_print(eap); } -/* - * ":[addr]@r": execute register - */ +/// ":[addr]@r": execute register static void ex_at(exarg_T *eap) { int prev_len = typebuf.tb_len; @@ -8196,14 +8619,13 @@ static void ex_at(exarg_T *eap) check_cursor_col(); // Get the register name. No name means use the previous one. - int c = *eap->arg; + int c = (uint8_t)(*eap->arg); if (c == NUL) { c = '@'; } // Put the register in the typeahead buffer with the "silent" flag. - if (do_execreg(c, TRUE, vim_strchr(p_cpo, CPO_EXECBUF) != NULL, TRUE) - == FAIL) { + if (do_execreg(c, true, vim_strchr(p_cpo, CPO_EXECBUF) != NULL, true) == FAIL) { beep_flush(); } else { bool save_efr = exec_from_reg; @@ -8223,40 +8645,64 @@ static void ex_at(exarg_T *eap) } } -/* - * ":!". - */ +/// ":!". static void ex_bang(exarg_T *eap) { do_bang(eap->addr_count, eap, eap->forceit, true, true); } -/* - * ":undo". - */ +/// ":undo". static void ex_undo(exarg_T *eap) { - if (eap->addr_count == 1) { // :undo 123 - undo_time(eap->line2, false, false, true); - } else { - u_undo(1); + if (eap->addr_count != 1) { + if (eap->forceit) { + u_undo_and_forget(1); // :undo! + } else { + u_undo(1); // :undo + } + return; + } + + long step = eap->line2; + + if (eap->forceit) { // undo! 123 + // change number for "undo!" must be lesser than current change number + if (step >= curbuf->b_u_seq_cur) { + emsg(_(e_undobang_cannot_redo_or_move_branch)); + return; + } + // ensure that target change number is in same branch + // while also counting the amount of undoes it'd take to reach target + u_header_T *uhp; + int count = 0; + + for (uhp = curbuf->b_u_curhead ? curbuf->b_u_curhead : curbuf->b_u_newhead; + uhp != NULL && uhp->uh_seq > step; + uhp = uhp->uh_next.ptr, ++count) {} + if (step != 0 && (uhp == NULL || uhp->uh_seq < step)) { + emsg(_(e_undobang_cannot_redo_or_move_branch)); + return; + } + u_undo_and_forget(count); + } else { // :undo 123 + undo_time(step, false, false, true); } } static void ex_wundo(exarg_T *eap) { - char_u hash[UNDO_HASH_SIZE]; + char hash[UNDO_HASH_SIZE]; - u_compute_hash(curbuf, hash); - u_write_undo((char *)eap->arg, eap->forceit, curbuf, hash); + u_compute_hash(curbuf, (char_u *)hash); + u_write_undo(eap->arg, eap->forceit, curbuf, (char_u *)hash); } static void ex_rundo(exarg_T *eap) { - char_u hash[UNDO_HASH_SIZE]; + char hash[UNDO_HASH_SIZE]; - u_compute_hash(curbuf, hash); - u_read_undo((char *)eap->arg, hash, NULL); + u_compute_hash(curbuf, (char_u *)hash); + u_read_undo(eap->arg, (char_u *)hash, NULL); } /// ":redo". @@ -8271,12 +8717,12 @@ static void ex_later(exarg_T *eap) long count = 0; bool sec = false; bool file = false; - char_u *p = eap->arg; + char *p = eap->arg; if (*p == NUL) { count = 1; } else if (isdigit(*p)) { - count = getdigits_long(&p, false, 0); + count = getdigits_long((char_u **)&p, false, 0); switch (*p) { case 's': ++p; sec = true; break; @@ -8299,14 +8745,12 @@ static void ex_later(exarg_T *eap) } } -/* - * ":redir": start/stop redirection. - */ +/// ":redir": start/stop redirection. static void ex_redir(exarg_T *eap) { char *mode; - char_u *fname; - char_u *arg = eap->arg; + char *fname; + char *arg = eap->arg; if (STRICMP(eap->arg, "END") == 0) { close_redir(); @@ -8329,14 +8773,14 @@ static void ex_redir(exarg_T *eap) return; } - redir_fd = open_exfile(fname, eap->forceit, mode); + redir_fd = open_exfile((char_u *)fname, eap->forceit, mode); xfree(fname); } else if (*arg == '@') { // redirect to a register a-z (resp. A-Z for appending) close_redir(); ++arg; if (valid_yank_reg(*arg, true) && *arg != '_') { - redir_reg = *arg++; + redir_reg = (char_u)(*arg++); if (*arg == '>' && arg[1] == '>') { // append arg += 2; } else { @@ -8388,7 +8832,7 @@ static void ex_redir(exarg_T *eap) /// ":redraw": force redraw static void ex_redraw(exarg_T *eap) { - if (State & CMDPREVIEW) { + if (cmdpreview) { return; // Ignore :redraw during 'inccommand' preview. #9777 } int r = RedrawingDisabled; @@ -8419,10 +8863,10 @@ static void ex_redraw(exarg_T *eap) ui_flush(); } -/// ":redrawstatus": force redraw of status line(s) +/// ":redrawstatus": force redraw of status line(s) and window bar(s) static void ex_redrawstatus(exarg_T *eap) { - if (State & CMDPREVIEW) { + if (cmdpreview) { return; // Ignore :redrawstatus during 'inccommand' preview. #9777 } int r = RedrawingDisabled; @@ -8435,14 +8879,13 @@ static void ex_redrawstatus(exarg_T *eap) } else { status_redraw_curbuf(); } - update_screen(VIsual_active ? INVERTED : - 0); + update_screen(VIsual_active ? INVERTED : 0); RedrawingDisabled = r; p_lz = p; ui_flush(); } -// ":redrawtabline": force redraw of the tabline +/// ":redrawtabline": force redraw of the tabline static void ex_redrawtabline(exarg_T *eap FUNC_ATTR_UNUSED) { const int r = RedrawingDisabled; @@ -8516,9 +8959,7 @@ FILE *open_exfile(char_u *fname, int forceit, char *mode) return fd; } -/* - * ":mark" and ":k". - */ +/// ":mark" and ":k". static void ex_mark(exarg_T *eap) { pos_T pos; @@ -8538,9 +8979,7 @@ static void ex_mark(exarg_T *eap) } } -/* - * Update w_topline, w_leftcol and the cursor position. - */ +/// Update w_topline, w_leftcol and the cursor position. void update_topline_cursor(void) { check_cursor(); // put cursor on valid line @@ -8551,8 +8990,9 @@ void update_topline_cursor(void) update_curswant(); } -// Save the current State and go to Normal mode. -// Return true if the typeahead could be saved. +/// Save the current State and go to Normal mode. +/// +/// @return true if the typeahead could be saved. bool save_current_state(save_state_T *sst) FUNC_ATTR_NONNULL_ALL { @@ -8560,14 +9000,13 @@ bool save_current_state(save_state_T *sst) sst->save_restart_edit = restart_edit; sst->save_msg_didout = msg_didout; sst->save_State = State; - sst->save_insertmode = p_im; sst->save_finish_op = finish_op; sst->save_opcount = opcount; sst->save_reg_executing = reg_executing; + sst->save_pending_end_reg_executing = pending_end_reg_executing; msg_scroll = false; // no msg scrolling in Normal mode restart_edit = 0; // don't go to Insert mode - p_im = false; // don't use 'insertmode // Save the current typeahead. This is required to allow using ":normal" // from an event handler and makes sure we don't hang when the argument @@ -8590,10 +9029,10 @@ void restore_current_state(save_state_T *sst) // override the value of restart_edit anyway. restart_edit = sst->save_restart_edit; } - p_im = sst->save_insertmode; finish_op = sst->save_finish_op; opcount = sst->save_opcount; reg_executing = sst->save_reg_executing; + pending_end_reg_executing = sst->save_pending_end_reg_executing; // don't reset msg_didout now msg_didout |= sst->save_msg_didout; @@ -8604,19 +9043,17 @@ void restore_current_state(save_state_T *sst) ui_cursor_shape(); // may show different cursor shape } -/* - * ":normal[!] {commands}": Execute normal mode commands. - */ +/// ":normal[!] {commands}": Execute normal mode commands. static void ex_normal(exarg_T *eap) { - if (curbuf->terminal && State & TERM_FOCUS) { + if (curbuf->terminal && State & MODE_TERMINAL) { emsg("Can't re-enter normal mode from terminal mode"); return; } save_state_T save_state; - char_u *arg = NULL; + char *arg = NULL; int l; - char_u *p; + char *p; if (ex_normal_lock > 0) { emsg(_(e_secure)); @@ -8627,7 +9064,7 @@ static void ex_normal(exarg_T *eap) return; } - // vgetc() expects a CSI and K_SPECIAL to have been escaped. Don't do + // vgetc() expects K_SPECIAL to have been escaped. Don't do // this for the K_SPECIAL leading byte, otherwise special keys will not // work. { @@ -8636,21 +9073,20 @@ static void ex_normal(exarg_T *eap) // Count the number of characters to be escaped. for (p = eap->arg; *p != NUL; p++) { for (l = utfc_ptr2len(p) - 1; l > 0; l--) { - if (*++p == K_SPECIAL // trailbyte K_SPECIAL or CSI - ) { + if (*++p == (char)K_SPECIAL) { // trailbyte K_SPECIAL len += 2; } } } if (len > 0) { - arg = xmalloc(STRLEN(eap->arg) + len + 1); + arg = xmalloc(STRLEN(eap->arg) + (size_t)len + 1); len = 0; - for (p = eap->arg; *p != NUL; ++p) { + for (p = eap->arg; *p != NUL; p++) { arg[len++] = *p; for (l = utfc_ptr2len(p) - 1; l > 0; l--) { arg[len++] = *++p; - if (*p == K_SPECIAL) { - arg[len++] = KS_SPECIAL; + if (*p == (char)K_SPECIAL) { + arg[len++] = (char)KS_SPECIAL; arg[len++] = KE_FILLER; } } @@ -8671,7 +9107,7 @@ static void ex_normal(exarg_T *eap) check_cursor_moved(curwin); } - exec_normal_cmd(arg != NULL ? arg : eap->arg, + exec_normal_cmd((char_u *)(arg != NULL ? arg : eap->arg), eap->forceit ? REMAP_NONE : REMAP_YES, false); } while (eap->addr_count > 0 && eap->line1 <= eap->line2 && !got_int); } @@ -8688,9 +9124,7 @@ static void ex_normal(exarg_T *eap) xfree(arg); } -/* - * ":startinsert", ":startreplace" and ":startgreplace" - */ +/// ":startinsert", ":startreplace" and ":startgreplace" static void ex_startinsert(exarg_T *eap) { if (eap->forceit) { @@ -8703,7 +9137,7 @@ static void ex_startinsert(exarg_T *eap) // Ignore the command when already in Insert mode. Inserting an // expression register that invokes a function can do this. - if (State & INSERT) { + if (State & MODE_INSERT) { return; } @@ -8727,9 +9161,7 @@ static void ex_startinsert(exarg_T *eap) } } -/* - * ":stopinsert" - */ +/// ":stopinsert" static void ex_stopinsert(exarg_T *eap) { restart_edit = 0; @@ -8737,14 +9169,12 @@ static void ex_stopinsert(exarg_T *eap) clearmode(); } -/* - * Execute normal mode command "cmd". - * "remap" can be REMAP_NONE or REMAP_YES. - */ +/// Execute normal mode command "cmd". +/// "remap" can be REMAP_NONE or REMAP_YES. void exec_normal_cmd(char_u *cmd, int remap, bool silent) { // Stuff the argument into the typeahead buffer. - ins_typebuf(cmd, remap, 0, true, silent); + ins_typebuf((char *)cmd, remap, 0, true, silent); exec_normal(false); } @@ -8773,12 +9203,10 @@ static void ex_checkpath(exarg_T *eap) (linenr_T)1, (linenr_T)MAXLNUM); } -/* - * ":psearch" - */ +/// ":psearch" static void ex_psearch(exarg_T *eap) { - g_do_tagpreview = p_pvh; + g_do_tagpreview = (int)p_pvh; ex_findpat(eap); g_do_tagpreview = 0; } @@ -8787,7 +9215,7 @@ static void ex_findpat(exarg_T *eap) { bool whole = true; long n; - char_u *p; + char *p; int action; switch (cmdnames[eap->cmdidx].cmd_name[2]) { @@ -8811,13 +9239,13 @@ static void ex_findpat(exarg_T *eap) n = 1; if (ascii_isdigit(*eap->arg)) { // get count - n = getdigits_long(&eap->arg, false, 0); + n = getdigits_long((char_u **)&eap->arg, false, 0); eap->arg = skipwhite(eap->arg); } if (*eap->arg == '/') { // Match regexp, not just whole words whole = false; eap->arg++; - p = skip_regexp(eap->arg, '/', p_magic, NULL); + p = (char *)skip_regexp((char_u *)eap->arg, '/', p_magic, NULL); if (*p) { *p++ = NUL; p = skipwhite(p); @@ -8826,36 +9254,31 @@ static void ex_findpat(exarg_T *eap) if (!ends_excmd(*p)) { eap->errmsg = e_trailing; } else { - eap->nextcmd = check_nextcmd(p); + eap->nextcmd = (char *)check_nextcmd((char_u *)p); } } } if (!eap->skip) { - find_pattern_in_path(eap->arg, 0, STRLEN(eap->arg), whole, !eap->forceit, + find_pattern_in_path((char_u *)eap->arg, 0, STRLEN(eap->arg), whole, !eap->forceit, *eap->cmd == 'd' ? FIND_DEFINE : FIND_ANY, n, action, eap->line1, eap->line2); } } - -/* - * ":ptag", ":ptselect", ":ptjump", ":ptnext", etc. - */ +/// ":ptag", ":ptselect", ":ptjump", ":ptnext", etc. static void ex_ptag(exarg_T *eap) { - g_do_tagpreview = p_pvh; // will be reset to 0 in ex_tag_cmd() + g_do_tagpreview = (int)p_pvh; // will be reset to 0 in ex_tag_cmd() ex_tag_cmd(eap, cmdnames[eap->cmdidx].cmd_name + 1); } -/* - * ":pedit" - */ +/// ":pedit" static void ex_pedit(exarg_T *eap) { win_T *curwin_save = curwin; // Open the preview window or popup and make it the current window. - g_do_tagpreview = p_pvh; + g_do_tagpreview = (int)p_pvh; prepare_tagpreview(true); // Edit the file. @@ -8870,28 +9293,24 @@ static void ex_pedit(exarg_T *eap) g_do_tagpreview = 0; } -/* - * ":stag", ":stselect" and ":stjump". - */ +/// ":stag", ":stselect" and ":stjump". static void ex_stag(exarg_T *eap) { postponed_split = -1; - postponed_split_flags = cmdmod.split; - postponed_split_tab = cmdmod.tab; + postponed_split_flags = cmdmod.cmod_split; + postponed_split_tab = cmdmod.cmod_tab; ex_tag_cmd(eap, cmdnames[eap->cmdidx].cmd_name + 1); postponed_split_flags = 0; postponed_split_tab = 0; } -/* - * ":tag", ":tselect", ":tjump", ":tnext", etc. - */ +/// ":tag", ":tselect", ":tjump", ":tnext", etc. static void ex_tag(exarg_T *eap) { ex_tag_cmd(eap, cmdnames[eap->cmdidx].cmd_name); } -static void ex_tag_cmd(exarg_T *eap, char_u *name) +static void ex_tag_cmd(exarg_T *eap, char *name) { int cmd; @@ -8932,8 +9351,8 @@ static void ex_tag_cmd(exarg_T *eap, char_u *name) cmd = DT_LTAG; } - do_tag(eap->arg, cmd, eap->addr_count > 0 ? (int)eap->line2 : 1, - eap->forceit, TRUE); + do_tag((char_u *)eap->arg, cmd, eap->addr_count > 0 ? (int)eap->line2 : 1, + eap->forceit, true); } enum { @@ -8954,11 +9373,9 @@ enum { // SPEC_CLIENT, }; -/* - * Check "str" for starting with a special cmdline variable. - * If found return one of the SPEC_ values and set "*usedlen" to the length of - * the variable. Otherwise return -1 and "*usedlen" is unchanged. - */ +/// Check "str" for starting with a special cmdline variable. +/// If found return one of the SPEC_ values and set "*usedlen" to the length of +/// the variable. Otherwise return -1 and "*usedlen" is unchanged. ssize_t find_cmdline_var(const char_u *src, size_t *usedlen) FUNC_ATTR_NONNULL_ALL { @@ -9023,9 +9440,9 @@ char_u *eval_vars(char_u *src, char_u *srcstart, size_t *usedlen, linenr_T *lnum int *escaped) { int i; - char_u *s; - char_u *result; - char_u *resultbuf = NULL; + char *s; + char *result; + char *resultbuf = NULL; size_t resultlen; buf_T *buf; int valid = VALID_HEAD | VALID_PATH; // Assume valid result. @@ -9063,7 +9480,7 @@ char_u *eval_vars(char_u *src, char_u *srcstart, size_t *usedlen, linenr_T *lnum if (spec_idx == SPEC_CWORD || spec_idx == SPEC_CCWORD || spec_idx == SPEC_CEXPR) { - resultlen = find_ident_under_cursor(&result, + resultlen = find_ident_under_cursor((char_u **)&result, spec_idx == SPEC_CWORD ? (FIND_IDENT | FIND_STRING) : (spec_idx == SPEC_CEXPR @@ -9084,7 +9501,7 @@ char_u *eval_vars(char_u *src, char_u *srcstart, size_t *usedlen, linenr_T *lnum switch (spec_idx) { case SPEC_PERC: // '%': current file if (curbuf->b_fname == NULL) { - result = (char_u *)""; + result = ""; valid = 0; // Must have ":p:h" to be valid } else { result = curbuf->b_fname; @@ -9103,16 +9520,16 @@ char_u *eval_vars(char_u *src, char_u *srcstart, size_t *usedlen, linenr_T *lnum skip_mod = true; break; } - s = src + 1; + s = (char *)src + 1; if (*s == '<') { // "#<99" uses v:oldfiles. s++; } i = getdigits_int(&s, false, 0); - if (s == src + 2 && src[1] == '-') { + if ((char_u *)s == src + 2 && src[1] == '-') { // just a minus sign, don't skip over it s--; } - *usedlen = (size_t)(s - src); // length of what we expand + *usedlen = (size_t)((char_u *)s - src); // length of what we expand if (src[1] == '<' && i != 0) { if (*usedlen < 2) { @@ -9120,8 +9537,7 @@ char_u *eval_vars(char_u *src, char_u *srcstart, size_t *usedlen, linenr_T *lnum *usedlen = 1; return NULL; } - result = (char_u *)tv_list_find_str(get_vim_var_list(VV_OLDFILES), - i - 1); + result = (char *)tv_list_find_str(get_vim_var_list(VV_OLDFILES), i - 1); if (result == NULL) { *errormsg = ""; return NULL; @@ -9139,7 +9555,7 @@ char_u *eval_vars(char_u *src, char_u *srcstart, size_t *usedlen, linenr_T *lnum *lnump = ECMD_LAST; } if (buf->b_fname == NULL) { - result = (char_u *)""; + result = ""; valid = 0; // Must have ":p:h" to be valid } else { result = buf->b_fname; @@ -9149,7 +9565,7 @@ char_u *eval_vars(char_u *src, char_u *srcstart, size_t *usedlen, linenr_T *lnum break; case SPEC_CFILE: // file name under cursor - result = file_name_at_cursor(FNAME_MESS|FNAME_HYP, 1L, NULL); + result = (char *)file_name_at_cursor(FNAME_MESS|FNAME_HYP, 1L, NULL); if (result == NULL) { *errormsg = ""; return NULL; @@ -9159,12 +9575,12 @@ char_u *eval_vars(char_u *src, char_u *srcstart, size_t *usedlen, linenr_T *lnum case SPEC_AFILE: // file name for autocommand if (autocmd_fname != NULL - && !path_is_absolute(autocmd_fname) + && !path_is_absolute((char_u *)autocmd_fname) // For CmdlineEnter and related events, <afile> is not a path! #9348 - && !strequal("/", (char *)autocmd_fname)) { + && !strequal("/", autocmd_fname)) { // Still need to turn the fname into a full path. It was // postponed to avoid a delay when <afile> is not used. - result = (char_u *)FullName_save((char *)autocmd_fname, false); + result = FullName_save(autocmd_fname, false); // Copy into `autocmd_fname`, don't reassign it. #8165 STRLCPY(autocmd_fname, result, MAXPATHL); xfree(result); @@ -9174,7 +9590,7 @@ char_u *eval_vars(char_u *src, char_u *srcstart, size_t *usedlen, linenr_T *lnum *errormsg = _("E495: no autocommand file name to substitute for \"<afile>\""); return NULL; } - result = path_try_shorten_fname(result); + result = (char *)path_try_shorten_fname((char_u *)result); break; case SPEC_ABUF: // buffer number for autocommand @@ -9183,7 +9599,7 @@ char_u *eval_vars(char_u *src, char_u *srcstart, size_t *usedlen, linenr_T *lnum return NULL; } snprintf(strbuf, sizeof(strbuf), "%d", autocmd_bufnr); - result = (char_u *)strbuf; + result = strbuf; break; case SPEC_AMATCH: // match name for autocommand @@ -9208,7 +9624,7 @@ char_u *eval_vars(char_u *src, char_u *srcstart, size_t *usedlen, linenr_T *lnum return NULL; } snprintf(strbuf, sizeof(strbuf), "%" PRIdLINENR, sourcing_lnum); - result = (char_u *)strbuf; + result = strbuf; break; case SPEC_SFLNUM: // line in script file @@ -9218,7 +9634,7 @@ char_u *eval_vars(char_u *src, char_u *srcstart, size_t *usedlen, linenr_T *lnum } snprintf((char *)strbuf, sizeof(strbuf), "%" PRIdLINENR, current_sctx.sc_lnum + sourcing_lnum); - result = (char_u *)strbuf; + result = strbuf; break; case SPEC_SID: @@ -9228,13 +9644,13 @@ char_u *eval_vars(char_u *src, char_u *srcstart, size_t *usedlen, linenr_T *lnum } snprintf(strbuf, sizeof(strbuf), "<SNR>%" PRIdSCID "_", current_sctx.sc_sid); - result = (char_u *)strbuf; + result = strbuf; break; default: // should not happen *errormsg = ""; - result = (char_u *)""; // avoid gcc warning + result = ""; // avoid gcc warning break; } @@ -9243,11 +9659,12 @@ char_u *eval_vars(char_u *src, char_u *srcstart, size_t *usedlen, linenr_T *lnum // Remove the file name extension. if (src[*usedlen] == '<') { (*usedlen)++; - if ((s = STRRCHR(result, '.')) != NULL && s >= path_tail(result)) { + if ((s = (char *)STRRCHR(result, '.')) != NULL + && s >= path_tail(result)) { resultlen = (size_t)(s - result); } } else if (!skip_mod) { - valid |= modify_fname(src, tilde_file, usedlen, &result, + valid |= modify_fname((char *)src, tilde_file, usedlen, &result, &resultbuf, &resultlen); if (result == NULL) { *errormsg = ""; @@ -9265,23 +9682,21 @@ char_u *eval_vars(char_u *src, char_u *srcstart, size_t *usedlen, linenr_T *lnum } result = NULL; } else { - result = vim_strnsave(result, resultlen); + result = xstrnsave(result, resultlen); } xfree(resultbuf); - return result; + return (char_u *)result; } -/* - * Concatenate all files in the argument list, separated by spaces, and return - * it in one allocated string. - * Spaces and backslashes in the file names are escaped with a backslash. - */ -static char_u *arg_all(void) +/// Concatenate all files in the argument list, separated by spaces, and return +/// it in one allocated string. +/// Spaces and backslashes in the file names are escaped with a backslash. +static char *arg_all(void) { int len; int idx; - char_u *retval = NULL; - char_u *p; + char *retval = NULL; + char *p; /* * Do this loop two times: @@ -9290,7 +9705,7 @@ static char_u *arg_all(void) */ for (;;) { len = 0; - for (idx = 0; idx < ARGCOUNT; ++idx) { + for (idx = 0; idx < ARGCOUNT; idx++) { p = alist_name(&ARGLIST[idx]); if (p == NULL) { continue; @@ -9328,35 +9743,33 @@ static char_u *arg_all(void) } // allocate memory - retval = xmalloc(len + 1); + retval = xmalloc((size_t)len + 1); } return retval; } -/* - * Expand the <sfile> string in "arg". - * - * Returns an allocated string, or NULL for any error. - */ -char_u *expand_sfile(char_u *arg) +/// Expand the <sfile> string in "arg". +/// +/// @return an allocated string, or NULL for any error. +char *expand_sfile(char *arg) { char *errormsg; size_t len; - char_u *result; - char_u *newres; - char_u *repl; + char *result; + char *newres; + char *repl; size_t srclen; - char_u *p; + char *p; - result = vim_strsave(arg); + result = xstrdup(arg); for (p = result; *p;) { if (STRNCMP(p, "<sfile>", 7) != 0) { ++p; } else { // replace "<sfile>" with the sourced file name, and do ":" stuff - repl = eval_vars(p, result, &srclen, NULL, &errormsg, NULL); + repl = (char *)eval_vars((char_u *)p, (char_u *)result, &srclen, NULL, &errormsg, NULL); if (errormsg != NULL) { if (*errormsg) { emsg(errormsg); @@ -9384,40 +9797,34 @@ char_u *expand_sfile(char_u *arg) return result; } -/* - * ":rshada" and ":wshada". - */ +/// ":rshada" and ":wshada". static void ex_shada(exarg_T *eap) { - char_u *save_shada; + char *save_shada; - save_shada = p_shada; + save_shada = (char *)p_shada; if (*p_shada == NUL) { p_shada = (char_u *)"'100"; } if (eap->cmdidx == CMD_rviminfo || eap->cmdidx == CMD_rshada) { - (void)shada_read_everything((char *)eap->arg, eap->forceit, false); + (void)shada_read_everything(eap->arg, eap->forceit, false); } else { - shada_write_file((char *)eap->arg, eap->forceit); + shada_write_file(eap->arg, eap->forceit); } - p_shada = save_shada; + p_shada = (char_u *)save_shada; } -/* - * Make a dialog message in "buff[DIALOG_MSG_SIZE]". - * "format" must contain "%s". - */ -void dialog_msg(char_u *buff, char *format, char_u *fname) +/// Make a dialog message in "buff[DIALOG_MSG_SIZE]". +/// "format" must contain "%s". +void dialog_msg(char *buff, char *format, char *fname) { if (fname == NULL) { - fname = (char_u *)_("Untitled"); + fname = _("Untitled"); } - vim_snprintf((char *)buff, DIALOG_MSG_SIZE, format, fname); + vim_snprintf(buff, DIALOG_MSG_SIZE, format, fname); } -/* - * ":behave {mswin,xterm}" - */ +/// ":behave {mswin,xterm}" static void ex_behave(exarg_T *eap) { if (STRCMP(eap->arg, "mswin") == 0) { @@ -9435,35 +9842,33 @@ static void ex_behave(exarg_T *eap) } } -/* - * Function given to ExpandGeneric() to obtain the possible arguments of the - * ":behave {mswin,xterm}" command. - */ -char_u *get_behave_arg(expand_T *xp, int idx) +/// Function given to ExpandGeneric() to obtain the possible arguments of the +/// ":behave {mswin,xterm}" command. +char *get_behave_arg(expand_T *xp, int idx) { if (idx == 0) { - return (char_u *)"mswin"; + return "mswin"; } if (idx == 1) { - return (char_u *)"xterm"; + return "xterm"; } return NULL; } -// Function given to ExpandGeneric() to obtain the possible arguments of the -// ":messages {clear}" command. -char_u *get_messages_arg(expand_T *xp FUNC_ATTR_UNUSED, int idx) +/// Function given to ExpandGeneric() to obtain the possible arguments of the +/// ":messages {clear}" command. +char *get_messages_arg(expand_T *xp FUNC_ATTR_UNUSED, int idx) { if (idx == 0) { - return (char_u *)"clear"; + return "clear"; } return NULL; } -char_u *get_mapclear_arg(expand_T *xp FUNC_ATTR_UNUSED, int idx) +char *get_mapclear_arg(expand_T *xp FUNC_ATTR_UNUSED, int idx) { if (idx == 0) { - return (char_u *)"<buffer>"; + return "<buffer>"; } return NULL; } @@ -9472,18 +9877,16 @@ static TriState filetype_detect = kNone; static TriState filetype_plugin = kNone; static TriState filetype_indent = kNone; -/* - * ":filetype [plugin] [indent] {on,off,detect}" - * on: Load the filetype.vim file to install autocommands for file types. - * off: Load the ftoff.vim file to remove all autocommands for file types. - * plugin on: load filetype.vim and ftplugin.vim - * plugin off: load ftplugof.vim - * indent on: load filetype.vim and indent.vim - * indent off: load indoff.vim - */ +/// ":filetype [plugin] [indent] {on,off,detect}" +/// on: Load the filetype.vim file to install autocommands for file types. +/// off: Load the ftoff.vim file to remove all autocommands for file types. +/// plugin on: load filetype.vim and ftplugin.vim +/// plugin off: load ftplugof.vim +/// indent on: load filetype.vim and indent.vim +/// indent off: load indoff.vim static void ex_filetype(exarg_T *eap) { - char_u *arg = eap->arg; + char *arg = eap->arg; bool plugin = false; bool indent = false; @@ -9491,8 +9894,8 @@ static void ex_filetype(exarg_T *eap) // Print current status. smsg("filetype detection:%s plugin:%s indent:%s", filetype_detect == kTrue ? "ON" : "OFF", - filetype_plugin == kTrue ? (filetype_detect == kTrue ? "ON" : "(on)") : "OFF", // NOLINT(whitespace/line_length) - filetype_indent == kTrue ? (filetype_detect == kTrue ? "ON" : "(on)") : "OFF"); // NOLINT(whitespace/line_length) + filetype_plugin == kTrue ? (filetype_detect == kTrue ? "ON" : "(on)") : "OFF", + filetype_indent == kTrue ? (filetype_detect == kTrue ? "ON" : "(on)") : "OFF"); return; } @@ -9524,7 +9927,7 @@ static void ex_filetype(exarg_T *eap) } } if (*arg == 'd') { - (void)do_doautocmd((char_u *)"filetypedetect BufRead", true, NULL); + (void)do_doautocmd("filetypedetect BufRead", true, NULL); do_modelines(0); } } else if (STRCMP(arg, "off") == 0) { @@ -9546,16 +9949,12 @@ static void ex_filetype(exarg_T *eap) } } -/// Set all :filetype options ON if user did not explicitly set any to OFF. -void filetype_maybe_enable(void) +/// Source ftplugin.vim and indent.vim to create the necessary FileType +/// autocommands. We do this separately from filetype.vim so that these +/// autocommands will always fire first (and thus can be overridden) while still +/// allowing general filetype detection to be disabled in the user's init file. +void filetype_plugin_enable(void) { - if (filetype_detect == kNone) { - // Normally .vim files are sourced before .lua files when both are - // supported, but we reverse the order here because we want the Lua - // autocommand to be defined first so that it runs first - source_runtime(FILETYPE_FILE, DIP_ALL); - filetype_detect = kTrue; - } if (filetype_plugin == kNone) { source_runtime(FTPLUGIN_FILE, DIP_ALL); filetype_plugin = kTrue; @@ -9566,17 +9965,29 @@ void filetype_maybe_enable(void) } } +/// Enable filetype detection if the user did not explicitly disable it. +void filetype_maybe_enable(void) +{ + if (filetype_detect == kNone) { + // Normally .vim files are sourced before .lua files when both are + // supported, but we reverse the order here because we want the Lua + // autocommand to be defined first so that it runs first + source_runtime(FILETYPE_FILE, DIP_ALL); + filetype_detect = kTrue; + } +} + /// ":setfiletype [FALLBACK] {name}" static void ex_setfiletype(exarg_T *eap) { if (!did_filetype) { - char_u *arg = eap->arg; + char *arg = eap->arg; if (STRNCMP(arg, "FALLBACK ", 9) == 0) { arg += 9; } - set_option_value("filetype", 0L, (char *)arg, OPT_LOCAL); + set_option_value("filetype", 0L, arg, OPT_LOCAL); if (arg != eap->arg) { did_filetype = false; } @@ -9586,103 +9997,25 @@ static void ex_setfiletype(exarg_T *eap) static void ex_digraphs(exarg_T *eap) { if (*eap->arg != NUL) { - putdigraph(eap->arg); + putdigraph((char_u *)eap->arg); } else { listdigraphs(eap->forceit); } } -static void ex_set(exarg_T *eap) -{ - int flags = 0; - - if (eap->cmdidx == CMD_setlocal) { - flags = OPT_LOCAL; - } else if (eap->cmdidx == CMD_setglobal) { - flags = OPT_GLOBAL; - } - (void)do_set(eap->arg, flags); -} - void set_no_hlsearch(bool flag) { no_hlsearch = flag; set_vim_var_nr(VV_HLSEARCH, !no_hlsearch && p_hls); } -/* - * ":nohlsearch" - */ +/// ":nohlsearch" static void ex_nohlsearch(exarg_T *eap) { set_no_hlsearch(true); redraw_all_later(SOME_VALID); } -// ":[N]match {group} {pattern}" -// Sets nextcmd to the start of the next command, if any. Also called when -// skipping commands to find the next command. -static void ex_match(exarg_T *eap) -{ - char_u *p; - char_u *g = NULL; - char_u *end; - int c; - int id; - - if (eap->line2 <= 3) { - id = eap->line2; - } else { - emsg(e_invcmd); - return; - } - - // First clear any old pattern. - if (!eap->skip) { - match_delete(curwin, id, false); - } - - if (ends_excmd(*eap->arg)) { - end = eap->arg; - } else if ((STRNICMP(eap->arg, "none", 4) == 0 - && (ascii_iswhite(eap->arg[4]) || ends_excmd(eap->arg[4])))) { - end = eap->arg + 4; - } else { - p = skiptowhite(eap->arg); - if (!eap->skip) { - g = vim_strnsave(eap->arg, p - eap->arg); - } - p = skipwhite(p); - if (*p == NUL) { - // There must be two arguments. - xfree(g); - semsg(_(e_invarg2), eap->arg); - return; - } - end = skip_regexp(p + 1, *p, true, NULL); - if (!eap->skip) { - if (*end != NUL && !ends_excmd(*skipwhite(end + 1))) { - xfree(g); - eap->errmsg = e_trailing; - return; - } - if (*end != *p) { - xfree(g); - semsg(_(e_invarg2), p); - return; - } - - c = *end; - *end = NUL; - match_add(curwin, (const char *)g, (const char *)p + 1, 10, id, - NULL, NULL); - xfree(g); - *end = c; - } - } - eap->nextcmd = find_nextcmd(end); -} - static void ex_fold(exarg_T *eap) { if (foldManualAllowed(true)) { @@ -9712,8 +10045,8 @@ static void ex_folddo(exarg_T *eap) ml_clearmarked(); // clear rest of the marks } -// Returns true if the supplied Ex cmdidx is for a location list command -// instead of a quickfix command. +/// @return true if the supplied Ex cmdidx is for a location list command +/// instead of a quickfix command. bool is_loclist_cmd(int cmdidx) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { @@ -9739,7 +10072,7 @@ static void ex_terminal(exarg_T *eap) char ex_cmd[1024]; if (*eap->arg != NUL) { // Run {cmd} in 'shell'. - char *name = (char *)vim_strsave_escaped(eap->arg, (char_u *)"\"\\"); + char *name = (char *)vim_strsave_escaped((char_u *)eap->arg, (char_u *)"\"\\"); snprintf(ex_cmd, sizeof(ex_cmd), ":enew%s | call termopen(\"%s\")", eap->forceit ? "!" : "", name); @@ -9770,51 +10103,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_u *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); - } - char_u *end = find_command(&ea, NULL); - - switch (ea.cmdidx) { - case CMD_substitute: - case CMD_smagic: - case CMD_snomagic: - // Only preview once the pattern delimiter has been typed - if (*end && !ASCII_ISALNUM(*end)) { - return true; - } - break; - default: - break; - } - - return false; -} - /// Gets a map of maps describing user-commands defined for buffer `buf` or /// defined globally if `buf` is NULL. /// @@ -9838,6 +10126,8 @@ Dictionary commands_array(buf_T *buf) PUT(d, "bang", BOOLEAN_OBJ(!!(cmd->uc_argt & EX_BANG))); PUT(d, "bar", BOOLEAN_OBJ(!!(cmd->uc_argt & EX_TRLBAR))); PUT(d, "register", BOOLEAN_OBJ(!!(cmd->uc_argt & EX_REGSTR))); + PUT(d, "keepscript", BOOLEAN_OBJ(!!(cmd->uc_argt & EX_KEEPSCRIPT))); + PUT(d, "preview", BOOLEAN_OBJ(!!(cmd->uc_argt & EX_PREVIEW))); switch (cmd->uc_argt & (EX_EXTRA | EX_NOSPC | EX_NEEDARG)) { case 0: @@ -9898,9 +10188,9 @@ Dictionary commands_array(buf_T *buf) return rv; } -void verify_command(char_u *cmd) +void verify_command(char *cmd) { - if (strcmp("smile", (char *)cmd)) { + if (strcmp("smile", cmd)) { return; // acceptable non-existing command } msg(" #xxn` #xnxx` ,+x@##@Mz;` .xxx" @@ -10187,3 +10477,9 @@ void verify_command(char_u *cmd) msg("` `.:.`.,:iii;;;;;;;;iii;;;:` `.`` " " `nW"); } + +/// Get argt of command with id +uint32_t get_cmd_argt(cmdidx_T cmdidx) +{ + return cmdnames[(int)cmdidx].cmd_argt; +} |