neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

ex_docmd.c (247316B)


      1 // ex_docmd.c: functions for executing an Ex command line.
      2 
      3 #include <assert.h>
      4 #include <ctype.h>
      5 #include <inttypes.h>
      6 #include <limits.h>
      7 #include <stdbool.h>
      8 #include <stddef.h>
      9 #include <stdio.h>
     10 #include <stdlib.h>
     11 #include <string.h>
     12 #include <uv.h>
     13 
     14 #include "auto/config.h"
     15 #include "nvim/api/private/defs.h"
     16 #include "nvim/api/private/dispatch.h"
     17 #include "nvim/api/private/helpers.h"
     18 #include "nvim/api/ui.h"
     19 #include "nvim/api/vimscript.h"
     20 #include "nvim/arglist.h"
     21 #include "nvim/ascii_defs.h"
     22 #include "nvim/autocmd.h"
     23 #include "nvim/autocmd_defs.h"
     24 #include "nvim/buffer.h"
     25 #include "nvim/buffer_defs.h"
     26 #include "nvim/change.h"
     27 #include "nvim/channel.h"
     28 #include "nvim/charset.h"
     29 #include "nvim/clipboard.h"
     30 #include "nvim/cmdexpand.h"
     31 #include "nvim/cmdexpand_defs.h"
     32 #include "nvim/cursor.h"
     33 #include "nvim/debugger.h"
     34 #include "nvim/digraph.h"
     35 #include "nvim/drawscreen.h"
     36 #include "nvim/edit.h"
     37 #include "nvim/errors.h"
     38 #include "nvim/eval/fs.h"
     39 #include "nvim/eval/typval.h"
     40 #include "nvim/eval/typval_defs.h"
     41 #include "nvim/eval/userfunc.h"
     42 #include "nvim/eval/vars.h"
     43 #include "nvim/event/loop.h"
     44 #include "nvim/event/multiqueue.h"
     45 #include "nvim/ex_cmds.h"
     46 #include "nvim/ex_cmds2.h"
     47 #include "nvim/ex_cmds_defs.h"
     48 #include "nvim/ex_docmd.h"
     49 #include "nvim/ex_eval.h"
     50 #include "nvim/ex_eval_defs.h"
     51 #include "nvim/ex_getln.h"
     52 #include "nvim/file_search.h"
     53 #include "nvim/fileio.h"
     54 #include "nvim/fold.h"
     55 #include "nvim/garray.h"
     56 #include "nvim/garray_defs.h"
     57 #include "nvim/getchar.h"
     58 #include "nvim/gettext_defs.h"
     59 #include "nvim/globals.h"
     60 #include "nvim/highlight_defs.h"
     61 #include "nvim/highlight_group.h"
     62 #include "nvim/input.h"
     63 #include "nvim/keycodes.h"
     64 #include "nvim/lua/executor.h"
     65 #include "nvim/macros_defs.h"
     66 #include "nvim/main.h"
     67 #include "nvim/mark.h"
     68 #include "nvim/mark_defs.h"
     69 #include "nvim/mbyte.h"
     70 #include "nvim/memline.h"
     71 #include "nvim/memline_defs.h"
     72 #include "nvim/memory.h"
     73 #include "nvim/message.h"
     74 #include "nvim/mouse.h"
     75 #include "nvim/move.h"
     76 #include "nvim/normal.h"
     77 #include "nvim/normal_defs.h"
     78 #include "nvim/option.h"
     79 #include "nvim/option_defs.h"
     80 #include "nvim/option_vars.h"
     81 #include "nvim/optionstr.h"
     82 #include "nvim/os/fs.h"
     83 #include "nvim/os/input.h"
     84 #include "nvim/os/os.h"
     85 #include "nvim/os/os_defs.h"
     86 #include "nvim/os/shell.h"
     87 #include "nvim/path.h"
     88 #include "nvim/plines.h"
     89 #include "nvim/popupmenu.h"
     90 #include "nvim/pos_defs.h"
     91 #include "nvim/profile.h"
     92 #include "nvim/quickfix.h"
     93 #include "nvim/regexp.h"
     94 #include "nvim/regexp_defs.h"
     95 #include "nvim/runtime.h"
     96 #include "nvim/runtime_defs.h"
     97 #include "nvim/search.h"
     98 #include "nvim/shada.h"
     99 #include "nvim/state.h"
    100 #include "nvim/state_defs.h"
    101 #include "nvim/statusline.h"
    102 #include "nvim/strings.h"
    103 #include "nvim/tag.h"
    104 #include "nvim/types_defs.h"
    105 #include "nvim/ui.h"
    106 #include "nvim/ui_client.h"
    107 #include "nvim/undo.h"
    108 #include "nvim/undo_defs.h"
    109 #include "nvim/usercmd.h"
    110 #include "nvim/vim_defs.h"
    111 #include "nvim/window.h"
    112 #include "nvim/winfloat.h"
    113 
    114 static const char e_ambiguous_use_of_user_defined_command[]
    115  = N_("E464: Ambiguous use of user-defined command");
    116 static const char e_no_call_stack_to_substitute_for_stack[]
    117  = N_("E489: No call stack to substitute for \"<stack>\"");
    118 static const char e_not_an_editor_command[]
    119  = N_("E492: Not an editor command");
    120 static const char e_no_autocommand_file_name_to_substitute_for_afile[]
    121  = N_("E495: No autocommand file name to substitute for \"<afile>\"");
    122 static const char e_no_autocommand_buffer_number_to_substitute_for_abuf[]
    123  = N_("E496: No autocommand buffer number to substitute for \"<abuf>\"");
    124 static const char e_no_autocommand_match_name_to_substitute_for_amatch[]
    125  = N_("E497: No autocommand match name to substitute for \"<amatch>\"");
    126 static const char e_no_source_file_name_to_substitute_for_sfile[]
    127  = N_("E498: No :source file name to substitute for \"<sfile>\"");
    128 static const char e_no_line_number_to_use_for_slnum[]
    129  = N_("E842: No line number to use for \"<slnum>\"");
    130 static const char e_no_line_number_to_use_for_sflnum[]
    131  = N_("E961: No line number to use for \"<sflnum>\"");
    132 static const char e_no_script_file_name_to_substitute_for_script[]
    133  = N_("E1274: No script file name to substitute for \"<script>\"");
    134 
    135 static int quitmore = 0;
    136 static bool ex_pressedreturn = false;
    137 
    138 // Struct for storing a line inside a while/for loop
    139 typedef struct {
    140  char *line;            // command line
    141  linenr_T lnum;                // sourcing_lnum of the line
    142 } wcmd_T;
    143 
    144 #define FREE_WCMD(wcmd) xfree((wcmd)->line)
    145 
    146 /// Structure used to store info for line position in a while or for loop.
    147 /// This is required, because do_one_cmd() may invoke ex_function(), which
    148 /// reads more lines that may come from the while/for loop.
    149 struct loop_cookie {
    150  garray_T *lines_gap;               // growarray with line info
    151  int current_line;                     // last read line from growarray
    152  int repeating;                        // true when looping a second time
    153  // When "repeating" is false use "getline" and "cookie" to get lines
    154  LineGetter lc_getline;
    155  void *cookie;
    156 };
    157 
    158 // Struct to save a few things while debugging.  Used in do_cmdline() only.
    159 struct dbg_stuff {
    160  int trylevel;
    161  int force_abort;
    162  except_T *caught_stack;
    163  char *vv_exception;
    164  char *vv_throwpoint;
    165  int did_emsg;
    166  int got_int;
    167  bool did_throw;
    168  int need_rethrow;
    169  int check_cstack;
    170  except_T *current_exception;
    171 };
    172 
    173 #include "ex_docmd.c.generated.h"
    174 
    175 // Declare cmdnames[].
    176 #include "ex_cmds_defs.generated.h"
    177 
    178 static char dollar_command[2] = { '$', 0 };
    179 
    180 static void save_dbg_stuff(struct dbg_stuff *dsp)
    181 {
    182  dsp->trylevel = trylevel;
    183  trylevel = 0;
    184  dsp->force_abort = force_abort;
    185  force_abort = false;
    186  dsp->caught_stack = caught_stack;
    187  caught_stack = NULL;
    188  dsp->vv_exception = v_exception(NULL);
    189  dsp->vv_throwpoint = v_throwpoint(NULL);
    190 
    191  // Necessary for debugging an inactive ":catch", ":finally", ":endtry".
    192  dsp->did_emsg = did_emsg;
    193  did_emsg = false;
    194  dsp->got_int = got_int;
    195  got_int = false;
    196  dsp->did_throw = did_throw;
    197  did_throw = false;
    198  dsp->need_rethrow = need_rethrow;
    199  need_rethrow = false;
    200  dsp->check_cstack = check_cstack;
    201  check_cstack = false;
    202  dsp->current_exception = current_exception;
    203  current_exception = NULL;
    204 }
    205 
    206 static void restore_dbg_stuff(struct dbg_stuff *dsp)
    207 {
    208  suppress_errthrow = false;
    209  trylevel = dsp->trylevel;
    210  force_abort = dsp->force_abort;
    211  caught_stack = dsp->caught_stack;
    212  v_exception(dsp->vv_exception);
    213  v_throwpoint(dsp->vv_throwpoint);
    214  did_emsg = dsp->did_emsg;
    215  got_int = dsp->got_int;
    216  did_throw = dsp->did_throw;
    217  need_rethrow = dsp->need_rethrow;
    218  check_cstack = dsp->check_cstack;
    219  current_exception = dsp->current_exception;
    220 }
    221 
    222 /// Check if ffname differs from fnum.
    223 /// fnum is a buffer number. 0 == current buffer, 1-or-more must be a valid buffer ID.
    224 /// ffname is a full path to where a buffer lives on-disk or would live on-disk.
    225 static bool is_other_file(int fnum, char *ffname)
    226 {
    227  if (fnum != 0) {
    228    if (fnum == curbuf->b_fnum) {
    229      return false;
    230    }
    231 
    232    return true;
    233  }
    234 
    235  if (ffname == NULL) {
    236    return true;
    237  }
    238 
    239  if (*ffname == NUL) {
    240    return false;
    241  }
    242 
    243  if (!curbuf->file_id_valid
    244      && curbuf->b_sfname != NULL
    245      && *curbuf->b_sfname != NUL) {
    246    // This occurs with unsaved buffers. In which case `ffname`
    247    // actually corresponds to curbuf->b_sfname
    248    return path_fnamecmp(ffname, curbuf->b_sfname) != 0;
    249  }
    250 
    251  return otherfile(ffname);
    252 }
    253 
    254 /// Repeatedly get commands for Ex mode, until the ":vi" command is given.
    255 void do_exmode(void)
    256 {
    257  exmode_active = true;
    258  State = MODE_NORMAL;
    259  may_trigger_modechanged();
    260 
    261  // When using ":global /pat/ visual" and then "Q" we return to continue
    262  // the :global command.
    263  if (global_busy) {
    264    return;
    265  }
    266 
    267  int save_msg_scroll = msg_scroll;
    268  RedrawingDisabled++;  // don't redisplay the window
    269  no_wait_return++;  // don't wait for return
    270 
    271  msg(_("Entering Ex mode.  Type \"visual\" to go to Normal mode."), 0);
    272  while (exmode_active) {
    273    // Check for a ":normal" command and no more characters left.
    274    if (ex_normal_busy > 0 && typebuf.tb_len == 0) {
    275      exmode_active = false;
    276      break;
    277    }
    278    msg_scroll = true;
    279    need_wait_return = false;
    280    ex_pressedreturn = false;
    281    ex_no_reprint = false;
    282    varnumber_T changedtick = buf_get_changedtick(curbuf);
    283    int prev_msg_row = msg_row;
    284    linenr_T prev_line = curwin->w_cursor.lnum;
    285    cmdline_row = msg_row;
    286    do_cmdline(NULL, getexline, NULL, 0);
    287    lines_left = Rows - 1;
    288 
    289    if ((prev_line != curwin->w_cursor.lnum
    290         || changedtick != buf_get_changedtick(curbuf)) && !ex_no_reprint) {
    291      if (curbuf->b_ml.ml_flags & ML_EMPTY) {
    292        emsg(_(e_empty_buffer));
    293      } else {
    294        if (ex_pressedreturn) {
    295          // Make sure the message overwrites the right line and isn't throttled.
    296          msg_scroll_flush();
    297          // go up one line, to overwrite the ":<CR>" line, so the
    298          // output doesn't contain empty lines.
    299          msg_row = prev_msg_row;
    300          if (prev_msg_row == Rows - 1) {
    301            msg_row--;
    302          }
    303        }
    304        msg_col = 0;
    305        print_line_no_prefix(curwin->w_cursor.lnum, false, false);
    306        msg_clr_eos();
    307      }
    308    } else if (ex_pressedreturn && !ex_no_reprint) {  // must be at EOF
    309      if (curbuf->b_ml.ml_flags & ML_EMPTY) {
    310        emsg(_(e_empty_buffer));
    311      } else {
    312        emsg(_("E501: At end-of-file"));
    313      }
    314    }
    315  }
    316 
    317  RedrawingDisabled--;
    318  no_wait_return--;
    319  redraw_all_later(UPD_NOT_VALID);
    320  update_screen();
    321  need_wait_return = false;
    322  msg_scroll = save_msg_scroll;
    323 }
    324 
    325 /// Print the executed command for when 'verbose' is set.
    326 ///
    327 /// @param lnum  if 0, only print the command.
    328 static void msg_verbose_cmd(linenr_T lnum, char *cmd)
    329  FUNC_ATTR_NONNULL_ALL
    330 {
    331  no_wait_return++;
    332  verbose_enter_scroll();
    333 
    334  if (lnum == 0) {
    335    smsg(0, _("Executing: %s"), cmd);
    336  } else {
    337    smsg(0, _("line %" PRIdLINENR ": %s"), lnum, cmd);
    338  }
    339  if (msg_silent == 0) {
    340    msg_puts("\n");   // don't overwrite this
    341  }
    342 
    343  verbose_leave_scroll();
    344  no_wait_return--;
    345 }
    346 
    347 static int cmdline_call_depth = 0;  ///< recursiveness
    348 
    349 /// Start executing an Ex command line.
    350 ///
    351 /// @return  FAIL if too recursive, OK otherwise.
    352 static int do_cmdline_start(void)
    353 {
    354  assert(cmdline_call_depth >= 0);
    355  // It's possible to create an endless loop with ":execute", catch that
    356  // here.  The value of 200 allows nested function calls, ":source", etc.
    357  // Allow 200 or 'maxfuncdepth', whatever is larger.
    358  if (cmdline_call_depth >= 200 && cmdline_call_depth >= p_mfd) {
    359    return FAIL;
    360  }
    361  cmdline_call_depth++;
    362  start_batch_changes();
    363  return OK;
    364 }
    365 
    366 /// End executing an Ex command line.
    367 static void do_cmdline_end(void)
    368 {
    369  cmdline_call_depth--;
    370  assert(cmdline_call_depth >= 0);
    371  end_batch_changes();
    372 }
    373 
    374 /// Execute a simple command line.  Used for translated commands like "*".
    375 int do_cmdline_cmd(const char *cmd)
    376 {
    377  return do_cmdline((char *)cmd, NULL, NULL, DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_KEYTYPED);
    378 }
    379 
    380 /// do_cmdline(): execute one Ex command line
    381 ///
    382 /// 1. Execute "cmdline" when it is not NULL.
    383 ///    If "cmdline" is NULL, or more lines are needed, fgetline() is used.
    384 /// 2. Split up in parts separated with '|'.
    385 ///
    386 /// This function can be called recursively!
    387 ///
    388 /// flags:
    389 ///   DOCMD_VERBOSE  - The command will be included in the error message.
    390 ///   DOCMD_NOWAIT   - Don't call wait_return() and friends.
    391 ///   DOCMD_REPEAT   - Repeat execution until fgetline() returns NULL.
    392 ///   DOCMD_KEYTYPED - Don't reset KeyTyped.
    393 ///   DOCMD_EXCRESET - Reset the exception environment (used for debugging).
    394 ///   DOCMD_KEEPLINE - Store first typed line (for repeating with ".").
    395 ///
    396 /// @param cookie  argument for fgetline()
    397 ///
    398 /// @return FAIL if cmdline could not be executed, OK otherwise
    399 int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
    400 {
    401  char *next_cmdline;                   // next cmd to execute
    402  char *cmdline_copy = NULL;            // copy of cmd line
    403  bool used_getline = false;            // used "fgetline" to obtain command
    404  static int recursive = 0;             // recursive depth
    405  bool msg_didout_before_start = false;
    406  int count = 0;                        // line number count
    407  bool did_inc = false;                 // incremented RedrawingDisabled
    408  bool did_block = false;               // emitted cmdline_block event
    409  int retval = OK;
    410  cstack_T cstack = {                   // conditional stack
    411    .cs_idx = -1,
    412  };
    413  garray_T lines_ga;                    // keep lines for ":while"/":for"
    414  int current_line = 0;                 // active line in lines_ga
    415  char *fname = NULL;                   // function or script name
    416  linenr_T *breakpoint = NULL;          // ptr to breakpoint field in cookie
    417  int *dbg_tick = NULL;                 // ptr to dbg_tick field in cookie
    418  struct dbg_stuff debug_saved;         // saved things for debug mode
    419  msglist_T *private_msg_list;
    420 
    421  // "fgetline" and "cookie" passed to do_one_cmd()
    422  char *(*cmd_getline)(int, void *, int, bool);
    423  void *cmd_cookie;
    424  struct loop_cookie cmd_loop_cookie;
    425 
    426  // For every pair of do_cmdline()/do_one_cmd() calls, use an extra memory
    427  // location for storing error messages to be converted to an exception.
    428  // This ensures that the do_errthrow() call in do_one_cmd() does not
    429  // combine the messages stored by an earlier invocation of do_one_cmd()
    430  // with the command name of the later one.  This would happen when
    431  // BufWritePost autocommands are executed after a write error.
    432  msglist_T **saved_msg_list = msg_list;
    433  msg_list = &private_msg_list;
    434  private_msg_list = NULL;
    435 
    436  if (do_cmdline_start() == FAIL) {
    437    emsg(_(e_command_too_recursive));
    438    // When converting to an exception, we do not include the command name
    439    // since this is not an error of the specific command.
    440    do_errthrow((cstack_T *)NULL, NULL);
    441    msg_list = saved_msg_list;
    442    return FAIL;
    443  }
    444 
    445  ga_init(&lines_ga, (int)sizeof(wcmd_T), 10);
    446 
    447  void *real_cookie = getline_cookie(fgetline, cookie);
    448 
    449  // Inside a function use a higher nesting level.
    450  bool getline_is_func = getline_equal(fgetline, cookie, get_func_line);
    451  if (getline_is_func && ex_nesting_level == func_level(real_cookie)) {
    452    ex_nesting_level++;
    453  }
    454 
    455  // Get the function or script name and the address where the next breakpoint
    456  // line and the debug tick for a function or script are stored.
    457  if (getline_is_func) {
    458    fname = func_name(real_cookie);
    459    breakpoint = func_breakpoint(real_cookie);
    460    dbg_tick = func_dbg_tick(real_cookie);
    461  } else if (getline_equal(fgetline, cookie, getsourceline)) {
    462    fname = SOURCING_NAME;
    463    breakpoint = source_breakpoint(real_cookie);
    464    dbg_tick = source_dbg_tick(real_cookie);
    465  }
    466 
    467  // Initialize "force_abort"  and "suppress_errthrow" at the top level.
    468  if (!recursive) {
    469    force_abort = false;
    470    suppress_errthrow = false;
    471  }
    472 
    473  // If requested, store and reset the global values controlling the
    474  // exception handling (used when debugging).  Otherwise clear it to avoid
    475  // a bogus compiler warning when the optimizer uses inline functions...
    476  if (flags & DOCMD_EXCRESET) {
    477    save_dbg_stuff(&debug_saved);
    478  } else {
    479    CLEAR_FIELD(debug_saved);
    480  }
    481 
    482  int initial_trylevel = trylevel;
    483 
    484  // "did_throw" will be set to true when an exception is being thrown.
    485  did_throw = false;
    486  // "did_emsg" will be set to true when emsg() is used, in which case we
    487  // cancel the whole command line, and any if/endif or loop.
    488  // If force_abort is set, we cancel everything.
    489  did_emsg = false;
    490 
    491  // KeyTyped is only set when calling vgetc().  Reset it here when not
    492  // calling vgetc() (sourced command lines).
    493  if (!(flags & DOCMD_KEYTYPED)
    494      && !getline_equal(fgetline, cookie, getexline)) {
    495    KeyTyped = false;
    496  }
    497 
    498  // Continue executing command lines:
    499  // - when inside an ":if", ":while" or ":for"
    500  // - for multiple commands on one line, separated with '|'
    501  // - when repeating until there are no more lines (for ":source")
    502  next_cmdline = cmdline;
    503  do {
    504    getline_is_func = getline_equal(fgetline, cookie, get_func_line);
    505 
    506    // stop skipping cmds for an error msg after all endif/while/for
    507    if (next_cmdline == NULL
    508        && !force_abort
    509        && cstack.cs_idx < 0
    510        && !(getline_is_func
    511             && func_has_abort(real_cookie))) {
    512      did_emsg = false;
    513    }
    514 
    515    // 1. If repeating a line in a loop, get a line from lines_ga.
    516    // 2. If no line given: Get an allocated line with fgetline().
    517    // 3. If a line is given: Make a copy, so we can mess with it.
    518 
    519    // 1. If repeating, get a previous line from lines_ga.
    520    if (cstack.cs_looplevel > 0 && current_line < lines_ga.ga_len) {
    521      // Each '|' separated command is stored separately in lines_ga, to
    522      // be able to jump to it.  Don't use next_cmdline now.
    523      XFREE_CLEAR(cmdline_copy);
    524 
    525      // Check if a function has returned or, unless it has an unclosed
    526      // try conditional, aborted.
    527      if (getline_is_func) {
    528        if (do_profiling == PROF_YES) {
    529          func_line_end(real_cookie);
    530        }
    531        if (func_has_ended(real_cookie)) {
    532          retval = FAIL;
    533          break;
    534        }
    535      } else if (do_profiling == PROF_YES
    536                 && getline_equal(fgetline, cookie, getsourceline)) {
    537        script_line_end();
    538      }
    539 
    540      // Check if a sourced file hit a ":finish" command.
    541      if (source_finished(fgetline, cookie)) {
    542        retval = FAIL;
    543        break;
    544      }
    545 
    546      // If breakpoints have been added/deleted need to check for it.
    547      if (breakpoint != NULL && dbg_tick != NULL
    548          && *dbg_tick != debug_tick) {
    549        *breakpoint = dbg_find_breakpoint(getline_equal(fgetline, cookie, getsourceline),
    550                                          fname, SOURCING_LNUM);
    551        *dbg_tick = debug_tick;
    552      }
    553 
    554      next_cmdline = ((wcmd_T *)(lines_ga.ga_data))[current_line].line;
    555      SOURCING_LNUM = ((wcmd_T *)(lines_ga.ga_data))[current_line].lnum;
    556 
    557      // Did we encounter a breakpoint?
    558      if (breakpoint != NULL && *breakpoint != 0 && *breakpoint <= SOURCING_LNUM) {
    559        dbg_breakpoint(fname, SOURCING_LNUM);
    560        // Find next breakpoint.
    561        *breakpoint = dbg_find_breakpoint(getline_equal(fgetline, cookie, getsourceline),
    562                                          fname, SOURCING_LNUM);
    563        *dbg_tick = debug_tick;
    564      }
    565      if (do_profiling == PROF_YES) {
    566        if (getline_is_func) {
    567          func_line_start(real_cookie);
    568        } else if (getline_equal(fgetline, cookie, getsourceline)) {
    569          script_line_start();
    570        }
    571      }
    572    }
    573 
    574    // 2. If no line given, get an allocated line with fgetline().
    575    if (next_cmdline == NULL) {
    576      int indent = cstack.cs_idx < 0 ? 0 : (cstack.cs_idx + 1) * 2;
    577 
    578      if (count == 1 && getline_equal(fgetline, cookie, getexline)) {
    579        if (ui_has(kUICmdline)) {
    580          // Emit cmdline_block event for loop/conditional block.
    581          ui_ext_cmdline_block_append(0, last_cmdline);
    582          did_block = true;
    583        }
    584        // Need to set msg_didout for the first line after an ":if",
    585        // otherwise the ":if" will be overwritten.
    586        msg_didout = true;
    587      }
    588 
    589      if (fgetline == NULL || (next_cmdline = fgetline(':', cookie, indent, true)) == NULL) {
    590        // Don't call wait_return() for aborted command line.  The NULL
    591        // returned for the end of a sourced file or executed function
    592        // doesn't do this.
    593        if (KeyTyped && !(flags & DOCMD_REPEAT)) {
    594          need_wait_return = false;
    595        }
    596        retval = FAIL;
    597        break;
    598      }
    599      used_getline = true;
    600 
    601      // Emit all but the first cmdline_block event immediately; waiting until after
    602      // command execution would mess up event ordering with nested command lines.
    603      if (ui_has(kUICmdline) && count > 0 && getline_equal(fgetline, cookie, getexline)) {
    604        ui_ext_cmdline_block_append((size_t)indent, next_cmdline);
    605      }
    606 
    607      // Keep the first typed line.  Clear it when more lines are typed.
    608      if (flags & DOCMD_KEEPLINE) {
    609        xfree(repeat_cmdline);
    610        if (count == 0) {
    611          repeat_cmdline = xstrdup(next_cmdline);
    612        } else {
    613          repeat_cmdline = NULL;
    614        }
    615      }
    616    } else if (cmdline_copy == NULL) {
    617      // 3. Make a copy of the command so we can mess with it.
    618      next_cmdline = xstrdup(next_cmdline);
    619    }
    620    cmdline_copy = next_cmdline;
    621 
    622    int current_line_before = 0;
    623    // Inside a while/for loop, and when the command looks like a ":while"
    624    // or ":for", the line is stored, because we may need it later when
    625    // looping.
    626    //
    627    // When there is a '|' and another command, it is stored separately,
    628    // because we need to be able to jump back to it from an
    629    // :endwhile/:endfor.
    630    //
    631    // Pass a different "fgetline" function to do_one_cmd() below,
    632    // that it stores lines in or reads them from "lines_ga".  Makes it
    633    // possible to define a function inside a while/for loop.
    634    if ((cstack.cs_looplevel > 0 || has_loop_cmd(next_cmdline))) {
    635      cmd_getline = get_loop_line;
    636      cmd_cookie = (void *)&cmd_loop_cookie;
    637      cmd_loop_cookie.lines_gap = &lines_ga;
    638      cmd_loop_cookie.current_line = current_line;
    639      cmd_loop_cookie.lc_getline = fgetline;
    640      cmd_loop_cookie.cookie = cookie;
    641      cmd_loop_cookie.repeating = (current_line < lines_ga.ga_len);
    642 
    643      // Save the current line when encountering it the first time.
    644      if (current_line == lines_ga.ga_len) {
    645        store_loop_line(&lines_ga, next_cmdline);
    646      }
    647      current_line_before = current_line;
    648    } else {
    649      cmd_getline = fgetline;
    650      cmd_cookie = cookie;
    651    }
    652 
    653    did_endif = false;
    654 
    655    if (count++ == 0) {
    656      // All output from the commands is put below each other, without
    657      // waiting for a return. Don't do this when executing commands
    658      // from a script or when being called recursive (e.g. for ":e
    659      // +command file").
    660      if (!(flags & DOCMD_NOWAIT) && !recursive) {
    661        msg_didout_before_start = msg_didout;
    662        msg_didany = false;         // no output yet
    663        msg_start();
    664        msg_scroll = true;          // put messages below each other
    665        no_wait_return++;           // don't wait for return until finished
    666        RedrawingDisabled++;
    667        did_inc = true;
    668      }
    669    }
    670 
    671    if ((p_verbose >= 15 && SOURCING_NAME != NULL) || p_verbose >= 16) {
    672      msg_verbose_cmd(SOURCING_LNUM, cmdline_copy);
    673    }
    674 
    675    // 2. Execute one '|' separated command.
    676    //    do_one_cmd() will return NULL if there is no trailing '|'.
    677    //    "cmdline_copy" can change, e.g. for '%' and '#' expansion.
    678    recursive++;
    679    next_cmdline = do_one_cmd(&cmdline_copy, flags, &cstack, cmd_getline, cmd_cookie);
    680    recursive--;
    681 
    682    if (cmd_cookie == (void *)&cmd_loop_cookie) {
    683      // Use "current_line" from "cmd_loop_cookie", it may have been
    684      // incremented when defining a function.
    685      current_line = cmd_loop_cookie.current_line;
    686    }
    687 
    688    if (next_cmdline == NULL) {
    689      XFREE_CLEAR(cmdline_copy);
    690 
    691      // If the command was typed, remember it for the ':' register.
    692      // Do this AFTER executing the command to make :@: work.
    693      if (getline_equal(fgetline, cookie, getexline) && new_last_cmdline != NULL) {
    694        xfree(last_cmdline);
    695        last_cmdline = new_last_cmdline;
    696        new_last_cmdline = NULL;
    697      }
    698    } else {
    699      // need to copy the command after the '|' to cmdline_copy, for the
    700      // next do_one_cmd()
    701      STRMOVE(cmdline_copy, next_cmdline);
    702      next_cmdline = cmdline_copy;
    703    }
    704 
    705    // reset did_emsg for a function that is not aborted by an error
    706    if (did_emsg && !force_abort
    707        && getline_equal(fgetline, cookie, get_func_line)
    708        && !func_has_abort(real_cookie)) {
    709      did_emsg = false;
    710    }
    711 
    712    if (cstack.cs_looplevel > 0) {
    713      current_line++;
    714 
    715      // An ":endwhile", ":endfor" and ":continue" is handled here.
    716      // If we were executing commands, jump back to the ":while" or
    717      // ":for".
    718      // If we were not executing commands, decrement cs_looplevel.
    719      if (cstack.cs_lflags & (CSL_HAD_CONT | CSL_HAD_ENDLOOP)) {
    720        cstack.cs_lflags &= ~(CSL_HAD_CONT | CSL_HAD_ENDLOOP);
    721 
    722        // Jump back to the matching ":while" or ":for".  Be careful
    723        // not to use a cs_line[] from an entry that isn't a ":while"
    724        // or ":for": It would make "current_line" invalid and can
    725        // cause a crash.
    726        if (!did_emsg && !got_int && !did_throw
    727            && cstack.cs_idx >= 0
    728            && (cstack.cs_flags[cstack.cs_idx]
    729                & (CSF_WHILE | CSF_FOR))
    730            && cstack.cs_line[cstack.cs_idx] >= 0
    731            && (cstack.cs_flags[cstack.cs_idx] & CSF_ACTIVE)) {
    732          current_line = cstack.cs_line[cstack.cs_idx];
    733          // remember we jumped there
    734          cstack.cs_lflags |= CSL_HAD_LOOP;
    735          line_breakcheck();                    // check if CTRL-C typed
    736 
    737          // Check for the next breakpoint at or after the ":while"
    738          // or ":for".
    739          if (breakpoint != NULL && lines_ga.ga_len > current_line) {
    740            *breakpoint = dbg_find_breakpoint(getline_equal(fgetline, cookie, getsourceline), fname,
    741                                              ((wcmd_T *)lines_ga.ga_data)[current_line].lnum - 1);
    742            *dbg_tick = debug_tick;
    743          }
    744        } else {
    745          // can only get here with ":endwhile" or ":endfor"
    746          if (cstack.cs_idx >= 0) {
    747            rewind_conditionals(&cstack, cstack.cs_idx - 1,
    748                                CSF_WHILE | CSF_FOR, &cstack.cs_looplevel);
    749          }
    750        }
    751      } else if (cstack.cs_lflags & CSL_HAD_LOOP) {
    752        // For a ":while" or ":for" we need to remember the line number.
    753        cstack.cs_lflags &= ~CSL_HAD_LOOP;
    754        cstack.cs_line[cstack.cs_idx] = current_line_before;
    755      }
    756    }
    757 
    758    // When not inside any ":while" loop, clear remembered lines.
    759    if (cstack.cs_looplevel == 0) {
    760      if (!GA_EMPTY(&lines_ga)) {
    761        SOURCING_LNUM = ((wcmd_T *)lines_ga.ga_data)[lines_ga.ga_len - 1].lnum;
    762        GA_DEEP_CLEAR(&lines_ga, wcmd_T, FREE_WCMD);
    763      }
    764      current_line = 0;
    765    }
    766 
    767    // A ":finally" makes did_emsg, got_int and did_throw pending for
    768    // being restored at the ":endtry".  Reset them here and set the
    769    // ACTIVE and FINALLY flags, so that the finally clause gets executed.
    770    // This includes the case where a missing ":endif", ":endwhile" or
    771    // ":endfor" was detected by the ":finally" itself.
    772    if (cstack.cs_lflags & CSL_HAD_FINA) {
    773      cstack.cs_lflags &= ~CSL_HAD_FINA;
    774      report_make_pending((cstack.cs_pending[cstack.cs_idx]
    775                           & (CSTP_ERROR | CSTP_INTERRUPT | CSTP_THROW)),
    776                          did_throw ? current_exception : NULL);
    777      did_emsg = got_int = did_throw = false;
    778      cstack.cs_flags[cstack.cs_idx] |= CSF_ACTIVE | CSF_FINALLY;
    779    }
    780 
    781    // Update global "trylevel" for recursive calls to do_cmdline() from
    782    // within this loop.
    783    trylevel = initial_trylevel + cstack.cs_trylevel;
    784 
    785    // If the outermost try conditional (across function calls and sourced
    786    // files) is aborted because of an error, an interrupt, or an uncaught
    787    // exception, cancel everything.  If it is left normally, reset
    788    // force_abort to get the non-EH compatible abortion behavior for
    789    // the rest of the script.
    790    if (trylevel == 0 && !did_emsg && !got_int && !did_throw) {
    791      force_abort = false;
    792    }
    793 
    794    // Convert an interrupt to an exception if appropriate.
    795    do_intthrow(&cstack);
    796 
    797    // Continue executing command lines when:
    798    // - no CTRL-C typed, no aborting error, no exception thrown or try
    799    //   conditionals need to be checked for executing finally clauses or
    800    //   catching an interrupt exception
    801    // - didn't get an error message or lines are not typed
    802    // - there is a command after '|', inside a :if, :while, :for or :try, or
    803    //   looping for ":source" command or function call.
    804  } while (!((got_int || (did_emsg && force_abort) || did_throw)
    805             && cstack.cs_trylevel == 0)
    806           && !(did_emsg
    807                // Keep going when inside try/catch, so that the error can be
    808                // deal with, except when it is a syntax error, it may cause
    809                // the :endtry to be missed.
    810                && (cstack.cs_trylevel == 0 || did_emsg_syntax)
    811                && used_getline
    812                && getline_equal(fgetline, cookie, getexline))
    813           && (next_cmdline != NULL
    814               || cstack.cs_idx >= 0
    815               || (flags & DOCMD_REPEAT)));
    816 
    817  xfree(cmdline_copy);
    818  did_emsg_syntax = false;
    819  GA_DEEP_CLEAR(&lines_ga, wcmd_T, FREE_WCMD);
    820 
    821  if (cstack.cs_idx >= 0) {
    822    // If a sourced file or executed function ran to its end, report the
    823    // unclosed conditional.
    824    if (!got_int && !did_throw && !aborting()
    825        && ((getline_equal(fgetline, cookie, getsourceline)
    826             && !source_finished(fgetline, cookie))
    827            || (getline_equal(fgetline, cookie, get_func_line)
    828                && !func_has_ended(real_cookie)))) {
    829      if (cstack.cs_flags[cstack.cs_idx] & CSF_TRY) {
    830        emsg(_(e_endtry));
    831      } else if (cstack.cs_flags[cstack.cs_idx] & CSF_WHILE) {
    832        emsg(_(e_endwhile));
    833      } else if (cstack.cs_flags[cstack.cs_idx] & CSF_FOR) {
    834        emsg(_(e_endfor));
    835      } else {
    836        emsg(_(e_endif));
    837      }
    838    }
    839 
    840    // Reset "trylevel" in case of a ":finish" or ":return" or a missing
    841    // ":endtry" in a sourced file or executed function.  If the try
    842    // conditional is in its finally clause, ignore anything pending.
    843    // If it is in a catch clause, finish the caught exception.
    844    // Also cleanup any "cs_forinfo" structures.
    845    do {
    846      int idx = cleanup_conditionals(&cstack, 0, true);
    847 
    848      if (idx >= 0) {
    849        idx--;              // remove try block not in its finally clause
    850      }
    851      rewind_conditionals(&cstack, idx, CSF_WHILE | CSF_FOR,
    852                          &cstack.cs_looplevel);
    853    } while (cstack.cs_idx >= 0);
    854    trylevel = initial_trylevel;
    855  }
    856 
    857  // If a missing ":endtry", ":endwhile", ":endfor", or ":endif" or a memory
    858  // lack was reported above and the error message is to be converted to an
    859  // exception, do this now after rewinding the cstack.
    860  do_errthrow(&cstack, getline_equal(fgetline, cookie, get_func_line) ? "endfunction" : NULL);
    861 
    862  if (trylevel == 0) {
    863    // When an exception is being thrown out of the outermost try
    864    // conditional, discard the uncaught exception, disable the conversion
    865    // of interrupts or errors to exceptions, and ensure that no more
    866    // commands are executed.
    867    if (did_throw) {
    868      handle_did_throw();
    869    } else if (got_int || (did_emsg && force_abort)) {
    870      // On an interrupt or an aborting error not converted to an exception,
    871      // disable the conversion of errors to exceptions.  (Interrupts are not
    872      // converted any more, here.) This enables also the interrupt message
    873      // when force_abort is set and did_emsg unset in case of an interrupt
    874      // from a finally clause after an error.
    875      suppress_errthrow = true;
    876    }
    877  }
    878 
    879  // The current cstack will be freed when do_cmdline() returns.  An uncaught
    880  // exception will have to be rethrown in the previous cstack.  If a function
    881  // has just returned or a script file was just finished and the previous
    882  // cstack belongs to the same function or, respectively, script file, it
    883  // will have to be checked for finally clauses to be executed due to the
    884  // ":return" or ":finish".  This is done in do_one_cmd().
    885  if (did_throw) {
    886    need_rethrow = true;
    887  }
    888  if ((getline_equal(fgetline, cookie, getsourceline)
    889       && ex_nesting_level > source_level(real_cookie))
    890      || (getline_equal(fgetline, cookie, get_func_line)
    891          && ex_nesting_level > func_level(real_cookie) + 1)) {
    892    if (!did_throw) {
    893      check_cstack = true;
    894    }
    895  } else {
    896    // When leaving a function, reduce nesting level.
    897    if (getline_equal(fgetline, cookie, get_func_line)) {
    898      ex_nesting_level--;
    899    }
    900    // Go to debug mode when returning from a function in which we are
    901    // single-stepping.
    902    if ((getline_equal(fgetline, cookie, getsourceline)
    903         || getline_equal(fgetline, cookie, get_func_line))
    904        && ex_nesting_level + 1 <= debug_break_level) {
    905      do_debug(getline_equal(fgetline, cookie, getsourceline)
    906               ? _("End of sourced file")
    907               : _("End of function"));
    908    }
    909  }
    910 
    911  // Restore the exception environment (done after returning from the
    912  // debugger).
    913  if (flags & DOCMD_EXCRESET) {
    914    restore_dbg_stuff(&debug_saved);
    915  }
    916 
    917  msg_list = saved_msg_list;
    918 
    919  // Cleanup if "cs_emsg_silent_list" remains.
    920  if (cstack.cs_emsg_silent_list != NULL) {
    921    eslist_T *temp;
    922    for (eslist_T *elem = cstack.cs_emsg_silent_list; elem != NULL; elem = temp) {
    923      temp = elem->next;
    924      xfree(elem);
    925    }
    926  }
    927 
    928  // If there was too much output to fit on the command line, ask the user to
    929  // hit return before redrawing the screen. With the ":global" command we do
    930  // this only once after the command is finished.
    931  if (did_inc) {
    932    RedrawingDisabled--;
    933    no_wait_return--;
    934    msg_scroll = false;
    935 
    936    // When just finished an ":if"-":else" which was typed, no need to
    937    // wait for hit-return.  Also for an error situation.
    938    if (retval == FAIL
    939        || (did_endif && KeyTyped && !did_emsg)) {
    940      need_wait_return = false;
    941      msg_didany = false;               // don't wait when restarting edit
    942    } else if (need_wait_return) {
    943      // The msg_start() above clears msg_didout. The wait_return() we do
    944      // here should not overwrite the command that may be shown before
    945      // doing that.
    946      msg_didout |= msg_didout_before_start;
    947      wait_return(false);
    948    }
    949  }
    950 
    951  if (did_block) {
    952    ui_ext_cmdline_block_leave();
    953  }
    954 
    955  did_endif = false;    // in case do_cmdline used recursively
    956 
    957  do_cmdline_end();
    958  return retval;
    959 }
    960 
    961 /// Handle when "did_throw" is set after executing commands.
    962 void handle_did_throw(void)
    963 {
    964  assert(current_exception != NULL);
    965  char *p = NULL;
    966  msglist_T *messages = NULL;
    967  ESTACK_CHECK_DECLARATION;
    968 
    969  // If the uncaught exception is a user exception, report it as an
    970  // error.  If it is an error exception, display the saved error
    971  // message now.  For an interrupt exception, do nothing; the
    972  // interrupt message is given elsewhere.
    973  switch (current_exception->type) {
    974  case ET_USER:
    975    vim_snprintf(IObuff, IOSIZE,
    976                 _("E605: Exception not caught: %s"),
    977                 current_exception->value);
    978    p = xstrdup(IObuff);
    979    break;
    980  case ET_ERROR:
    981    messages = current_exception->messages;
    982    current_exception->messages = NULL;
    983    break;
    984  case ET_INTERRUPT:
    985    break;
    986  }
    987 
    988  estack_push(ETYPE_EXCEPT, current_exception->throw_name, current_exception->throw_lnum);
    989  ESTACK_CHECK_SETUP;
    990  current_exception->throw_name = NULL;
    991 
    992  discard_current_exception();              // uses IObuff if 'verbose'
    993 
    994  // If "silent!" is active the uncaught exception is not fatal.
    995  if (emsg_silent == 0) {
    996    suppress_errthrow = true;
    997    force_abort = true;
    998  }
    999 
   1000  if (messages != NULL) {
   1001    do {
   1002      msglist_T *next = messages->next;
   1003      emsg_multiline(messages->msg, "emsg", HLF_E, messages->multiline);
   1004      xfree(messages->msg);
   1005      xfree(messages->sfile);
   1006      xfree(messages);
   1007      messages = next;
   1008    } while (messages != NULL);
   1009  } else if (p != NULL) {
   1010    emsg(p);
   1011    xfree(p);
   1012  }
   1013  xfree(SOURCING_NAME);
   1014  ESTACK_CHECK_NOW;
   1015  estack_pop();
   1016 }
   1017 
   1018 /// Obtain a line when inside a ":while" or ":for" loop.
   1019 static char *get_loop_line(int c, void *cookie, int indent, bool do_concat)
   1020 {
   1021  struct loop_cookie *cp = (struct loop_cookie *)cookie;
   1022 
   1023  if (cp->current_line + 1 >= cp->lines_gap->ga_len) {
   1024    if (cp->repeating) {
   1025      return NULL;              // trying to read past ":endwhile"/":endfor"
   1026    }
   1027    char *line;
   1028    // First time inside the ":while"/":for": get line normally.
   1029    if (cp->lc_getline == NULL) {
   1030      line = getcmdline(c, 0, indent, do_concat);
   1031    } else {
   1032      line = cp->lc_getline(c, cp->cookie, indent, do_concat);
   1033    }
   1034    if (line != NULL) {
   1035      store_loop_line(cp->lines_gap, line);
   1036      cp->current_line++;
   1037    }
   1038 
   1039    return line;
   1040  }
   1041 
   1042  KeyTyped = false;
   1043  cp->current_line++;
   1044  wcmd_T *wp = (wcmd_T *)(cp->lines_gap->ga_data) + cp->current_line;
   1045  SOURCING_LNUM = wp->lnum;
   1046  return xstrdup(wp->line);
   1047 }
   1048 
   1049 /// Store a line in "gap" so that a ":while" loop can execute it again.
   1050 static void store_loop_line(garray_T *gap, char *line)
   1051 {
   1052  wcmd_T *p = GA_APPEND_VIA_PTR(wcmd_T, gap);
   1053  p->line = xstrdup(line);
   1054  p->lnum = SOURCING_LNUM;
   1055 }
   1056 
   1057 /// If "fgetline" is get_loop_line(), return true if the getline it uses equals
   1058 /// "func".  * Otherwise return true when "fgetline" equals "func".
   1059 ///
   1060 /// @param cookie  argument for fgetline()
   1061 bool getline_equal(LineGetter fgetline, void *cookie, LineGetter func)
   1062 {
   1063  // When "fgetline" is "get_loop_line()" use the "cookie" to find the
   1064  // function that's originally used to obtain the lines.  This may be
   1065  // nested several levels.
   1066  LineGetter gp = fgetline;
   1067  struct loop_cookie *cp = (struct loop_cookie *)cookie;
   1068  while (gp == get_loop_line) {
   1069    gp = cp->lc_getline;
   1070    cp = cp->cookie;
   1071  }
   1072  return gp == func;
   1073 }
   1074 
   1075 /// If "fgetline" is get_loop_line(), return the cookie used by the original
   1076 /// getline function.  Otherwise return "cookie".
   1077 ///
   1078 /// @param cookie  argument for fgetline()
   1079 void *getline_cookie(LineGetter fgetline, void *cookie)
   1080 {
   1081  // When "fgetline" is "get_loop_line()" use the "cookie" to find the
   1082  // cookie that's originally used to obtain the lines.  This may be nested
   1083  // several levels.
   1084  LineGetter gp = fgetline;
   1085  struct loop_cookie *cp = (struct loop_cookie *)cookie;
   1086  while (gp == get_loop_line) {
   1087    gp = cp->lc_getline;
   1088    cp = cp->cookie;
   1089  }
   1090  return cp;
   1091 }
   1092 
   1093 /// Helper function to apply an offset for buffer commands, i.e. ":bdelete",
   1094 /// ":bwipeout", etc.
   1095 ///
   1096 /// @return  the buffer number.
   1097 static int compute_buffer_local_count(cmd_addr_T addr_type, linenr_T lnum, int offset)
   1098 {
   1099  int count = offset;
   1100 
   1101  buf_T *buf = firstbuf;
   1102  while (buf->b_next != NULL && buf->b_fnum < lnum) {
   1103    buf = buf->b_next;
   1104  }
   1105  while (count != 0) {
   1106    count += (count < 0) ? 1 : -1;
   1107    buf_T *nextbuf = (offset < 0) ? buf->b_prev : buf->b_next;
   1108    if (nextbuf == NULL) {
   1109      break;
   1110    }
   1111    buf = nextbuf;
   1112    if (addr_type == ADDR_LOADED_BUFFERS) {
   1113      // skip over unloaded buffers
   1114      while (buf->b_ml.ml_mfp == NULL) {
   1115        nextbuf = (offset < 0) ? buf->b_prev : buf->b_next;
   1116        if (nextbuf == NULL) {
   1117          break;
   1118        }
   1119        buf = nextbuf;
   1120      }
   1121    }
   1122  }
   1123  // we might have gone too far, last buffer is not loaded
   1124  if (addr_type == ADDR_LOADED_BUFFERS) {
   1125    while (buf->b_ml.ml_mfp == NULL) {
   1126      buf_T *nextbuf = (offset >= 0) ? buf->b_prev : buf->b_next;
   1127      if (nextbuf == NULL) {
   1128        break;
   1129      }
   1130      buf = nextbuf;
   1131    }
   1132  }
   1133  return buf->b_fnum;
   1134 }
   1135 
   1136 /// @return  the window number of "win" or,
   1137 ///          the number of windows if "win" is NULL
   1138 static int current_win_nr(const win_T *win)
   1139  FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
   1140 {
   1141  int nr = 0;
   1142 
   1143  FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
   1144    nr++;
   1145    if (wp == win) {
   1146      break;
   1147    }
   1148  }
   1149  return nr;
   1150 }
   1151 
   1152 static int current_tab_nr(tabpage_T *tab)
   1153 {
   1154  int nr = 0;
   1155 
   1156  FOR_ALL_TABS(tp) {
   1157    nr++;
   1158    if (tp == tab) {
   1159      break;
   1160    }
   1161  }
   1162  return nr;
   1163 }
   1164 
   1165 #define CURRENT_WIN_NR current_win_nr(curwin)
   1166 #define LAST_WIN_NR current_win_nr(NULL)
   1167 #define CURRENT_TAB_NR current_tab_nr(curtab)
   1168 #define LAST_TAB_NR current_tab_nr(NULL)
   1169 
   1170 /// Figure out the address type for ":wincmd".
   1171 static void get_wincmd_addr_type(const char *arg, exarg_T *eap)
   1172 {
   1173  switch (*arg) {
   1174  case 'S':
   1175  case Ctrl_S:
   1176  case 's':
   1177  case Ctrl_N:
   1178  case 'n':
   1179  case 'j':
   1180  case Ctrl_J:
   1181  case 'k':
   1182  case Ctrl_K:
   1183  case 'T':
   1184  case Ctrl_R:
   1185  case 'r':
   1186  case 'R':
   1187  case 'K':
   1188  case 'J':
   1189  case '+':
   1190  case '-':
   1191  case Ctrl__:
   1192  case '_':
   1193  case '|':
   1194  case ']':
   1195  case Ctrl_RSB:
   1196  case 'g':
   1197  case Ctrl_G:
   1198  case Ctrl_V:
   1199  case 'v':
   1200  case 'h':
   1201  case Ctrl_H:
   1202  case 'l':
   1203  case Ctrl_L:
   1204  case 'H':
   1205  case 'L':
   1206  case '>':
   1207  case '<':
   1208  case '}':
   1209  case 'f':
   1210  case 'F':
   1211  case Ctrl_F:
   1212  case 'i':
   1213  case Ctrl_I:
   1214  case 'd':
   1215  case Ctrl_D:
   1216    // window size or any count
   1217    eap->addr_type = ADDR_OTHER;
   1218    break;
   1219 
   1220  case Ctrl_HAT:
   1221  case '^':
   1222    // buffer number
   1223    eap->addr_type = ADDR_BUFFERS;
   1224    break;
   1225 
   1226  case Ctrl_Q:
   1227  case 'q':
   1228  case Ctrl_C:
   1229  case 'c':
   1230  case Ctrl_O:
   1231  case 'o':
   1232  case Ctrl_W:
   1233  case 'w':
   1234  case 'W':
   1235  case 'x':
   1236  case Ctrl_X:
   1237    // window number
   1238    eap->addr_type = ADDR_WINDOWS;
   1239    break;
   1240 
   1241  case Ctrl_Z:
   1242  case 'z':
   1243  case 'P':
   1244  case 't':
   1245  case Ctrl_T:
   1246  case 'b':
   1247  case Ctrl_B:
   1248  case 'p':
   1249  case Ctrl_P:
   1250  case '=':
   1251  case CAR:
   1252    // no count
   1253    eap->addr_type = ADDR_NONE;
   1254    break;
   1255  }
   1256 }
   1257 
   1258 /// Skip colons and trailing whitespace, returning a pointer to the first
   1259 /// non-colon, non-whitespace character.
   1260 //
   1261 /// @param skipleadingwhite Skip leading whitespace too
   1262 static char *skip_colon_white(const char *p, bool skipleadingwhite)
   1263 {
   1264  if (skipleadingwhite) {
   1265    p = skipwhite(p);
   1266  }
   1267 
   1268  while (*p == ':') {
   1269    p = skipwhite(p + 1);
   1270  }
   1271 
   1272  return (char *)p;
   1273 }
   1274 
   1275 /// Set the addr type for command
   1276 ///
   1277 /// @param p pointer to character after command name in cmdline
   1278 void set_cmd_addr_type(exarg_T *eap, char *p)
   1279 {
   1280  // ea.addr_type for user commands is set by find_ucmd
   1281  if (IS_USER_CMDIDX(eap->cmdidx)) {
   1282    return;
   1283  }
   1284  if (eap->cmdidx != CMD_SIZE) {
   1285    eap->addr_type = cmdnames[(int)eap->cmdidx].cmd_addr_type;
   1286  } else {
   1287    eap->addr_type = ADDR_LINES;
   1288  }
   1289  // :wincmd range depends on the argument
   1290  if (eap->cmdidx == CMD_wincmd && p != NULL) {
   1291    get_wincmd_addr_type(skipwhite(p), eap);
   1292  }
   1293  // :.cc in quickfix window uses line number
   1294  if ((eap->cmdidx == CMD_cc || eap->cmdidx == CMD_ll) && bt_quickfix(curbuf)) {
   1295    eap->addr_type = ADDR_OTHER;
   1296  }
   1297 }
   1298 
   1299 /// Get default range number for command based on its address type
   1300 linenr_T get_cmd_default_range(exarg_T *eap)
   1301 {
   1302  switch (eap->addr_type) {
   1303  case ADDR_LINES:
   1304  case ADDR_OTHER:
   1305    // Default is the cursor line number.  Avoid using an invalid
   1306    // line number though.
   1307    return MIN(curwin->w_cursor.lnum, curbuf->b_ml.ml_line_count);
   1308    break;
   1309  case ADDR_WINDOWS:
   1310    return CURRENT_WIN_NR;
   1311    break;
   1312  case ADDR_ARGUMENTS:
   1313    return MIN(curwin->w_arg_idx + 1, ARGCOUNT);
   1314    break;
   1315  case ADDR_LOADED_BUFFERS:
   1316  case ADDR_BUFFERS:
   1317    return curbuf->b_fnum;
   1318    break;
   1319  case ADDR_TABS:
   1320    return CURRENT_TAB_NR;
   1321    break;
   1322  case ADDR_TABS_RELATIVE:
   1323  case ADDR_UNSIGNED:
   1324    return 1;
   1325    break;
   1326  case ADDR_QUICKFIX:
   1327    return (linenr_T)qf_get_cur_idx(eap);
   1328    break;
   1329  case ADDR_QUICKFIX_VALID:
   1330    return qf_get_cur_valid_idx(eap);
   1331    break;
   1332  default:
   1333    return 0;
   1334    // Will give an error later if a range is found.
   1335    break;
   1336  }
   1337 }
   1338 
   1339 /// Set default command range for -range=% based on the addr type of the command
   1340 void set_cmd_dflall_range(exarg_T *eap)
   1341 {
   1342  buf_T *buf;
   1343 
   1344  eap->line1 = 1;
   1345  switch (eap->addr_type) {
   1346  case ADDR_LINES:
   1347  case ADDR_OTHER:
   1348    eap->line2 = curbuf->b_ml.ml_line_count;
   1349    break;
   1350  case ADDR_LOADED_BUFFERS:
   1351    buf = firstbuf;
   1352    while (buf->b_next != NULL && buf->b_ml.ml_mfp == NULL) {
   1353      buf = buf->b_next;
   1354    }
   1355    eap->line1 = buf->b_fnum;
   1356    buf = lastbuf;
   1357    while (buf->b_prev != NULL && buf->b_ml.ml_mfp == NULL) {
   1358      buf = buf->b_prev;
   1359    }
   1360    eap->line2 = buf->b_fnum;
   1361    break;
   1362  case ADDR_BUFFERS:
   1363    eap->line1 = firstbuf->b_fnum;
   1364    eap->line2 = lastbuf->b_fnum;
   1365    break;
   1366  case ADDR_WINDOWS:
   1367    eap->line2 = LAST_WIN_NR;
   1368    break;
   1369  case ADDR_TABS:
   1370    eap->line2 = LAST_TAB_NR;
   1371    break;
   1372  case ADDR_TABS_RELATIVE:
   1373    eap->line2 = 1;
   1374    break;
   1375  case ADDR_ARGUMENTS:
   1376    if (ARGCOUNT == 0) {
   1377      eap->line1 = eap->line2 = 0;
   1378    } else {
   1379      eap->line2 = ARGCOUNT;
   1380    }
   1381    break;
   1382  case ADDR_QUICKFIX_VALID:
   1383    eap->line2 = (linenr_T)qf_get_valid_size(eap);
   1384    if (eap->line2 == 0) {
   1385      eap->line2 = 1;
   1386    }
   1387    break;
   1388  case ADDR_NONE:
   1389  case ADDR_UNSIGNED:
   1390  case ADDR_QUICKFIX:
   1391    iemsg(_("INTERNAL: Cannot use EX_DFLALL "
   1392            "with ADDR_NONE, ADDR_UNSIGNED or ADDR_QUICKFIX"));
   1393    break;
   1394  }
   1395 }
   1396 
   1397 static void parse_register(exarg_T *eap)
   1398 {
   1399  // Accept numbered register only when no count allowed (:put)
   1400  if ((eap->argt & EX_REGSTR)
   1401      && *eap->arg != NUL
   1402      // Do not allow register = for user commands
   1403      && (!IS_USER_CMDIDX(eap->cmdidx) || *eap->arg != '=')
   1404      && !((eap->argt & EX_COUNT) && ascii_isdigit(*eap->arg))) {
   1405    if (valid_yank_reg(*eap->arg,
   1406                       (!IS_USER_CMDIDX(eap->cmdidx)
   1407                        && eap->cmdidx != CMD_put && eap->cmdidx != CMD_iput))) {
   1408      eap->regname = (uint8_t)(*eap->arg++);
   1409      // for '=' register: accept the rest of the line as an expression
   1410      if (eap->arg[-1] == '=' && eap->arg[0] != NUL) {
   1411        if (!eap->skip) {
   1412          set_expr_line(xstrdup(eap->arg));
   1413        }
   1414        eap->arg += strlen(eap->arg);
   1415      }
   1416      eap->arg = skipwhite(eap->arg);
   1417    }
   1418  }
   1419 }
   1420 
   1421 // Change line1 and line2 of Ex command to use count
   1422 void set_cmd_count(exarg_T *eap, linenr_T count, bool validate)
   1423 {
   1424  if (eap->addr_type != ADDR_LINES) {  // e.g. :buffer 2, :sleep 3
   1425    eap->line2 = count;
   1426    if (eap->addr_count == 0) {
   1427      eap->addr_count = 1;
   1428    }
   1429  } else {
   1430    eap->line1 = eap->line2;
   1431    if (eap->line2 >= INT32_MAX - (count - 1)) {
   1432      eap->line2 = INT32_MAX;
   1433    } else {
   1434      eap->line2 += count - 1;
   1435    }
   1436    eap->addr_count++;
   1437    // Be vi compatible: no error message for out of range.
   1438    if (validate && eap->line2 > curbuf->b_ml.ml_line_count) {
   1439      eap->line2 = curbuf->b_ml.ml_line_count;
   1440    }
   1441  }
   1442 }
   1443 
   1444 static int parse_count(exarg_T *eap, const char **errormsg, bool validate)
   1445 {
   1446  // Check for a count.  When accepting a EX_BUFNAME, don't use "123foo" as a
   1447  // count, it's a buffer name.
   1448  char *p;
   1449 
   1450  if ((eap->argt & EX_COUNT) && ascii_isdigit(*eap->arg)
   1451      && (!(eap->argt & EX_BUFNAME) || *(p = skipdigits(eap->arg + 1)) == NUL
   1452          || ascii_iswhite(*p))) {
   1453    linenr_T n = getdigits_int32(&eap->arg, false, INT32_MAX);
   1454    eap->arg = skipwhite(eap->arg);
   1455 
   1456    if (eap->args != NULL) {
   1457      assert(eap->argc > 0 && eap->arg >= eap->args[0]);
   1458      // If eap->arg is still pointing to the first argument, just make eap->args[0] point to the
   1459      // same location. This is needed for usecases like vim.cmd.sleep('10m'). If eap->arg is
   1460      // pointing outside the first argument, shift arguments by 1.
   1461      if (eap->arg < eap->args[0] + eap->arglens[0]) {
   1462        eap->arglens[0] -= (size_t)(eap->arg - eap->args[0]);
   1463        eap->args[0] = eap->arg;
   1464      } else {
   1465        shift_cmd_args(eap);
   1466      }
   1467    }
   1468 
   1469    if (n <= 0 && (eap->argt & EX_ZEROR) == 0) {
   1470      if (errormsg != NULL) {
   1471        *errormsg = _(e_zerocount);
   1472      }
   1473      return FAIL;
   1474    }
   1475    set_cmd_count(eap, n, validate);
   1476  }
   1477 
   1478  return OK;
   1479 }
   1480 
   1481 /// Check if command is not implemented
   1482 bool is_cmd_ni(cmdidx_T cmdidx)
   1483 {
   1484  return !IS_USER_CMDIDX(cmdidx) && (cmdnames[cmdidx].cmd_func == ex_ni
   1485                                     || cmdnames[cmdidx].cmd_func == ex_script_ni);
   1486 }
   1487 
   1488 // Find the command name after skipping range specifiers.
   1489 static char *find_excmd_after_range(exarg_T *eap)
   1490 {
   1491  // Save location after command modifiers.
   1492  char *cmd = eap->cmd;
   1493  eap->cmd = skip_range(eap->cmd, NULL);
   1494  char *p = find_ex_command(eap, NULL);
   1495  eap->cmd = cmd;  // Restore original position for address parsing
   1496  return p;
   1497 }
   1498 
   1499 // Set the forceit flag based on the presence of '!' after the command.
   1500 static bool parse_bang(const exarg_T *eap, char **p)
   1501 {
   1502  if (**p == '!'
   1503      && eap->cmdidx != CMD_substitute
   1504      && eap->cmdidx != CMD_smagic
   1505      && eap->cmdidx != CMD_snomagic) {
   1506    (*p)++;
   1507    return true;
   1508  }
   1509  return false;
   1510 }
   1511 
   1512 /// Check if command expects expression arguments that need special parsing
   1513 bool cmd_has_expr_args(cmdidx_T cmdidx)
   1514 {
   1515  return cmdidx == CMD_execute
   1516         || cmdidx == CMD_echo
   1517         || cmdidx == CMD_echon
   1518         || cmdidx == CMD_echomsg
   1519         || cmdidx == CMD_echoerr;
   1520 }
   1521 
   1522 /// Parse command line and return information about the first command.
   1523 /// If parsing is done successfully, need to free cmod_filter_pat and cmod_filter_regmatch.regprog
   1524 /// after calling, usually done using undo_cmdmod() or execute_cmd().
   1525 ///
   1526 /// @param cmdline Command line string
   1527 /// @param[out] eap Ex command arguments
   1528 /// @param[out] cmdinfo Command parse information
   1529 /// @param[out] errormsg Error message, if any
   1530 ///
   1531 /// @return Success or failure
   1532 bool parse_cmdline(char **cmdline, exarg_T *eap, CmdParseInfo *cmdinfo, const char **errormsg)
   1533 {
   1534  char *after_modifier = NULL;
   1535  bool retval = false;
   1536  // parsing the command modifiers may set ex_pressedreturn
   1537  const bool save_ex_pressedreturn = ex_pressedreturn;
   1538  // parsing the command range may require moving the cursor
   1539  const pos_T save_cursor = curwin->w_cursor;
   1540  // parsing the command range may set the last search pattern
   1541  save_last_search_pattern();
   1542 
   1543  // Initialize cmdinfo
   1544  CLEAR_POINTER(cmdinfo);
   1545 
   1546  // Initialize eap
   1547  *eap = (exarg_T){
   1548    .line1 = 1,
   1549    .line2 = 1,
   1550    .cmd = *cmdline,
   1551    .cmdlinep = cmdline,
   1552    .ea_getline = NULL,
   1553    .cookie = NULL,
   1554  };
   1555 
   1556  // Parse command modifiers
   1557  if (parse_command_modifiers(eap, errormsg, &cmdinfo->cmdmod, false) == FAIL) {
   1558    goto end;
   1559  }
   1560  after_modifier = eap->cmd;
   1561 
   1562  // We need the command name to know what kind of range it uses.
   1563  char *p = find_excmd_after_range(eap);
   1564  if (p == NULL) {
   1565    *errormsg = _(e_ambiguous_use_of_user_defined_command);
   1566    goto end;
   1567  }
   1568 
   1569  // Set command address type and parse command range
   1570  set_cmd_addr_type(eap, p);
   1571  if (parse_cmd_address(eap, errormsg, true) == FAIL) {
   1572    goto end;
   1573  }
   1574 
   1575  // Skip colon and whitespace
   1576  eap->cmd = skip_colon_white(eap->cmd, true);
   1577  // Fail if command is a comment or if command doesn't exist
   1578  if (*eap->cmd == NUL || *eap->cmd == '"') {
   1579    goto end;
   1580  }
   1581  // Fail if command is invalid
   1582  if (eap->cmdidx == CMD_SIZE) {
   1583    xstrlcpy(IObuff, _(e_not_an_editor_command), IOSIZE);
   1584    // If the modifier was parsed OK the error must be in the following command
   1585    char *cmdname = after_modifier ? after_modifier : *cmdline;
   1586    append_command(cmdname);
   1587    *errormsg = IObuff;
   1588    goto end;
   1589  }
   1590 
   1591  // Correctly set 'forceit' for commands
   1592  eap->forceit = parse_bang(eap, &p);
   1593 
   1594  // Parse arguments.
   1595  if (!IS_USER_CMDIDX(eap->cmdidx)) {
   1596    eap->argt = cmdnames[(int)eap->cmdidx].cmd_argt;
   1597  }
   1598  // Skip to start of argument.
   1599  // Don't do this for the ":!" command, because ":!! -l" needs the space.
   1600  eap->arg = eap->cmdidx == CMD_bang ? p : skipwhite(p);
   1601 
   1602  // Don't treat ":r! filter" like a bang
   1603  if (eap->cmdidx == CMD_read && eap->forceit) {
   1604    eap->forceit = false;  // :r! filter
   1605  }
   1606 
   1607  // Check for '|' to separate commands and '"' to start comments.
   1608  // Don't do this for ":read !cmd" and ":write !cmd".
   1609  if ((eap->argt & EX_TRLBAR)) {
   1610    separate_nextcmd(eap);
   1611  } else if (cmd_has_expr_args(eap->cmdidx)) {
   1612    // For commands without EX_TRLBAR, check for '|' separator
   1613    // by skipping over expressions (including string literals)
   1614    char *arg = eap->arg;
   1615    while (*arg != NUL && *arg != '|' && *arg != '\n') {
   1616      char *start = arg;
   1617      skip_expr(&arg, NULL);
   1618      // If skip_expr didn't advance, move forward to avoid infinite loop
   1619      if (arg == start) {
   1620        arg++;
   1621      }
   1622    }
   1623    if (*arg == '|' || *arg == '\n') {
   1624      eap->nextcmd = check_nextcmd(arg);
   1625      *arg = NUL;
   1626    }
   1627  }
   1628  // Fail if command doesn't support bang but is used with a bang
   1629  if (!(eap->argt & EX_BANG) && eap->forceit) {
   1630    *errormsg = _(e_nobang);
   1631    goto end;
   1632  }
   1633  // Fail if command doesn't support a range but it is given a range
   1634  if (!(eap->argt & EX_RANGE) && eap->addr_count > 0) {
   1635    *errormsg = _(e_norange);
   1636    goto end;
   1637  }
   1638  // Set default range for command if required
   1639  if ((eap->argt & EX_DFLALL) && eap->addr_count == 0) {
   1640    set_cmd_dflall_range(eap);
   1641  }
   1642 
   1643  // Parse register and count
   1644  parse_register(eap);
   1645  if (parse_count(eap, errormsg, false) == FAIL) {
   1646    goto end;
   1647  }
   1648 
   1649  // Remove leading whitespace and colon from next command
   1650  if (eap->nextcmd) {
   1651    eap->nextcmd = skip_colon_white(eap->nextcmd, true);
   1652  }
   1653 
   1654  // Set the "magic" values (characters that get treated specially)
   1655  if (eap->argt & EX_XFILE) {
   1656    cmdinfo->magic.file = true;
   1657  }
   1658  if (eap->argt & EX_TRLBAR) {
   1659    cmdinfo->magic.bar = true;
   1660  }
   1661 
   1662  retval = true;
   1663 end:
   1664  if (!retval) {
   1665    undo_cmdmod(&cmdinfo->cmdmod);
   1666  }
   1667  ex_pressedreturn = save_ex_pressedreturn;
   1668  curwin->w_cursor = save_cursor;
   1669  restore_last_search_pattern();
   1670  return retval;
   1671 }
   1672 
   1673 // Shift Ex-command arguments to the right.
   1674 static void shift_cmd_args(exarg_T *eap)
   1675 {
   1676  assert(eap->args != NULL && eap->argc > 0);
   1677 
   1678  char **oldargs = eap->args;
   1679  size_t *oldarglens = eap->arglens;
   1680 
   1681  eap->argc--;
   1682  eap->args = eap->argc > 0 ? xcalloc(eap->argc, sizeof(char *)) : NULL;
   1683  eap->arglens = eap->argc > 0 ? xcalloc(eap->argc, sizeof(size_t)) : NULL;
   1684 
   1685  for (size_t i = 0; i < eap->argc; i++) {
   1686    eap->args[i] = oldargs[i + 1];
   1687    eap->arglens[i] = oldarglens[i + 1];
   1688  }
   1689 
   1690  // If there are no arguments, make eap->arg point to the end of string.
   1691  eap->arg = (eap->argc > 0 ? eap->args[0] : (oldargs[0] + oldarglens[0]));
   1692 
   1693  xfree(oldargs);
   1694  xfree(oldarglens);
   1695 }
   1696 
   1697 static int execute_cmd0(int *retv, exarg_T *eap, const char **errormsg, bool preview)
   1698 {
   1699  // If filename expansion is enabled, expand filenames
   1700  if (eap->argt & EX_XFILE) {
   1701    if (expand_filename(eap, eap->cmdlinep, errormsg) == FAIL) {
   1702      return FAIL;
   1703    }
   1704  }
   1705 
   1706  // Accept buffer name.  Cannot be used at the same time with a buffer
   1707  // number.  Don't do this for a user command.
   1708  if ((eap->argt & EX_BUFNAME) && *eap->arg != NUL && eap->addr_count == 0
   1709      && !IS_USER_CMDIDX(eap->cmdidx)) {
   1710    if (eap->args == NULL) {
   1711      // If argument positions are not specified, search the argument for the buffer name.
   1712      // :bdelete, :bwipeout and :bunload take several arguments, separated by spaces:
   1713      // find next space (skipping over escaped characters).
   1714      // The others take one argument: ignore trailing spaces.
   1715      char *p;
   1716 
   1717      if (eap->cmdidx == CMD_bdelete || eap->cmdidx == CMD_bwipeout
   1718          || eap->cmdidx == CMD_bunload) {
   1719        p = skiptowhite_esc(eap->arg);
   1720      } else {
   1721        p = eap->arg + strlen(eap->arg);
   1722        while (p > eap->arg && ascii_iswhite(p[-1])) {
   1723          p--;
   1724        }
   1725      }
   1726      eap->line2 = buflist_findpat(eap->arg, p, (eap->argt & EX_BUFUNL) != 0,
   1727                                   false, false);
   1728      eap->addr_count = 1;
   1729      eap->arg = skipwhite(p);
   1730    } else {
   1731      // If argument positions are specified, just use the first argument
   1732      eap->line2 = buflist_findpat(eap->args[0],
   1733                                   eap->args[0] + eap->arglens[0],
   1734                                   (eap->argt & EX_BUFUNL) != 0, false, false);
   1735      eap->addr_count = 1;
   1736      shift_cmd_args(eap);
   1737    }
   1738    if (eap->line2 < 0) {  // failed
   1739      return FAIL;
   1740    }
   1741  }
   1742 
   1743  // The :try command saves the emsg_silent flag, reset it here when
   1744  // ":silent! try" was used, it should only apply to :try itself.
   1745  if (eap->cmdidx == CMD_try && cmdmod.cmod_did_esilent > 0) {
   1746    emsg_silent -= cmdmod.cmod_did_esilent;
   1747    emsg_silent = MAX(emsg_silent, 0);
   1748    cmdmod.cmod_did_esilent = 0;
   1749  }
   1750 
   1751  // Execute the command
   1752  if (IS_USER_CMDIDX(eap->cmdidx)) {
   1753    // Execute a user-defined command.
   1754    *retv = do_ucmd(eap, preview);
   1755  } else {
   1756    // Call the function to execute the builtin command or the preview callback.
   1757    eap->errmsg = NULL;
   1758    if (preview) {
   1759      *retv = (cmdnames[eap->cmdidx].cmd_preview_func)(eap, cmdpreview_get_ns(),
   1760                                                       cmdpreview_get_bufnr());
   1761    } else {
   1762      (cmdnames[eap->cmdidx].cmd_func)(eap);
   1763    }
   1764    if (eap->errmsg != NULL) {
   1765      *errormsg = eap->errmsg;
   1766    }
   1767  }
   1768 
   1769  return OK;
   1770 }
   1771 
   1772 /// Execute an Ex command using parsed command line information.
   1773 /// Does not do any validation of the Ex command arguments.
   1774 ///
   1775 /// @param eap Ex-command arguments
   1776 /// @param cmdinfo Command parse information
   1777 /// @param preview Execute command preview callback instead of actual command
   1778 int execute_cmd(exarg_T *eap, CmdParseInfo *cmdinfo, bool preview)
   1779 {
   1780  int retv = 0;
   1781  if (do_cmdline_start() == FAIL) {
   1782    emsg(_(e_command_too_recursive));
   1783    return retv;
   1784  }
   1785 
   1786  const char *errormsg = NULL;
   1787 
   1788  cmdmod_T save_cmdmod = cmdmod;
   1789  cmdmod = cmdinfo->cmdmod;
   1790 
   1791  // Apply command modifiers
   1792  apply_cmdmod(&cmdmod);
   1793 
   1794  if (!MODIFIABLE(curbuf) && (eap->argt & EX_MODIFY)
   1795      // allow :put in terminals
   1796      && !(curbuf->terminal && (eap->cmdidx == CMD_put || eap->cmdidx == CMD_iput))) {
   1797    errormsg = _(e_modifiable);
   1798    goto end;
   1799  }
   1800  if (!IS_USER_CMDIDX(eap->cmdidx)) {
   1801    if (cmdwin_type != 0 && !(eap->argt & EX_CMDWIN)) {
   1802      // Command not allowed in the command line window
   1803      errormsg = _(e_cmdwin);
   1804      goto end;
   1805    }
   1806    if (text_locked() && !(eap->argt & EX_LOCK_OK)) {
   1807      // Command not allowed when text is locked
   1808      errormsg = _(get_text_locked_msg());
   1809      goto end;
   1810    }
   1811  }
   1812  // Disallow editing another buffer when "curbuf->b_ro_locked" is set.
   1813  // Do allow ":checktime" (it is postponed).
   1814  // Do allow ":edit" (check for an argument later).
   1815  // Do allow ":file" with no arguments
   1816  if (!(eap->argt & EX_CMDWIN)
   1817      && eap->cmdidx != CMD_checktime
   1818      && eap->cmdidx != CMD_edit
   1819      && !(eap->cmdidx == CMD_file && *eap->arg == NUL)
   1820      && !IS_USER_CMDIDX(eap->cmdidx)
   1821      && curbuf_locked()) {
   1822    goto end;
   1823  }
   1824 
   1825  correct_range(eap);
   1826 
   1827  if (((eap->argt & EX_WHOLEFOLD) || eap->addr_count >= 2) && !global_busy
   1828      && eap->addr_type == ADDR_LINES) {
   1829    // Put the first line at the start of a closed fold, put the last line
   1830    // at the end of a closed fold.
   1831    hasFolding(curwin, eap->line1, &eap->line1, NULL);
   1832    hasFolding(curwin, eap->line2, NULL, &eap->line2);
   1833  }
   1834 
   1835  // Use first argument as count when possible
   1836  if (parse_count(eap, &errormsg, true) == FAIL) {
   1837    goto end;
   1838  }
   1839 
   1840  cstack_T cstack = { .cs_idx = -1 };
   1841  eap->cstack = &cstack;
   1842 
   1843  // Execute the command
   1844  execute_cmd0(&retv, eap, &errormsg, preview);
   1845 
   1846 end:
   1847  if (errormsg != NULL && *errormsg != NUL) {
   1848    emsg(errormsg);
   1849  }
   1850 
   1851  // Undo command modifiers
   1852  undo_cmdmod(&cmdmod);
   1853  cmdmod = save_cmdmod;
   1854 
   1855  do_cmdline_end();
   1856  return retv;
   1857 }
   1858 
   1859 static void profile_cmd(const exarg_T *eap, cstack_T *cstack, LineGetter fgetline, void *cookie)
   1860 {
   1861  // Count this line for profiling if skip is true.
   1862  if (do_profiling == PROF_YES
   1863      && (!eap->skip || cstack->cs_idx == 0
   1864          || (cstack->cs_idx > 0
   1865              && (cstack->cs_flags[cstack->cs_idx - 1] & CSF_ACTIVE)))) {
   1866    bool skip = did_emsg || got_int || did_throw;
   1867 
   1868    if (eap->cmdidx == CMD_catch) {
   1869      skip = !skip && !(cstack->cs_idx >= 0
   1870                        && (cstack->cs_flags[cstack->cs_idx] & CSF_THROWN)
   1871                        && !(cstack->cs_flags[cstack->cs_idx] & CSF_CAUGHT));
   1872    } else if (eap->cmdidx == CMD_else || eap->cmdidx == CMD_elseif) {
   1873      skip = skip || !(cstack->cs_idx >= 0
   1874                       && !(cstack->cs_flags[cstack->cs_idx]
   1875                            & (CSF_ACTIVE | CSF_TRUE)));
   1876    } else if (eap->cmdidx == CMD_finally) {
   1877      skip = false;
   1878    } else if (eap->cmdidx != CMD_endif
   1879               && eap->cmdidx != CMD_endfor
   1880               && eap->cmdidx != CMD_endtry
   1881               && eap->cmdidx != CMD_endwhile) {
   1882      skip = eap->skip;
   1883    }
   1884 
   1885    if (!skip) {
   1886      if (getline_equal(fgetline, cookie, get_func_line)) {
   1887        func_line_exec(getline_cookie(fgetline, cookie));
   1888      } else if (getline_equal(fgetline, cookie, getsourceline)) {
   1889        script_line_exec();
   1890      }
   1891    }
   1892  }
   1893 }
   1894 
   1895 static bool skip_cmd(const exarg_T *eap)
   1896 {
   1897  // Skip the command when it's not going to be executed.
   1898  // The commands like :if, :endif, etc. always need to be executed.
   1899  // Also make an exception for commands that handle a trailing command
   1900  // themselves.
   1901  if (eap->skip) {
   1902    switch (eap->cmdidx) {
   1903    // commands that need evaluation
   1904    case CMD_while:
   1905    case CMD_endwhile:
   1906    case CMD_for:
   1907    case CMD_endfor:
   1908    case CMD_if:
   1909    case CMD_elseif:
   1910    case CMD_else:
   1911    case CMD_endif:
   1912    case CMD_try:
   1913    case CMD_catch:
   1914    case CMD_finally:
   1915    case CMD_endtry:
   1916    case CMD_function:
   1917      break;
   1918 
   1919    // Commands that handle '|' themselves.  Check: A command should
   1920    // either have the EX_TRLBAR flag, appear in this list or appear in
   1921    // the list at ":help :bar".
   1922    case CMD_aboveleft:
   1923    case CMD_and:
   1924    case CMD_belowright:
   1925    case CMD_botright:
   1926    case CMD_browse:
   1927    case CMD_call:
   1928    case CMD_confirm:
   1929    case CMD_const:
   1930    case CMD_delfunction:
   1931    case CMD_djump:
   1932    case CMD_dlist:
   1933    case CMD_dsearch:
   1934    case CMD_dsplit:
   1935    case CMD_echo:
   1936    case CMD_echoerr:
   1937    case CMD_echomsg:
   1938    case CMD_echon:
   1939    case CMD_eval:
   1940    case CMD_execute:
   1941    case CMD_filter:
   1942    case CMD_help:
   1943    case CMD_hide:
   1944    case CMD_horizontal:
   1945    case CMD_ijump:
   1946    case CMD_ilist:
   1947    case CMD_isearch:
   1948    case CMD_isplit:
   1949    case CMD_keepalt:
   1950    case CMD_keepjumps:
   1951    case CMD_keepmarks:
   1952    case CMD_keeppatterns:
   1953    case CMD_leftabove:
   1954    case CMD_let:
   1955    case CMD_lockmarks:
   1956    case CMD_lockvar:
   1957    case CMD_lua:
   1958    case CMD_match:
   1959    case CMD_mzscheme:
   1960    case CMD_noautocmd:
   1961    case CMD_noswapfile:
   1962    case CMD_perl:
   1963    case CMD_psearch:
   1964    case CMD_python:
   1965    case CMD_py3:
   1966    case CMD_python3:
   1967    case CMD_pythonx:
   1968    case CMD_pyx:
   1969    case CMD_return:
   1970    case CMD_rightbelow:
   1971    case CMD_ruby:
   1972    case CMD_silent:
   1973    case CMD_smagic:
   1974    case CMD_snomagic:
   1975    case CMD_substitute:
   1976    case CMD_syntax:
   1977    case CMD_tab:
   1978    case CMD_tcl:
   1979    case CMD_throw:
   1980    case CMD_tilde:
   1981    case CMD_topleft:
   1982    case CMD_unlet:
   1983    case CMD_unlockvar:
   1984    case CMD_verbose:
   1985    case CMD_vertical:
   1986    case CMD_wincmd:
   1987      break;
   1988 
   1989    default:
   1990      return true;
   1991    }
   1992  }
   1993  return false;
   1994 }
   1995 
   1996 /// Execute one Ex command.
   1997 ///
   1998 /// If "flags" has DOCMD_VERBOSE, the command will be included in the error
   1999 /// message.
   2000 ///
   2001 /// 1. skip comment lines and leading space
   2002 /// 2. handle command modifiers
   2003 /// 3. skip over the range to find the command
   2004 /// 4. parse the range
   2005 /// 5. parse the command
   2006 /// 6. parse arguments
   2007 /// 7. switch on command name
   2008 ///
   2009 /// Note: "fgetline" can be NULL.
   2010 ///
   2011 /// This function may be called recursively!
   2012 ///
   2013 /// @param cookie  argument for fgetline()
   2014 static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter fgetline,
   2015                        void *cookie)
   2016 {
   2017  const char *errormsg = NULL;  // error message
   2018  const int save_reg_executing = reg_executing;
   2019  const bool save_pending_end_reg_executing = pending_end_reg_executing;
   2020 
   2021  exarg_T ea = {
   2022    .line1 = 1,
   2023    .line2 = 1,
   2024  };
   2025  ex_nesting_level++;
   2026 
   2027  // When the last file has not been edited :q has to be typed twice.
   2028  if (quitmore
   2029      // avoid that a function call in 'statusline' does this
   2030      && !getline_equal(fgetline, cookie, get_func_line)
   2031      // avoid that an autocommand, e.g. QuitPre, does this
   2032      && !getline_equal(fgetline, cookie, getnextac)) {
   2033    quitmore--;
   2034  }
   2035 
   2036  // Reset browse, confirm, etc..  They are restored when returning, for
   2037  // recursive calls.
   2038  cmdmod_T save_cmdmod = cmdmod;
   2039 
   2040  // "#!anything" is handled like a comment.
   2041  if ((*cmdlinep)[0] == '#' && (*cmdlinep)[1] == '!') {
   2042    goto doend;
   2043  }
   2044 
   2045  // 1. Skip comment lines and leading white space and colons.
   2046  // 2. Handle command modifiers.
   2047 
   2048  // The "ea" structure holds the arguments that can be used.
   2049  ea.cmd = *cmdlinep;
   2050  ea.cmdlinep = cmdlinep;
   2051  ea.ea_getline = fgetline;
   2052  ea.cookie = cookie;
   2053  ea.cstack = cstack;
   2054 
   2055  if (parse_command_modifiers(&ea, &errormsg, &cmdmod, false) == FAIL) {
   2056    goto doend;
   2057  }
   2058  apply_cmdmod(&cmdmod);
   2059 
   2060  char *after_modifier = ea.cmd;
   2061 
   2062  ea.skip = (did_emsg
   2063             || got_int
   2064             || did_throw
   2065             || (cstack->cs_idx >= 0
   2066                 && !(cstack->cs_flags[cstack->cs_idx] & CSF_ACTIVE)));
   2067 
   2068  // 3. Skip over the range to find the command. Let "p" point to after it.
   2069  //
   2070  // We need the command to know what kind of range it uses.
   2071  char *p = find_excmd_after_range(&ea);
   2072  profile_cmd(&ea, cstack, fgetline, cookie);
   2073 
   2074  if (!exiting) {
   2075    // May go to debug mode.  If this happens and the ">quit" debug command is
   2076    // used, throw an interrupt exception and skip the next command.
   2077    dbg_check_breakpoint(&ea);
   2078  }
   2079  if (!ea.skip && got_int) {
   2080    ea.skip = true;
   2081    do_intthrow(cstack);
   2082  }
   2083 
   2084  // 4. Parse a range specifier of the form: addr [,addr] [;addr] ..
   2085  //
   2086  // where 'addr' is:
   2087  //
   2088  // %          (entire file)
   2089  // $  [+-NUM]
   2090  // 'x [+-NUM] (where x denotes a currently defined mark)
   2091  // .  [+-NUM]
   2092  // [+-NUM]..
   2093  // NUM
   2094  //
   2095  // The ea.cmd pointer is updated to point to the first character following the
   2096  // range spec. If an initial address is found, but no second, the upper bound
   2097  // is equal to the lower.
   2098  set_cmd_addr_type(&ea, p);
   2099 
   2100  if (parse_cmd_address(&ea, &errormsg, false) == FAIL) {
   2101    goto doend;
   2102  }
   2103 
   2104  // 5. Parse the command.
   2105 
   2106  // Skip ':' and any white space
   2107  ea.cmd = skip_colon_white(ea.cmd, true);
   2108 
   2109  // If we got a line, but no command, then go to the line.
   2110  // If we find a '|' or '\n' we set ea.nextcmd.
   2111  if (*ea.cmd == NUL || *ea.cmd == '"'
   2112      || (ea.nextcmd = check_nextcmd(ea.cmd)) != NULL) {
   2113    // strange vi behaviour:
   2114    // ":3"     jumps to line 3
   2115    // ":3|..." prints line 3
   2116    // ":|"     prints current line
   2117    if (ea.skip) {  // skip this if inside :if
   2118      goto doend;
   2119    }
   2120    assert(errormsg == NULL);
   2121    errormsg = ex_range_without_command(&ea);
   2122    goto doend;
   2123  }
   2124 
   2125  // If this looks like an undefined user command and there are CmdUndefined
   2126  // autocommands defined, trigger the matching autocommands.
   2127  if (p != NULL && ea.cmdidx == CMD_SIZE && !ea.skip
   2128      && ASCII_ISUPPER(*ea.cmd)
   2129      && has_event(EVENT_CMDUNDEFINED)) {
   2130    char *cmdname = ea.cmd;
   2131    while (ASCII_ISALNUM(*cmdname)) {
   2132      cmdname++;
   2133    }
   2134    cmdname = xmemdupz(ea.cmd, (size_t)(cmdname - ea.cmd));
   2135    int ret = apply_autocmds(EVENT_CMDUNDEFINED, cmdname, cmdname, true, NULL);
   2136    xfree(cmdname);
   2137    // If the autocommands did something and didn't cause an error, try
   2138    // finding the command again.
   2139    p = (ret && !aborting()) ? find_ex_command(&ea, NULL) : ea.cmd;
   2140  }
   2141 
   2142  if (p == NULL) {
   2143    if (!ea.skip) {
   2144      errormsg = _(e_ambiguous_use_of_user_defined_command);
   2145    }
   2146    goto doend;
   2147  }
   2148 
   2149  // Check for wrong commands.
   2150  if (ea.cmdidx == CMD_SIZE) {
   2151    if (!ea.skip) {
   2152      xstrlcpy(IObuff, _(e_not_an_editor_command), IOSIZE);
   2153      // If the modifier was parsed OK the error must be in the following
   2154      // command
   2155      char *cmdname = after_modifier ? after_modifier : *cmdlinep;
   2156      if (!(flags & DOCMD_VERBOSE)) {
   2157        append_command(cmdname);
   2158      }
   2159      errormsg = IObuff;
   2160      did_emsg_syntax = true;
   2161      verify_command(cmdname);
   2162    }
   2163    goto doend;
   2164  }
   2165 
   2166  // set when Not Implemented
   2167  const int ni = is_cmd_ni(ea.cmdidx);
   2168 
   2169  // Determine if command has forceit flag ('!')
   2170  ea.forceit = parse_bang(&ea, &p);
   2171 
   2172  // 6. Parse arguments.  Then check for errors.
   2173  if (!IS_USER_CMDIDX(ea.cmdidx)) {
   2174    ea.argt = cmdnames[(int)ea.cmdidx].cmd_argt;
   2175  }
   2176 
   2177  if (!ea.skip) {
   2178    if (sandbox != 0 && !(ea.argt & EX_SBOXOK)) {
   2179      // Command not allowed in sandbox.
   2180      errormsg = _(e_sandbox);
   2181      goto doend;
   2182    }
   2183    if (!MODIFIABLE(curbuf) && (ea.argt & EX_MODIFY)
   2184        // allow :put in terminals
   2185        && !(curbuf->terminal && (ea.cmdidx == CMD_put || ea.cmdidx == CMD_iput))) {
   2186      // Command not allowed in non-'modifiable' buffer
   2187      errormsg = _(e_modifiable);
   2188      goto doend;
   2189    }
   2190 
   2191    if (!IS_USER_CMDIDX(ea.cmdidx)) {
   2192      if (cmdwin_type != 0 && !(ea.argt & EX_CMDWIN)) {
   2193        // Command not allowed in the command line window
   2194        errormsg = _(e_cmdwin);
   2195        goto doend;
   2196      }
   2197      if (text_locked() && !(ea.argt & EX_LOCK_OK)) {
   2198        // Command not allowed when text is locked
   2199        errormsg = _(get_text_locked_msg());
   2200        goto doend;
   2201      }
   2202    }
   2203 
   2204    // Disallow editing another buffer when "curbuf->b_ro_locked" is set.
   2205    // Do allow ":checktime" (it is postponed).
   2206    // Do allow ":edit" (check for an argument later).
   2207    // Do allow ":file" with no arguments (check for an argument later).
   2208    if (!(ea.argt & EX_CMDWIN)
   2209        && ea.cmdidx != CMD_checktime
   2210        && ea.cmdidx != CMD_edit
   2211        && ea.cmdidx != CMD_file
   2212        && !IS_USER_CMDIDX(ea.cmdidx)
   2213        && curbuf_locked()) {
   2214      goto doend;
   2215    }
   2216 
   2217    if (!ni && !(ea.argt & EX_RANGE) && ea.addr_count > 0) {
   2218      // no range allowed
   2219      errormsg = _(e_norange);
   2220      goto doend;
   2221    }
   2222  }
   2223 
   2224  if (!ni && !(ea.argt & EX_BANG) && ea.forceit) {  // no <!> allowed
   2225    errormsg = _(e_nobang);
   2226    goto doend;
   2227  }
   2228 
   2229  // Don't complain about the range if it is not used
   2230  // (could happen if line_count is accidentally set to 0).
   2231  if (!ea.skip && !ni && (ea.argt & EX_RANGE)) {
   2232    // If the range is backwards, ask for confirmation and, if given, swap
   2233    // ea.line1 & ea.line2 so it's forwards again.
   2234    // When global command is busy, don't ask, will fail below.
   2235    if (!global_busy && ea.line1 > ea.line2) {
   2236      if (msg_silent == 0) {
   2237        if ((flags & DOCMD_VERBOSE) || exmode_active) {
   2238          errormsg = _("E493: Backwards range given");
   2239          goto doend;
   2240        }
   2241        if (ask_yesno(_("Backwards range given, OK to swap")) != 'y') {
   2242          goto doend;
   2243        }
   2244      }
   2245      linenr_T lnum = ea.line1;
   2246      ea.line1 = ea.line2;
   2247      ea.line2 = lnum;
   2248    }
   2249    if ((errormsg = invalid_range(&ea)) != NULL) {
   2250      goto doend;
   2251    }
   2252  }
   2253 
   2254  if ((ea.addr_type == ADDR_OTHER) && ea.addr_count == 0) {
   2255    // default is 1, not cursor
   2256    ea.line2 = 1;
   2257  }
   2258 
   2259  correct_range(&ea);
   2260 
   2261  if (((ea.argt & EX_WHOLEFOLD) || ea.addr_count >= 2) && !global_busy
   2262      && ea.addr_type == ADDR_LINES) {
   2263    // Put the first line at the start of a closed fold, put the last line
   2264    // at the end of a closed fold.
   2265    hasFolding(curwin, ea.line1, &ea.line1, NULL);
   2266    hasFolding(curwin, ea.line2, NULL, &ea.line2);
   2267  }
   2268 
   2269  // For the ":make" and ":grep" commands we insert the 'makeprg'/'grepprg'
   2270  // option here, so things like % get expanded.
   2271  p = replace_makeprg(&ea, p, cmdlinep);
   2272  if (p == NULL) {
   2273    goto doend;
   2274  }
   2275 
   2276  // Skip to start of argument.
   2277  // Don't do this for the ":!" command, because ":!! -l" needs the space.
   2278  ea.arg = ea.cmdidx == CMD_bang ? p : skipwhite(p);
   2279 
   2280  // ":file" cannot be run with an argument when "curbuf->b_ro_locked" is set
   2281  if (ea.cmdidx == CMD_file && *ea.arg != NUL && curbuf_locked()) {
   2282    goto doend;
   2283  }
   2284 
   2285  // Check for "++opt=val" argument.
   2286  // Must be first, allow ":w ++enc=utf8 !cmd"
   2287  if (ea.argt & EX_ARGOPT) {
   2288    while (ea.arg[0] == '+' && ea.arg[1] == '+') {
   2289      if (getargopt(&ea) == FAIL && !ni) {
   2290        errormsg = _(e_invarg);
   2291        goto doend;
   2292      }
   2293    }
   2294  }
   2295 
   2296  if (ea.cmdidx == CMD_write || ea.cmdidx == CMD_update) {
   2297    if (*ea.arg == '>') {                       // append
   2298      if (*++ea.arg != '>') {                   // typed wrong
   2299        errormsg = _("E494: Use w or w>>");
   2300        goto doend;
   2301      }
   2302      ea.arg = skipwhite(ea.arg + 1);
   2303      ea.append = true;
   2304    } else if (*ea.arg == '!' && ea.cmdidx == CMD_write) {  // :w !filter
   2305      ea.arg++;
   2306      ea.usefilter = true;
   2307    }
   2308  } else if (ea.cmdidx == CMD_read) {
   2309    if (ea.forceit) {
   2310      ea.usefilter = true;                      // :r! filter if ea.forceit
   2311      ea.forceit = false;
   2312    } else if (*ea.arg == '!') {              // :r !filter
   2313      ea.arg++;
   2314      ea.usefilter = true;
   2315    }
   2316  } else if (ea.cmdidx == CMD_lshift || ea.cmdidx == CMD_rshift) {
   2317    ea.amount = 1;
   2318    while (*ea.arg == *ea.cmd) {                // count number of '>' or '<'
   2319      ea.arg++;
   2320      ea.amount++;
   2321    }
   2322    ea.arg = skipwhite(ea.arg);
   2323  }
   2324 
   2325  // Check for "+command" argument, before checking for next command.
   2326  // Don't do this for ":read !cmd" and ":write !cmd".
   2327  if ((ea.argt & EX_CMDARG) && !ea.usefilter) {
   2328    ea.do_ecmd_cmd = getargcmd(&ea.arg);
   2329  }
   2330 
   2331  // Check for '|' to separate commands and '"' to start comments.
   2332  // Don't do this for ":read !cmd" and ":write !cmd".
   2333  if ((ea.argt & EX_TRLBAR) && !ea.usefilter) {
   2334    separate_nextcmd(&ea);
   2335  } else if (ea.cmdidx == CMD_bang
   2336             || ea.cmdidx == CMD_terminal
   2337             || ea.cmdidx == CMD_global
   2338             || ea.cmdidx == CMD_vglobal
   2339             || ea.usefilter) {
   2340    // Check for <newline> to end a shell command.
   2341    // Also do this for ":read !cmd", ":write !cmd" and ":global".
   2342    // Any others?
   2343    for (char *s = ea.arg; *s; s++) {
   2344      // Remove one backslash before a newline.
   2345      if (*s == '\\' && s[1] == '\n') {
   2346        STRMOVE(s, s + 1);
   2347      } else if (*s == '\n') {
   2348        ea.nextcmd = s + 1;
   2349        *s = NUL;
   2350        break;
   2351      }
   2352    }
   2353  }
   2354 
   2355  if ((ea.argt & EX_DFLALL) && ea.addr_count == 0) {
   2356    set_cmd_dflall_range(&ea);
   2357  }
   2358 
   2359  // Parse register and count
   2360  parse_register(&ea);
   2361  if (parse_count(&ea, &errormsg, true) == FAIL) {
   2362    goto doend;
   2363  }
   2364 
   2365  // Check for flags: 'l', 'p' and '#'.
   2366  if (ea.argt & EX_FLAGS) {
   2367    get_flags(&ea);
   2368  }
   2369  if (!ni && !(ea.argt & EX_EXTRA) && *ea.arg != NUL
   2370      && *ea.arg != '"' && (*ea.arg != '|' || (ea.argt & EX_TRLBAR) == 0)) {
   2371    // no arguments allowed but there is something
   2372    errormsg = ex_errmsg(e_trailing_arg, ea.arg);
   2373    goto doend;
   2374  }
   2375 
   2376  if (!ni && (ea.argt & EX_NEEDARG) && *ea.arg == NUL) {
   2377    errormsg = _(e_argreq);
   2378    goto doend;
   2379  }
   2380 
   2381  if (skip_cmd(&ea)) {
   2382    goto doend;
   2383  }
   2384 
   2385  // 7. Execute the command.
   2386  int retv = 0;
   2387  if (execute_cmd0(&retv, &ea, &errormsg, false) == FAIL) {
   2388    goto doend;
   2389  }
   2390 
   2391  // If the command just executed called do_cmdline(), any throw or ":return"
   2392  // or ":finish" encountered there must also check the cstack of the still
   2393  // active do_cmdline() that called this do_one_cmd().  Rethrow an uncaught
   2394  // exception, or reanimate a returned function or finished script file and
   2395  // return or finish it again.
   2396  if (need_rethrow) {
   2397    do_throw(cstack);
   2398  } else if (check_cstack) {
   2399    if (source_finished(fgetline, cookie)) {
   2400      do_finish(&ea, true);
   2401    } else if (getline_equal(fgetline, cookie, get_func_line)
   2402               && current_func_returned()) {
   2403      do_return(&ea, true, false, NULL);
   2404    }
   2405  }
   2406  need_rethrow = check_cstack = false;
   2407 
   2408 doend:
   2409  // can happen with zero line number
   2410  if (curwin->w_cursor.lnum == 0) {
   2411    curwin->w_cursor.lnum = 1;
   2412    curwin->w_cursor.col = 0;
   2413  }
   2414 
   2415  if (errormsg != NULL && *errormsg != NUL && !did_emsg) {
   2416    if (flags & DOCMD_VERBOSE) {
   2417      if (errormsg != IObuff) {
   2418        xstrlcpy(IObuff, errormsg, IOSIZE);
   2419        errormsg = IObuff;
   2420      }
   2421      append_command(*ea.cmdlinep);
   2422    }
   2423    emsg(errormsg);
   2424  }
   2425  do_errthrow(cstack,
   2426              (ea.cmdidx != CMD_SIZE
   2427               && !IS_USER_CMDIDX(ea.cmdidx)) ? cmdnames[(int)ea.cmdidx].cmd_name : NULL);
   2428 
   2429  undo_cmdmod(&cmdmod);
   2430  cmdmod = save_cmdmod;
   2431  reg_executing = save_reg_executing;
   2432  pending_end_reg_executing = save_pending_end_reg_executing;
   2433 
   2434  if (ea.nextcmd && *ea.nextcmd == NUL) {       // not really a next command
   2435    ea.nextcmd = NULL;
   2436  }
   2437 
   2438  ex_nesting_level--;
   2439  xfree(ea.cmdline_tofree);
   2440 
   2441  return ea.nextcmd;
   2442 }
   2443 
   2444 static char ex_error_buf[MSG_BUF_LEN];
   2445 
   2446 /// @return an error message with argument included.
   2447 /// Uses a static buffer, only the last error will be kept.
   2448 /// "msg" will be translated, caller should use N_().
   2449 char *ex_errmsg(const char *const msg, const char *const arg)
   2450  FUNC_ATTR_NONNULL_ALL
   2451 {
   2452  vim_snprintf(ex_error_buf, MSG_BUF_LEN, _(msg), arg);
   2453  return ex_error_buf;
   2454 }
   2455 
   2456 /// The "+" string used in place of an empty command in Ex mode.
   2457 /// This string is used in pointer comparison.
   2458 static char exmode_plus[] = "+";
   2459 
   2460 /// Handle a range without a command.
   2461 /// Returns an error message on failure.
   2462 static char *ex_range_without_command(exarg_T *eap)
   2463 {
   2464  char *errormsg = NULL;
   2465 
   2466  if (*eap->cmd == '|' || (exmode_active && eap->cmd != exmode_plus + 1)) {
   2467    eap->cmdidx = CMD_print;
   2468    eap->argt = EX_RANGE | EX_COUNT | EX_TRLBAR;
   2469    if ((errormsg = invalid_range(eap)) == NULL) {
   2470      correct_range(eap);
   2471      ex_print(eap);
   2472    }
   2473  } else if (eap->addr_count != 0) {
   2474    eap->line2 = MIN(eap->line2, curbuf->b_ml.ml_line_count);
   2475 
   2476    if (eap->line2 < 0) {
   2477      errormsg = _(e_invrange);
   2478    } else {
   2479      if (eap->line2 == 0) {
   2480        curwin->w_cursor.lnum = 1;
   2481      } else {
   2482        curwin->w_cursor.lnum = eap->line2;
   2483      }
   2484      beginline(BL_SOL | BL_FIX);
   2485    }
   2486  }
   2487  return errormsg;
   2488 }
   2489 
   2490 /// Parse and skip over command modifiers:
   2491 /// - update eap->cmd
   2492 /// - store flags in "cmod".
   2493 /// - Set ex_pressedreturn for an empty command line.
   2494 ///
   2495 /// @param skip_only      if false, undo_cmdmod() must be called later to free
   2496 ///                       any cmod_filter_pat and cmod_filter_regmatch.regprog,
   2497 ///                       and ex_pressedreturn may be set.
   2498 /// @param[out] errormsg  potential error message.
   2499 ///
   2500 /// Call apply_cmdmod() to get the side effects of the modifiers:
   2501 /// - Increment "sandbox" for ":sandbox"
   2502 /// - set p_verbose for ":verbose"
   2503 /// - set msg_silent for ":silent"
   2504 /// - set 'eventignore' to "all" for ":noautocmd"
   2505 ///
   2506 /// @return  FAIL when the command is not to be executed.
   2507 int parse_command_modifiers(exarg_T *eap, const char **errormsg, cmdmod_T *cmod, bool skip_only)
   2508 {
   2509  char *orig_cmd = eap->cmd;
   2510  char *cmd_start = NULL;
   2511  bool use_plus_cmd = false;
   2512  bool has_visual_range = false;
   2513  CLEAR_POINTER(cmod);
   2514 
   2515  if (strncmp(eap->cmd, "'<,'>", 5) == 0) {
   2516    // The automatically inserted Visual area range is skipped, so that
   2517    // typing ":cmdmod cmd" in Visual mode works without having to move the
   2518    // range to after the modifiers. The command will be "'<,'>cmdmod cmd",
   2519    // parse "cmdmod cmd" and then put back "'<,'>" before "cmd" below.
   2520    eap->cmd += 5;
   2521    cmd_start = eap->cmd;
   2522    has_visual_range = true;
   2523  }
   2524 
   2525  // Repeat until no more command modifiers are found.
   2526  while (true) {
   2527    while (*eap->cmd == ' '
   2528           || *eap->cmd == '\t'
   2529           || *eap->cmd == ':') {
   2530      eap->cmd++;
   2531    }
   2532 
   2533    // in ex mode, an empty command (after modifiers) works like :+
   2534    if (*eap->cmd == NUL && exmode_active
   2535        && getline_equal(eap->ea_getline, eap->cookie, getexline)
   2536        && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) {
   2537      eap->cmd = exmode_plus;
   2538      use_plus_cmd = true;
   2539      if (!skip_only) {
   2540        ex_pressedreturn = true;
   2541      }
   2542      break;  // no modifiers following
   2543    }
   2544 
   2545    // ignore comment and empty lines
   2546    if (*eap->cmd == '"') {
   2547      // a comment ends at a NL
   2548      eap->nextcmd = vim_strchr(eap->cmd, '\n');
   2549      if (eap->nextcmd != NULL) {
   2550        eap->nextcmd++;
   2551      }
   2552      return FAIL;
   2553    }
   2554    if (*eap->cmd == '\n') {
   2555      eap->nextcmd = eap->cmd + 1;
   2556      return FAIL;
   2557    }
   2558    if (*eap->cmd == NUL) {
   2559      if (!skip_only) {
   2560        ex_pressedreturn = true;
   2561      }
   2562      return FAIL;
   2563    }
   2564 
   2565    char *p = skip_range(eap->cmd, NULL);
   2566    switch (*p) {
   2567    // When adding an entry, also modify cmdmods[]
   2568    case 'a':
   2569      if (!checkforcmd(&eap->cmd, "aboveleft", 3)) {
   2570        break;
   2571      }
   2572      cmod->cmod_split |= WSP_ABOVE;
   2573      continue;
   2574 
   2575    case 'b':
   2576      if (checkforcmd(&eap->cmd, "belowright", 3)) {
   2577        cmod->cmod_split |= WSP_BELOW;
   2578        continue;
   2579      }
   2580      if (checkforcmd(&eap->cmd, "browse", 3)) {
   2581        cmod->cmod_flags |= CMOD_BROWSE;
   2582        continue;
   2583      }
   2584      if (!checkforcmd(&eap->cmd, "botright", 2)) {
   2585        break;
   2586      }
   2587      cmod->cmod_split |= WSP_BOT;
   2588      continue;
   2589 
   2590    case 'c':
   2591      if (!checkforcmd(&eap->cmd, "confirm", 4)) {
   2592        break;
   2593      }
   2594      cmod->cmod_flags |= CMOD_CONFIRM;
   2595      continue;
   2596 
   2597    case 'k':
   2598      if (checkforcmd(&eap->cmd, "keepmarks", 3)) {
   2599        cmod->cmod_flags |= CMOD_KEEPMARKS;
   2600        continue;
   2601      }
   2602      if (checkforcmd(&eap->cmd, "keepalt", 5)) {
   2603        cmod->cmod_flags |= CMOD_KEEPALT;
   2604        continue;
   2605      }
   2606      if (checkforcmd(&eap->cmd, "keeppatterns", 5)) {
   2607        cmod->cmod_flags |= CMOD_KEEPPATTERNS;
   2608        continue;
   2609      }
   2610      if (!checkforcmd(&eap->cmd, "keepjumps", 5)) {
   2611        break;
   2612      }
   2613      cmod->cmod_flags |= CMOD_KEEPJUMPS;
   2614      continue;
   2615 
   2616    case 'f': {  // only accept ":filter {pat} cmd"
   2617      char *reg_pat;
   2618 
   2619      if (!checkforcmd(&p, "filter", 4) || *p == NUL || ends_excmd(*p)) {
   2620        break;
   2621      }
   2622      if (*p == '!') {
   2623        cmod->cmod_filter_force = true;
   2624        p = skipwhite(p + 1);
   2625        if (*p == NUL || ends_excmd(*p)) {
   2626          break;
   2627        }
   2628      }
   2629      if (skip_only) {
   2630        p = skip_vimgrep_pat(p, NULL, NULL);
   2631      } else {
   2632        // NOTE: This puts a NUL after the pattern.
   2633        p = skip_vimgrep_pat(p, &reg_pat, NULL);
   2634      }
   2635      if (p == NULL || *p == NUL) {
   2636        break;
   2637      }
   2638      if (!skip_only) {
   2639        cmod->cmod_filter_pat = xstrdup(reg_pat);
   2640        cmod->cmod_filter_regmatch.regprog = vim_regcomp(reg_pat, RE_MAGIC);
   2641        if (cmod->cmod_filter_regmatch.regprog == NULL) {
   2642          break;
   2643        }
   2644      }
   2645      eap->cmd = p;
   2646      continue;
   2647    }
   2648 
   2649    case 'h':
   2650      if (checkforcmd(&eap->cmd, "horizontal", 3)) {
   2651        cmod->cmod_split |= WSP_HOR;
   2652        continue;
   2653      }
   2654      // ":hide" and ":hide | cmd" are not modifiers
   2655      if (p != eap->cmd || !checkforcmd(&p, "hide", 3)
   2656          || *p == NUL || ends_excmd(*p)) {
   2657        break;
   2658      }
   2659      eap->cmd = p;
   2660      cmod->cmod_flags |= CMOD_HIDE;
   2661      continue;
   2662 
   2663    case 'l':
   2664      if (checkforcmd(&eap->cmd, "lockmarks", 3)) {
   2665        cmod->cmod_flags |= CMOD_LOCKMARKS;
   2666        continue;
   2667      }
   2668 
   2669      if (!checkforcmd(&eap->cmd, "leftabove", 5)) {
   2670        break;
   2671      }
   2672      cmod->cmod_split |= WSP_ABOVE;
   2673      continue;
   2674 
   2675    case 'n':
   2676      if (checkforcmd(&eap->cmd, "noautocmd", 3)) {
   2677        cmod->cmod_flags |= CMOD_NOAUTOCMD;
   2678        continue;
   2679      }
   2680      if (!checkforcmd(&eap->cmd, "noswapfile", 3)) {
   2681        break;
   2682      }
   2683      cmod->cmod_flags |= CMOD_NOSWAPFILE;
   2684      continue;
   2685 
   2686    case 'r':
   2687      if (!checkforcmd(&eap->cmd, "rightbelow", 6)) {
   2688        break;
   2689      }
   2690      cmod->cmod_split |= WSP_BELOW;
   2691      continue;
   2692 
   2693    case 's':
   2694      if (checkforcmd(&eap->cmd, "sandbox", 3)) {
   2695        cmod->cmod_flags |= CMOD_SANDBOX;
   2696        continue;
   2697      }
   2698      if (!checkforcmd(&eap->cmd, "silent", 3)) {
   2699        break;
   2700      }
   2701      cmod->cmod_flags |= CMOD_SILENT;
   2702      if (*eap->cmd == '!' && !ascii_iswhite(eap->cmd[-1])) {
   2703        // ":silent!", but not "silent !cmd"
   2704        eap->cmd = skipwhite(eap->cmd + 1);
   2705        cmod->cmod_flags |= CMOD_ERRSILENT;
   2706      }
   2707      continue;
   2708 
   2709    case 't':
   2710      if (checkforcmd(&p, "tab", 3)) {
   2711        if (!skip_only) {
   2712          int tabnr = (int)get_address(eap, &eap->cmd, ADDR_TABS, eap->skip, skip_only,
   2713                                       false, 1, errormsg);
   2714          if (eap->cmd == NULL) {
   2715            return false;
   2716          }
   2717 
   2718          if (tabnr == MAXLNUM) {
   2719            cmod->cmod_tab = tabpage_index(curtab) + 1;
   2720          } else {
   2721            if (tabnr < 0 || tabnr > LAST_TAB_NR) {
   2722              *errormsg = _(e_invrange);
   2723              return false;
   2724            }
   2725            cmod->cmod_tab = tabnr + 1;
   2726          }
   2727        }
   2728        eap->cmd = p;
   2729        continue;
   2730      }
   2731      if (!checkforcmd(&eap->cmd, "topleft", 2)) {
   2732        break;
   2733      }
   2734      cmod->cmod_split |= WSP_TOP;
   2735      continue;
   2736 
   2737    case 'u':
   2738      if (!checkforcmd(&eap->cmd, "unsilent", 3)) {
   2739        break;
   2740      }
   2741      cmod->cmod_flags |= CMOD_UNSILENT;
   2742      continue;
   2743 
   2744    case 'v':
   2745      if (checkforcmd(&eap->cmd, "vertical", 4)) {
   2746        cmod->cmod_split |= WSP_VERT;
   2747        continue;
   2748      }
   2749      if (!checkforcmd(&p, "verbose", 4)) {
   2750        break;
   2751      }
   2752      if (ascii_isdigit(*eap->cmd)) {
   2753        // zero means not set, one is verbose == 0, etc.
   2754        cmod->cmod_verbose = atoi(eap->cmd) + 1;
   2755      } else {
   2756        cmod->cmod_verbose = 2;  // default: verbose == 1
   2757      }
   2758      eap->cmd = p;
   2759      continue;
   2760    }
   2761    break;
   2762  }
   2763 
   2764  if (has_visual_range) {
   2765    if (eap->cmd > cmd_start) {
   2766      // Move the '<,'> range to after the modifiers and insert a colon.
   2767      // Since the modifiers have been parsed put the colon on top of the
   2768      // space: "'<,'>mod cmd" -> "mod:'<,'>cmd
   2769      // Put eap->cmd after the colon.
   2770      if (use_plus_cmd) {
   2771        size_t len = strlen(cmd_start);
   2772 
   2773        // Special case: empty command uses "+":
   2774        //  "'<,'>mods" -> "mods *+
   2775        //  Use "*" instead of "'<,'>" to avoid the command getting
   2776        //  longer, in case is was allocated.
   2777        memmove(orig_cmd, cmd_start, len);
   2778        xmemcpyz(orig_cmd + len, S_LEN(" *+"));
   2779      } else {
   2780        memmove(cmd_start - 5, cmd_start, (size_t)(eap->cmd - cmd_start));
   2781        eap->cmd -= 5;
   2782        memmove(eap->cmd - 1, ":'<,'>", 6);
   2783      }
   2784    } else {
   2785      // No modifiers, move the pointer back.
   2786      // Special case: change empty command to "+".
   2787      if (use_plus_cmd) {
   2788        eap->cmd = "'<,'>+";
   2789      } else {
   2790        eap->cmd = orig_cmd;
   2791      }
   2792    }
   2793  } else if (use_plus_cmd) {
   2794    eap->cmd = exmode_plus;
   2795  }
   2796 
   2797  return OK;
   2798 }
   2799 
   2800 /// Apply the command modifiers.  Saves current state in "cmdmod", call
   2801 /// undo_cmdmod() later.
   2802 void apply_cmdmod(cmdmod_T *cmod)
   2803 {
   2804  if ((cmod->cmod_flags & CMOD_SANDBOX) && !cmod->cmod_did_sandbox) {
   2805    sandbox++;
   2806    cmod->cmod_did_sandbox = true;
   2807  }
   2808  if (cmod->cmod_verbose > 0) {
   2809    if (cmod->cmod_verbose_save == 0) {
   2810      cmod->cmod_verbose_save = p_verbose + 1;
   2811    }
   2812    p_verbose = cmod->cmod_verbose - 1;
   2813  }
   2814 
   2815  if ((cmod->cmod_flags & (CMOD_SILENT | CMOD_UNSILENT))
   2816      && cmod->cmod_save_msg_silent == 0) {
   2817    cmod->cmod_save_msg_silent = msg_silent + 1;
   2818    cmod->cmod_save_msg_scroll = msg_scroll;
   2819  }
   2820  if (cmod->cmod_flags & CMOD_SILENT) {
   2821    msg_silent++;
   2822  }
   2823  if (cmod->cmod_flags & CMOD_UNSILENT) {
   2824    msg_silent = 0;
   2825  }
   2826 
   2827  if (cmod->cmod_flags & CMOD_ERRSILENT) {
   2828    emsg_silent++;
   2829    cmod->cmod_did_esilent++;
   2830  }
   2831 
   2832  if ((cmod->cmod_flags & CMOD_NOAUTOCMD) && cmod->cmod_save_ei == NULL) {
   2833    // Set 'eventignore' to "all".
   2834    // First save the existing option value for restoring it later.
   2835    cmod->cmod_save_ei = xstrdup(p_ei);
   2836    set_option_direct(kOptEventignore, STATIC_CSTR_AS_OPTVAL("all"), 0, SID_NONE);
   2837  }
   2838 }
   2839 
   2840 /// Undo and free contents of "cmod".
   2841 void undo_cmdmod(cmdmod_T *cmod)
   2842  FUNC_ATTR_NONNULL_ALL
   2843 {
   2844  if (cmod->cmod_verbose_save > 0) {
   2845    p_verbose = cmod->cmod_verbose_save - 1;
   2846    cmod->cmod_verbose_save = 0;
   2847  }
   2848 
   2849  if (cmod->cmod_did_sandbox) {
   2850    sandbox--;
   2851    cmod->cmod_did_sandbox = false;
   2852  }
   2853 
   2854  if (cmod->cmod_save_ei != NULL) {
   2855    // Restore 'eventignore' to the value before ":noautocmd".
   2856    set_option_direct(kOptEventignore, CSTR_AS_OPTVAL(cmod->cmod_save_ei), 0, SID_NONE);
   2857    free_string_option(cmod->cmod_save_ei);
   2858    cmod->cmod_save_ei = NULL;
   2859  }
   2860 
   2861  xfree(cmod->cmod_filter_pat);
   2862  vim_regfree(cmod->cmod_filter_regmatch.regprog);
   2863 
   2864  if (cmod->cmod_save_msg_silent > 0) {
   2865    // messages could be enabled for a serious error, need to check if the
   2866    // counters don't become negative
   2867    if (!did_emsg || msg_silent > cmod->cmod_save_msg_silent - 1) {
   2868      msg_silent = cmod->cmod_save_msg_silent - 1;
   2869    }
   2870    emsg_silent -= cmod->cmod_did_esilent;
   2871    emsg_silent = MAX(emsg_silent, 0);
   2872    // Restore msg_scroll, it's set by file I/O commands, even when no
   2873    // message is actually displayed.
   2874    msg_scroll = cmod->cmod_save_msg_scroll;
   2875 
   2876    // "silent reg" or "silent echo x" inside "redir" leaves msg_col
   2877    // somewhere in the line.  Put it back in the first column.
   2878    if (redirecting()) {
   2879      msg_col = 0;
   2880    }
   2881 
   2882    cmod->cmod_save_msg_silent = 0;
   2883    cmod->cmod_did_esilent = 0;
   2884  }
   2885 }
   2886 
   2887 /// Parse the address range, if any, in "eap".
   2888 /// May set the last search pattern, unless "silent" is true.
   2889 ///
   2890 /// @return  FAIL and set "errormsg" or return OK.
   2891 int parse_cmd_address(exarg_T *eap, const char **errormsg, bool silent)
   2892  FUNC_ATTR_NONNULL_ALL
   2893 {
   2894  int address_count = 1;
   2895  linenr_T lnum;
   2896  bool need_check_cursor = false;
   2897  int ret = FAIL;
   2898 
   2899  // Repeat for all ',' or ';' separated addresses.
   2900  while (true) {
   2901    eap->line1 = eap->line2;
   2902    eap->line2 = get_cmd_default_range(eap);
   2903    eap->cmd = skipwhite(eap->cmd);
   2904    lnum = get_address(eap, &eap->cmd, eap->addr_type, eap->skip, silent,
   2905                       eap->addr_count == 0, address_count++, errormsg);
   2906    if (eap->cmd == NULL) {  // error detected
   2907      goto theend;
   2908    }
   2909    if (lnum == MAXLNUM) {
   2910      if (*eap->cmd == '%') {  // '%' - all lines
   2911        eap->cmd++;
   2912        switch (eap->addr_type) {
   2913        case ADDR_LINES:
   2914        case ADDR_OTHER:
   2915          eap->line1 = 1;
   2916          eap->line2 = curbuf->b_ml.ml_line_count;
   2917          break;
   2918        case ADDR_LOADED_BUFFERS: {
   2919          buf_T *buf = firstbuf;
   2920 
   2921          while (buf->b_next != NULL && buf->b_ml.ml_mfp == NULL) {
   2922            buf = buf->b_next;
   2923          }
   2924          eap->line1 = buf->b_fnum;
   2925          buf = lastbuf;
   2926          while (buf->b_prev != NULL && buf->b_ml.ml_mfp == NULL) {
   2927            buf = buf->b_prev;
   2928          }
   2929          eap->line2 = buf->b_fnum;
   2930          break;
   2931        }
   2932        case ADDR_BUFFERS:
   2933          eap->line1 = firstbuf->b_fnum;
   2934          eap->line2 = lastbuf->b_fnum;
   2935          break;
   2936        case ADDR_WINDOWS:
   2937        case ADDR_TABS:
   2938          if (IS_USER_CMDIDX(eap->cmdidx)) {
   2939            eap->line1 = 1;
   2940            eap->line2 = eap->addr_type == ADDR_WINDOWS
   2941                         ? LAST_WIN_NR : LAST_TAB_NR;
   2942          } else {
   2943            // there is no Vim command which uses '%' and
   2944            // ADDR_WINDOWS or ADDR_TABS
   2945            *errormsg = _(e_invrange);
   2946            goto theend;
   2947          }
   2948          break;
   2949        case ADDR_TABS_RELATIVE:
   2950        case ADDR_UNSIGNED:
   2951        case ADDR_QUICKFIX:
   2952          *errormsg = _(e_invrange);
   2953          goto theend;
   2954        case ADDR_ARGUMENTS:
   2955          if (ARGCOUNT == 0) {
   2956            eap->line1 = eap->line2 = 0;
   2957          } else {
   2958            eap->line1 = 1;
   2959            eap->line2 = ARGCOUNT;
   2960          }
   2961          break;
   2962        case ADDR_QUICKFIX_VALID:
   2963          eap->line1 = 1;
   2964          eap->line2 = (linenr_T)qf_get_valid_size(eap);
   2965          if (eap->line2 == 0) {
   2966            eap->line2 = 1;
   2967          }
   2968          break;
   2969        case ADDR_NONE:
   2970          // Will give an error later if a range is found.
   2971          break;
   2972        }
   2973        eap->addr_count++;
   2974      } else if (*eap->cmd == '*') {
   2975        // '*' - visual area
   2976        if (eap->addr_type != ADDR_LINES) {
   2977          *errormsg = _(e_invrange);
   2978          goto theend;
   2979        }
   2980 
   2981        eap->cmd++;
   2982        if (!eap->skip) {
   2983          fmark_T *fm = mark_get_visual(curbuf, '<');
   2984          if (!mark_check(fm, errormsg)) {
   2985            goto theend;
   2986          }
   2987          assert(fm != NULL);
   2988          eap->line1 = fm->mark.lnum;
   2989          fm = mark_get_visual(curbuf, '>');
   2990          if (!mark_check(fm, errormsg)) {
   2991            goto theend;
   2992          }
   2993          assert(fm != NULL);
   2994          eap->line2 = fm->mark.lnum;
   2995          eap->addr_count++;
   2996        }
   2997      }
   2998    } else {
   2999      eap->line2 = lnum;
   3000    }
   3001    eap->addr_count++;
   3002 
   3003    if (*eap->cmd == ';') {
   3004      if (!eap->skip) {
   3005        curwin->w_cursor.lnum = eap->line2;
   3006 
   3007        // Don't leave the cursor on an illegal line or column, but do
   3008        // accept zero as address, so 0;/PATTERN/ works correctly
   3009        // (where zero usually means to use the first line).
   3010        // Check the cursor position before returning.
   3011        if (eap->line2 > 0) {
   3012          check_cursor(curwin);
   3013        } else {
   3014          check_cursor_col(curwin);
   3015        }
   3016        need_check_cursor = true;
   3017      }
   3018    } else if (*eap->cmd != ',') {
   3019      break;
   3020    }
   3021    eap->cmd++;
   3022  }
   3023 
   3024  // One address given: set start and end lines.
   3025  if (eap->addr_count == 1) {
   3026    eap->line1 = eap->line2;
   3027    // ... but only implicit: really no address given
   3028    if (lnum == MAXLNUM) {
   3029      eap->addr_count = 0;
   3030    }
   3031  }
   3032  ret = OK;
   3033 
   3034 theend:
   3035  if (need_check_cursor) {
   3036    check_cursor(curwin);
   3037  }
   3038  return ret;
   3039 }
   3040 
   3041 /// Check for an Ex command with optional tail.
   3042 /// If there is a match advance "pp" to the argument and return true.
   3043 ///
   3044 /// @param pp   start of command
   3045 /// @param cmd  name of command
   3046 /// @param len  required length
   3047 bool checkforcmd(char **pp, const char *cmd, int len)
   3048 {
   3049  int i;
   3050 
   3051  for (i = 0; cmd[i] != NUL; i++) {
   3052    if ((cmd)[i] != (*pp)[i]) {
   3053      break;
   3054    }
   3055  }
   3056  if (i >= len && !ASCII_ISALPHA((*pp)[i])) {
   3057    *pp = skipwhite(*pp + i);
   3058    return true;
   3059  }
   3060  return false;
   3061 }
   3062 
   3063 /// Append "cmd" to the error message in IObuff.
   3064 /// Takes care of limiting the length and handling 0xa0, which would be
   3065 /// invisible otherwise.
   3066 static void append_command(const char *cmd)
   3067 {
   3068  size_t len = strlen(IObuff);
   3069  const char *s = cmd;
   3070  char *d;
   3071 
   3072  if (len > IOSIZE - 100) {
   3073    // Not enough space, truncate and put in "...".
   3074    d = IObuff + IOSIZE - 100;
   3075    d -= utf_head_off(IObuff, d);
   3076    STRCPY(d, "...");
   3077  }
   3078  xstrlcat(IObuff, ": ", IOSIZE);
   3079  d = IObuff + strlen(IObuff);
   3080  while (*s != NUL && d - IObuff + 5 < IOSIZE) {
   3081    if ((uint8_t)s[0] == 0xc2 && (uint8_t)s[1] == 0xa0) {
   3082      s += 2;
   3083      STRCPY(d, "<a0>");
   3084      d += 4;
   3085    } else if (d - IObuff + utfc_ptr2len(s) + 1 >= IOSIZE) {
   3086      break;
   3087    } else {
   3088      mb_copy_char(&s, &d);
   3089    }
   3090  }
   3091  *d = NUL;
   3092 }
   3093 
   3094 /// Return true and set "*idx" if "p" points to a one letter command.
   3095 /// - The 'k' command can directly be followed by any character
   3096 ///          but :keepa[lt] is another command, as are :keepj[umps],
   3097 ///          :kee[pmarks] and :keepp[atterns].
   3098 /// - The 's' command can be followed directly by 'c', 'g', 'i', 'I' or 'r'
   3099 ///          but :sre[wind] is another command, as are :scr[iptnames],
   3100 ///          :scs[cope], :sim[alt], :sig[ns] and :sil[ent].
   3101 static int one_letter_cmd(const char *p, cmdidx_T *idx)
   3102 {
   3103  if (p[0] == 'k'
   3104      && (p[1] != 'e' || (p[1] == 'e' && p[2] != 'e'))) {
   3105    *idx = CMD_k;
   3106    return true;
   3107  }
   3108  if (p[0] == 's'
   3109      && ((p[1] == 'c'
   3110           && (p[2] == NUL
   3111               || (p[2] != 's' && p[2] != 'r'
   3112                   && (p[3] == NUL
   3113                       || (p[3] != 'i' && p[4] != 'p')))))
   3114          || p[1] == 'g'
   3115          || (p[1] == 'i' && p[2] != 'm' && p[2] != 'l' && p[2] != 'g')
   3116          || p[1] == 'I'
   3117          || (p[1] == 'r' && p[2] != 'e'))) {
   3118    *idx = CMD_substitute;
   3119    return true;
   3120  }
   3121  return false;
   3122 }
   3123 
   3124 /// Find an Ex command by its name, either built-in or user.
   3125 /// Start of the name can be found at eap->cmd.
   3126 /// Sets eap->cmdidx and returns a pointer to char after the command name.
   3127 /// "full" is set to true if the whole command name matched.
   3128 ///
   3129 /// @return  NULL for an ambiguous user command.
   3130 char *find_ex_command(exarg_T *eap, int *full)
   3131  FUNC_ATTR_NONNULL_ARG(1)
   3132 {
   3133  // Isolate the command and search for it in the command table.
   3134  char *p = eap->cmd;
   3135  if (one_letter_cmd(p, &eap->cmdidx)) {
   3136    p++;
   3137    if (full != NULL) {
   3138      *full = true;
   3139    }
   3140  } else {
   3141    while (ASCII_ISALPHA(*p)) {
   3142      p++;
   3143    }
   3144    // for python 3.x support ":py3", ":python3", ":py3file", etc.
   3145    if (eap->cmd[0] == 'p' && eap->cmd[1] == 'y') {
   3146      while (ASCII_ISALNUM(*p)) {
   3147        p++;
   3148      }
   3149    }
   3150 
   3151    // check for non-alpha command
   3152    if (p == eap->cmd && vim_strchr("@!=><&~#", (uint8_t)(*p)) != NULL) {
   3153      p++;
   3154    }
   3155    int len = (int)(p - eap->cmd);
   3156    // The "d" command can directly be followed by 'l' or 'p' flag.
   3157    if (*eap->cmd == 'd' && (p[-1] == 'l' || p[-1] == 'p')) {
   3158      // Check for ":dl", ":dell", etc. to ":deletel": that's
   3159      // :delete with the 'l' flag.  Same for 'p'.
   3160      int i;
   3161      for (i = 0; i < len; i++) {
   3162        if (eap->cmd[i] != ("delete")[i]) {
   3163          break;
   3164        }
   3165      }
   3166      if (i == len - 1) {
   3167        len--;
   3168        if (p[-1] == 'l') {
   3169          eap->flags |= EXFLAG_LIST;
   3170        } else {
   3171          eap->flags |= EXFLAG_PRINT;
   3172        }
   3173      }
   3174    }
   3175 
   3176    if (ASCII_ISLOWER(eap->cmd[0])) {
   3177      const int c1 = (uint8_t)eap->cmd[0];
   3178      const int c2 = len == 1 ? NUL : eap->cmd[1];
   3179 
   3180      if (command_count != CMD_SIZE) {
   3181        iemsg(_("E943: Command table needs to be updated, run 'make'"));
   3182        getout(1);
   3183      }
   3184 
   3185      // Use a precomputed index for fast look-up in cmdnames[]
   3186      // taking into account the first 2 letters of eap->cmd.
   3187      eap->cmdidx = cmdidxs1[CHAR_ORD_LOW(c1)];
   3188      if (ASCII_ISLOWER(c2)) {
   3189        eap->cmdidx += cmdidxs2[CHAR_ORD_LOW(c1)][CHAR_ORD_LOW(c2)];
   3190      }
   3191    } else if (ASCII_ISUPPER(eap->cmd[0])) {
   3192      eap->cmdidx = CMD_Next;
   3193    } else {
   3194      eap->cmdidx = CMD_bang;
   3195    }
   3196    assert(eap->cmdidx >= 0);
   3197 
   3198    if (len == 3 && strncmp("def", eap->cmd, 3) == 0) {
   3199      // Make :def an unknown command to avoid confusing behavior. #23149
   3200      eap->cmdidx = CMD_SIZE;
   3201    }
   3202 
   3203    for (; (int)eap->cmdidx < CMD_SIZE;
   3204         eap->cmdidx = (cmdidx_T)((int)eap->cmdidx + 1)) {
   3205      if (strncmp(cmdnames[(int)eap->cmdidx].cmd_name, eap->cmd,
   3206                  (size_t)len) == 0) {
   3207        if (full != NULL
   3208            && cmdnames[(int)eap->cmdidx].cmd_name[len] == NUL) {
   3209          *full = true;
   3210        }
   3211        break;
   3212      }
   3213    }
   3214 
   3215    // Look for a user defined command as a last resort.
   3216    if ((eap->cmdidx == CMD_SIZE)
   3217        && *eap->cmd >= 'A' && *eap->cmd <= 'Z') {
   3218      // User defined commands may contain digits.
   3219      while (ASCII_ISALNUM(*p)) {
   3220        p++;
   3221      }
   3222      p = find_ucmd(eap, p, full, NULL, NULL);
   3223    }
   3224    if (p == eap->cmd) {
   3225      eap->cmdidx = CMD_SIZE;
   3226    }
   3227  }
   3228 
   3229  return p;
   3230 }
   3231 
   3232 static struct cmdmod {
   3233  char *name;
   3234  int minlen;
   3235  int has_count;            // :123verbose  :3tab
   3236 } cmdmods[] = {
   3237  { "aboveleft", 3, false },
   3238  { "belowright", 3, false },
   3239  { "botright", 2, false },
   3240  { "browse", 3, false },
   3241  { "confirm", 4, false },
   3242  { "filter", 4, false },
   3243  { "hide", 3, false },
   3244  { "horizontal", 3, false },
   3245  { "keepalt", 5, false },
   3246  { "keepjumps", 5, false },
   3247  { "keepmarks", 3, false },
   3248  { "keeppatterns", 5, false },
   3249  { "leftabove", 5, false },
   3250  { "lockmarks", 3, false },
   3251  { "noautocmd", 3, false },
   3252  { "noswapfile", 3, false },
   3253  { "rightbelow", 6, false },
   3254  { "sandbox", 3, false },
   3255  { "silent", 3, false },
   3256  { "tab", 3, true },
   3257  { "topleft", 2, false },
   3258  { "unsilent", 3, false },
   3259  { "verbose", 4, true },
   3260  { "vertical", 4, false },
   3261 };
   3262 
   3263 /// @return  length of a command modifier (including optional count) or,
   3264 ///          zero when it's not a modifier.
   3265 int modifier_len(char *cmd)
   3266 {
   3267  char *p = cmd;
   3268 
   3269  if (ascii_isdigit(*cmd)) {
   3270    p = skipwhite(skipdigits(cmd + 1));
   3271  }
   3272  for (int i = 0; i < (int)ARRAY_SIZE(cmdmods); i++) {
   3273    int j;
   3274    for (j = 0; p[j] != NUL; j++) {
   3275      if (p[j] != cmdmods[i].name[j]) {
   3276        break;
   3277      }
   3278    }
   3279    if (j >= cmdmods[i].minlen
   3280        && !ASCII_ISALPHA(p[j])
   3281        && (p == cmd || cmdmods[i].has_count)) {
   3282      return j + (int)(p - cmd);
   3283    }
   3284  }
   3285  return 0;
   3286 }
   3287 
   3288 /// @return  > 0 if an Ex command "name" exists or,
   3289 ///            2 if there is an exact match or,
   3290 ///            3 if there is an ambiguous match.
   3291 int cmd_exists(const char *const name)
   3292 {
   3293  // Check command modifiers.
   3294  for (int i = 0; i < (int)ARRAY_SIZE(cmdmods); i++) {
   3295    int j;
   3296    for (j = 0; name[j] != NUL; j++) {
   3297      if (name[j] != cmdmods[i].name[j]) {
   3298        break;
   3299      }
   3300    }
   3301    if (name[j] == NUL && j >= cmdmods[i].minlen) {
   3302      return cmdmods[i].name[j] == NUL ? 2 : 1;
   3303    }
   3304  }
   3305 
   3306  // Check built-in commands and user defined commands.
   3307  // For ":2match" and ":3match" we need to skip the number.
   3308  exarg_T ea;
   3309  ea.cmd = (char *)((*name == '2' || *name == '3') ? name + 1 : name);
   3310  ea.cmdidx = 0;
   3311  ea.flags = 0;
   3312  int full = false;
   3313  char *p = find_ex_command(&ea, &full);
   3314  if (p == NULL) {
   3315    return 3;
   3316  }
   3317  if (ascii_isdigit(*name) && ea.cmdidx != CMD_match) {
   3318    return 0;
   3319  }
   3320  if (*skipwhite(p) != NUL) {
   3321    return 0;           // trailing garbage
   3322  }
   3323  return ea.cmdidx == CMD_SIZE ? 0 : (full ? 2 : 1);
   3324 }
   3325 
   3326 /// "fullcommand" function
   3327 void f_fullcommand(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
   3328 {
   3329  char *name = (char *)tv_get_string(&argvars[0]);
   3330 
   3331  rettv->v_type = VAR_STRING;
   3332  rettv->vval.v_string = NULL;
   3333 
   3334  while (*name == ':') {
   3335    name++;
   3336  }
   3337  name = skip_range(name, NULL);
   3338 
   3339  exarg_T ea;
   3340  ea.cmd = (*name == '2' || *name == '3') ? name + 1 : name;
   3341  ea.cmdidx = 0;
   3342  ea.flags = 0;
   3343  char *p = find_ex_command(&ea, NULL);
   3344  if (p == NULL || ea.cmdidx == CMD_SIZE) {
   3345    return;
   3346  }
   3347 
   3348  rettv->vval.v_string = xstrdup(IS_USER_CMDIDX(ea.cmdidx)
   3349                                 ? get_user_command_name(ea.useridx, ea.cmdidx)
   3350                                 : cmdnames[ea.cmdidx].cmd_name);
   3351 }
   3352 
   3353 cmdidx_T excmd_get_cmdidx(const char *cmd, size_t len)
   3354 {
   3355  if (len == 3 && strncmp("def", cmd, 3) == 0) {
   3356    // Make :def an unknown command to avoid confusing behavior. #23149
   3357    return CMD_SIZE;
   3358  }
   3359 
   3360  cmdidx_T idx;
   3361 
   3362  if (!one_letter_cmd(cmd, &idx)) {
   3363    for (idx = 0; (int)idx < CMD_SIZE; idx = (cmdidx_T)((int)idx + 1)) {
   3364      if (strncmp(cmdnames[(int)idx].cmd_name, cmd, len) == 0) {
   3365        break;
   3366      }
   3367    }
   3368  }
   3369 
   3370  return idx;
   3371 }
   3372 
   3373 uint32_t excmd_get_argt(cmdidx_T idx)
   3374 {
   3375  return cmdnames[(int)idx].cmd_argt;
   3376 }
   3377 
   3378 /// Skip a range specifier of the form: addr [,addr] [;addr] ..
   3379 ///
   3380 /// Backslashed delimiters after / or ? will be skipped, and commands will
   3381 /// not be expanded between /'s and ?'s or after "'".
   3382 ///
   3383 /// Also skip white space and ":" characters.
   3384 ///
   3385 /// @param ctx  pointer to xp_context or NULL
   3386 ///
   3387 /// @return the "cmd" pointer advanced to beyond the range.
   3388 char *skip_range(const char *cmd, int *ctx)
   3389 {
   3390  while (vim_strchr(" \t0123456789.$%'/?-+,;\\", (uint8_t)(*cmd)) != NULL) {
   3391    if (*cmd == '\\') {
   3392      if (cmd[1] == '?' || cmd[1] == '/' || cmd[1] == '&') {
   3393        cmd++;
   3394      } else {
   3395        break;
   3396      }
   3397    } else if (*cmd == '\'') {
   3398      if (*++cmd == NUL && ctx != NULL) {
   3399        *ctx = EXPAND_NOTHING;
   3400      }
   3401    } else if (*cmd == '/' || *cmd == '?') {
   3402      unsigned delim = (unsigned)(*cmd++);
   3403      while (*cmd != NUL && *cmd != (char)delim) {
   3404        if (*cmd++ == '\\' && *cmd != NUL) {
   3405          cmd++;
   3406        }
   3407      }
   3408      if (*cmd == NUL && ctx != NULL) {
   3409        *ctx = EXPAND_NOTHING;
   3410      }
   3411    }
   3412    if (*cmd != NUL) {
   3413      cmd++;
   3414    }
   3415  }
   3416 
   3417  // Skip ":" and white space.
   3418  cmd = skip_colon_white(cmd, false);
   3419 
   3420  // Skip "*" used for Visual range.
   3421  if (*cmd == '*') {
   3422    cmd = skipwhite(cmd + 1);
   3423  }
   3424 
   3425  return (char *)cmd;
   3426 }
   3427 
   3428 static const char *addr_error(cmd_addr_T addr_type)
   3429 {
   3430  if (addr_type == ADDR_NONE) {
   3431    return _(e_norange);
   3432  } else {
   3433    return _(e_invrange);
   3434  }
   3435 }
   3436 
   3437 /// Gets a single EX address.
   3438 ///
   3439 /// Sets ptr to the next character after the part that was interpreted.
   3440 /// Sets ptr to NULL when an error is encountered (stored in `errormsg`).
   3441 /// May set the last used search pattern.
   3442 ///
   3443 /// @param skip           only skip the address, don't use it
   3444 /// @param silent         no errors or side effects
   3445 /// @param to_other_file  flag: may jump to other file
   3446 /// @param address_count  1 for first, >1 after comma
   3447 /// @param errormsg       Error message, if any
   3448 ///
   3449 /// @return               MAXLNUM when no Ex address was found.
   3450 linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, bool skip, bool silent,
   3451                     int to_other_file, int address_count, const char **errormsg)
   3452  FUNC_ATTR_NONNULL_ARG(2, 8)
   3453 {
   3454  int c;
   3455  int i;
   3456  linenr_T n;
   3457  pos_T pos;
   3458  buf_T *buf;
   3459 
   3460  char *cmd = skipwhite(*ptr);
   3461  linenr_T lnum = MAXLNUM;
   3462  do {
   3463    switch (*cmd) {
   3464    case '.':                               // '.' - Cursor position
   3465      cmd++;
   3466      switch (addr_type) {
   3467      case ADDR_LINES:
   3468      case ADDR_OTHER:
   3469        lnum = curwin->w_cursor.lnum;
   3470        break;
   3471      case ADDR_WINDOWS:
   3472        lnum = CURRENT_WIN_NR;
   3473        break;
   3474      case ADDR_ARGUMENTS:
   3475        lnum = curwin->w_arg_idx + 1;
   3476        break;
   3477      case ADDR_LOADED_BUFFERS:
   3478      case ADDR_BUFFERS:
   3479        lnum = curbuf->b_fnum;
   3480        break;
   3481      case ADDR_TABS:
   3482        lnum = CURRENT_TAB_NR;
   3483        break;
   3484      case ADDR_NONE:
   3485      case ADDR_TABS_RELATIVE:
   3486      case ADDR_UNSIGNED:
   3487        *errormsg = addr_error(addr_type);
   3488        cmd = NULL;
   3489        goto error;
   3490        break;
   3491      case ADDR_QUICKFIX:
   3492        lnum = (linenr_T)qf_get_cur_idx(eap);
   3493        break;
   3494      case ADDR_QUICKFIX_VALID:
   3495        lnum = qf_get_cur_valid_idx(eap);
   3496        break;
   3497      }
   3498      break;
   3499 
   3500    case '$':                               // '$' - last line
   3501      cmd++;
   3502      switch (addr_type) {
   3503      case ADDR_LINES:
   3504      case ADDR_OTHER:
   3505        lnum = curbuf->b_ml.ml_line_count;
   3506        break;
   3507      case ADDR_WINDOWS:
   3508        lnum = LAST_WIN_NR;
   3509        break;
   3510      case ADDR_ARGUMENTS:
   3511        lnum = ARGCOUNT;
   3512        break;
   3513      case ADDR_LOADED_BUFFERS:
   3514        buf = lastbuf;
   3515        while (buf->b_ml.ml_mfp == NULL) {
   3516          if (buf->b_prev == NULL) {
   3517            break;
   3518          }
   3519          buf = buf->b_prev;
   3520        }
   3521        lnum = buf->b_fnum;
   3522        break;
   3523      case ADDR_BUFFERS:
   3524        lnum = lastbuf->b_fnum;
   3525        break;
   3526      case ADDR_TABS:
   3527        lnum = LAST_TAB_NR;
   3528        break;
   3529      case ADDR_NONE:
   3530      case ADDR_TABS_RELATIVE:
   3531      case ADDR_UNSIGNED:
   3532        *errormsg = addr_error(addr_type);
   3533        cmd = NULL;
   3534        goto error;
   3535        break;
   3536      case ADDR_QUICKFIX:
   3537        lnum = (linenr_T)qf_get_size(eap);
   3538        if (lnum == 0) {
   3539          lnum = 1;
   3540        }
   3541        break;
   3542      case ADDR_QUICKFIX_VALID:
   3543        lnum = (linenr_T)qf_get_valid_size(eap);
   3544        if (lnum == 0) {
   3545          lnum = 1;
   3546        }
   3547        break;
   3548      }
   3549      break;
   3550 
   3551    case '\'':                              // ''' - mark
   3552      if (*++cmd == NUL) {
   3553        cmd = NULL;
   3554        goto error;
   3555      }
   3556      if (addr_type != ADDR_LINES) {
   3557        *errormsg = addr_error(addr_type);
   3558        cmd = NULL;
   3559        goto error;
   3560      }
   3561      if (skip) {
   3562        cmd++;
   3563      } else {
   3564        // Only accept a mark in another file when it is
   3565        // used by itself: ":'M".
   3566        MarkGet flag = to_other_file && cmd[1] == NUL ? kMarkAll : kMarkBufLocal;
   3567        fmark_T *fm = mark_get(curbuf, curwin, NULL, flag, *cmd);
   3568        cmd++;
   3569        if (fm != NULL && fm->fnum != curbuf->handle) {
   3570          mark_move_to(fm, 0);
   3571          // Jumped to another file.
   3572          lnum = curwin->w_cursor.lnum;
   3573        } else {
   3574          if (!mark_check(fm, errormsg)) {
   3575            cmd = NULL;
   3576            goto error;
   3577          }
   3578          assert(fm != NULL);
   3579          lnum = fm->mark.lnum;
   3580        }
   3581      }
   3582      break;
   3583 
   3584    case '/':
   3585    case '?':                           // '/' or '?' - search
   3586      c = (uint8_t)(*cmd++);
   3587      if (addr_type != ADDR_LINES) {
   3588        *errormsg = addr_error(addr_type);
   3589        cmd = NULL;
   3590        goto error;
   3591      }
   3592      if (skip) {                       // skip "/pat/"
   3593        cmd = skip_regexp(cmd, c, magic_isset());
   3594        if (*cmd == c) {
   3595          cmd++;
   3596        }
   3597      } else {
   3598        int flags;
   3599 
   3600        pos = curwin->w_cursor;  // save curwin->w_cursor
   3601 
   3602        // When '/' or '?' follows another address, start from
   3603        // there.
   3604        if (lnum > 0 && lnum != MAXLNUM) {
   3605          curwin->w_cursor.lnum
   3606            = lnum > curbuf->b_ml.ml_line_count ? curbuf->b_ml.ml_line_count : lnum;
   3607        }
   3608 
   3609        // Start a forward search at the end of the line (unless
   3610        // before the first line).
   3611        // Start a backward search at the start of the line.
   3612        // This makes sure we never match in the current
   3613        // line, and can match anywhere in the
   3614        // next/previous line.
   3615        curwin->w_cursor.col = (c == '/' && curwin->w_cursor.lnum > 0) ? MAXCOL : 0;
   3616        searchcmdlen = 0;
   3617        flags = silent ? SEARCH_KEEP : SEARCH_HIS | SEARCH_MSG;
   3618        if (!do_search(NULL, c, c, cmd, strlen(cmd), 1, flags, NULL)) {
   3619          curwin->w_cursor = pos;
   3620          cmd = NULL;
   3621          goto error;
   3622        }
   3623        lnum = curwin->w_cursor.lnum;
   3624        curwin->w_cursor = pos;
   3625        // adjust command string pointer
   3626        cmd += searchcmdlen;
   3627      }
   3628      break;
   3629 
   3630    case '\\':                      // "\?", "\/" or "\&", repeat search
   3631      cmd++;
   3632      if (addr_type != ADDR_LINES) {
   3633        *errormsg = addr_error(addr_type);
   3634        cmd = NULL;
   3635        goto error;
   3636      }
   3637      if (*cmd == '&') {
   3638        i = RE_SUBST;
   3639      } else if (*cmd == '?' || *cmd == '/') {
   3640        i = RE_SEARCH;
   3641      } else {
   3642        *errormsg = _(e_backslash);
   3643        cmd = NULL;
   3644        goto error;
   3645      }
   3646 
   3647      if (!skip) {
   3648        // When search follows another address, start from there.
   3649        pos.lnum = (lnum != MAXLNUM) ? lnum : curwin->w_cursor.lnum;
   3650        // Start the search just like for the above do_search().
   3651        pos.col = (*cmd != '?') ? MAXCOL : 0;
   3652        pos.coladd = 0;
   3653        if (searchit(curwin, curbuf, &pos, NULL,
   3654                     *cmd == '?' ? BACKWARD : FORWARD,
   3655                     "", 0, 1, SEARCH_MSG, i, NULL) != FAIL) {
   3656          lnum = pos.lnum;
   3657        } else {
   3658          cmd = NULL;
   3659          goto error;
   3660        }
   3661      }
   3662      cmd++;
   3663      break;
   3664 
   3665    default:
   3666      if (ascii_isdigit(*cmd)) {                // absolute line number
   3667        lnum = (linenr_T)getdigits(&cmd, false, 0);
   3668      }
   3669    }
   3670 
   3671    while (true) {
   3672      cmd = skipwhite(cmd);
   3673      if (*cmd != '-' && *cmd != '+' && !ascii_isdigit(*cmd)) {
   3674        break;
   3675      }
   3676 
   3677      if (lnum == MAXLNUM) {
   3678        switch (addr_type) {
   3679        case ADDR_LINES:
   3680        case ADDR_OTHER:
   3681          // "+1" is same as ".+1"
   3682          lnum = curwin->w_cursor.lnum;
   3683          break;
   3684        case ADDR_WINDOWS:
   3685          lnum = CURRENT_WIN_NR;
   3686          break;
   3687        case ADDR_ARGUMENTS:
   3688          lnum = curwin->w_arg_idx + 1;
   3689          break;
   3690        case ADDR_LOADED_BUFFERS:
   3691        case ADDR_BUFFERS:
   3692          lnum = curbuf->b_fnum;
   3693          break;
   3694        case ADDR_TABS:
   3695          lnum = CURRENT_TAB_NR;
   3696          break;
   3697        case ADDR_TABS_RELATIVE:
   3698          lnum = 1;
   3699          break;
   3700        case ADDR_QUICKFIX:
   3701          lnum = (linenr_T)qf_get_cur_idx(eap);
   3702          break;
   3703        case ADDR_QUICKFIX_VALID:
   3704          lnum = qf_get_cur_valid_idx(eap);
   3705          break;
   3706        case ADDR_NONE:
   3707        case ADDR_UNSIGNED:
   3708          lnum = 0;
   3709          break;
   3710        }
   3711      }
   3712 
   3713      if (ascii_isdigit(*cmd)) {
   3714        i = '+';                        // "number" is same as "+number"
   3715      } else {
   3716        i = (uint8_t)(*cmd++);
   3717      }
   3718      if (!ascii_isdigit(*cmd)) {       // '+' is '+1'
   3719        n = 1;
   3720      } else {
   3721        // "number", "+number" or "-number"
   3722        n = getdigits_int32(&cmd, false, MAXLNUM);
   3723        if (n == MAXLNUM) {
   3724          *errormsg = _(e_line_number_out_of_range);
   3725          cmd = NULL;
   3726          goto error;
   3727        }
   3728      }
   3729 
   3730      if (addr_type == ADDR_TABS_RELATIVE) {
   3731        *errormsg = _(e_invrange);
   3732        cmd = NULL;
   3733        goto error;
   3734      } else if (addr_type == ADDR_LOADED_BUFFERS || addr_type == ADDR_BUFFERS) {
   3735        lnum = compute_buffer_local_count(addr_type, lnum, (i == '-') ? -1 * n : n);
   3736      } else {
   3737        // Relative line addressing: need to adjust for lines in a
   3738        // closed fold after the first address.
   3739        if (addr_type == ADDR_LINES && (i == '-' || i == '+')
   3740            && address_count >= 2) {
   3741          hasFolding(curwin, lnum, NULL, &lnum);
   3742        }
   3743        if (i == '-') {
   3744          lnum -= n;
   3745        } else {
   3746          if (lnum >= 0 && n >= INT32_MAX - lnum) {
   3747            *errormsg = _(e_line_number_out_of_range);
   3748            cmd = NULL;
   3749            goto error;
   3750          }
   3751          lnum += n;
   3752        }
   3753      }
   3754    }
   3755  } while (*cmd == '/' || *cmd == '?');
   3756 
   3757 error:
   3758  *ptr = cmd;
   3759  return lnum;
   3760 }
   3761 
   3762 /// Get flags from an Ex command argument.
   3763 static void get_flags(exarg_T *eap)
   3764 {
   3765  while (vim_strchr("lp#", (uint8_t)(*eap->arg)) != NULL) {
   3766    if (*eap->arg == 'l') {
   3767      eap->flags |= EXFLAG_LIST;
   3768    } else if (*eap->arg == 'p') {
   3769      eap->flags |= EXFLAG_PRINT;
   3770    } else {
   3771      eap->flags |= EXFLAG_NR;
   3772    }
   3773    eap->arg = skipwhite(eap->arg + 1);
   3774  }
   3775 }
   3776 
   3777 /// Stub function for command which is Not Implemented. NI!
   3778 void ex_ni(exarg_T *eap)
   3779 {
   3780  if (!eap->skip) {
   3781    eap->errmsg = _("E319: The command is not available in this version");
   3782  }
   3783 }
   3784 
   3785 /// Stub function for script command which is Not Implemented. NI!
   3786 /// Skips over ":perl <<EOF" constructs.
   3787 static void ex_script_ni(exarg_T *eap)
   3788 {
   3789  if (!eap->skip) {
   3790    ex_ni(eap);
   3791  } else {
   3792    size_t len;
   3793    xfree(script_get(eap, &len));
   3794  }
   3795 }
   3796 
   3797 /// Check range in Ex command for validity.
   3798 ///
   3799 /// @return  NULL when valid, error message when invalid.
   3800 char *invalid_range(exarg_T *eap)
   3801 {
   3802  buf_T *buf;
   3803  if (eap->line1 < 0 || eap->line2 < 0 || eap->line1 > eap->line2) {
   3804    return _(e_invrange);
   3805  }
   3806 
   3807  if (eap->argt & EX_RANGE) {
   3808    switch (eap->addr_type) {
   3809    case ADDR_LINES:
   3810      if (eap->line2 >
   3811          (curbuf->b_ml.ml_line_count
   3812           + (eap->cmdidx == CMD_diffget || eap->cmdidx == CMD_diffput))) {
   3813        return _(e_invrange);
   3814      }
   3815      break;
   3816    case ADDR_ARGUMENTS:
   3817      // add 1 if ARGCOUNT is 0
   3818      if (eap->line2 > ARGCOUNT + (!ARGCOUNT)) {
   3819        return _(e_invrange);
   3820      }
   3821      break;
   3822    case ADDR_BUFFERS:
   3823      // Only a boundary check, not whether the buffers actually
   3824      // exist.
   3825      if (eap->line1 < 1 || eap->line2 > get_highest_fnum()) {
   3826        return _(e_invrange);
   3827      }
   3828      break;
   3829    case ADDR_LOADED_BUFFERS:
   3830      buf = firstbuf;
   3831      while (buf->b_ml.ml_mfp == NULL) {
   3832        if (buf->b_next == NULL) {
   3833          return _(e_invrange);
   3834        }
   3835        buf = buf->b_next;
   3836      }
   3837      if (eap->line1 < buf->b_fnum) {
   3838        return _(e_invrange);
   3839      }
   3840      buf = lastbuf;
   3841      while (buf->b_ml.ml_mfp == NULL) {
   3842        if (buf->b_prev == NULL) {
   3843          return _(e_invrange);
   3844        }
   3845        buf = buf->b_prev;
   3846      }
   3847      if (eap->line2 > buf->b_fnum) {
   3848        return _(e_invrange);
   3849      }
   3850      break;
   3851    case ADDR_WINDOWS:
   3852      if (eap->line2 > LAST_WIN_NR) {
   3853        return _(e_invrange);
   3854      }
   3855      break;
   3856    case ADDR_TABS:
   3857      if (eap->line2 > LAST_TAB_NR) {
   3858        return _(e_invrange);
   3859      }
   3860      break;
   3861    case ADDR_TABS_RELATIVE:
   3862    case ADDR_OTHER:
   3863      // Any range is OK.
   3864      break;
   3865    case ADDR_QUICKFIX:
   3866      assert(eap->line2 >= 0);
   3867      // No error for value that is too big, will use the last entry.
   3868      if (eap->line2 <= 0) {
   3869        if (eap->addr_count == 0) {
   3870          return _(e_no_errors);
   3871        }
   3872        return _(e_invrange);
   3873      }
   3874      break;
   3875    case ADDR_QUICKFIX_VALID:
   3876      if ((eap->line2 != 1 && (size_t)eap->line2 > qf_get_valid_size(eap))
   3877          || eap->line2 < 0) {
   3878        return _(e_invrange);
   3879      }
   3880      break;
   3881    case ADDR_UNSIGNED:
   3882    case ADDR_NONE:
   3883      // Will give an error elsewhere.
   3884      break;
   3885    }
   3886  }
   3887  return NULL;
   3888 }
   3889 
   3890 /// Correct the range for zero line number, if required.
   3891 static void correct_range(exarg_T *eap)
   3892 {
   3893  if (!(eap->argt & EX_ZEROR)) {  // zero in range not allowed
   3894    if (eap->line1 == 0) {
   3895      eap->line1 = 1;
   3896    }
   3897    if (eap->line2 == 0) {
   3898      eap->line2 = 1;
   3899    }
   3900  }
   3901 }
   3902 
   3903 /// For a ":vimgrep" or ":vimgrepadd" command return a pointer past the
   3904 /// pattern.  Otherwise return eap->arg.
   3905 static char *skip_grep_pat(exarg_T *eap)
   3906 {
   3907  char *p = eap->arg;
   3908 
   3909  if (*p != NUL && (eap->cmdidx == CMD_vimgrep || eap->cmdidx == CMD_lvimgrep
   3910                    || eap->cmdidx == CMD_vimgrepadd
   3911                    || eap->cmdidx == CMD_lvimgrepadd
   3912                    || grep_internal(eap->cmdidx))) {
   3913    p = skip_vimgrep_pat(p, NULL, NULL);
   3914    if (p == NULL) {
   3915      p = eap->arg;
   3916    }
   3917  }
   3918  return p;
   3919 }
   3920 
   3921 /// For the ":make" and ":grep" commands insert the 'makeprg'/'grepprg' option
   3922 /// in the command line, so that things like % get expanded.
   3923 char *replace_makeprg(exarg_T *eap, char *arg, char **cmdlinep)
   3924 {
   3925  bool isgrep = eap->cmdidx == CMD_grep
   3926                || eap->cmdidx == CMD_lgrep
   3927                || eap->cmdidx == CMD_grepadd
   3928                || eap->cmdidx == CMD_lgrepadd;
   3929 
   3930  // Don't do it when ":vimgrep" is used for ":grep".
   3931  if ((eap->cmdidx == CMD_make || eap->cmdidx == CMD_lmake || isgrep)
   3932      && !grep_internal(eap->cmdidx)) {
   3933    const char *program = isgrep ? (*curbuf->b_p_gp == NUL ? p_gp : curbuf->b_p_gp)
   3934                                 : (*curbuf->b_p_mp == NUL ? p_mp : curbuf->b_p_mp);
   3935 
   3936    arg = skipwhite(arg);
   3937 
   3938    char *new_cmdline;
   3939    // Replace $* by given arguments
   3940    if ((new_cmdline = strrep(program, "$*", arg)) == NULL) {
   3941      // No $* in arg, build "<makeprg> <arg>" instead
   3942      new_cmdline = xmalloc(strlen(program) + strlen(arg) + 2);
   3943      STRCPY(new_cmdline, program);
   3944      strcat(new_cmdline, " ");
   3945      strcat(new_cmdline, arg);
   3946    }
   3947 
   3948    msg_make(arg);
   3949 
   3950    // 'eap->cmd' is not set here, because it is not used at CMD_make
   3951    xfree(*cmdlinep);
   3952    *cmdlinep = new_cmdline;
   3953    arg = new_cmdline;
   3954  }
   3955  return arg;
   3956 }
   3957 
   3958 /// Expand file name in Ex command argument.
   3959 /// When an error is detected, "errormsgp" is set to a non-NULL pointer.
   3960 ///
   3961 /// @return  FAIL for failure, OK otherwise.
   3962 int expand_filename(exarg_T *eap, char **cmdlinep, const char **errormsgp)
   3963 {
   3964  // Skip a regexp pattern for ":vimgrep[add] pat file..."
   3965  char *p = skip_grep_pat(eap);
   3966 
   3967  // Decide to expand wildcards *before* replacing '%', '#', etc.  If
   3968  // the file name contains a wildcard it should not cause expanding.
   3969  // (it will be expanded anyway if there is a wildcard before replacing).
   3970  bool has_wildcards = path_has_wildcard(p);
   3971  while (*p != NUL) {
   3972    // Skip over `=expr`, wildcards in it are not expanded.
   3973    if (p[0] == '`' && p[1] == '=') {
   3974      p += 2;
   3975      skip_expr(&p, NULL);
   3976      if (*p == '`') {
   3977        p++;
   3978      }
   3979      continue;
   3980    }
   3981    // Quick check if this cannot be the start of a special string.
   3982    // Also removes backslash before '%', '#' and '<'.
   3983    if (vim_strchr("%#<", (uint8_t)(*p)) == NULL) {
   3984      p++;
   3985      continue;
   3986    }
   3987 
   3988    // Try to find a match at this position.
   3989    size_t srclen;
   3990    int escaped;
   3991    char *repl = eval_vars(p, eap->arg, &srclen, &(eap->do_ecmd_lnum),
   3992                           errormsgp, &escaped, true);
   3993    if (*errormsgp != NULL) {           // error detected
   3994      return FAIL;
   3995    }
   3996    if (repl == NULL) {                 // no match found
   3997      p += srclen;
   3998      continue;
   3999    }
   4000 
   4001    // Wildcards won't be expanded below, the replacement is taken
   4002    // literally.  But do expand "~/file", "~user/file" and "$HOME/file".
   4003    if (vim_strchr(repl, '$') != NULL || vim_strchr(repl, '~') != NULL) {
   4004      char *l = repl;
   4005 
   4006      repl = expand_env_save(repl);
   4007      xfree(l);
   4008    }
   4009 
   4010    // Need to escape white space et al. with a backslash.
   4011    // Don't do this for:
   4012    // - replacement that already has been escaped: "##"
   4013    // - shell commands (may have to use quotes instead).
   4014    if (!eap->usefilter
   4015        && !escaped
   4016        && eap->cmdidx != CMD_bang
   4017        && eap->cmdidx != CMD_grep
   4018        && eap->cmdidx != CMD_grepadd
   4019        && eap->cmdidx != CMD_lgrep
   4020        && eap->cmdidx != CMD_lgrepadd
   4021        && eap->cmdidx != CMD_lmake
   4022        && eap->cmdidx != CMD_make
   4023        && eap->cmdidx != CMD_terminal
   4024        && !(eap->argt & EX_NOSPC)) {
   4025      char *l;
   4026 #ifdef BACKSLASH_IN_FILENAME
   4027      // Don't escape a backslash here, because rem_backslash() doesn't
   4028      // remove it later.
   4029      static char *nobslash = " \t\"|";
   4030 # define ESCAPE_CHARS nobslash
   4031 #else
   4032 # define ESCAPE_CHARS escape_chars
   4033 #endif
   4034 
   4035      for (l = repl; *l; l++) {
   4036        if (vim_strchr(ESCAPE_CHARS, (uint8_t)(*l)) != NULL) {
   4037          l = vim_strsave_escaped(repl, ESCAPE_CHARS);
   4038          xfree(repl);
   4039          repl = l;
   4040          break;
   4041        }
   4042      }
   4043    }
   4044 
   4045    // For a shell command a '!' must be escaped.
   4046    if ((eap->usefilter
   4047         || eap->cmdidx == CMD_bang
   4048         || eap->cmdidx == CMD_terminal)
   4049        && strpbrk(repl, "!") != NULL) {
   4050      char *l = vim_strsave_escaped(repl, "!");
   4051      xfree(repl);
   4052      repl = l;
   4053    }
   4054 
   4055    p = repl_cmdline(eap, p, srclen, repl, cmdlinep);
   4056    xfree(repl);
   4057  }
   4058 
   4059  // One file argument: Expand wildcards.
   4060  // Don't do this with ":r !command" or ":w !command".
   4061  if ((eap->argt & EX_NOSPC) && !eap->usefilter) {
   4062    // Replace environment variables.
   4063    if (has_wildcards) {
   4064      // May expand environment variables.  This
   4065      // can be done much faster with expand_env() than with
   4066      // something else (e.g., calling a shell).
   4067      // After expanding environment variables, check again
   4068      // if there are still wildcards present.
   4069      if (vim_strchr(eap->arg, '$') != NULL
   4070          || vim_strchr(eap->arg, '~') != NULL) {
   4071        expand_env_esc(eap->arg, NameBuff, MAXPATHL, true, true, NULL);
   4072        has_wildcards = path_has_wildcard(NameBuff);
   4073        p = NameBuff;
   4074      } else {
   4075        p = NULL;
   4076      }
   4077      if (p != NULL) {
   4078        repl_cmdline(eap, eap->arg, strlen(eap->arg), p, cmdlinep);
   4079      }
   4080    }
   4081 
   4082    // Halve the number of backslashes (this is Vi compatible).
   4083    // For Unix, when wildcards are expanded, this is
   4084    // done by ExpandOne() below.
   4085 #ifdef UNIX
   4086    if (!has_wildcards) {
   4087      backslash_halve(eap->arg);
   4088    }
   4089 #else
   4090    backslash_halve(eap->arg);
   4091 #endif
   4092 
   4093    if (has_wildcards) {
   4094      expand_T xpc;
   4095      int options = WILD_LIST_NOTFOUND | WILD_NOERROR | WILD_ADD_SLASH;
   4096 
   4097      ExpandInit(&xpc);
   4098      xpc.xp_context = EXPAND_FILES;
   4099      if (p_wic) {
   4100        options += WILD_ICASE;
   4101      }
   4102      p = ExpandOne(&xpc, eap->arg, NULL, options, WILD_EXPAND_FREE);
   4103      if (p == NULL) {
   4104        return FAIL;
   4105      }
   4106      repl_cmdline(eap, eap->arg, strlen(eap->arg), p, cmdlinep);
   4107      xfree(p);
   4108    }
   4109  }
   4110  return OK;
   4111 }
   4112 
   4113 /// Replace part of the command line, keeping eap->cmd, eap->arg, eap->args and
   4114 /// eap->nextcmd correct.
   4115 /// "src" points to the part that is to be replaced, of length "srclen".
   4116 /// "repl" is the replacement string.
   4117 ///
   4118 /// @return  a pointer to the character after the replaced string.
   4119 static char *repl_cmdline(exarg_T *eap, char *src, size_t srclen, char *repl, char **cmdlinep)
   4120 {
   4121  // The new command line is build in new_cmdline[].
   4122  // First allocate it.
   4123  // Careful: a "+cmd" argument may have been NUL terminated.
   4124  size_t len = strlen(repl);
   4125  size_t i = (size_t)(src - *cmdlinep) + strlen(src + srclen) + len + 3;
   4126  if (eap->nextcmd != NULL) {
   4127    i += strlen(eap->nextcmd);    // add space for next command
   4128  }
   4129  char *new_cmdline = xmalloc(i);
   4130  size_t offset = (size_t)(src - *cmdlinep);
   4131 
   4132  // Copy the stuff before the expanded part.
   4133  // Copy the expanded stuff.
   4134  // Copy what came after the expanded part.
   4135  // Copy the next commands, if there are any.
   4136  i = offset;   // length of part before match
   4137  memmove(new_cmdline, *cmdlinep, i);
   4138 
   4139  memmove(new_cmdline + i, repl, len);
   4140  i += len;                             // remember the end of the string
   4141  STRCPY(new_cmdline + i, src + srclen);
   4142  src = new_cmdline + i;                // remember where to continue
   4143 
   4144  if (eap->nextcmd != NULL) {           // append next command
   4145    i = strlen(new_cmdline) + 1;
   4146    STRCPY(new_cmdline + i, eap->nextcmd);
   4147    eap->nextcmd = new_cmdline + i;
   4148  }
   4149  eap->cmd = new_cmdline + (eap->cmd - *cmdlinep);
   4150  eap->arg = new_cmdline + (eap->arg - *cmdlinep);
   4151 
   4152  for (size_t j = 0; j < eap->argc; j++) {
   4153    if (offset >= (size_t)(eap->args[j] - *cmdlinep)) {
   4154      // If replaced text is after or in the same position as the argument,
   4155      // the argument's position relative to the beginning of the cmdline stays the same.
   4156      eap->args[j] = new_cmdline + (eap->args[j] - *cmdlinep);
   4157    } else {
   4158      // Otherwise, argument gets shifted alongside the replaced text.
   4159      // The amount of the shift is equal to the difference of the old and new string length.
   4160      eap->args[j] = new_cmdline + ((eap->args[j] - *cmdlinep) + (ptrdiff_t)(len - srclen));
   4161    }
   4162  }
   4163 
   4164  if (eap->do_ecmd_cmd != NULL && eap->do_ecmd_cmd != dollar_command) {
   4165    eap->do_ecmd_cmd = new_cmdline + (eap->do_ecmd_cmd - *cmdlinep);
   4166  }
   4167  xfree(*cmdlinep);
   4168  *cmdlinep = new_cmdline;
   4169 
   4170  return src;
   4171 }
   4172 
   4173 /// Check for '|' to separate commands and '"' to start comments.
   4174 void separate_nextcmd(exarg_T *eap)
   4175 {
   4176  char *p = skip_grep_pat(eap);
   4177 
   4178  for (; *p; MB_PTR_ADV(p)) {
   4179    if (*p == Ctrl_V) {
   4180      if (eap->argt & (EX_CTRLV | EX_XFILE)) {
   4181        p++;  // skip CTRL-V and next char
   4182      } else {
   4183        // remove CTRL-V and skip next char
   4184        STRMOVE(p, p + 1);
   4185      }
   4186      if (*p == NUL) {  // stop at NUL after CTRL-V
   4187        break;
   4188      }
   4189    } else if (p[0] == '`' && p[1] == '=' && (eap->argt & EX_XFILE)) {
   4190      // Skip over `=expr` when wildcards are expanded.
   4191      p += 2;
   4192      skip_expr(&p, NULL);
   4193      if (*p == NUL) {  // stop at NUL after CTRL-V
   4194        break;
   4195      }
   4196    } else if (
   4197               // Check for '"': start of comment or '|': next command
   4198               // :@" does not start a comment!
   4199               // :redir @" doesn't either.
   4200               (*p == '"'
   4201                && !(eap->argt & EX_NOTRLCOM)
   4202                && (eap->cmdidx != CMD_at || p != eap->arg)
   4203                && (eap->cmdidx != CMD_redir
   4204                    || p != eap->arg + 1 || p[-1] != '@'))
   4205               || (*p == '|'
   4206                   && eap->cmdidx != CMD_append
   4207                   && eap->cmdidx != CMD_change
   4208                   && eap->cmdidx != CMD_insert)
   4209               || *p == '\n') {
   4210      // We remove the '\' before the '|', unless EX_CTRLV is used
   4211      // AND 'b' is present in 'cpoptions'.
   4212      if ((vim_strchr(p_cpo, CPO_BAR) == NULL
   4213           || !(eap->argt & EX_CTRLV)) && *(p - 1) == '\\') {
   4214        STRMOVE(p - 1, p);  // remove the '\'
   4215        p--;
   4216      } else {
   4217        eap->nextcmd = check_nextcmd(p);
   4218        *p = NUL;
   4219        break;
   4220      }
   4221    }
   4222  }
   4223 
   4224  if (!(eap->argt & EX_NOTRLCOM)) {  // remove trailing spaces
   4225    del_trailing_spaces(eap->arg);
   4226  }
   4227 }
   4228 
   4229 /// get + command from ex argument
   4230 char *getargcmd(char **argp)
   4231 {
   4232  char *arg = *argp;
   4233  char *command = NULL;
   4234 
   4235  if (*arg == '+') {        // +[command]
   4236    arg++;
   4237    if (ascii_isspace(*arg) || *arg == NUL) {
   4238      command = dollar_command;
   4239    } else {
   4240      command = arg;
   4241      arg = skip_cmd_arg(command, true);
   4242      if (*arg != NUL) {
   4243        *arg++ = NUL;                   // terminate command with NUL
   4244      }
   4245    }
   4246 
   4247    arg = skipwhite(arg);       // skip over spaces
   4248    *argp = arg;
   4249  }
   4250  return command;
   4251 }
   4252 
   4253 /// Find end of "+command" argument.  Skip over "\ " and "\\".
   4254 ///
   4255 /// @param rembs  true to halve the number of backslashes
   4256 char *skip_cmd_arg(char *p, bool rembs)
   4257 {
   4258  while (*p && !ascii_isspace(*p)) {
   4259    if (*p == '\\' && p[1] != NUL) {
   4260      if (rembs) {
   4261        STRMOVE(p, p + 1);
   4262      } else {
   4263        p++;
   4264      }
   4265    }
   4266    MB_PTR_ADV(p);
   4267  }
   4268  return p;
   4269 }
   4270 
   4271 int get_bad_opt(const char *p, exarg_T *eap)
   4272  FUNC_ATTR_NONNULL_ALL
   4273 {
   4274  if (STRICMP(p, "keep") == 0) {
   4275    eap->bad_char = BAD_KEEP;
   4276  } else if (STRICMP(p, "drop") == 0) {
   4277    eap->bad_char = BAD_DROP;
   4278  } else if (MB_BYTE2LEN((uint8_t)(*p)) == 1 && p[1] == NUL) {
   4279    eap->bad_char = (uint8_t)(*p);
   4280  } else {
   4281    return FAIL;
   4282  }
   4283  return OK;
   4284 }
   4285 
   4286 /// Function given to ExpandGeneric() to obtain the list of bad= names.
   4287 static char *get_bad_name(expand_T *xp FUNC_ATTR_UNUSED, int idx)
   4288 {
   4289  // Note: Keep this in sync with get_bad_opt().
   4290  static char *(p_bad_values[]) = {
   4291    "?",
   4292    "keep",
   4293    "drop",
   4294  };
   4295 
   4296  if (idx < (int)ARRAY_SIZE(p_bad_values)) {
   4297    return p_bad_values[idx];
   4298  }
   4299  return NULL;
   4300 }
   4301 
   4302 /// Get "++opt=arg" argument.
   4303 ///
   4304 /// @return  FAIL or OK.
   4305 int getargopt(exarg_T *eap)
   4306 {
   4307  char *arg = eap->arg + 2;
   4308  int *pp = NULL;
   4309  int bad_char_idx;
   4310 
   4311  // Note: Keep this in sync with get_argopt_name.
   4312 
   4313  // ":edit ++[no]bin[ary] file"
   4314  if (strncmp(arg, "bin", 3) == 0 || strncmp(arg, "nobin", 5) == 0) {
   4315    if (*arg == 'n') {
   4316      arg += 2;
   4317      eap->force_bin = FORCE_NOBIN;
   4318    } else {
   4319      eap->force_bin = FORCE_BIN;
   4320    }
   4321    if (!checkforcmd(&arg, "binary", 3)) {
   4322      return FAIL;
   4323    }
   4324    eap->arg = skipwhite(arg);
   4325    return OK;
   4326  }
   4327 
   4328  // ":read ++edit file"
   4329  if (strncmp(arg, "edit", 4) == 0) {
   4330    eap->read_edit = true;
   4331    eap->arg = skipwhite(arg + 4);
   4332    return OK;
   4333  }
   4334 
   4335  // ":write ++p foo/bar/file
   4336  if (strncmp(arg, "p", 1) == 0) {
   4337    eap->mkdir_p = true;
   4338    eap->arg = skipwhite(arg + 1);
   4339    return OK;
   4340  }
   4341 
   4342  if (strncmp(arg, "ff", 2) == 0) {
   4343    arg += 2;
   4344    pp = &eap->force_ff;
   4345  } else if (strncmp(arg, "fileformat", 10) == 0) {
   4346    arg += 10;
   4347    pp = &eap->force_ff;
   4348  } else if (strncmp(arg, "enc", 3) == 0) {
   4349    if (strncmp(arg, "encoding", 8) == 0) {
   4350      arg += 8;
   4351    } else {
   4352      arg += 3;
   4353    }
   4354    pp = &eap->force_enc;
   4355  } else if (strncmp(arg, "bad", 3) == 0) {
   4356    arg += 3;
   4357    pp = &bad_char_idx;
   4358  }
   4359 
   4360  if (pp == NULL || *arg != '=') {
   4361    return FAIL;
   4362  }
   4363 
   4364  arg++;
   4365  *pp = (int)(arg - eap->cmd);
   4366  arg = skip_cmd_arg(arg, false);
   4367  eap->arg = skipwhite(arg);
   4368  *arg = NUL;
   4369 
   4370  if (pp == &eap->force_ff) {
   4371    if (check_ff_value(eap->cmd + eap->force_ff) == FAIL) {
   4372      return FAIL;
   4373    }
   4374    eap->force_ff = (uint8_t)eap->cmd[eap->force_ff];
   4375  } else if (pp == &eap->force_enc) {
   4376    // Make 'fileencoding' lower case.
   4377    for (char *p = eap->cmd + eap->force_enc; *p != NUL; p++) {
   4378      *p = (char)TOLOWER_ASC(*p);
   4379    }
   4380  } else {
   4381    // Check ++bad= argument.  Must be a single-byte character, "keep" or
   4382    // "drop".
   4383    if (get_bad_opt(eap->cmd + bad_char_idx, eap) == FAIL) {
   4384      return FAIL;
   4385    }
   4386  }
   4387 
   4388  return OK;
   4389 }
   4390 
   4391 /// Function given to ExpandGeneric() to obtain the list of ++opt names.
   4392 static char *get_argopt_name(expand_T *xp FUNC_ATTR_UNUSED, int idx)
   4393 {
   4394  // Note: Keep this in sync with getargopt().
   4395  static char *(p_opt_values[]) = {
   4396    "fileformat=",
   4397    "encoding=",
   4398    "binary",
   4399    "nobinary",
   4400    "bad=",
   4401    "edit",
   4402    "p",
   4403  };
   4404 
   4405  if (idx < (int)ARRAY_SIZE(p_opt_values)) {
   4406    return p_opt_values[idx];
   4407  }
   4408  return NULL;
   4409 }
   4410 
   4411 /// Command-line expansion for ++opt=name.
   4412 int expand_argopt(char *pat, expand_T *xp, regmatch_T *rmp, char ***matches, int *numMatches)
   4413 {
   4414  if (xp->xp_pattern > xp->xp_line && *(xp->xp_pattern - 1) == '=') {
   4415    CompleteListItemGetter cb = NULL;
   4416 
   4417    char *name_end = xp->xp_pattern - 1;
   4418    if (name_end - xp->xp_line >= 2
   4419        && strncmp(name_end - 2, "ff", 2) == 0) {
   4420      cb = get_fileformat_name;
   4421    } else if (name_end - xp->xp_line >= 10
   4422               && strncmp(name_end - 10, "fileformat", 10) == 0) {
   4423      cb = get_fileformat_name;
   4424    } else if (name_end - xp->xp_line >= 3
   4425               && strncmp(name_end - 3, "enc", 3) == 0) {
   4426      cb = get_encoding_name;
   4427    } else if (name_end - xp->xp_line >= 8
   4428               && strncmp(name_end - 8, "encoding", 8) == 0) {
   4429      cb = get_encoding_name;
   4430    } else if (name_end - xp->xp_line >= 3
   4431               && strncmp(name_end - 3, "bad", 3) == 0) {
   4432      cb = get_bad_name;
   4433    }
   4434 
   4435    if (cb != NULL) {
   4436      ExpandGeneric(pat, xp, rmp, matches, numMatches, cb, false);
   4437      return OK;
   4438    }
   4439    return FAIL;
   4440  }
   4441 
   4442  // Special handling of "ff" which acts as a short form of
   4443  // "fileformat", as "ff" is not a substring of it.
   4444  if (xp->xp_pattern_len == 2
   4445      && strncmp(xp->xp_pattern, "ff", xp->xp_pattern_len) == 0) {
   4446    *matches = xmalloc(sizeof(char *));
   4447    *numMatches = 1;
   4448    (*matches)[0] = xstrdup("fileformat=");
   4449    return OK;
   4450  }
   4451 
   4452  ExpandGeneric(pat, xp, rmp, matches, numMatches, get_argopt_name, false);
   4453  return OK;
   4454 }
   4455 
   4456 /// Handle the argument for a tabpage related ex command.
   4457 /// When an error is encountered then eap->errmsg is set.
   4458 ///
   4459 /// @return  a tabpage number.
   4460 static int get_tabpage_arg(exarg_T *eap)
   4461 {
   4462  int tab_number = 0;
   4463  int unaccept_arg0 = (eap->cmdidx == CMD_tabmove) ? 0 : 1;
   4464 
   4465  if (eap->arg && *eap->arg != NUL) {
   4466    char *p = eap->arg;
   4467    int relative = 0;  // argument +N/-N means: go to N places to the
   4468                       // right/left relative to the current position.
   4469 
   4470    if (*p == '-') {
   4471      relative = -1;
   4472      p++;
   4473    } else if (*p == '+') {
   4474      relative = 1;
   4475      p++;
   4476    }
   4477 
   4478    char *p_save = p;
   4479    tab_number = (int)getdigits(&p, false, tab_number);
   4480 
   4481    if (relative == 0) {
   4482      if (strcmp(p, "$") == 0) {
   4483        tab_number = LAST_TAB_NR;
   4484      } else if (strcmp(p, "#") == 0) {
   4485        if (valid_tabpage(lastused_tabpage)) {
   4486          tab_number = tabpage_index(lastused_tabpage);
   4487        } else {
   4488          eap->errmsg = ex_errmsg(e_invargval, eap->arg);
   4489          tab_number = 0;
   4490          goto theend;
   4491        }
   4492      } else if (p == p_save || *p_save == '-' || *p != NUL
   4493                 || tab_number > LAST_TAB_NR) {
   4494        // No numbers as argument.
   4495        eap->errmsg = ex_errmsg(e_invarg2, eap->arg);
   4496        goto theend;
   4497      }
   4498    } else {
   4499      if (*p_save == NUL) {
   4500        tab_number = 1;
   4501      } else if (p == p_save || *p_save == '-' || *p != NUL || tab_number == 0) {
   4502        // No numbers as argument.
   4503        eap->errmsg = ex_errmsg(e_invarg2, eap->arg);
   4504        goto theend;
   4505      }
   4506      tab_number = tab_number * relative + tabpage_index(curtab);
   4507      if (!unaccept_arg0 && relative == -1) {
   4508        tab_number--;
   4509      }
   4510    }
   4511    if (tab_number < unaccept_arg0 || tab_number > LAST_TAB_NR) {
   4512      eap->errmsg = ex_errmsg(e_invarg2, eap->arg);
   4513    }
   4514  } else if (eap->addr_count > 0) {
   4515    if (unaccept_arg0 && eap->line2 == 0) {
   4516      eap->errmsg = _(e_invrange);
   4517      tab_number = 0;
   4518    } else {
   4519      tab_number = (int)eap->line2;
   4520      if (!unaccept_arg0) {
   4521        char *cmdp = eap->cmd;
   4522        while (--cmdp > *eap->cmdlinep
   4523               && (ascii_iswhite(*cmdp) || ascii_isdigit(*cmdp))) {}
   4524        if (*cmdp == '-') {
   4525          tab_number--;
   4526          if (tab_number < unaccept_arg0) {
   4527            eap->errmsg = _(e_invrange);
   4528          }
   4529        }
   4530      }
   4531    }
   4532  } else {
   4533    switch (eap->cmdidx) {
   4534    case CMD_tabnext:
   4535      tab_number = tabpage_index(curtab) + 1;
   4536      if (tab_number > LAST_TAB_NR) {
   4537        tab_number = 1;
   4538      }
   4539      break;
   4540    case CMD_tabmove:
   4541      tab_number = LAST_TAB_NR;
   4542      break;
   4543    default:
   4544      tab_number = tabpage_index(curtab);
   4545    }
   4546  }
   4547 
   4548 theend:
   4549  return tab_number;
   4550 }
   4551 
   4552 static void ex_autocmd(exarg_T *eap)
   4553 {
   4554  // Disallow autocommands in secure mode.
   4555  if (secure) {
   4556    secure = 2;
   4557    eap->errmsg = _(e_curdir);
   4558  } else if (eap->cmdidx == CMD_autocmd) {
   4559    do_autocmd(eap, eap->arg, eap->forceit);
   4560  } else {
   4561    do_augroup(eap->arg, eap->forceit);
   4562  }
   4563 }
   4564 
   4565 /// ":doautocmd": Apply the automatic commands to the current buffer.
   4566 static void ex_doautocmd(exarg_T *eap)
   4567 {
   4568  char *arg = eap->arg;
   4569  int call_do_modelines = check_nomodeline(&arg);
   4570  bool did_aucmd;
   4571 
   4572  do_doautocmd(arg, false, &did_aucmd);
   4573  // Only when there is no <nomodeline>.
   4574  if (call_do_modelines && did_aucmd) {
   4575    do_modelines(0);
   4576  }
   4577 }
   4578 
   4579 /// :[N]bunload[!] [N] [bufname] unload buffer
   4580 /// :[N]bdelete[!] [N] [bufname] delete buffer from buffer list
   4581 /// :[N]bwipeout[!] [N] [bufname] delete buffer really
   4582 static void ex_bunload(exarg_T *eap)
   4583 {
   4584  eap->errmsg = do_bufdel(eap->cmdidx == CMD_bdelete
   4585                          ? DOBUF_DEL
   4586                          : eap->cmdidx == CMD_bwipeout
   4587                          ? DOBUF_WIPE
   4588                          : DOBUF_UNLOAD,
   4589                          eap->arg, eap->addr_count, (int)eap->line1, (int)eap->line2,
   4590                          eap->forceit);
   4591 }
   4592 
   4593 /// :[N]buffer [N]       to buffer N
   4594 /// :[N]sbuffer [N]      to buffer N
   4595 static void ex_buffer(exarg_T *eap)
   4596 {
   4597  do_exbuffer(eap);
   4598 }
   4599 
   4600 /// ":buffer" command and alike.
   4601 static void do_exbuffer(exarg_T *eap)
   4602 {
   4603  if (*eap->arg) {
   4604    eap->errmsg = ex_errmsg(e_trailing_arg, eap->arg);
   4605  } else {
   4606    if (eap->addr_count == 0) {  // default is current buffer
   4607      goto_buffer(eap, DOBUF_CURRENT, FORWARD, 0);
   4608    } else {
   4609      goto_buffer(eap, DOBUF_FIRST, FORWARD, (int)eap->line2);
   4610    }
   4611    if (eap->do_ecmd_cmd != NULL) {
   4612      do_cmdline_cmd(eap->do_ecmd_cmd);
   4613    }
   4614  }
   4615 }
   4616 
   4617 /// :[N]bmodified [N]    to next mod. buffer
   4618 /// :[N]sbmodified [N]   to next mod. buffer
   4619 static void ex_bmodified(exarg_T *eap)
   4620 {
   4621  goto_buffer(eap, DOBUF_MOD, FORWARD, (int)eap->line2);
   4622  if (eap->do_ecmd_cmd != NULL) {
   4623    do_cmdline_cmd(eap->do_ecmd_cmd);
   4624  }
   4625 }
   4626 
   4627 /// :[N]bnext [N]        to next buffer
   4628 /// :[N]sbnext [N]       split and to next buffer
   4629 static void ex_bnext(exarg_T *eap)
   4630 {
   4631  goto_buffer(eap, DOBUF_CURRENT, FORWARD, (int)eap->line2);
   4632  if (eap->do_ecmd_cmd != NULL) {
   4633    do_cmdline_cmd(eap->do_ecmd_cmd);
   4634  }
   4635 }
   4636 
   4637 /// :[N]bNext [N]        to previous buffer
   4638 /// :[N]bprevious [N]    to previous buffer
   4639 /// :[N]sbNext [N]       split and to previous buffer
   4640 /// :[N]sbprevious [N]   split and to previous buffer
   4641 static void ex_bprevious(exarg_T *eap)
   4642 {
   4643  goto_buffer(eap, DOBUF_CURRENT, BACKWARD, (int)eap->line2);
   4644  if (eap->do_ecmd_cmd != NULL) {
   4645    do_cmdline_cmd(eap->do_ecmd_cmd);
   4646  }
   4647 }
   4648 
   4649 /// :brewind             to first buffer
   4650 /// :bfirst              to first buffer
   4651 /// :sbrewind            split and to first buffer
   4652 /// :sbfirst             split and to first buffer
   4653 static void ex_brewind(exarg_T *eap)
   4654 {
   4655  goto_buffer(eap, DOBUF_FIRST, FORWARD, 0);
   4656  if (eap->do_ecmd_cmd != NULL) {
   4657    do_cmdline_cmd(eap->do_ecmd_cmd);
   4658  }
   4659 }
   4660 
   4661 /// :blast               to last buffer
   4662 /// :sblast              split and to last buffer
   4663 static void ex_blast(exarg_T *eap)
   4664 {
   4665  goto_buffer(eap, DOBUF_LAST, BACKWARD, 0);
   4666  if (eap->do_ecmd_cmd != NULL) {
   4667    do_cmdline_cmd(eap->do_ecmd_cmd);
   4668  }
   4669 }
   4670 
   4671 int ends_excmd(int c) FUNC_ATTR_CONST
   4672 {
   4673  return c == NUL || c == '|' || c == '"' || c == '\n';
   4674 }
   4675 
   4676 /// @return  the next command, after the first '|' or '\n' or,
   4677 ///          NULL if not found.
   4678 char *find_nextcmd(const char *p)
   4679 {
   4680  while (*p != '|' && *p != '\n') {
   4681    if (*p == NUL) {
   4682      return NULL;
   4683    }
   4684    p++;
   4685  }
   4686  return (char *)p + 1;
   4687 }
   4688 
   4689 /// Check if *p is a separator between Ex commands, skipping over white space.
   4690 ///
   4691 /// @return  NULL if it isn't, the following character if it is.
   4692 char *check_nextcmd(char *p)
   4693 {
   4694  char *s = skipwhite(p);
   4695 
   4696  if (*s == '|' || *s == '\n') {
   4697    return s + 1;
   4698  }
   4699  return NULL;
   4700 }
   4701 
   4702 /// - if there are more files to edit
   4703 /// - and this is the last window
   4704 /// - and forceit not used
   4705 /// - and not repeated twice on a row
   4706 ///
   4707 /// @param   message  when false check only, no messages
   4708 ///
   4709 /// @return  FAIL and give error message if 'message' true, return OK otherwise
   4710 static int check_more(bool message, bool forceit)
   4711 {
   4712  int n = ARGCOUNT - curwin->w_arg_idx - 1;
   4713 
   4714  if (!forceit && only_one_window()
   4715      && ARGCOUNT > 1 && !arg_had_last && n > 0 && quitmore == 0) {
   4716    if (message) {
   4717      if ((p_confirm || (cmdmod.cmod_flags & CMOD_CONFIRM)) && curbuf->b_fname != NULL) {
   4718        char buff[DIALOG_MSG_SIZE];
   4719 
   4720        vim_snprintf(buff, DIALOG_MSG_SIZE,
   4721                     NGETTEXT("%d more file to edit.  Quit anyway?",
   4722                              "%d more files to edit.  Quit anyway?", n), n);
   4723        if (vim_dialog_yesno(VIM_QUESTION, NULL, buff, 1) == VIM_YES) {
   4724          return OK;
   4725        }
   4726        return FAIL;
   4727      }
   4728      semsg(NGETTEXT("E173: %d more file to edit",
   4729                     "E173: %d more files to edit", n), n);
   4730      quitmore = 2;                 // next try to quit is allowed
   4731    }
   4732    return FAIL;
   4733  }
   4734  return OK;
   4735 }
   4736 
   4737 /// Function given to ExpandGeneric() to obtain the list of command names.
   4738 char *get_command_name(expand_T *xp, int idx)
   4739 {
   4740  if (idx >= CMD_SIZE) {
   4741    return expand_user_command_name(idx);
   4742  }
   4743  return cmdnames[idx].cmd_name;
   4744 }
   4745 
   4746 static void ex_colorscheme(exarg_T *eap)
   4747 {
   4748  if (*eap->arg == NUL) {
   4749    char *expr = xstrdup("g:colors_name");
   4750 
   4751    emsg_off++;
   4752    char *p = eval_to_string(expr, false, false);
   4753    emsg_off--;
   4754    xfree(expr);
   4755 
   4756    msg_ext_set_kind("list_cmd");
   4757    if (p != NULL) {
   4758      msg(p, 0);
   4759      xfree(p);
   4760    } else {
   4761      msg("default", 0);
   4762    }
   4763  } else if (load_colors(eap->arg) == FAIL) {
   4764    semsg(_("E185: Cannot find color scheme '%s'"), eap->arg);
   4765  }
   4766 }
   4767 
   4768 static void ex_highlight(exarg_T *eap)
   4769 {
   4770  if (*eap->arg == NUL && eap->cmd[2] == '!') {
   4771    msg(_("Greetings, Vim user!"), 0);
   4772  }
   4773  do_highlight(eap->arg, eap->forceit, false);
   4774 }
   4775 
   4776 /// Call this function if we thought we were going to exit, but we won't
   4777 /// (because of an error).  May need to restore the terminal mode.
   4778 void not_exiting(bool save_exiting)
   4779 {
   4780  exiting = save_exiting;
   4781 }
   4782 
   4783 /// Call this function if we thought we were going to restart, but we won't
   4784 /// (because of an error).
   4785 void not_restarting(void)
   4786 {
   4787  restarting = false;
   4788 }
   4789 
   4790 bool before_quit_autocmds(win_T *wp, bool quit_all, bool forceit)
   4791 {
   4792  apply_autocmds(EVENT_QUITPRE, NULL, NULL, false, wp->w_buffer);
   4793 
   4794  // Bail out when autocommands closed the window.
   4795  // Refuse to quit when the buffer in the last window is being closed (can
   4796  // only happen in autocommands).
   4797  if (!win_valid(wp)
   4798      || curbuf_locked()
   4799      || (wp->w_buffer->b_nwindows == 1 && wp->w_buffer->b_locked > 0)) {
   4800    return true;
   4801  }
   4802 
   4803  if (quit_all
   4804      || (check_more(false, forceit) == OK && only_one_window())) {
   4805    apply_autocmds(EVENT_EXITPRE, NULL, NULL, false, curbuf);
   4806    // Refuse to quit when locked or when the window was closed or the
   4807    // buffer in the last window is being closed (can only happen in
   4808    // autocommands).
   4809    if (!win_valid(wp)
   4810        || curbuf_locked()
   4811        || (curbuf->b_nwindows == 1 && curbuf->b_locked > 0)) {
   4812      return true;
   4813    }
   4814  }
   4815 
   4816  return false;
   4817 }
   4818 
   4819 /// ":quit": quit current window, quit Vim if the last window is closed.
   4820 /// ":{nr}quit": quit window {nr}
   4821 static void ex_quit(exarg_T *eap)
   4822 {
   4823  if (cmdwin_type != 0) {
   4824    cmdwin_result = Ctrl_C;
   4825    return;
   4826  }
   4827  // Don't quit while editing the command line.
   4828  if (text_locked()) {
   4829    text_locked_msg();
   4830    return;
   4831  }
   4832 
   4833  win_T *wp;
   4834 
   4835  if (eap->addr_count > 0) {
   4836    linenr_T wnr = eap->line2;
   4837 
   4838    for (wp = firstwin; wp->w_next != NULL; wp = wp->w_next) {
   4839      if (--wnr <= 0) {
   4840        break;
   4841      }
   4842    }
   4843  } else {
   4844    wp = curwin;
   4845  }
   4846 
   4847  // Refuse to quit when locked.
   4848  if (curbuf_locked()) {
   4849    return;
   4850  }
   4851 
   4852  // Trigger QuitPre and maybe ExitPre
   4853  if (before_quit_autocmds(wp, false, eap->forceit)) {
   4854    return;
   4855  }
   4856 
   4857  bool save_exiting = exiting;
   4858  // If there is only one relevant window we will exit.
   4859  if (check_more(false, eap->forceit) == OK && only_one_window()) {
   4860    exiting = true;
   4861  }
   4862  if ((!buf_hide(wp->w_buffer)
   4863       && check_changed(wp->w_buffer, (p_awa ? CCGD_AW : 0)
   4864                        | (eap->forceit ? CCGD_FORCEIT : 0)
   4865                        | CCGD_EXCMD))
   4866      || check_more(true, eap->forceit) == FAIL
   4867      || (only_one_window() && check_changed_any(eap->forceit, true))) {
   4868    not_exiting(save_exiting);
   4869  } else {
   4870    // quit last window
   4871    // Note: only_one_window() returns true, even so a help window is
   4872    // still open. In that case only quit, if no address has been
   4873    // specified. Example:
   4874    // :h|wincmd w|1q     - don't quit
   4875    // :h|wincmd w|q      - quit
   4876    if (only_one_window() && (ONE_WINDOW || eap->addr_count == 0)) {
   4877      getout(0);
   4878    }
   4879    not_exiting(save_exiting);
   4880    // close window; may free buffer
   4881    win_close(wp, !buf_hide(wp->w_buffer) || eap->forceit, eap->forceit);
   4882  }
   4883 }
   4884 
   4885 /// ":cquit".
   4886 static void ex_cquit(exarg_T *eap)
   4887  FUNC_ATTR_NORETURN
   4888 {
   4889  // this does not always pass on the exit code to the Manx compiler. why?
   4890  int status = eap->addr_count > 0 ? (int)eap->line2 : EXIT_FAILURE;
   4891  ui_call_error_exit(status);
   4892  getout(status);
   4893 }
   4894 
   4895 /// Do preparations for "qall" and "wqall".
   4896 /// Returns FAIL when quitting should be aborted.
   4897 int before_quit_all(exarg_T *eap)
   4898 {
   4899  if (cmdwin_type != 0) {
   4900    cmdwin_result = eap->forceit
   4901                    ? K_XF1  // open_cmdwin() takes care of this
   4902                    : K_XF2;
   4903    return FAIL;
   4904  }
   4905 
   4906  // Don't quit while editing the command line.
   4907  if (text_locked()) {
   4908    text_locked_msg();
   4909    return FAIL;
   4910  }
   4911 
   4912  if (before_quit_autocmds(curwin, true, eap->forceit)) {
   4913    return FAIL;
   4914  }
   4915 
   4916  return OK;
   4917 }
   4918 
   4919 /// ":qall": try to quit all windows
   4920 static void ex_quitall(exarg_T *eap)
   4921 {
   4922  if (before_quit_all(eap) == FAIL) {
   4923    return;
   4924  }
   4925  bool save_exiting = exiting;
   4926  exiting = true;
   4927  if (eap->forceit || !check_changed_any(false, false)) {
   4928    getout(0);
   4929  }
   4930  not_exiting(save_exiting);
   4931 }
   4932 
   4933 /// ":restart": restart the Nvim server (using ":qall!").
   4934 /// ":restart +cmd": restart the Nvim server using ":cmd".
   4935 /// ":restart +cmd <command>": restart the Nvim server using ":cmd" and add -c <command> to the new server.
   4936 static void ex_restart(exarg_T *eap)
   4937 {
   4938  const list_T *l = get_vim_var_list(VV_ARGV);
   4939  int argc = tv_list_len(l);
   4940  list_T *argv_cpy = tv_list_alloc(eap->arg ? argc + 2 : argc);
   4941 
   4942  // Copy v:argv, skipping unwanted items.
   4943  for (listitem_T *li = l != NULL ? l->lv_first : NULL; li != NULL; li = li->li_next) {
   4944    const char *arg = tv_get_string(TV_LIST_ITEM_TV(li));
   4945    size_t arg_size = strlen(arg);
   4946    assert(arg_size <= (size_t)SSIZE_MAX);
   4947 
   4948    if (strequal(arg, "--embed") || strequal(arg, "--headless")) {
   4949      continue;  // Drop --embed/--headless: the client decides how to start+attach the server.
   4950    } else if (strequal(arg, "-")) {
   4951      continue;  // Drop stdin ("-") argument.
   4952    } else if (strequal(arg, "-s")) {
   4953      // Drop "-s <scriptfile>": skip the scriptfile arg too.
   4954      if (li->li_next != NULL) {
   4955        li = li->li_next;
   4956      }
   4957      continue;
   4958    } else if (strequal(arg, "+:::")) {
   4959      // The special placeholder "+:::" marks a previous :restart command.
   4960      // Drop the `"+:::", "-c", "…"` triplet, to avoid "stacking" commands from previous :restart(s).
   4961      listitem_T *next1 = li->li_next;
   4962      if (next1 != NULL && strequal(tv_get_string(TV_LIST_ITEM_TV(next1)), "-c")) {
   4963        listitem_T *next2 = next1->li_next;
   4964        if (next2 != NULL) {
   4965          li = next2;
   4966          continue;
   4967        }
   4968      }
   4969      continue;  // If the triplet is incomplete, just skip "+:::"
   4970    } else if (strequal(arg, "--")) {
   4971      break;  // Drop "-- [files…]". Usually isn't wanted. User can :mksession instead.
   4972    }
   4973 
   4974    tv_list_append_string(argv_cpy, arg, (ssize_t)arg_size);
   4975  }
   4976  // Append `"+:::", "-c", "<command>"` to end of v:argv.
   4977  // The "+:::" item is a no-op placeholder to mark the :restart "<command>".
   4978  if (eap->arg && eap->arg[0] != '\0') {
   4979    tv_list_append_string(argv_cpy, S_LEN("+:::"));
   4980    tv_list_append_string(argv_cpy, S_LEN("-c"));
   4981    tv_list_append_string(argv_cpy, eap->arg, (ssize_t)strlen(eap->arg));
   4982  }
   4983  set_vim_var_list(VV_ARGV, argv_cpy);
   4984 
   4985  char *quit_cmd = (eap->do_ecmd_cmd) ? eap->do_ecmd_cmd : "qall";
   4986  char *quit_cmd_copy = NULL;
   4987 
   4988  // Prepend "confirm " to cmd if :confirm is used
   4989  if (cmdmod.cmod_flags & CMOD_CONFIRM) {
   4990    quit_cmd_copy = concat_str("confirm ", quit_cmd);
   4991    quit_cmd = quit_cmd_copy;
   4992  }
   4993 
   4994  Error err = ERROR_INIT;
   4995  restarting = true;
   4996  nvim_command(cstr_as_string(quit_cmd), &err);
   4997  xfree(quit_cmd_copy);
   4998  if (ERROR_SET(&err)) {
   4999    emsg(err.msg);  // Could not exit
   5000    api_clear_error(&err);
   5001    not_restarting();
   5002    return;
   5003  }
   5004  if (!exiting) {
   5005    emsg("restart failed: +cmd did not quit the server");
   5006    not_restarting();
   5007  }
   5008 }
   5009 
   5010 /// ":close": close current window, unless it is the last one
   5011 static void ex_close(exarg_T *eap)
   5012 {
   5013  win_T *win = NULL;
   5014  int winnr = 0;
   5015  if (cmdwin_type != 0) {
   5016    cmdwin_result = Ctrl_C;
   5017  } else if (!text_locked() && !curbuf_locked()) {
   5018    if (eap->addr_count == 0) {
   5019      ex_win_close(eap->forceit, curwin, NULL);
   5020    } else {
   5021      FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
   5022        winnr++;
   5023        if (winnr == eap->line2) {
   5024          win = wp;
   5025          break;
   5026        }
   5027      }
   5028      if (win == NULL) {
   5029        win = lastwin;
   5030      }
   5031      ex_win_close(eap->forceit, win, NULL);
   5032    }
   5033  }
   5034 }
   5035 
   5036 /// ":pclose": Close any preview window.
   5037 static void ex_pclose(exarg_T *eap)
   5038 {
   5039  FOR_ALL_WINDOWS_IN_TAB(win, curtab) {
   5040    if (win->w_p_pvw) {
   5041      ex_win_close(eap->forceit, win, NULL);
   5042      break;
   5043    }
   5044  }
   5045 }
   5046 
   5047 /// Close window "win" and take care of handling closing the last window for a
   5048 /// modified buffer.
   5049 ///
   5050 /// @param tp  NULL or the tab page "win" is in
   5051 void ex_win_close(int forceit, win_T *win, tabpage_T *tp)
   5052 {
   5053  // Never close the autocommand window.
   5054  if (is_aucmd_win(win)) {
   5055    emsg(_(e_autocmd_close));
   5056    return;
   5057  }
   5058  if (!win->w_floating && window_layout_locked(CMD_close)) {
   5059    return;
   5060  }
   5061 
   5062  buf_T *buf = win->w_buffer;
   5063 
   5064  bool need_hide = (bufIsChanged(buf) && buf->b_nwindows <= 1);
   5065  if (need_hide && !buf_hide(buf) && !forceit) {
   5066    if ((p_confirm || (cmdmod.cmod_flags & CMOD_CONFIRM)) && p_write) {
   5067      bufref_T bufref;
   5068      set_bufref(&bufref, buf);
   5069      dialog_changed(buf, false);
   5070      if (bufref_valid(&bufref) && bufIsChanged(buf)) {
   5071        return;
   5072      }
   5073      need_hide = false;
   5074    } else {
   5075      no_write_message();
   5076      return;
   5077    }
   5078  }
   5079 
   5080  // free buffer when not hiding it or when it's a scratch buffer
   5081  if (tp == NULL) {
   5082    win_close(win, !need_hide && !buf_hide(buf), forceit);
   5083  } else {
   5084    win_close_othertab(win, !need_hide && !buf_hide(buf), tp, forceit);
   5085  }
   5086 }
   5087 
   5088 /// ":tabclose": close current tab page, unless it is the last one.
   5089 /// ":tabclose N": close tab page N.
   5090 static void ex_tabclose(exarg_T *eap)
   5091 {
   5092  if (cmdwin_type != 0) {
   5093    cmdwin_result = K_IGNORE;
   5094    return;
   5095  }
   5096 
   5097  if (first_tabpage->tp_next == NULL) {
   5098    emsg(_("E784: Cannot close last tab page"));
   5099    return;
   5100  }
   5101 
   5102  if (window_layout_locked(CMD_tabclose)) {
   5103    return;
   5104  }
   5105 
   5106  int tab_number = get_tabpage_arg(eap);
   5107  if (eap->errmsg != NULL) {
   5108    return;
   5109  }
   5110 
   5111  tabpage_T *tp = find_tabpage(tab_number);
   5112  if (tp == NULL) {
   5113    beep_flush();
   5114    return;
   5115  }
   5116  if (tp != curtab) {
   5117    tabpage_close_other(tp, eap->forceit);
   5118    return;
   5119  } else if (!text_locked() && !curbuf_locked()) {
   5120    tabpage_close(eap->forceit);
   5121  }
   5122 }
   5123 
   5124 /// ":tabonly": close all tab pages except the current one
   5125 static void ex_tabonly(exarg_T *eap)
   5126 {
   5127  if (cmdwin_type != 0) {
   5128    cmdwin_result = K_IGNORE;
   5129    return;
   5130  }
   5131 
   5132  if (first_tabpage->tp_next == NULL) {
   5133    msg(_("Already only one tab page"), 0);
   5134    return;
   5135  }
   5136 
   5137  if (window_layout_locked(CMD_tabonly)) {
   5138    return;
   5139  }
   5140 
   5141  int tab_number = get_tabpage_arg(eap);
   5142  if (eap->errmsg != NULL) {
   5143    return;
   5144  }
   5145 
   5146  goto_tabpage(tab_number);
   5147  // Repeat this up to a 1000 times, because autocommands may
   5148  // mess up the lists.
   5149  for (int done = 0; done < 1000; done++) {
   5150    FOR_ALL_TABS(tp) {
   5151      if (tp->tp_topframe != topframe) {
   5152        tabpage_close_other(tp, eap->forceit);
   5153        // if we failed to close it quit
   5154        if (valid_tabpage(tp)) {
   5155          done = 1000;
   5156        }
   5157        // start over, "tp" is now invalid
   5158        break;
   5159      }
   5160    }
   5161    assert(first_tabpage);
   5162    if (first_tabpage->tp_next == NULL) {
   5163      break;
   5164    }
   5165  }
   5166 }
   5167 
   5168 /// Close the current tab page.
   5169 void tabpage_close(int forceit)
   5170 {
   5171  if (window_layout_locked(CMD_tabclose)) {
   5172    return;
   5173  }
   5174 
   5175  trigger_tabclosedpre(curtab);
   5176  curtab->tp_did_tabclosedpre = true;
   5177  tabpage_T *const save_curtab = curtab;
   5178 
   5179  // First close all the windows but the current one.  If that worked then
   5180  // close the last window in this tab, that will close it.
   5181  while (curwin->w_floating) {
   5182    ex_win_close(forceit, curwin, NULL);
   5183  }
   5184  if (!ONE_WINDOW) {
   5185    close_others(true, forceit);
   5186  }
   5187  if (ONE_WINDOW) {
   5188    ex_win_close(forceit, curwin, NULL);
   5189  }
   5190  if (curtab == save_curtab) {
   5191    // When closing the tab page failed, reset tp_did_tabclosedpre so that
   5192    // TabClosedPre behaves consistently on next :close vs :tabclose.
   5193    curtab->tp_did_tabclosedpre = false;
   5194  }
   5195 }
   5196 
   5197 /// Close tab page "tp", which is not the current tab page.
   5198 /// Note that autocommands may make "tp" invalid.
   5199 /// Also takes care of the tab pages line disappearing when closing the
   5200 /// last-but-one tab page.
   5201 void tabpage_close_other(tabpage_T *tp, int forceit)
   5202 {
   5203  int done = 0;
   5204  char prev_idx[NUMBUFLEN];
   5205 
   5206  if (window_layout_locked(CMD_SIZE)) {
   5207    return;
   5208  }
   5209 
   5210  trigger_tabclosedpre(tp);
   5211  tp->tp_did_tabclosedpre = true;
   5212 
   5213  // Limit to 1000 windows, autocommands may add a window while we close
   5214  // one.  OK, so I'm paranoid...
   5215  while (++done < 1000) {
   5216    snprintf(prev_idx, sizeof(prev_idx), "%i", tabpage_index(tp));
   5217    win_T *wp = tp->tp_lastwin;
   5218    ex_win_close(forceit, wp, tp);
   5219 
   5220    // Autocommands may delete the tab page under our fingers.
   5221    if (!valid_tabpage(tp)) {
   5222      break;
   5223    }
   5224    // We may fail to close a window with a modified buffer.
   5225    if (tp->tp_lastwin == wp) {
   5226      done = 1000;
   5227      break;
   5228    }
   5229  }
   5230  if (done >= 1000) {
   5231    // When closing the tab page failed, reset tp_did_tabclosedpre so that
   5232    // TabClosedPre behaves consistently on next :close vs :tabclose.
   5233    tp->tp_did_tabclosedpre = false;
   5234    return;
   5235  }
   5236 }
   5237 
   5238 /// ":only".
   5239 static void ex_only(exarg_T *eap)
   5240 {
   5241  if (window_layout_locked(CMD_only)) {
   5242    return;
   5243  }
   5244 
   5245  if (eap->addr_count > 0) {
   5246    win_T *wp;
   5247    linenr_T wnr = eap->line2;
   5248    for (wp = firstwin; --wnr > 0;) {
   5249      if (wp->w_next == NULL) {
   5250        break;
   5251      }
   5252      wp = wp->w_next;
   5253    }
   5254    if (wp != curwin) {
   5255      win_goto(wp);
   5256    }
   5257  }
   5258  close_others(true, eap->forceit);
   5259 }
   5260 
   5261 static void ex_hide(exarg_T *eap)
   5262 {
   5263  // ":hide" or ":hide | cmd": hide current window
   5264  if (eap->skip) {
   5265    return;
   5266  }
   5267 
   5268  win_T *win = NULL;
   5269  if (eap->addr_count == 0) {
   5270    win = curwin;
   5271  } else {
   5272    int winnr = 0;
   5273 
   5274    FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
   5275      winnr++;
   5276      if (winnr == eap->line2) {
   5277        win = wp;
   5278        break;
   5279      }
   5280    }
   5281    if (win == NULL) {
   5282      win = lastwin;
   5283    }
   5284  }
   5285 
   5286  if (!win->w_floating && window_layout_locked(CMD_hide)) {
   5287    return;
   5288  }
   5289 
   5290  win_close(win, false, eap->forceit);  // don't free buffer
   5291 }
   5292 
   5293 /// ":stop" and ":suspend": Suspend Vim.
   5294 static void ex_stop(exarg_T *eap)
   5295 {
   5296  if (!eap->forceit) {
   5297    autowrite_all();
   5298  }
   5299  may_trigger_vim_suspend_resume(true);
   5300  ui_call_suspend();
   5301  ui_flush();
   5302 }
   5303 
   5304 /// ":exit", ":xit" and ":wq": Write file and quit the current window.
   5305 static void ex_exit(exarg_T *eap)
   5306 {
   5307  if (cmdwin_type != 0) {
   5308    cmdwin_result = Ctrl_C;
   5309    return;
   5310  }
   5311  // Don't quit while editing the command line.
   5312  if (text_locked()) {
   5313    text_locked_msg();
   5314    return;
   5315  }
   5316 
   5317  bool save_exiting = exiting;
   5318  // we plan to exit if there is only one relevant window
   5319  if (check_more(false, eap->forceit) == OK && only_one_window()) {
   5320    exiting = true;
   5321  }
   5322  // Write the buffer for ":wq" or when it was changed.
   5323  // Trigger QuitPre and ExitPre.
   5324  // Check if we can exit now, after autocommands have changed things.
   5325  if (((eap->cmdidx == CMD_wq || curbufIsChanged()) && do_write(eap) == FAIL)
   5326      || before_quit_autocmds(curwin, false, eap->forceit)
   5327      || check_more(true, eap->forceit) == FAIL
   5328      || (only_one_window() && check_changed_any(eap->forceit, false))) {
   5329    not_exiting(save_exiting);
   5330  } else {
   5331    if (only_one_window()) {
   5332      // quit last window, exit Vim
   5333      getout(0);
   5334    }
   5335    not_exiting(save_exiting);
   5336    // Quit current window, may free the buffer.
   5337    win_close(curwin, !buf_hide(curwin->w_buffer), eap->forceit);
   5338  }
   5339 }
   5340 
   5341 /// ":print", ":list", ":number".
   5342 static void ex_print(exarg_T *eap)
   5343 {
   5344  if (curbuf->b_ml.ml_flags & ML_EMPTY) {
   5345    emsg(_(e_empty_buffer));
   5346  } else {
   5347    for (linenr_T line = eap->line1; line <= eap->line2 && !got_int; os_breakcheck()) {
   5348      print_line(line,
   5349                 (eap->cmdidx == CMD_number || eap->cmdidx == CMD_pound
   5350                  || (eap->flags & EXFLAG_NR)),
   5351                 eap->cmdidx == CMD_list || (eap->flags & EXFLAG_LIST),
   5352                 line == eap->line1);
   5353      line++;
   5354    }
   5355    setpcmark();
   5356    // put cursor at last line
   5357    curwin->w_cursor.lnum = eap->line2;
   5358    beginline(BL_SOL | BL_FIX);
   5359  }
   5360 
   5361  ex_no_reprint = true;
   5362 }
   5363 
   5364 static void ex_goto(exarg_T *eap)
   5365 {
   5366  goto_byte(eap->line2);
   5367 }
   5368 
   5369 /// ":preserve".
   5370 static void ex_preserve(exarg_T *eap)
   5371 {
   5372  ml_preserve(curbuf, true, true);
   5373 }
   5374 
   5375 /// ":recover".
   5376 static void ex_recover(exarg_T *eap)
   5377 {
   5378  // Set recoverymode right away to avoid the ATTENTION prompt.
   5379  recoverymode = true;
   5380  if (!check_changed(curbuf, (p_awa ? CCGD_AW : 0)
   5381                     | CCGD_MULTWIN
   5382                     | (eap->forceit ? CCGD_FORCEIT : 0)
   5383                     | CCGD_EXCMD)
   5384 
   5385      && (*eap->arg == NUL
   5386          || setfname(curbuf, eap->arg, NULL, true) == OK)) {
   5387    ml_recover(true);
   5388  }
   5389  recoverymode = false;
   5390 }
   5391 
   5392 /// Command modifier used in a wrong way.
   5393 static void ex_wrongmodifier(exarg_T *eap)
   5394 {
   5395  eap->errmsg = _(e_invcmd);
   5396 }
   5397 
   5398 /// callback function for 'findfunc'
   5399 static Callback ffu_cb;
   5400 
   5401 static Callback *get_findfunc_callback(void)
   5402 {
   5403  return *curbuf->b_p_ffu != NUL ? &curbuf->b_ffu_cb : &ffu_cb;
   5404 }
   5405 
   5406 /// Call 'findfunc' to obtain a list of file names.
   5407 static list_T *call_findfunc(char *pat, BoolVarValue cmdcomplete)
   5408 {
   5409  const sctx_T saved_sctx = current_sctx;
   5410 
   5411  typval_T args[3];
   5412  args[0].v_type = VAR_STRING;
   5413  args[0].vval.v_string = pat;
   5414  args[1].v_type = VAR_BOOL;
   5415  args[1].vval.v_bool = cmdcomplete;
   5416  args[2].v_type = VAR_UNKNOWN;
   5417 
   5418  // Lock the text to prevent weird things from happening.  Also disallow
   5419  // switching to another window, it should not be needed and may end up in
   5420  // Insert mode in another buffer.
   5421  textlock++;
   5422 
   5423  sctx_T *ctx = get_option_sctx(kOptFindfunc);
   5424  if (ctx != NULL) {
   5425    current_sctx = *ctx;
   5426  }
   5427 
   5428  Callback *cb = get_findfunc_callback();
   5429  typval_T rettv;
   5430  int retval = callback_call(cb, 2, args, &rettv);
   5431 
   5432  current_sctx = saved_sctx;
   5433 
   5434  textlock--;
   5435 
   5436  list_T *retlist = NULL;
   5437 
   5438  if (retval == OK) {
   5439    if (rettv.v_type == VAR_LIST) {
   5440      retlist = tv_list_copy(NULL, rettv.vval.v_list, false, get_copyID());
   5441    } else {
   5442      emsg(_(e_invalid_return_type_from_findfunc));
   5443    }
   5444 
   5445    tv_clear(&rettv);
   5446  }
   5447 
   5448  return retlist;
   5449 }
   5450 
   5451 /// Find file names matching "pat" using 'findfunc' and return it in "files".
   5452 /// Used for expanding the :find, :sfind and :tabfind command argument.
   5453 /// Returns OK on success and FAIL otherwise.
   5454 int expand_findfunc(char *pat, char ***files, int *numMatches)
   5455 {
   5456  *numMatches = 0;
   5457  *files = NULL;
   5458 
   5459  list_T *l = call_findfunc(pat, kBoolVarTrue);
   5460  if (l == NULL) {
   5461    return FAIL;
   5462  }
   5463 
   5464  int len = tv_list_len(l);
   5465  if (len == 0) {  // empty List
   5466    return FAIL;
   5467  }
   5468 
   5469  *files = xmalloc(sizeof(char *) * (size_t)len);
   5470 
   5471  // Copy all the List items
   5472  int idx = 0;
   5473  TV_LIST_ITER_CONST(l, li, {
   5474    if (TV_LIST_ITEM_TV(li)->v_type == VAR_STRING) {
   5475      (*files)[idx] = xstrdup(TV_LIST_ITEM_TV(li)->vval.v_string);
   5476      idx++;
   5477    }
   5478  });
   5479 
   5480  *numMatches = idx;
   5481  tv_list_free(l);
   5482 
   5483  return OK;
   5484 }
   5485 
   5486 /// Use 'findfunc' to find file 'findarg'.  The 'count' argument is used to find
   5487 /// the n'th matching file.
   5488 static char *findfunc_find_file(char *findarg, size_t findarg_len, int count)
   5489 {
   5490  char *ret_fname = NULL;
   5491 
   5492  const char cc = findarg[findarg_len];
   5493  findarg[findarg_len] = NUL;
   5494 
   5495  list_T *fname_list = call_findfunc(findarg, kBoolVarFalse);
   5496  int fname_count = tv_list_len(fname_list);
   5497 
   5498  if (fname_count == 0) {
   5499    semsg(_(e_cant_find_file_str_in_path), findarg);
   5500  } else {
   5501    if (count > fname_count) {
   5502      semsg(_(e_no_more_file_str_found_in_path), findarg);
   5503    } else {
   5504      listitem_T *li = tv_list_find(fname_list, count - 1);
   5505      if (li != NULL && TV_LIST_ITEM_TV(li)->v_type == VAR_STRING) {
   5506        ret_fname = xstrdup(TV_LIST_ITEM_TV(li)->vval.v_string);
   5507      }
   5508    }
   5509  }
   5510 
   5511  if (fname_list != NULL) {
   5512    tv_list_free(fname_list);
   5513  }
   5514 
   5515  findarg[findarg_len] = cc;
   5516 
   5517  return ret_fname;
   5518 }
   5519 
   5520 /// Process the 'findfunc' option value.
   5521 /// Returns NULL on success and an error message on failure.
   5522 const char *did_set_findfunc(optset_T *args)
   5523 {
   5524  buf_T *buf = (buf_T *)args->os_buf;
   5525  int retval;
   5526 
   5527  if (args->os_flags & OPT_LOCAL) {
   5528    // buffer-local option set
   5529    retval = option_set_callback_func(buf->b_p_ffu, &buf->b_ffu_cb);
   5530  } else {
   5531    // global option set
   5532    retval = option_set_callback_func(p_ffu, &ffu_cb);
   5533    // when using :set, free the local callback
   5534    if (!(args->os_flags & OPT_GLOBAL)) {
   5535      callback_free(&buf->b_ffu_cb);
   5536    }
   5537  }
   5538 
   5539  if (retval == FAIL) {
   5540    return e_invarg;
   5541  }
   5542 
   5543  // If the option value starts with <SID> or s:, then replace that with
   5544  // the script identifier.
   5545  char **varp = (char **)args->os_varp;
   5546  char *name = get_scriptlocal_funcname(*varp);
   5547  if (name != NULL) {
   5548    free_string_option(*varp);
   5549    *varp = name;
   5550  }
   5551 
   5552  return NULL;
   5553 }
   5554 
   5555 void free_findfunc_option(void)
   5556 {
   5557  callback_free(&ffu_cb);
   5558 }
   5559 
   5560 /// Mark the global 'findfunc' callback with "copyID" so that it is not
   5561 /// garbage collected.
   5562 bool set_ref_in_findfunc(int copyID)
   5563 {
   5564  bool abort = false;
   5565  abort = set_ref_in_callback(&ffu_cb, copyID, NULL, NULL);
   5566  return abort;
   5567 }
   5568 
   5569 /// :sview [+command] file       split window with new file, read-only
   5570 /// :split [[+command] file]     split window with current or new file
   5571 /// :vsplit [[+command] file]    split window vertically with current or new file
   5572 /// :new [[+command] file]       split window with no or new file
   5573 /// :vnew [[+command] file]      split vertically window with no or new file
   5574 /// :sfind [+command] file       split window with file in 'path'
   5575 ///
   5576 /// :tabedit                     open new Tab page with empty window
   5577 /// :tabedit [+command] file     open new Tab page and edit "file"
   5578 /// :tabnew [[+command] file]    just like :tabedit
   5579 /// :tabfind [+command] file     open new Tab page and find "file"
   5580 void ex_splitview(exarg_T *eap)
   5581 {
   5582  win_T *old_curwin = curwin;
   5583  char *fname = NULL;
   5584  const bool use_tab = eap->cmdidx == CMD_tabedit
   5585                       || eap->cmdidx == CMD_tabfind
   5586                       || eap->cmdidx == CMD_tabnew;
   5587 
   5588  // A ":split" in the quickfix window works like ":new".  Don't want two
   5589  // quickfix windows.  But it's OK when doing ":tab split".
   5590  if (bt_quickfix(curbuf) && cmdmod.cmod_tab == 0) {
   5591    if (eap->cmdidx == CMD_split) {
   5592      eap->cmdidx = CMD_new;
   5593    }
   5594    if (eap->cmdidx == CMD_vsplit) {
   5595      eap->cmdidx = CMD_vnew;
   5596    }
   5597  }
   5598 
   5599  if (eap->cmdidx == CMD_sfind || eap->cmdidx == CMD_tabfind) {
   5600    if (*get_findfunc() != NUL) {
   5601      fname = findfunc_find_file(eap->arg, strlen(eap->arg),
   5602                                 eap->addr_count > 0 ? eap->line2 : 1);
   5603    } else {
   5604      char *file_to_find = NULL;
   5605      char *search_ctx = NULL;
   5606      fname = find_file_in_path(eap->arg, strlen(eap->arg), FNAME_MESS, true,
   5607                                curbuf->b_ffname, &file_to_find, &search_ctx);
   5608      xfree(file_to_find);
   5609      vim_findfile_cleanup(search_ctx);
   5610    }
   5611    if (fname == NULL) {
   5612      goto theend;
   5613    }
   5614    eap->arg = fname;
   5615  }
   5616 
   5617  // Either open new tab page or split the window.
   5618  if (use_tab) {
   5619    if (win_new_tabpage(cmdmod.cmod_tab != 0 ? cmdmod.cmod_tab : eap->addr_count == 0
   5620                        ? 0 : (int)eap->line2 + 1, eap->arg) != FAIL) {
   5621      do_exedit(eap, old_curwin);
   5622      apply_autocmds(EVENT_TABNEWENTERED, NULL, NULL, false, curbuf);
   5623 
   5624      // set the alternate buffer for the window we came from
   5625      if (curwin != old_curwin
   5626          && win_valid(old_curwin)
   5627          && old_curwin->w_buffer != curbuf
   5628          && (cmdmod.cmod_flags & CMOD_KEEPALT) == 0) {
   5629        old_curwin->w_alt_fnum = curbuf->b_fnum;
   5630      }
   5631    }
   5632  } else if (win_split(eap->addr_count > 0 ? (int)eap->line2 : 0,
   5633                       *eap->cmd == 'v' ? WSP_VERT : 0) != FAIL) {
   5634    // Reset 'scrollbind' when editing another file, but keep it when
   5635    // doing ":split" without arguments.
   5636    if (*eap->arg != NUL) {
   5637      RESET_BINDING(curwin);
   5638    } else {
   5639      do_check_scrollbind(false);
   5640    }
   5641    do_exedit(eap, old_curwin);
   5642  }
   5643 
   5644 theend:
   5645  xfree(fname);
   5646 }
   5647 
   5648 /// Open a new tab page.
   5649 void tabpage_new(void)
   5650 {
   5651  exarg_T ea = {
   5652    .cmdidx = CMD_tabnew,
   5653    .cmd = "tabn",
   5654    .arg = "",
   5655  };
   5656  ex_splitview(&ea);
   5657 }
   5658 
   5659 /// :tabnext command
   5660 static void ex_tabnext(exarg_T *eap)
   5661 {
   5662  int tab_number;
   5663 
   5664  switch (eap->cmdidx) {
   5665  case CMD_tabfirst:
   5666  case CMD_tabrewind:
   5667    goto_tabpage(1);
   5668    break;
   5669  case CMD_tablast:
   5670    goto_tabpage(9999);
   5671    break;
   5672  case CMD_tabprevious:
   5673  case CMD_tabNext:
   5674    if (eap->arg && *eap->arg != NUL) {
   5675      char *p = eap->arg;
   5676      char *p_save = p;
   5677      tab_number = (int)getdigits(&p, false, 0);
   5678      if (p == p_save || *p_save == '-' || *p_save == '+' || *p != NUL
   5679          || tab_number == 0) {
   5680        // No numbers as argument.
   5681        eap->errmsg = ex_errmsg(e_invarg2, eap->arg);
   5682        return;
   5683      }
   5684    } else {
   5685      if (eap->addr_count == 0) {
   5686        tab_number = 1;
   5687      } else {
   5688        tab_number = (int)eap->line2;
   5689        if (tab_number < 1) {
   5690          eap->errmsg = _(e_invrange);
   5691          return;
   5692        }
   5693      }
   5694    }
   5695    goto_tabpage(-tab_number);
   5696    break;
   5697  default:       // CMD_tabnext
   5698    tab_number = get_tabpage_arg(eap);
   5699    if (eap->errmsg == NULL) {
   5700      goto_tabpage(tab_number);
   5701    }
   5702    break;
   5703  }
   5704 }
   5705 
   5706 /// :tabmove command
   5707 static void ex_tabmove(exarg_T *eap)
   5708 {
   5709  int tab_number = get_tabpage_arg(eap);
   5710  if (eap->errmsg == NULL) {
   5711    tabpage_move(tab_number);
   5712  }
   5713 }
   5714 
   5715 /// :tabs command: List tabs and their contents.
   5716 static void ex_tabs(exarg_T *eap)
   5717 {
   5718  int tabcount = 1;
   5719 
   5720  msg_ext_set_kind("list_cmd");
   5721  msg_start();
   5722  msg_scroll = true;
   5723 
   5724  win_T *lastused_win = valid_tabpage(lastused_tabpage)
   5725                        ? lastused_tabpage->tp_curwin
   5726                        : NULL;
   5727 
   5728  FOR_ALL_TABS(tp) {
   5729    if (got_int) {
   5730      break;
   5731    }
   5732 
   5733    if (msg_col > 0) {
   5734      msg_putchar('\n');
   5735    }
   5736    vim_snprintf(IObuff, IOSIZE, _("Tab page %d"), tabcount++);
   5737    msg_outtrans(IObuff, HLF_T, false);
   5738    os_breakcheck();
   5739 
   5740    FOR_ALL_WINDOWS_IN_TAB(wp, tp) {
   5741      if (got_int) {
   5742        break;
   5743      } else if (!wp->w_config.focusable || wp->w_config.hide) {
   5744        continue;
   5745      }
   5746 
   5747      msg_putchar('\n');
   5748      msg_putchar(wp == curwin ? '>' : wp == lastused_win ? '#' : ' ');
   5749      msg_putchar(' ');
   5750      msg_putchar(bufIsChanged(wp->w_buffer) ? '+' : ' ');
   5751      msg_putchar(' ');
   5752      if (buf_spname(wp->w_buffer) != NULL) {
   5753        xstrlcpy(IObuff, buf_spname(wp->w_buffer), IOSIZE);
   5754      } else {
   5755        home_replace(wp->w_buffer, wp->w_buffer->b_fname, IObuff, IOSIZE, true);
   5756      }
   5757      msg_outtrans(IObuff, 0, false);
   5758      os_breakcheck();
   5759    }
   5760  }
   5761 }
   5762 
   5763 /// ":detach"
   5764 ///
   5765 /// Detaches the current UI.
   5766 ///
   5767 /// ":detach!" with bang (!) detaches all UIs _except_ the current UI.
   5768 static void ex_detach(exarg_T *eap)
   5769 {
   5770  // come on pooky let's burn this mf down
   5771  if (eap && eap->forceit) {
   5772    emsg("bang (!) not supported yet");
   5773  } else {
   5774    // 1. Send "error_exit" UI-event (notification only).
   5775    // 2. Perform server-side UI detach.
   5776    // 3. Close server-side channel without self-exit.
   5777 
   5778    if (!current_ui) {
   5779      emsg("UI not attached");
   5780      return;
   5781    }
   5782 
   5783    Channel *chan = find_channel(current_ui);
   5784    if (!chan) {
   5785      emsg(e_invchan);
   5786      return;
   5787    }
   5788    chan->detach = true;  // Prevent self-exit on channel-close.
   5789 
   5790    // Server-side UI detach. Doesn't close the channel.
   5791    Error err2 = ERROR_INIT;
   5792    remote_ui_disconnect(chan->id, &err2, true);
   5793    if (ERROR_SET(&err2)) {
   5794      emsg(err2.msg);  // UI disappeared already?
   5795      api_clear_error(&err2);
   5796      return;
   5797    }
   5798 
   5799    // Server-side channel close.
   5800    const char *err = NULL;
   5801    bool rv = channel_close(chan->id, kChannelPartAll, &err);
   5802    if (!rv && err) {
   5803      emsg(err);  // UI disappeared already?
   5804      return;
   5805    }
   5806    // XXX: Can't do this, channel_decref() is async...
   5807    // assert(!find_channel(chan->id));
   5808 
   5809    ILOG("detach current_ui=%" PRId64, chan->id);
   5810  }
   5811 }
   5812 
   5813 /// ":connect"
   5814 ///
   5815 /// Connects the current UI to a different server
   5816 ///
   5817 /// ":connect <address>" detaches the current UI and connects to the given server.
   5818 /// ":connect! <address>" stops the current server if no other UIs are attached, then connects to the given server.
   5819 static void ex_connect(exarg_T *eap)
   5820 {
   5821  bool stop_server = eap->forceit ? (ui_active() == 1) : false;
   5822 
   5823  Error err = ERROR_INIT;
   5824  remote_ui_connect(current_ui, eap->arg, &err);
   5825 
   5826  if (ERROR_SET(&err)) {
   5827    emsg(err.msg);
   5828    api_clear_error(&err);
   5829    return;
   5830  }
   5831 
   5832  ex_detach(NULL);
   5833  if (stop_server) {
   5834    exiting = true;
   5835    getout(0);
   5836  }
   5837 }
   5838 
   5839 /// ":mode":
   5840 /// If no argument given, get the screen size and redraw.
   5841 static void ex_mode(exarg_T *eap)
   5842 {
   5843  if (*eap->arg == NUL) {
   5844    must_redraw = UPD_CLEAR;
   5845    ex_redraw(eap);
   5846  } else {
   5847    emsg(_(e_screenmode));
   5848  }
   5849 }
   5850 
   5851 /// ":resize".
   5852 /// set, increment or decrement current window height
   5853 static void ex_resize(exarg_T *eap)
   5854 {
   5855  win_T *wp = curwin;
   5856 
   5857  if (eap->addr_count > 0) {
   5858    int n = (int)eap->line2;
   5859    for (wp = firstwin; wp->w_next != NULL && --n > 0; wp = wp->w_next) {}
   5860  }
   5861 
   5862  int n = (int)atol(eap->arg);
   5863  if (cmdmod.cmod_split & WSP_VERT) {
   5864    if (*eap->arg == '-' || *eap->arg == '+') {
   5865      n += wp->w_width;
   5866    } else if (n == 0 && eap->arg[0] == NUL) {  // default is very wide
   5867      n = Columns;
   5868    }
   5869    win_setwidth_win(n, wp);
   5870  } else {
   5871    if (*eap->arg == '-' || *eap->arg == '+') {
   5872      n += wp->w_height;
   5873    } else if (n == 0 && eap->arg[0] == NUL) {  // default is very high
   5874      n = Rows - 1;
   5875    }
   5876    win_setheight_win(n, wp);
   5877  }
   5878 }
   5879 
   5880 /// ":find [+command] <file>" command.
   5881 static void ex_find(exarg_T *eap)
   5882 {
   5883  if (!check_can_set_curbuf_forceit(eap->forceit)) {
   5884    return;
   5885  }
   5886 
   5887  char *fname = NULL;
   5888  if (*get_findfunc() != NUL) {
   5889    fname = findfunc_find_file(eap->arg, strlen(eap->arg),
   5890                               eap->addr_count > 0 ? eap->line2 : 1);
   5891  } else {
   5892    char *file_to_find = NULL;
   5893    char *search_ctx = NULL;
   5894    fname = find_file_in_path(eap->arg, strlen(eap->arg), FNAME_MESS, true,
   5895                              curbuf->b_ffname, &file_to_find, &search_ctx);
   5896    if (eap->addr_count > 0) {
   5897      // Repeat finding the file "count" times.  This matters when it appears
   5898      // several times in the path.
   5899      linenr_T count = eap->line2;
   5900      while (fname != NULL && --count > 0) {
   5901        xfree(fname);
   5902        fname = find_file_in_path(NULL, 0, FNAME_MESS, false,
   5903                                  curbuf->b_ffname, &file_to_find, &search_ctx);
   5904      }
   5905    }
   5906    xfree(file_to_find);
   5907    vim_findfile_cleanup(search_ctx);
   5908  }
   5909 
   5910  if (fname == NULL) {
   5911    return;
   5912  }
   5913 
   5914  eap->arg = fname;
   5915  do_exedit(eap, NULL);
   5916  xfree(fname);
   5917 }
   5918 
   5919 /// ":edit", ":badd", ":balt", ":visual".
   5920 static void ex_edit(exarg_T *eap)
   5921 {
   5922  char *ffname = eap->cmdidx == CMD_enew ? NULL : eap->arg;
   5923 
   5924  // Exclude commands which keep the window's current buffer
   5925  if (eap->cmdidx != CMD_badd
   5926      && eap->cmdidx != CMD_balt
   5927      // All other commands must obey 'winfixbuf' / ! rules
   5928      && (is_other_file(0, ffname) && !check_can_set_curbuf_forceit(eap->forceit))) {
   5929    return;
   5930  }
   5931 
   5932  // prevent use of :edit on prompt-buffers
   5933  if (bt_prompt(curbuf) && eap->cmdidx == CMD_edit && *eap->arg == NUL) {
   5934    emsg("cannot :edit a prompt buffer");
   5935    return;
   5936  }
   5937 
   5938  do_exedit(eap, NULL);
   5939 }
   5940 
   5941 /// ":edit <file>" command and alike.
   5942 ///
   5943 /// @param old_curwin  curwin before doing a split or NULL
   5944 void do_exedit(exarg_T *eap, win_T *old_curwin)
   5945 {
   5946  // ":vi" command ends Ex mode.
   5947  if (exmode_active && (eap->cmdidx == CMD_visual
   5948                        || eap->cmdidx == CMD_view)) {
   5949    exmode_active = false;
   5950    ex_pressedreturn = false;
   5951    if (ui_has(kUICmdline)) {
   5952      ui_ext_cmdline_block_leave();
   5953    }
   5954    if (*eap->arg == NUL) {
   5955      // Special case:  ":global/pat/visual\NLvi-commands"
   5956      if (global_busy) {
   5957        if (eap->nextcmd != NULL) {
   5958          stuffReadbuff(eap->nextcmd);
   5959          eap->nextcmd = NULL;
   5960        }
   5961 
   5962        const int save_rd = RedrawingDisabled;
   5963        RedrawingDisabled = 0;
   5964        const int save_nwr = no_wait_return;
   5965        no_wait_return = 0;
   5966        need_wait_return = false;
   5967        const int save_ms = msg_scroll;
   5968        msg_scroll = 0;
   5969        redraw_all_later(UPD_NOT_VALID);
   5970        pending_exmode_active = true;
   5971 
   5972        normal_enter(false, true);
   5973 
   5974        pending_exmode_active = false;
   5975        RedrawingDisabled = save_rd;
   5976        no_wait_return = save_nwr;
   5977        msg_scroll = save_ms;
   5978      }
   5979      return;
   5980    }
   5981  }
   5982 
   5983  if ((eap->cmdidx == CMD_new
   5984       || eap->cmdidx == CMD_tabnew
   5985       || eap->cmdidx == CMD_tabedit
   5986       || eap->cmdidx == CMD_vnew) && *eap->arg == NUL) {
   5987    // ":new" or ":tabnew" without argument: edit a new empty buffer
   5988    setpcmark();
   5989    do_ecmd(0, NULL, NULL, eap, ECMD_ONE,
   5990            ECMD_HIDE + (eap->forceit ? ECMD_FORCEIT : 0),
   5991            old_curwin == NULL ? curwin : NULL);
   5992  } else if ((eap->cmdidx != CMD_split && eap->cmdidx != CMD_vsplit)
   5993             || *eap->arg != NUL) {
   5994    // Can't edit another file when "textlock" or "curbuf->b_ro_locked" is set.
   5995    // Only ":edit" or ":script" can bring us here, others are stopped earlier.
   5996    if (*eap->arg != NUL && text_or_buf_locked()) {
   5997      return;
   5998    }
   5999    int n = readonlymode;
   6000    if (eap->cmdidx == CMD_view || eap->cmdidx == CMD_sview) {
   6001      readonlymode = true;
   6002    } else if (eap->cmdidx == CMD_enew) {
   6003      readonlymode = false;  // 'readonly' doesn't make sense
   6004                             // in an empty buffer
   6005    }
   6006    if (eap->cmdidx != CMD_balt && eap->cmdidx != CMD_badd) {
   6007      setpcmark();
   6008    }
   6009    if (do_ecmd(0, eap->cmdidx == CMD_enew ? NULL : eap->arg,
   6010                NULL, eap, eap->do_ecmd_lnum,
   6011                (buf_hide(curbuf) ? ECMD_HIDE : 0)
   6012                + (eap->forceit ? ECMD_FORCEIT : 0)
   6013                // After a split we can use an existing buffer.
   6014                + (old_curwin != NULL ? ECMD_OLDBUF : 0)
   6015                + (eap->cmdidx == CMD_badd ? ECMD_ADDBUF : 0)
   6016                + (eap->cmdidx == CMD_balt ? ECMD_ALTBUF : 0),
   6017                old_curwin == NULL ? curwin : NULL) == FAIL) {
   6018      // Editing the file failed.  If the window was split, close it.
   6019      if (old_curwin != NULL) {
   6020        bool need_hide = (curbufIsChanged() && curbuf->b_nwindows <= 1);
   6021        if (!need_hide || buf_hide(curbuf)) {
   6022          cleanup_T cs;
   6023 
   6024          // Reset the error/interrupt/exception state here so that
   6025          // aborting() returns false when closing a window.
   6026          enter_cleanup(&cs);
   6027          win_close(curwin, !need_hide && !buf_hide(curbuf), false);
   6028 
   6029          // Restore the error/interrupt/exception state if not
   6030          // discarded by a new aborting error, interrupt, or
   6031          // uncaught exception.
   6032          leave_cleanup(&cs);
   6033        }
   6034      }
   6035    } else if (readonlymode && curbuf->b_nwindows == 1) {
   6036      // When editing an already visited buffer, 'readonly' won't be set
   6037      // but the previous value is kept.  With ":view" and ":sview" we
   6038      // want the  file to be readonly, except when another window is
   6039      // editing the same buffer.
   6040      curbuf->b_p_ro = true;
   6041    }
   6042    readonlymode = n;
   6043  } else {
   6044    if (eap->do_ecmd_cmd != NULL) {
   6045      do_cmdline_cmd(eap->do_ecmd_cmd);
   6046    }
   6047    int n = curwin->w_arg_idx_invalid;
   6048    check_arg_idx(curwin);
   6049    if (n != curwin->w_arg_idx_invalid) {
   6050      maketitle();
   6051    }
   6052  }
   6053 
   6054  // if ":split file" worked, set alternate file name in old window to new
   6055  // file
   6056  if (old_curwin != NULL
   6057      && *eap->arg != NUL
   6058      && curwin != old_curwin
   6059      && win_valid(old_curwin)
   6060      && old_curwin->w_buffer != curbuf
   6061      && (cmdmod.cmod_flags & CMOD_KEEPALT) == 0) {
   6062    old_curwin->w_alt_fnum = curbuf->b_fnum;
   6063  }
   6064 
   6065  ex_no_reprint = true;
   6066 }
   6067 
   6068 /// ":gui" and ":gvim" when there is no GUI.
   6069 static void ex_nogui(exarg_T *eap)
   6070 {
   6071  eap->errmsg = _("E25: Nvim does not have a built-in GUI");
   6072 }
   6073 
   6074 static void ex_popup(exarg_T *eap)
   6075 {
   6076  pum_make_popup(eap->arg, eap->forceit);
   6077 }
   6078 
   6079 static void ex_swapname(exarg_T *eap)
   6080 {
   6081  if (curbuf->b_ml.ml_mfp == NULL || curbuf->b_ml.ml_mfp->mf_fname == NULL) {
   6082    msg(_("No swap file"), 0);
   6083  } else {
   6084    msg(curbuf->b_ml.ml_mfp->mf_fname, 0);
   6085  }
   6086 }
   6087 
   6088 /// ":syncbind" forces all 'scrollbind' windows to have the same relative
   6089 /// offset.
   6090 /// (1998-11-02 16:21:01  R. Edward Ralston <eralston@computer.org>)
   6091 static void ex_syncbind(exarg_T *eap)
   6092 {
   6093  linenr_T vtopline;  // Target topline (including fill)
   6094 
   6095  linenr_T old_linenr = curwin->w_cursor.lnum;
   6096 
   6097  setpcmark();
   6098 
   6099  // determine max (virtual) topline
   6100  if (curwin->w_p_scb) {
   6101    vtopline = get_vtopline(curwin);
   6102    FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
   6103      if (wp->w_p_scb && wp->w_buffer) {
   6104        linenr_T y = plines_m_win_fill(wp, 1, wp->w_buffer->b_ml.ml_line_count)
   6105                     - get_scrolloff_value(curwin);
   6106        vtopline = MIN(vtopline, y);
   6107      }
   6108    }
   6109    vtopline = MAX(vtopline, 1);
   6110  } else {
   6111    vtopline = 1;
   6112  }
   6113 
   6114  // Set all scrollbind windows to the same topline.
   6115  FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
   6116    if (wp->w_p_scb) {
   6117      int y = vtopline - get_vtopline(wp);
   6118      if (y > 0) {
   6119        scrollup(wp, y, true);
   6120      } else {
   6121        scrolldown(wp, -y, true);
   6122      }
   6123      wp->w_scbind_pos = vtopline;
   6124      redraw_later(wp, UPD_VALID);
   6125      cursor_correct(wp);
   6126      wp->w_redr_status = true;
   6127    }
   6128  }
   6129 
   6130  if (curwin->w_p_scb) {
   6131    did_syncbind = true;
   6132    checkpcmark();
   6133    if (old_linenr != curwin->w_cursor.lnum) {
   6134      char ctrl_o[2];
   6135 
   6136      ctrl_o[0] = Ctrl_O;
   6137      ctrl_o[1] = 0;
   6138      ins_typebuf(ctrl_o, REMAP_NONE, 0, true, false);
   6139    }
   6140  }
   6141 }
   6142 
   6143 static void ex_read(exarg_T *eap)
   6144 {
   6145  int empty = (curbuf->b_ml.ml_flags & ML_EMPTY);
   6146 
   6147  if (eap->usefilter) {  // :r!cmd
   6148    do_bang(1, eap, false, false, true);
   6149    return;
   6150  }
   6151 
   6152  if (u_save(eap->line2, (linenr_T)(eap->line2 + 1)) == FAIL) {
   6153    return;
   6154  }
   6155 
   6156  int i;
   6157  if (*eap->arg == NUL) {
   6158    if (check_fname() == FAIL) {       // check for no file name
   6159      return;
   6160    }
   6161    i = readfile(curbuf->b_ffname, curbuf->b_fname,
   6162                 eap->line2, 0, (linenr_T)MAXLNUM, eap, 0, false);
   6163  } else {
   6164    if (vim_strchr(p_cpo, CPO_ALTREAD) != NULL) {
   6165      setaltfname(eap->arg, eap->arg, 1);
   6166    }
   6167    i = readfile(eap->arg, NULL,
   6168                 eap->line2, 0, (linenr_T)MAXLNUM, eap, 0, false);
   6169  }
   6170  if (i != OK) {
   6171    if (!aborting()) {
   6172      semsg(_(e_notopen), eap->arg);
   6173    }
   6174  } else {
   6175    if (empty && exmode_active) {
   6176      // Delete the empty line that remains.  Historically ex does
   6177      // this but vi doesn't.
   6178      linenr_T lnum;
   6179      if (eap->line2 == 0) {
   6180        lnum = curbuf->b_ml.ml_line_count;
   6181      } else {
   6182        lnum = 1;
   6183      }
   6184      if (*ml_get(lnum) == NUL && u_savedel(lnum, 1) == OK) {
   6185        ml_delete(lnum);
   6186        if (curwin->w_cursor.lnum > 1
   6187            && curwin->w_cursor.lnum >= lnum) {
   6188          curwin->w_cursor.lnum--;
   6189        }
   6190        deleted_lines_mark(lnum, 1);
   6191      }
   6192    }
   6193    redraw_curbuf_later(UPD_VALID);
   6194  }
   6195 }
   6196 
   6197 static char *prev_dir = NULL;
   6198 
   6199 #if defined(EXITFREE)
   6200 void free_cd_dir(void)
   6201 {
   6202  XFREE_CLEAR(prev_dir);
   6203  XFREE_CLEAR(globaldir);
   6204 }
   6205 
   6206 #endif
   6207 
   6208 /// Get the previous directory for the given chdir scope.
   6209 static char *get_prevdir(CdScope scope)
   6210 {
   6211  switch (scope) {
   6212  case kCdScopeTabpage:
   6213    return curtab->tp_prevdir;
   6214    break;
   6215  case kCdScopeWindow:
   6216    return curwin->w_prevdir;
   6217    break;
   6218  default:
   6219    return prev_dir;
   6220  }
   6221 }
   6222 
   6223 /// Deal with the side effects of changing the current directory.
   6224 ///
   6225 /// @param scope  Scope of the function call (global, tab or window).
   6226 static void post_chdir(CdScope scope, bool trigger_dirchanged)
   6227 {
   6228  // Always overwrite the window-local CWD.
   6229  XFREE_CLEAR(curwin->w_localdir);
   6230 
   6231  // Overwrite the tab-local CWD for :cd, :tcd.
   6232  if (scope >= kCdScopeTabpage) {
   6233    XFREE_CLEAR(curtab->tp_localdir);
   6234  }
   6235 
   6236  if (scope < kCdScopeGlobal) {
   6237    char *pdir = get_prevdir(scope);
   6238    // If still in global directory, set CWD as the global directory.
   6239    if (globaldir == NULL && pdir != NULL) {
   6240      globaldir = xstrdup(pdir);
   6241    }
   6242  }
   6243 
   6244  char cwd[MAXPATHL];
   6245  if (os_dirname(cwd, MAXPATHL) != OK) {
   6246    return;
   6247  }
   6248  switch (scope) {
   6249  case kCdScopeGlobal:
   6250    // We are now in the global directory, no need to remember its name.
   6251    XFREE_CLEAR(globaldir);
   6252    break;
   6253  case kCdScopeTabpage:
   6254    curtab->tp_localdir = xstrdup(cwd);
   6255    break;
   6256  case kCdScopeWindow:
   6257    curwin->w_localdir = xstrdup(cwd);
   6258    break;
   6259  case kCdScopeInvalid:
   6260    abort();
   6261  }
   6262 
   6263  last_chdir_reason = NULL;
   6264  shorten_fnames(vim_strchr(p_cpo, CPO_NOSYMLINKS) == NULL);
   6265 
   6266  if (trigger_dirchanged) {
   6267    do_autocmd_dirchanged(cwd, scope, kCdCauseManual, false);
   6268  }
   6269 }
   6270 
   6271 /// Change directory function used by :cd/:tcd/:lcd Ex commands and the chdir() function.
   6272 /// @param new_dir  The directory to change to.
   6273 /// @param scope    Scope of the function call (global, tab or window).
   6274 /// @return true if the directory is successfully changed.
   6275 bool changedir_func(char *new_dir, CdScope scope)
   6276 {
   6277  if (new_dir == NULL || allbuf_locked()) {
   6278    return false;
   6279  }
   6280 
   6281  char *pdir = NULL;
   6282  // ":cd -": Change to previous directory
   6283  if (strcmp(new_dir, "-") == 0) {
   6284    pdir = get_prevdir(scope);
   6285    if (pdir == NULL) {
   6286      emsg(_("E186: No previous directory"));
   6287      return false;
   6288    }
   6289    new_dir = pdir;
   6290  }
   6291 
   6292  if (os_dirname(NameBuff, MAXPATHL) == OK) {
   6293    pdir = xstrdup(NameBuff);
   6294  } else {
   6295    pdir = NULL;
   6296  }
   6297 
   6298  // For UNIX ":cd" means: go to home directory.
   6299  // On other systems too if 'cdhome' is set.
   6300  if (*new_dir == NUL && p_cdh) {
   6301    // Use NameBuff for home directory name.
   6302    expand_env("$HOME", NameBuff, MAXPATHL);
   6303    new_dir = NameBuff;
   6304  }
   6305 
   6306  bool dir_differs = pdir == NULL || pathcmp(pdir, new_dir, -1) != 0;
   6307  if (dir_differs) {
   6308    do_autocmd_dirchanged(new_dir, scope, kCdCauseManual, true);
   6309    if (vim_chdir(new_dir) != 0) {
   6310      emsg(_(e_failed));
   6311      xfree(pdir);
   6312      return false;
   6313    }
   6314  }
   6315 
   6316  char **pp;
   6317  switch (scope) {
   6318  case kCdScopeTabpage:
   6319    pp = &curtab->tp_prevdir;
   6320    break;
   6321  case kCdScopeWindow:
   6322    pp = &curwin->w_prevdir;
   6323    break;
   6324  default:
   6325    pp = &prev_dir;
   6326  }
   6327  xfree(*pp);
   6328  *pp = pdir;
   6329 
   6330  post_chdir(scope, dir_differs);
   6331 
   6332  return true;
   6333 }
   6334 
   6335 /// ":cd", ":tcd", ":lcd", ":chdir", "tchdir" and ":lchdir".
   6336 void ex_cd(exarg_T *eap)
   6337 {
   6338  char *new_dir = eap->arg;
   6339  // for non-UNIX ":cd" means: print current directory unless 'cdhome' is set
   6340  if (*new_dir == NUL && !p_cdh) {
   6341    ex_pwd(NULL);
   6342    return;
   6343  }
   6344 
   6345  CdScope scope = kCdScopeGlobal;
   6346  switch (eap->cmdidx) {
   6347  case CMD_tcd:
   6348  case CMD_tchdir:
   6349    scope = kCdScopeTabpage;
   6350    break;
   6351  case CMD_lcd:
   6352  case CMD_lchdir:
   6353    scope = kCdScopeWindow;
   6354    break;
   6355  default:
   6356    break;
   6357  }
   6358  if (changedir_func(new_dir, scope)) {
   6359    // Echo the new current directory if the command was typed.
   6360    if (KeyTyped || p_verbose >= 5) {
   6361      ex_pwd(eap);
   6362    }
   6363  }
   6364 }
   6365 
   6366 /// ":pwd".
   6367 static void ex_pwd(exarg_T *eap)
   6368 {
   6369  if (os_dirname(NameBuff, MAXPATHL) == OK) {
   6370 #ifdef BACKSLASH_IN_FILENAME
   6371    slash_adjust(NameBuff);
   6372 #endif
   6373    if (p_verbose > 0) {
   6374      char *context = "global";
   6375      if (last_chdir_reason != NULL) {
   6376        context = last_chdir_reason;
   6377      } else if (curwin->w_localdir != NULL) {
   6378        context = "window";
   6379      } else if (curtab->tp_localdir != NULL) {
   6380        context = "tabpage";
   6381      }
   6382      smsg(0, "[%s] %s", context, NameBuff);
   6383    } else {
   6384      msg(NameBuff, 0);
   6385    }
   6386  } else {
   6387    emsg(_("E187: Unknown"));
   6388  }
   6389 }
   6390 
   6391 /// ":=".
   6392 static void ex_equal(exarg_T *eap)
   6393 {
   6394  if (*eap->arg != NUL && *eap->arg != '|') {
   6395    // equivalent to :lua= expr
   6396    ex_lua(eap);
   6397  } else {
   6398    eap->nextcmd = find_nextcmd(eap->arg);
   6399    smsg(0, "%" PRId64, (int64_t)eap->line2);
   6400  }
   6401 }
   6402 
   6403 static void ex_sleep(exarg_T *eap)
   6404 {
   6405  if (cursor_valid(curwin)) {
   6406    setcursor_mayforce(curwin, true);
   6407  }
   6408 
   6409  int64_t len = eap->line2;
   6410  switch (*eap->arg) {
   6411  case 'm':
   6412    break;
   6413  case NUL:
   6414    len *= 1000; break;
   6415  default:
   6416    semsg(_(e_invarg2), eap->arg); return;
   6417  }
   6418 
   6419  // Hide the cursor if invoked with !
   6420  do_sleep(len, eap->forceit);
   6421 }
   6422 
   6423 /// Sleep for "msec" milliseconds, but return early on CTRL-C.
   6424 ///
   6425 /// @param hide_cursor  hide the cursor if true
   6426 void do_sleep(int64_t msec, bool hide_cursor)
   6427 {
   6428  if (hide_cursor) {
   6429    ui_busy_start();
   6430  }
   6431 
   6432  ui_flush();  // flush before waiting
   6433  LOOP_PROCESS_EVENTS_UNTIL(&main_loop, main_loop.events, msec, got_int);
   6434 
   6435  // If CTRL-C was typed to interrupt the sleep, drop the CTRL-C from the
   6436  // input buffer, otherwise a following call to input() fails.
   6437  if (got_int) {
   6438    vpeekc();
   6439  }
   6440 
   6441  if (hide_cursor) {
   6442    ui_busy_stop();
   6443  }
   6444 }
   6445 
   6446 /// ":winsize" command (obsolete).
   6447 static void ex_winsize(exarg_T *eap)
   6448 {
   6449  char *arg = eap->arg;
   6450 
   6451  if (!ascii_isdigit(*arg)) {
   6452    semsg(_(e_invarg2), arg);
   6453    return;
   6454  }
   6455  int w = getdigits_int(&arg, false, 10);
   6456  arg = skipwhite(arg);
   6457  char *p = arg;
   6458  int h = getdigits_int(&arg, false, 10);
   6459  if (*p != NUL && *arg == NUL) {
   6460    screen_resize(w, h);
   6461  } else {
   6462    emsg(_("E465: :winsize requires two number arguments"));
   6463  }
   6464 }
   6465 
   6466 static void ex_wincmd(exarg_T *eap)
   6467 {
   6468  int xchar = NUL;
   6469  char *p;
   6470 
   6471  if (*eap->arg == 'g' || *eap->arg == Ctrl_G) {
   6472    // CTRL-W g and CTRL-W CTRL-G  have an extra command character
   6473    if (eap->arg[1] == NUL) {
   6474      emsg(_(e_invarg));
   6475      return;
   6476    }
   6477    xchar = (uint8_t)eap->arg[1];
   6478    p = eap->arg + 2;
   6479  } else {
   6480    p = eap->arg + 1;
   6481  }
   6482 
   6483  eap->nextcmd = check_nextcmd(p);
   6484  p = skipwhite(p);
   6485  if (*p != NUL && *p != '"' && eap->nextcmd == NULL) {
   6486    emsg(_(e_invarg));
   6487  } else if (!eap->skip) {
   6488    // Pass flags on for ":vertical wincmd ]".
   6489    postponed_split_flags = cmdmod.cmod_split;
   6490    postponed_split_tab = cmdmod.cmod_tab;
   6491    do_window(*eap->arg, eap->addr_count > 0 ? eap->line2 : 0, xchar);
   6492    postponed_split_flags = 0;
   6493    postponed_split_tab = 0;
   6494  }
   6495 }
   6496 
   6497 /// Handle command that work like operators: ":delete", ":yank", ":>" and ":<".
   6498 static void ex_operators(exarg_T *eap)
   6499 {
   6500  oparg_T oa;
   6501 
   6502  clear_oparg(&oa);
   6503  oa.regname = eap->regname;
   6504  oa.start.lnum = eap->line1;
   6505  oa.end.lnum = eap->line2;
   6506  oa.line_count = eap->line2 - eap->line1 + 1;
   6507  oa.motion_type = kMTLineWise;
   6508  virtual_op = kFalse;
   6509  if (eap->cmdidx != CMD_yank) {  // position cursor for undo
   6510    setpcmark();
   6511    curwin->w_cursor.lnum = eap->line1;
   6512    beginline(BL_SOL | BL_FIX);
   6513  }
   6514 
   6515  if (VIsual_active) {
   6516    end_visual_mode();
   6517  }
   6518 
   6519  switch (eap->cmdidx) {
   6520  case CMD_delete:
   6521    oa.op_type = OP_DELETE;
   6522    op_delete(&oa);
   6523    break;
   6524 
   6525  case CMD_yank:
   6526    oa.op_type = OP_YANK;
   6527    op_yank(&oa, true);
   6528    break;
   6529 
   6530  default:          // CMD_rshift or CMD_lshift
   6531    if (
   6532        (eap->cmdidx == CMD_rshift) ^ curwin->w_p_rl) {
   6533      oa.op_type = OP_RSHIFT;
   6534    } else {
   6535      oa.op_type = OP_LSHIFT;
   6536    }
   6537    op_shift(&oa, false, eap->amount);
   6538    break;
   6539  }
   6540  virtual_op = kNone;
   6541  ex_may_print(eap);
   6542 }
   6543 
   6544 /// ":put".
   6545 static void ex_put(exarg_T *eap)
   6546 {
   6547  // ":0put" works like ":1put!".
   6548  if (eap->line2 == 0) {
   6549    eap->line2 = 1;
   6550    eap->forceit = true;
   6551  }
   6552  curwin->w_cursor.lnum = eap->line2;
   6553  check_cursor_col(curwin);
   6554  do_put(eap->regname, NULL, eap->forceit ? BACKWARD : FORWARD, 1,
   6555         PUT_LINE|PUT_CURSLINE);
   6556 }
   6557 
   6558 /// ":iput".
   6559 static void ex_iput(exarg_T *eap)
   6560 {
   6561  // ":0iput" works like ":1iput!".
   6562  if (eap->line2 == 0) {
   6563    eap->line2 = 1;
   6564    eap->forceit = true;
   6565  }
   6566  curwin->w_cursor.lnum = eap->line2;
   6567  check_cursor_col(curwin);
   6568  do_put(eap->regname, NULL, eap->forceit ? BACKWARD : FORWARD, 1L,
   6569         PUT_LINE|PUT_CURSLINE|PUT_FIXINDENT);
   6570 }
   6571 
   6572 /// Handle ":copy" and ":move".
   6573 static void ex_copymove(exarg_T *eap)
   6574 {
   6575  const char *errormsg = NULL;
   6576  linenr_T n = get_address(eap, &eap->arg, eap->addr_type, false, false, false, 1, &errormsg);
   6577  if (eap->arg == NULL) {  // error detected
   6578    if (errormsg != NULL) {
   6579      emsg(errormsg);
   6580    }
   6581    eap->nextcmd = NULL;
   6582    return;
   6583  }
   6584  get_flags(eap);
   6585 
   6586  // move or copy lines from 'eap->line1'-'eap->line2' to below line 'n'
   6587  if (n == MAXLNUM || n < 0 || n > curbuf->b_ml.ml_line_count) {
   6588    emsg(_(e_invrange));
   6589    return;
   6590  }
   6591 
   6592  if (eap->cmdidx == CMD_move) {
   6593    if (do_move(eap->line1, eap->line2, n) == FAIL) {
   6594      return;
   6595    }
   6596  } else {
   6597    ex_copy(eap->line1, eap->line2, n);
   6598  }
   6599  u_clearline(curbuf);
   6600  beginline(BL_SOL | BL_FIX);
   6601  ex_may_print(eap);
   6602 }
   6603 
   6604 /// Print the current line if flags were given to the Ex command.
   6605 void ex_may_print(exarg_T *eap)
   6606 {
   6607  if (eap->flags != 0) {
   6608    print_line(curwin->w_cursor.lnum, (eap->flags & EXFLAG_NR),
   6609               (eap->flags & EXFLAG_LIST), true);
   6610    ex_no_reprint = true;
   6611  }
   6612 }
   6613 
   6614 /// ":smagic" and ":snomagic".
   6615 static void ex_submagic(exarg_T *eap)
   6616 {
   6617  const optmagic_T saved = magic_overruled;
   6618 
   6619  magic_overruled = eap->cmdidx == CMD_smagic ? OPTION_MAGIC_ON : OPTION_MAGIC_OFF;
   6620  ex_substitute(eap);
   6621  magic_overruled = saved;
   6622 }
   6623 
   6624 /// ":smagic" and ":snomagic" preview callback.
   6625 static int ex_submagic_preview(exarg_T *eap, int cmdpreview_ns, handle_T cmdpreview_bufnr)
   6626 {
   6627  const optmagic_T saved = magic_overruled;
   6628 
   6629  magic_overruled = eap->cmdidx == CMD_smagic ? OPTION_MAGIC_ON : OPTION_MAGIC_OFF;
   6630  int retv = ex_substitute_preview(eap, cmdpreview_ns, cmdpreview_bufnr);
   6631  magic_overruled = saved;
   6632 
   6633  return retv;
   6634 }
   6635 
   6636 /// ":join".
   6637 static void ex_join(exarg_T *eap)
   6638 {
   6639  curwin->w_cursor.lnum = eap->line1;
   6640  if (eap->line1 == eap->line2) {
   6641    if (eap->addr_count >= 2) {     // :2,2join does nothing
   6642      return;
   6643    }
   6644    if (eap->line2 == curbuf->b_ml.ml_line_count) {
   6645      beep_flush();
   6646      return;
   6647    }
   6648    eap->line2++;
   6649  }
   6650  do_join((size_t)((ssize_t)eap->line2 - eap->line1 + 1), !eap->forceit, true, true, true);
   6651  beginline(BL_WHITE | BL_FIX);
   6652  ex_may_print(eap);
   6653 }
   6654 
   6655 /// ":[addr]@r": execute register
   6656 static void ex_at(exarg_T *eap)
   6657 {
   6658  int prev_len = typebuf.tb_len;
   6659 
   6660  curwin->w_cursor.lnum = eap->line2;
   6661  check_cursor_col(curwin);
   6662 
   6663  // Get the register name. No name means use the previous one.
   6664  int c = (uint8_t)(*eap->arg);
   6665  if (c == NUL) {
   6666    c = '@';
   6667  }
   6668 
   6669  // Put the register in the typeahead buffer with the "silent" flag.
   6670  if (do_execreg(c, true, vim_strchr(p_cpo, CPO_EXECBUF) != NULL, true) == FAIL) {
   6671    beep_flush();
   6672    return;
   6673  }
   6674 
   6675  const bool save_efr = exec_from_reg;
   6676 
   6677  exec_from_reg = true;
   6678 
   6679  // Execute from the typeahead buffer.
   6680  // Continue until the stuff buffer is empty and all added characters
   6681  // have been consumed.
   6682  while (!stuff_empty() || typebuf.tb_len > prev_len) {
   6683    do_cmdline(NULL, getexline, NULL, DOCMD_NOWAIT|DOCMD_VERBOSE);
   6684  }
   6685 
   6686  exec_from_reg = save_efr;
   6687 }
   6688 
   6689 /// ":!".
   6690 static void ex_bang(exarg_T *eap)
   6691 {
   6692  do_bang(eap->addr_count, eap, eap->forceit, true, true);
   6693 }
   6694 
   6695 /// ":undo".
   6696 static void ex_undo(exarg_T *eap)
   6697 {
   6698  if (eap->addr_count != 1) {
   6699    if (eap->forceit) {
   6700      u_undo_and_forget(1, true);   // :undo!
   6701    } else {
   6702      u_undo(1);                    // :undo
   6703    }
   6704    return;
   6705  }
   6706 
   6707  linenr_T step = eap->line2;
   6708 
   6709  if (eap->forceit) {             // undo! 123
   6710    // change number for "undo!" must be lesser than current change number
   6711    if (step >= curbuf->b_u_seq_cur) {
   6712      emsg(_(e_undobang_cannot_redo_or_move_branch));
   6713      return;
   6714    }
   6715    // ensure that target change number is in same branch
   6716    // while also counting the amount of undoes it'd take to reach target
   6717    u_header_T *uhp;
   6718    int count = 0;
   6719 
   6720    for (uhp = curbuf->b_u_curhead ? curbuf->b_u_curhead : curbuf->b_u_newhead;
   6721         uhp != NULL && uhp->uh_seq > step;
   6722         uhp = uhp->uh_next.ptr, ++count) {}
   6723    if (step != 0 && (uhp == NULL || uhp->uh_seq < step)) {
   6724      emsg(_(e_undobang_cannot_redo_or_move_branch));
   6725      return;
   6726    }
   6727    u_undo_and_forget(count, true);
   6728  } else {                        // :undo 123
   6729    undo_time(step, false, false, true);
   6730  }
   6731 }
   6732 
   6733 static void ex_wundo(exarg_T *eap)
   6734 {
   6735  uint8_t hash[UNDO_HASH_SIZE];
   6736 
   6737  u_compute_hash(curbuf, hash);
   6738  u_write_undo(eap->arg, eap->forceit, curbuf, hash);
   6739 }
   6740 
   6741 static void ex_rundo(exarg_T *eap)
   6742 {
   6743  uint8_t hash[UNDO_HASH_SIZE];
   6744 
   6745  u_compute_hash(curbuf, hash);
   6746  u_read_undo(eap->arg, hash, NULL);
   6747 }
   6748 
   6749 /// ":redo".
   6750 static void ex_redo(exarg_T *eap)
   6751 {
   6752  u_redo(1);
   6753 }
   6754 
   6755 /// ":earlier" and ":later".
   6756 static void ex_later(exarg_T *eap)
   6757 {
   6758  int count = 0;
   6759  bool sec = false;
   6760  bool file = false;
   6761  char *p = eap->arg;
   6762 
   6763  if (*p == NUL) {
   6764    count = 1;
   6765  } else if (isdigit((uint8_t)(*p))) {
   6766    count = getdigits_int(&p, false, 0);
   6767    switch (*p) {
   6768    case 's':
   6769      p++; sec = true; break;
   6770    case 'm':
   6771      p++; sec = true; count *= 60; break;
   6772    case 'h':
   6773      p++; sec = true; count *= 60 * 60; break;
   6774    case 'd':
   6775      p++; sec = true; count *= 24 * 60 * 60; break;
   6776    case 'f':
   6777      p++; file = true; break;
   6778    }
   6779  }
   6780 
   6781  if (*p != NUL) {
   6782    semsg(_(e_invarg2), eap->arg);
   6783  } else {
   6784    undo_time(eap->cmdidx == CMD_earlier ? -count : count,
   6785              sec, file, false);
   6786  }
   6787 }
   6788 
   6789 /// ":redir": start/stop redirection.
   6790 static void ex_redir(exarg_T *eap)
   6791 {
   6792  char *arg = eap->arg;
   6793 
   6794  if (STRICMP(eap->arg, "END") == 0) {
   6795    close_redir();
   6796  } else {
   6797    if (*arg == '>') {
   6798      arg++;
   6799      char *mode;
   6800      if (*arg == '>') {
   6801        arg++;
   6802        mode = "a";
   6803      } else {
   6804        mode = "w";
   6805      }
   6806      arg = skipwhite(arg);
   6807 
   6808      close_redir();
   6809 
   6810      // Expand environment variables and "~/".
   6811      char *fname = expand_env_save(arg);
   6812      if (fname == NULL) {
   6813        return;
   6814      }
   6815 
   6816      redir_fd = open_exfile(fname, eap->forceit, mode);
   6817      xfree(fname);
   6818    } else if (*arg == '@') {
   6819      // redirect to a register a-z (resp. A-Z for appending)
   6820      close_redir();
   6821      arg++;
   6822      if (valid_yank_reg(*arg, true) && *arg != '_') {
   6823        redir_reg = (uint8_t)(*arg++);
   6824        if (*arg == '>' && arg[1] == '>') {        // append
   6825          arg += 2;
   6826        } else {
   6827          // Can use both "@a" and "@a>".
   6828          if (*arg == '>') {
   6829            arg++;
   6830          }
   6831          // Make register empty when not using @A-@Z and the
   6832          // command is valid.
   6833          if (*arg == NUL && !isupper(redir_reg)) {
   6834            write_reg_contents(redir_reg, "", 0, false);
   6835          }
   6836        }
   6837      }
   6838      if (*arg != NUL) {
   6839        redir_reg = 0;
   6840        semsg(_(e_invarg2), eap->arg);
   6841      }
   6842    } else if (*arg == '=' && arg[1] == '>') {
   6843      bool append;
   6844 
   6845      // redirect to a variable
   6846      close_redir();
   6847      arg += 2;
   6848 
   6849      if (*arg == '>') {
   6850        arg++;
   6851        append = true;
   6852      } else {
   6853        append = false;
   6854      }
   6855 
   6856      if (var_redir_start(skipwhite(arg), append) == OK) {
   6857        redir_vname = true;
   6858      }
   6859    } else {  // TODO(vim): redirect to a buffer
   6860      semsg(_(e_invarg2), eap->arg);
   6861    }
   6862  }
   6863 
   6864  // Make sure redirection is not off.  Can happen for cmdline completion
   6865  // that indirectly invokes a command to catch its output.
   6866  if (redir_fd != NULL
   6867      || redir_reg || redir_vname) {
   6868    redir_off = false;
   6869  }
   6870 }
   6871 
   6872 /// ":redraw": force redraw
   6873 static void ex_redraw(exarg_T *eap)
   6874 {
   6875  if (cmdpreview) {
   6876    return;  // Ignore :redraw during 'inccommand' preview. #9777
   6877  }
   6878  int r = RedrawingDisabled;
   6879  int p = p_lz;
   6880 
   6881  RedrawingDisabled = 0;
   6882  p_lz = false;
   6883  validate_cursor(curwin);
   6884  update_topline(curwin);
   6885  if (eap->forceit) {
   6886    redraw_all_later(UPD_NOT_VALID);
   6887    redraw_cmdline = true;
   6888  } else if (VIsual_active) {
   6889    redraw_curbuf_later(UPD_INVERTED);
   6890  }
   6891  update_screen();
   6892  if (need_maketitle) {
   6893    maketitle();
   6894  }
   6895  RedrawingDisabled = r;
   6896  p_lz = p;
   6897 
   6898  // Reset msg_didout, so that a message that's there is overwritten.
   6899  msg_didout = false;
   6900  msg_col = 0;
   6901 
   6902  // No need to wait after an intentional redraw.
   6903  need_wait_return = false;
   6904 
   6905  ui_flush();
   6906 }
   6907 
   6908 /// ":redrawstatus": force redraw of status line(s) and window bar(s)
   6909 static void ex_redrawstatus(exarg_T *eap)
   6910 {
   6911  if (cmdpreview) {
   6912    return;  // Ignore :redrawstatus during 'inccommand' preview. #9777
   6913  }
   6914  int r = RedrawingDisabled;
   6915  int p = p_lz;
   6916 
   6917  if (eap->forceit) {
   6918    status_redraw_all();
   6919  } else {
   6920    status_redraw_curbuf();
   6921  }
   6922 
   6923  RedrawingDisabled = 0;
   6924  p_lz = false;
   6925  if (State & MODE_CMDLINE) {
   6926    redraw_statuslines();
   6927  } else {
   6928    if (VIsual_active) {
   6929      redraw_curbuf_later(UPD_INVERTED);
   6930    }
   6931    update_screen();
   6932  }
   6933  RedrawingDisabled = r;
   6934  p_lz = p;
   6935  ui_flush();
   6936 }
   6937 
   6938 /// ":redrawtabline": force redraw of the tabline
   6939 static void ex_redrawtabline(exarg_T *eap FUNC_ATTR_UNUSED)
   6940 {
   6941  const int r = RedrawingDisabled;
   6942  const int p = p_lz;
   6943 
   6944  RedrawingDisabled = 0;
   6945  p_lz = false;
   6946 
   6947  draw_tabline();
   6948 
   6949  RedrawingDisabled = r;
   6950  p_lz = p;
   6951  ui_flush();
   6952 }
   6953 
   6954 static void close_redir(void)
   6955 {
   6956  if (redir_fd != NULL) {
   6957    fclose(redir_fd);
   6958    redir_fd = NULL;
   6959  }
   6960  redir_reg = 0;
   6961  if (redir_vname) {
   6962    var_redir_stop();
   6963    redir_vname = false;
   6964  }
   6965 }
   6966 
   6967 /// Try creating a directory, give error message on failure
   6968 ///
   6969 /// @param[in]  name  Directory to create.
   6970 /// @param[in]  prot  Directory permissions.
   6971 ///
   6972 /// @return OK in case of success, FAIL otherwise.
   6973 int vim_mkdir_emsg(const char *const name, const int prot)
   6974  FUNC_ATTR_NONNULL_ALL
   6975 {
   6976  int ret;
   6977  if ((ret = os_mkdir(name, prot)) != 0) {
   6978    semsg(_(e_mkdir), name, os_strerror(ret));
   6979    return FAIL;
   6980  }
   6981  return OK;
   6982 }
   6983 
   6984 /// Open a file for writing for an Ex command, with some checks.
   6985 ///
   6986 /// @param mode  "w" for create new file or "a" for append
   6987 ///
   6988 /// @return  file descriptor, or NULL on failure.
   6989 FILE *open_exfile(char *fname, int forceit, char *mode)
   6990 {
   6991 #ifdef UNIX
   6992  // with Unix it is possible to open a directory
   6993  if (os_isdir(fname)) {
   6994    semsg(_(e_isadir2), fname);
   6995    return NULL;
   6996  }
   6997 #endif
   6998  if (!forceit && *mode != 'a' && os_path_exists(fname)) {
   6999    semsg(_("E189: \"%s\" exists (add ! to override)"), fname);
   7000    return NULL;
   7001  }
   7002 
   7003  FILE *fd;
   7004  if ((fd = os_fopen(fname, mode)) == NULL) {
   7005    semsg(_("E190: Cannot open \"%s\" for writing"), fname);
   7006  }
   7007 
   7008  return fd;
   7009 }
   7010 
   7011 /// ":mark" and ":k".
   7012 static void ex_mark(exarg_T *eap)
   7013 {
   7014  if (*eap->arg == NUL) {               // No argument?
   7015    emsg(_(e_argreq));
   7016    return;
   7017  }
   7018 
   7019  if (eap->arg[1] != NUL) {         // more than one character?
   7020    semsg(_(e_trailing_arg), eap->arg);
   7021    return;
   7022  }
   7023 
   7024  pos_T pos = curwin->w_cursor;             // save curwin->w_cursor
   7025  curwin->w_cursor.lnum = eap->line2;
   7026  beginline(BL_WHITE | BL_FIX);
   7027  if (setmark(*eap->arg) == FAIL) {   // set mark
   7028    emsg(_("E191: Argument must be a letter or forward/backward quote"));
   7029  }
   7030  curwin->w_cursor = pos;             // restore curwin->w_cursor
   7031 }
   7032 
   7033 /// Update w_topline, w_leftcol and the cursor position.
   7034 void update_topline_cursor(void)
   7035 {
   7036  check_cursor(curwin);               // put cursor on valid line
   7037  update_topline(curwin);
   7038  if (!curwin->w_p_wrap) {
   7039    validate_cursor(curwin);
   7040  }
   7041  update_curswant();
   7042 }
   7043 
   7044 /// Save the current State and go to Normal mode.
   7045 ///
   7046 /// @return  true if the typeahead could be saved.
   7047 bool save_current_state(save_state_T *sst)
   7048  FUNC_ATTR_NONNULL_ALL
   7049 {
   7050  sst->save_msg_scroll = msg_scroll;
   7051  sst->save_restart_edit = restart_edit;
   7052  sst->save_msg_didout = msg_didout;
   7053  sst->save_State = State;
   7054  sst->save_finish_op = finish_op;
   7055  sst->save_opcount = opcount;
   7056  sst->save_reg_executing = reg_executing;
   7057  sst->save_pending_end_reg_executing = pending_end_reg_executing;
   7058 
   7059  msg_scroll = false;   // no msg scrolling in Normal mode
   7060  restart_edit = 0;     // don't go to Insert mode
   7061 
   7062  // Save the current typeahead.  This is required to allow using ":normal"
   7063  // from an event handler and makes sure we don't hang when the argument
   7064  // ends with half a command.
   7065  save_typeahead(&sst->tabuf);
   7066  return sst->tabuf.typebuf_valid;
   7067 }
   7068 
   7069 void restore_current_state(save_state_T *sst)
   7070  FUNC_ATTR_NONNULL_ALL
   7071 {
   7072  // Restore the previous typeahead.
   7073  restore_typeahead(&sst->tabuf);
   7074 
   7075  msg_scroll = sst->save_msg_scroll;
   7076  if (force_restart_edit) {
   7077    force_restart_edit = false;
   7078  } else {
   7079    // Some function (terminal_enter()) was aware of ex_normal and decided to
   7080    // override the value of restart_edit anyway.
   7081    restart_edit = sst->save_restart_edit;
   7082  }
   7083  finish_op = sst->save_finish_op;
   7084  opcount = sst->save_opcount;
   7085  reg_executing = sst->save_reg_executing;
   7086  pending_end_reg_executing = sst->save_pending_end_reg_executing;
   7087 
   7088  // don't reset msg_didout now
   7089  msg_didout |= sst->save_msg_didout;
   7090 
   7091  // Restore the state (needed when called from a function executed for
   7092  // 'indentexpr'). Update the mouse and cursor, they may have changed.
   7093  State = sst->save_State;
   7094  ui_cursor_shape();  // may show different cursor shape
   7095 }
   7096 
   7097 bool expr_map_locked(void)
   7098 {
   7099  return expr_map_lock > 0 && !(curbuf->b_flags & BF_DUMMY);
   7100 }
   7101 
   7102 /// ":normal[!] {commands}": Execute normal mode commands.
   7103 static void ex_normal(exarg_T *eap)
   7104 {
   7105  if (curbuf->terminal && State & MODE_TERMINAL) {
   7106    emsg("Can't re-enter normal mode from terminal mode");
   7107    return;
   7108  }
   7109  char *arg = NULL;
   7110 
   7111  if (expr_map_locked()) {
   7112    emsg(_(e_secure));
   7113    return;
   7114  }
   7115 
   7116  if (ex_normal_busy >= p_mmd) {
   7117    emsg(_("E192: Recursive use of :normal too deep"));
   7118    return;
   7119  }
   7120 
   7121  // vgetc() expects K_SPECIAL to have been escaped.  Don't do
   7122  // this for the K_SPECIAL leading byte, otherwise special keys will not
   7123  // work.
   7124  {
   7125    int len = 0;
   7126 
   7127    // Count the number of characters to be escaped.
   7128    int l;
   7129    for (char *p = eap->arg; *p != NUL; p++) {
   7130      for (l = utfc_ptr2len(p) - 1; l > 0; l--) {
   7131        if (*++p == (char)K_SPECIAL) {  // trailbyte K_SPECIAL
   7132          len += 2;
   7133        }
   7134      }
   7135    }
   7136    if (len > 0) {
   7137      arg = xmalloc(strlen(eap->arg) + (size_t)len + 1);
   7138      len = 0;
   7139      for (char *p = eap->arg; *p != NUL; p++) {
   7140        arg[len++] = *p;
   7141        for (l = utfc_ptr2len(p) - 1; l > 0; l--) {
   7142          arg[len++] = *++p;
   7143          if (*p == (char)K_SPECIAL) {
   7144            arg[len++] = (char)KS_SPECIAL;
   7145            arg[len++] = KE_FILLER;
   7146          }
   7147        }
   7148        arg[len] = NUL;
   7149      }
   7150    }
   7151  }
   7152 
   7153  ex_normal_busy++;
   7154  save_state_T save_state;
   7155  if (save_current_state(&save_state)) {
   7156    // Repeat the :normal command for each line in the range.  When no
   7157    // range given, execute it just once, without positioning the cursor
   7158    // first.
   7159    do {
   7160      if (eap->addr_count != 0) {
   7161        curwin->w_cursor.lnum = eap->line1++;
   7162        curwin->w_cursor.col = 0;
   7163        check_cursor_moved(curwin);
   7164      }
   7165 
   7166      exec_normal_cmd((arg != NULL ? arg : eap->arg),
   7167                      eap->forceit ? REMAP_NONE : REMAP_YES, false);
   7168    } while (eap->addr_count > 0 && eap->line1 <= eap->line2 && !got_int);
   7169  }
   7170 
   7171  // Might not return to the main loop when in an event handler.
   7172  update_topline_cursor();
   7173 
   7174  restore_current_state(&save_state);
   7175 
   7176  ex_normal_busy--;
   7177 
   7178  setmouse();
   7179  ui_cursor_shape();  // may show different cursor shape
   7180  xfree(arg);
   7181 }
   7182 
   7183 /// ":startinsert", ":startreplace" and ":startgreplace"
   7184 static void ex_startinsert(exarg_T *eap)
   7185 {
   7186  if (eap->forceit) {
   7187    // cursor line can be zero on startup
   7188    if (!curwin->w_cursor.lnum) {
   7189      curwin->w_cursor.lnum = 1;
   7190    }
   7191    set_cursor_for_append_to_line();
   7192  }
   7193 
   7194  // Ignore the command when already in Insert mode.  Inserting an
   7195  // expression register that invokes a function can do this.
   7196  if (State & MODE_INSERT) {
   7197    return;
   7198  }
   7199 
   7200  if (eap->cmdidx == CMD_startinsert) {
   7201    restart_edit = 'a';
   7202  } else if (eap->cmdidx == CMD_startreplace) {
   7203    restart_edit = 'R';
   7204  } else {
   7205    restart_edit = 'V';
   7206  }
   7207 
   7208  if (!eap->forceit) {
   7209    if (eap->cmdidx == CMD_startinsert) {
   7210      restart_edit = 'i';
   7211    }
   7212    curwin->w_curswant = 0;  // avoid MAXCOL
   7213  }
   7214 
   7215  if (VIsual_active) {
   7216    showmode();
   7217  }
   7218 }
   7219 
   7220 /// ":stopinsert"
   7221 static void ex_stopinsert(exarg_T *eap)
   7222 {
   7223  restart_edit = 0;
   7224  stop_insert_mode = true;
   7225  clearmode();
   7226 }
   7227 
   7228 /// Execute normal mode command "cmd".
   7229 /// "remap" can be REMAP_NONE or REMAP_YES.
   7230 void exec_normal_cmd(char *cmd, int remap, bool silent)
   7231 {
   7232  // Stuff the argument into the typeahead buffer.
   7233  ins_typebuf(cmd, remap, 0, true, silent);
   7234  exec_normal(false, false);
   7235 }
   7236 
   7237 /// Execute normal_cmd() until there is no typeahead left.
   7238 ///
   7239 /// @param was_typed whether or not something was typed
   7240 /// @param use_vpeekc  true to use vpeekc() to check for available chars
   7241 void exec_normal(bool was_typed, bool use_vpeekc)
   7242 {
   7243  oparg_T oa;
   7244  int c;
   7245 
   7246  // When calling vpeekc() from feedkeys() it will return Ctrl_C when there
   7247  // is nothing to get, so also check for Ctrl_C.
   7248  clear_oparg(&oa);
   7249  finish_op = false;
   7250  while ((!stuff_empty()
   7251          || ((was_typed || !typebuf_typed())
   7252              && typebuf.tb_len > 0)
   7253          || (use_vpeekc && (c = vpeekc()) != NUL && c != Ctrl_C))
   7254         && !got_int) {
   7255    update_topline_cursor();
   7256    normal_cmd(&oa, true);      // execute a Normal mode cmd
   7257  }
   7258 }
   7259 
   7260 static void ex_checkpath(exarg_T *eap)
   7261 {
   7262  find_pattern_in_path(NULL, 0, 0, false, false, CHECK_PATH, 1,
   7263                       eap->forceit ? ACTION_SHOW_ALL : ACTION_SHOW,
   7264                       1, (linenr_T)MAXLNUM, eap->forceit, false);
   7265 }
   7266 
   7267 /// ":psearch"
   7268 static void ex_psearch(exarg_T *eap)
   7269 {
   7270  g_do_tagpreview = (int)p_pvh;
   7271  ex_findpat(eap);
   7272  g_do_tagpreview = 0;
   7273 }
   7274 
   7275 static void ex_findpat(exarg_T *eap)
   7276 {
   7277  bool whole = true;
   7278  int action;
   7279 
   7280  switch (cmdnames[eap->cmdidx].cmd_name[2]) {
   7281  case 'e':             // ":psearch", ":isearch" and ":dsearch"
   7282    if (cmdnames[eap->cmdidx].cmd_name[0] == 'p') {
   7283      action = ACTION_GOTO;
   7284    } else {
   7285      action = ACTION_SHOW;
   7286    }
   7287    break;
   7288  case 'i':             // ":ilist" and ":dlist"
   7289    action = ACTION_SHOW_ALL;
   7290    break;
   7291  case 'u':             // ":ijump" and ":djump"
   7292    action = ACTION_GOTO;
   7293    break;
   7294  default:              // ":isplit" and ":dsplit"
   7295    action = ACTION_SPLIT;
   7296    break;
   7297  }
   7298 
   7299  int n = 1;
   7300  if (ascii_isdigit(*eap->arg)) {  // get count
   7301    n = getdigits_int(&eap->arg, false, 0);
   7302    eap->arg = skipwhite(eap->arg);
   7303  }
   7304  if (*eap->arg == '/') {   // Match regexp, not just whole words
   7305    whole = false;
   7306    eap->arg++;
   7307    char *p = skip_regexp(eap->arg, '/', magic_isset());
   7308    if (*p) {
   7309      *p++ = NUL;
   7310      p = skipwhite(p);
   7311 
   7312      // Check for trailing illegal characters.
   7313      if (!ends_excmd(*p)) {
   7314        eap->errmsg = ex_errmsg(e_trailing_arg, p);
   7315      } else {
   7316        eap->nextcmd = check_nextcmd(p);
   7317      }
   7318    }
   7319  }
   7320  if (!eap->skip) {
   7321    find_pattern_in_path(eap->arg, 0, strlen(eap->arg), whole, !eap->forceit,
   7322                         *eap->cmd == 'd' ? FIND_DEFINE : FIND_ANY, n, action,
   7323                         eap->line1, eap->line2, eap->forceit, false);
   7324  }
   7325 }
   7326 
   7327 /// ":ptag", ":ptselect", ":ptjump", ":ptnext", etc.
   7328 static void ex_ptag(exarg_T *eap)
   7329 {
   7330  g_do_tagpreview = (int)p_pvh;    // will be reset to 0 in ex_tag_cmd()
   7331  ex_tag_cmd(eap, cmdnames[eap->cmdidx].cmd_name + 1);
   7332 }
   7333 
   7334 /// ":pedit"
   7335 static void ex_pedit(exarg_T *eap)
   7336 {
   7337  win_T *curwin_save = curwin;
   7338  prepare_preview_window();
   7339 
   7340  // Edit the file.
   7341  do_exedit(eap, NULL);
   7342 
   7343  back_to_current_window(curwin_save);
   7344 }
   7345 
   7346 /// ":pbuffer"
   7347 static void ex_pbuffer(exarg_T *eap)
   7348 {
   7349  win_T *curwin_save = curwin;
   7350  prepare_preview_window();
   7351 
   7352  // Go to the buffer.
   7353  do_exbuffer(eap);
   7354 
   7355  back_to_current_window(curwin_save);
   7356 }
   7357 
   7358 static void prepare_preview_window(void)
   7359 {
   7360  // Open the preview window or popup and make it the current window.
   7361  g_do_tagpreview = (int)p_pvh;
   7362  prepare_tagpreview(true);
   7363 }
   7364 
   7365 static void back_to_current_window(win_T *curwin_save)
   7366 {
   7367  if (curwin != curwin_save && win_valid(curwin_save)) {
   7368    // Return cursor to where we were
   7369    validate_cursor(curwin);
   7370    redraw_later(curwin, UPD_VALID);
   7371    win_enter(curwin_save, true);
   7372  }
   7373  g_do_tagpreview = 0;
   7374 }
   7375 
   7376 /// ":stag", ":stselect" and ":stjump".
   7377 static void ex_stag(exarg_T *eap)
   7378 {
   7379  postponed_split = -1;
   7380  postponed_split_flags = cmdmod.cmod_split;
   7381  postponed_split_tab = cmdmod.cmod_tab;
   7382  ex_tag_cmd(eap, cmdnames[eap->cmdidx].cmd_name + 1);
   7383  postponed_split_flags = 0;
   7384  postponed_split_tab = 0;
   7385 }
   7386 
   7387 /// ":tag", ":tselect", ":tjump", ":tnext", etc.
   7388 static void ex_tag(exarg_T *eap)
   7389 {
   7390  ex_tag_cmd(eap, cmdnames[eap->cmdidx].cmd_name);
   7391 }
   7392 
   7393 static void ex_tag_cmd(exarg_T *eap, const char *name)
   7394 {
   7395  int cmd;
   7396 
   7397  switch (name[1]) {
   7398  case 'j':
   7399    cmd = DT_JUMP;              // ":tjump"
   7400    break;
   7401  case 's':
   7402    cmd = DT_SELECT;            // ":tselect"
   7403    break;
   7404  case 'p':                             // ":tprevious"
   7405  case 'N':
   7406    cmd = DT_PREV;              // ":tNext"
   7407    break;
   7408  case 'n':
   7409    cmd = DT_NEXT;              // ":tnext"
   7410    break;
   7411  case 'o':
   7412    cmd = DT_POP;               // ":pop"
   7413    break;
   7414  case 'f':                             // ":tfirst"
   7415  case 'r':
   7416    cmd = DT_FIRST;             // ":trewind"
   7417    break;
   7418  case 'l':
   7419    cmd = DT_LAST;              // ":tlast"
   7420    break;
   7421  default:                              // ":tag"
   7422    cmd = DT_TAG;
   7423    break;
   7424  }
   7425 
   7426  if (name[0] == 'l') {
   7427    cmd = DT_LTAG;
   7428  }
   7429 
   7430  do_tag(eap->arg, cmd, eap->addr_count > 0 ? (int)eap->line2 : 1,
   7431         eap->forceit, true);
   7432 }
   7433 
   7434 enum {
   7435  SPEC_PERC = 0,
   7436  SPEC_HASH,
   7437  SPEC_CWORD,
   7438  SPEC_CCWORD,
   7439  SPEC_CEXPR,
   7440  SPEC_CFILE,
   7441  SPEC_SFILE,
   7442  SPEC_SLNUM,
   7443  SPEC_STACK,
   7444  SPEC_SCRIPT,
   7445  SPEC_AFILE,
   7446  SPEC_ABUF,
   7447  SPEC_AMATCH,
   7448  SPEC_SFLNUM,
   7449  SPEC_SID,
   7450  // SPEC_CLIENT,
   7451 };
   7452 
   7453 /// Check "str" for starting with a special cmdline variable.
   7454 /// If found return one of the SPEC_ values and set "*usedlen" to the length of
   7455 /// the variable.  Otherwise return -1 and "*usedlen" is unchanged.
   7456 ssize_t find_cmdline_var(const char *src, size_t *usedlen)
   7457  FUNC_ATTR_NONNULL_ALL
   7458 {
   7459  static char *(spec_str[]) = {
   7460    [SPEC_PERC] = "%",
   7461    [SPEC_HASH] = "#",
   7462    [SPEC_CWORD] = "<cword>",           // cursor word
   7463    [SPEC_CCWORD] = "<cWORD>",          // cursor WORD
   7464    [SPEC_CEXPR] = "<cexpr>",           // expr under cursor
   7465    [SPEC_CFILE] = "<cfile>",           // cursor path name
   7466    [SPEC_SFILE] = "<sfile>",           // ":so" file name
   7467    [SPEC_SLNUM] = "<slnum>",           // ":so" file line number
   7468    [SPEC_STACK] = "<stack>",           // call stack
   7469    [SPEC_SCRIPT] = "<script>",         // script file name
   7470    [SPEC_AFILE] = "<afile>",           // autocommand file name
   7471    [SPEC_ABUF] = "<abuf>",             // autocommand buffer number
   7472    [SPEC_AMATCH] = "<amatch>",         // autocommand match name
   7473    [SPEC_SFLNUM] = "<sflnum>",         // script file line number
   7474    [SPEC_SID] = "<SID>",               // script ID: <SNR>123_
   7475    // [SPEC_CLIENT] = "<client>",
   7476  };
   7477 
   7478  for (size_t i = 0; i < ARRAY_SIZE(spec_str); i++) {
   7479    size_t len = strlen(spec_str[i]);
   7480    if (strncmp(src, spec_str[i], len) == 0) {
   7481      *usedlen = len;
   7482      assert(i <= SSIZE_MAX);
   7483      return (ssize_t)i;
   7484    }
   7485  }
   7486  return -1;
   7487 }
   7488 
   7489 /// Evaluate cmdline variables.
   7490 ///
   7491 /// change "%"       to curbuf->b_ffname
   7492 ///        "#"       to curwin->w_alt_fnum
   7493 ///        "<cword>" to word under the cursor
   7494 ///        "<cWORD>" to WORD under the cursor
   7495 ///        "<cexpr>" to C-expression under the cursor
   7496 ///        "<cfile>" to path name under the cursor
   7497 ///        "<sfile>" to sourced file name
   7498 ///        "<stack>" to call stack
   7499 ///        "<script>" to current script name
   7500 ///        "<slnum>" to sourced file line number
   7501 ///        "<afile>" to file name for autocommand
   7502 ///        "<abuf>"  to buffer number for autocommand
   7503 ///        "<amatch>" to matching name for autocommand
   7504 ///
   7505 /// When an error is detected, "errormsg" is set to a non-NULL pointer (may be
   7506 /// "" for error without a message) and NULL is returned.
   7507 ///
   7508 /// @param src             pointer into commandline
   7509 /// @param srcstart        beginning of valid memory for src
   7510 /// @param usedlen         characters after src that are used
   7511 /// @param lnump           line number for :e command, or NULL
   7512 /// @param errormsg        pointer to error message
   7513 /// @param escaped         return value has escaped white space (can be NULL)
   7514 /// @param empty_is_error  empty result is considered an error
   7515 ///
   7516 /// @return          an allocated string if a valid match was found.
   7517 ///                  Returns NULL if no match was found.  "usedlen" then still contains the
   7518 ///                  number of characters to skip.
   7519 char *eval_vars(char *src, const char *srcstart, size_t *usedlen, linenr_T *lnump,
   7520                const char **errormsg, int *escaped, bool empty_is_error)
   7521 {
   7522  char *result = "";
   7523  char *resultbuf = NULL;
   7524  size_t resultlen;
   7525  int valid = VALID_HEAD | VALID_PATH;  // Assume valid result.
   7526  bool tilde_file = false;
   7527  bool skip_mod = false;
   7528  char strbuf[30];
   7529 
   7530  *errormsg = NULL;
   7531  if (escaped != NULL) {
   7532    *escaped = false;
   7533  }
   7534 
   7535  // Check if there is something to do.
   7536  ssize_t spec_idx = find_cmdline_var(src, usedlen);
   7537  if (spec_idx < 0) {   // no match
   7538    *usedlen = 1;
   7539    return NULL;
   7540  }
   7541 
   7542  // Skip when preceded with a backslash "\%" and "\#".
   7543  // Note: In "\\%" the % is also not recognized!
   7544  if (src > srcstart && src[-1] == '\\') {
   7545    *usedlen = 0;
   7546    STRMOVE(src - 1, src);      // remove backslash
   7547    return NULL;
   7548  }
   7549 
   7550  // word or WORD under cursor
   7551  if (spec_idx == SPEC_CWORD
   7552      || spec_idx == SPEC_CCWORD
   7553      || spec_idx == SPEC_CEXPR) {
   7554    resultlen = find_ident_under_cursor(&result,
   7555                                        spec_idx == SPEC_CWORD
   7556                                        ? (FIND_IDENT | FIND_STRING)
   7557                                        : (spec_idx == SPEC_CEXPR
   7558                                           ? (FIND_IDENT | FIND_STRING | FIND_EVAL)
   7559                                           : FIND_STRING));
   7560    if (resultlen == 0) {
   7561      *errormsg = "";
   7562      return NULL;
   7563    }
   7564    //
   7565    // '#': Alternate file name
   7566    // '%': Current file name
   7567    //        File name under the cursor
   7568    //        File name for autocommand
   7569    //    and following modifiers
   7570    //
   7571  } else {
   7572    switch (spec_idx) {
   7573    case SPEC_PERC:             // '%': current file
   7574      if (curbuf->b_fname == NULL) {
   7575        result = "";
   7576        valid = 0;                  // Must have ":p:h" to be valid
   7577      } else {
   7578        result = curbuf->b_fname;
   7579        tilde_file = strcmp(result, "~") == 0;
   7580      }
   7581      break;
   7582 
   7583    case SPEC_HASH:             // '#' or "#99": alternate file
   7584      if (src[1] == '#') {          // "##": the argument list
   7585        result = arg_all();
   7586        resultbuf = result;
   7587        *usedlen = 2;
   7588        if (escaped != NULL) {
   7589          *escaped = true;
   7590        }
   7591        skip_mod = true;
   7592        break;
   7593      }
   7594      char *s = src + 1;
   7595      if (*s == '<') {                  // "#<99" uses v:oldfiles.
   7596        s++;
   7597      }
   7598      int i = getdigits_int(&s, false, 0);
   7599      if (s == src + 2 && src[1] == '-') {
   7600        // just a minus sign, don't skip over it
   7601        s--;
   7602      }
   7603      *usedlen = (size_t)(s - src);           // length of what we expand
   7604 
   7605      if (src[1] == '<' && i != 0) {
   7606        if (*usedlen < 2) {
   7607          // Should we give an error message for #<text?
   7608          *usedlen = 1;
   7609          return NULL;
   7610        }
   7611        result = (char *)tv_list_find_str(get_vim_var_list(VV_OLDFILES), i - 1);
   7612        if (result == NULL) {
   7613          *errormsg = "";
   7614          return NULL;
   7615        }
   7616      } else {
   7617        if (i == 0 && src[1] == '<' && *usedlen > 1) {
   7618          *usedlen = 1;
   7619        }
   7620        buf_T *buf = buflist_findnr(i);
   7621        if (buf == NULL) {
   7622          *errormsg = _("E194: No alternate file name to substitute for '#'");
   7623          return NULL;
   7624        }
   7625        if (lnump != NULL) {
   7626          *lnump = ECMD_LAST;
   7627        }
   7628        if (buf->b_fname == NULL) {
   7629          result = "";
   7630          valid = 0;                        // Must have ":p:h" to be valid
   7631        } else {
   7632          result = buf->b_fname;
   7633          tilde_file = strcmp(result, "~") == 0;
   7634        }
   7635      }
   7636      break;
   7637 
   7638    case SPEC_CFILE:            // file name under cursor
   7639      result = file_name_at_cursor(FNAME_MESS|FNAME_HYP, 1, NULL);
   7640      if (result == NULL) {
   7641        *errormsg = "";
   7642        return NULL;
   7643      }
   7644      resultbuf = result;                   // remember allocated string
   7645      break;
   7646 
   7647    case SPEC_AFILE:  // file name for autocommand
   7648      if (autocmd_fname != NULL && !autocmd_fname_full) {
   7649        // Still need to turn the fname into a full path.  It was
   7650        // postponed to avoid a delay when <afile> is not used.
   7651        autocmd_fname_full = true;
   7652        result = FullName_save(autocmd_fname, false);
   7653        // Copy into `autocmd_fname`, don't reassign it. #8165
   7654        xstrlcpy(autocmd_fname, result, MAXPATHL);
   7655        xfree(result);
   7656      }
   7657      result = autocmd_fname;
   7658      if (result == NULL) {
   7659        *errormsg = _(e_no_autocommand_file_name_to_substitute_for_afile);
   7660        return NULL;
   7661      }
   7662      result = path_try_shorten_fname(result);
   7663      break;
   7664 
   7665    case SPEC_ABUF:             // buffer number for autocommand
   7666      if (autocmd_bufnr <= 0) {
   7667        *errormsg = _(e_no_autocommand_buffer_number_to_substitute_for_abuf);
   7668        return NULL;
   7669      }
   7670      snprintf(strbuf, sizeof(strbuf), "%d", autocmd_bufnr);
   7671      result = strbuf;
   7672      break;
   7673 
   7674    case SPEC_AMATCH:           // match name for autocommand
   7675      result = autocmd_match;
   7676      if (result == NULL) {
   7677        *errormsg = _(e_no_autocommand_match_name_to_substitute_for_amatch);
   7678        return NULL;
   7679      }
   7680      break;
   7681 
   7682    case SPEC_SFILE:            // file name for ":so" command
   7683      result = estack_sfile(ESTACK_SFILE);
   7684      if (result == NULL) {
   7685        *errormsg = _(e_no_source_file_name_to_substitute_for_sfile);
   7686        return NULL;
   7687      }
   7688      resultbuf = result;  // remember allocated string
   7689      break;
   7690    case SPEC_STACK:            // call stack
   7691      result = estack_sfile(ESTACK_STACK);
   7692      if (result == NULL) {
   7693        *errormsg = _(e_no_call_stack_to_substitute_for_stack);
   7694        return NULL;
   7695      }
   7696      resultbuf = result;  // remember allocated string
   7697      break;
   7698    case SPEC_SCRIPT:           // script file name
   7699      result = estack_sfile(ESTACK_SCRIPT);
   7700      if (result == NULL) {
   7701        *errormsg = _(e_no_script_file_name_to_substitute_for_script);
   7702        return NULL;
   7703      }
   7704      resultbuf = result;  // remember allocated string
   7705      break;
   7706 
   7707    case SPEC_SLNUM:            // line in file for ":so" command
   7708      if (SOURCING_NAME == NULL || SOURCING_LNUM == 0) {
   7709        *errormsg = _(e_no_line_number_to_use_for_slnum);
   7710        return NULL;
   7711      }
   7712      snprintf(strbuf, sizeof(strbuf), "%" PRIdLINENR, SOURCING_LNUM);
   7713      result = strbuf;
   7714      break;
   7715 
   7716    case SPEC_SFLNUM:  // line in script file
   7717      if (current_sctx.sc_lnum + SOURCING_LNUM == 0) {
   7718        *errormsg = _(e_no_line_number_to_use_for_sflnum);
   7719        return NULL;
   7720      }
   7721      snprintf(strbuf, sizeof(strbuf), "%" PRIdLINENR,
   7722               current_sctx.sc_lnum + SOURCING_LNUM);
   7723      result = strbuf;
   7724      break;
   7725 
   7726    case SPEC_SID:
   7727      if (current_sctx.sc_sid <= 0) {
   7728        *errormsg = _(e_usingsid);
   7729        return NULL;
   7730      }
   7731      snprintf(strbuf, sizeof(strbuf), "<SNR>%" PRIdSCID "_", current_sctx.sc_sid);
   7732      result = strbuf;
   7733      break;
   7734 
   7735    default:
   7736      // should not happen
   7737      *errormsg = "";
   7738      break;
   7739    }
   7740 
   7741    // Length of new string.
   7742    resultlen = strlen(result);
   7743    // Remove the file name extension.
   7744    if (src[*usedlen] == '<') {
   7745      (*usedlen)++;
   7746      char *s;
   7747      if ((s = strrchr(result, '.')) != NULL
   7748          && s >= path_tail(result)) {
   7749        resultlen = (size_t)(s - result);
   7750      }
   7751    } else if (!skip_mod) {
   7752      valid |= modify_fname(src, tilde_file, usedlen, &result,
   7753                            &resultbuf, &resultlen);
   7754      if (result == NULL) {
   7755        *errormsg = "";
   7756        return NULL;
   7757      }
   7758    }
   7759  }
   7760 
   7761  if (resultlen == 0 || valid != VALID_HEAD + VALID_PATH) {
   7762    if (empty_is_error) {
   7763      if (valid != VALID_HEAD + VALID_PATH) {
   7764        // xgettext:no-c-format
   7765        *errormsg = _("E499: Empty file name for '%' or '#', only works with \":p:h\"");
   7766      } else {
   7767        *errormsg = _("E500: Evaluates to an empty string");
   7768      }
   7769    }
   7770    result = NULL;
   7771  } else {
   7772    result = xmemdupz(result, resultlen);
   7773  }
   7774  xfree(resultbuf);
   7775  return result;
   7776 }
   7777 
   7778 /// Expand the <sfile> string in "arg".
   7779 ///
   7780 /// @return  an allocated string, or NULL for any error.
   7781 char *expand_sfile(char *arg)
   7782 {
   7783  char *result = xstrdup(arg);
   7784 
   7785  for (char *p = result; *p;) {
   7786    if (strncmp(p, "<sfile>", 7) != 0) {
   7787      p++;
   7788    } else {
   7789      // replace "<sfile>" with the sourced file name, and do ":" stuff
   7790      size_t srclen;
   7791      const char *errormsg;
   7792      char *repl = eval_vars(p, result, &srclen, NULL, &errormsg, NULL, true);
   7793      if (errormsg != NULL) {
   7794        if (*errormsg) {
   7795          emsg(errormsg);
   7796        }
   7797        xfree(result);
   7798        return NULL;
   7799      }
   7800      if (repl == NULL) {               // no match (cannot happen)
   7801        p += srclen;
   7802        continue;
   7803      }
   7804      size_t len = strlen(result) - srclen + strlen(repl) + 1;
   7805      char *newres = xmalloc(len);
   7806      memmove(newres, result, (size_t)(p - result));
   7807      STRCPY(newres + (p - result), repl);
   7808      len = strlen(newres);
   7809      strcat(newres, p + srclen);
   7810      xfree(repl);
   7811      xfree(result);
   7812      result = newres;
   7813      p = newres + len;                 // continue after the match
   7814    }
   7815  }
   7816 
   7817  return result;
   7818 }
   7819 
   7820 /// ":rshada" and ":wshada".
   7821 static void ex_shada(exarg_T *eap)
   7822 {
   7823  char *save_shada = p_shada;
   7824  if (*p_shada == NUL) {
   7825    p_shada = "'100";
   7826  }
   7827  if (eap->cmdidx == CMD_rviminfo || eap->cmdidx == CMD_rshada) {
   7828    shada_read_everything(eap->arg, eap->forceit, false);
   7829  } else {
   7830    shada_write_file(eap->arg, eap->forceit);
   7831  }
   7832  p_shada = save_shada;
   7833 }
   7834 
   7835 /// Make a dialog message in "buff[DIALOG_MSG_SIZE]".
   7836 /// "format" must contain "%s".
   7837 void dialog_msg(char *buff, char *format, char *fname)
   7838 {
   7839  if (fname == NULL) {
   7840    fname = _("Untitled");
   7841  }
   7842  vim_snprintf(buff, DIALOG_MSG_SIZE, format, fname);
   7843 }
   7844 
   7845 static TriState filetype_detect = kNone;
   7846 static TriState filetype_plugin = kNone;
   7847 static TriState filetype_indent = kNone;
   7848 
   7849 /// ":filetype [plugin] [indent] {on,off,detect}"
   7850 /// on: Load the filetype.vim file to install autocommands for file types.
   7851 /// off: Load the ftoff.vim file to remove all autocommands for file types.
   7852 /// plugin on: load filetype.vim and ftplugin.vim
   7853 /// plugin off: load ftplugof.vim
   7854 /// indent on: load filetype.vim and indent.vim
   7855 /// indent off: load indoff.vim
   7856 static void ex_filetype(exarg_T *eap)
   7857 {
   7858  if (*eap->arg == NUL) {
   7859    // Print current status.
   7860    smsg(0, "filetype detection:%s  plugin:%s  indent:%s",
   7861         filetype_detect == kTrue ? "ON" : "OFF",
   7862         filetype_plugin == kTrue ? (filetype_detect == kTrue ? "ON" : "(on)") : "OFF",
   7863         filetype_indent == kTrue ? (filetype_detect == kTrue ? "ON" : "(on)") : "OFF");
   7864    return;
   7865  }
   7866 
   7867  char *arg = eap->arg;
   7868  bool plugin = false;
   7869  bool indent = false;
   7870 
   7871  // Accept "plugin" and "indent" in any order.
   7872  while (true) {
   7873    if (strncmp(arg, "plugin", 6) == 0) {
   7874      plugin = true;
   7875      arg = skipwhite(arg + 6);
   7876      continue;
   7877    }
   7878    if (strncmp(arg, "indent", 6) == 0) {
   7879      indent = true;
   7880      arg = skipwhite(arg + 6);
   7881      continue;
   7882    }
   7883    break;
   7884  }
   7885  if (strcmp(arg, "on") == 0 || strcmp(arg, "detect") == 0) {
   7886    if (*arg == 'o' || filetype_detect != kTrue) {
   7887      source_runtime(FILETYPE_FILE, DIP_ALL);
   7888      filetype_detect = kTrue;
   7889      if (plugin) {
   7890        source_runtime(FTPLUGIN_FILE, DIP_ALL);
   7891        filetype_plugin = kTrue;
   7892      }
   7893      if (indent) {
   7894        source_runtime(INDENT_FILE, DIP_ALL);
   7895        filetype_indent = kTrue;
   7896      }
   7897    }
   7898    if (*arg == 'd') {
   7899      do_doautocmd("filetypedetect BufRead", true, NULL);
   7900      do_modelines(0);
   7901    }
   7902  } else if (strcmp(arg, "off") == 0) {
   7903    if (plugin || indent) {
   7904      if (plugin) {
   7905        source_runtime(FTPLUGOF_FILE, DIP_ALL);
   7906        filetype_plugin = kFalse;
   7907      }
   7908      if (indent) {
   7909        source_runtime(INDOFF_FILE, DIP_ALL);
   7910        filetype_indent = kFalse;
   7911      }
   7912    } else {
   7913      source_runtime(FTOFF_FILE, DIP_ALL);
   7914      filetype_detect = kFalse;
   7915    }
   7916  } else {
   7917    semsg(_(e_invarg2), arg);
   7918  }
   7919 }
   7920 
   7921 /// Source ftplugin.vim and indent.vim to create the necessary FileType
   7922 /// autocommands. We do this separately from filetype.vim so that these
   7923 /// autocommands will always fire first (and thus can be overridden) while still
   7924 /// allowing general filetype detection to be disabled in the user's init file.
   7925 void filetype_plugin_enable(void)
   7926 {
   7927  if (filetype_plugin == kNone) {
   7928    source_runtime(FTPLUGIN_FILE, DIP_ALL);
   7929    filetype_plugin = kTrue;
   7930  }
   7931  if (filetype_indent == kNone) {
   7932    source_runtime(INDENT_FILE, DIP_ALL);
   7933    filetype_indent = kTrue;
   7934  }
   7935 }
   7936 
   7937 /// Enable filetype detection if the user did not explicitly disable it.
   7938 void filetype_maybe_enable(void)
   7939 {
   7940  if (filetype_detect == kNone) {
   7941    // Normally .vim files are sourced before .lua files when both are
   7942    // supported, but we reverse the order here because we want the Lua
   7943    // autocommand to be defined first so that it runs first
   7944    source_runtime(FILETYPE_FILE, DIP_ALL);
   7945    filetype_detect = kTrue;
   7946  }
   7947 }
   7948 
   7949 /// ":setfiletype [FALLBACK] {name}"
   7950 static void ex_setfiletype(exarg_T *eap)
   7951 {
   7952  if (curbuf->b_did_filetype) {
   7953    return;
   7954  }
   7955 
   7956  char *arg = eap->arg;
   7957  if (strncmp(arg, "FALLBACK ", 9) == 0) {
   7958    arg += 9;
   7959  }
   7960 
   7961  set_option_value_give_err(kOptFiletype, CSTR_AS_OPTVAL(arg), OPT_LOCAL);
   7962  if (arg != eap->arg) {
   7963    curbuf->b_did_filetype = false;
   7964  }
   7965 }
   7966 
   7967 static void ex_digraphs(exarg_T *eap)
   7968 {
   7969  if (*eap->arg != NUL) {
   7970    putdigraph(eap->arg);
   7971  } else {
   7972    listdigraphs(eap->forceit);
   7973  }
   7974 }
   7975 
   7976 void set_no_hlsearch(bool flag)
   7977 {
   7978  no_hlsearch = flag;
   7979  set_vim_var_nr(VV_HLSEARCH, !no_hlsearch && p_hls);
   7980 }
   7981 
   7982 /// ":nohlsearch"
   7983 static void ex_nohlsearch(exarg_T *eap)
   7984 {
   7985  set_no_hlsearch(true);
   7986  redraw_all_later(UPD_SOME_VALID);
   7987 }
   7988 
   7989 static void ex_fold(exarg_T *eap)
   7990 {
   7991  if (foldManualAllowed(true)) {
   7992    pos_T start = { eap->line1, 1, 0 };
   7993    pos_T end = { eap->line2, 1, 0 };
   7994    foldCreate(curwin, start, end);
   7995  }
   7996 }
   7997 
   7998 static void ex_foldopen(exarg_T *eap)
   7999 {
   8000  pos_T start = { eap->line1, 1, 0 };
   8001  pos_T end = { eap->line2, 1, 0 };
   8002  opFoldRange(start, end, eap->cmdidx == CMD_foldopen, eap->forceit, false);
   8003 }
   8004 
   8005 static void ex_folddo(exarg_T *eap)
   8006 {
   8007  // First set the marks for all lines closed/open.
   8008  for (linenr_T lnum = eap->line1; lnum <= eap->line2; lnum++) {
   8009    if (hasFolding(curwin, lnum, NULL, NULL) == (eap->cmdidx == CMD_folddoclosed)) {
   8010      ml_setmarked(lnum);
   8011    }
   8012  }
   8013 
   8014  global_exe(eap->arg);  // Execute the command on the marked lines.
   8015  ml_clearmarked();      // clear rest of the marks
   8016 }
   8017 
   8018 /// @return  true if the supplied Ex cmdidx is for a location list command
   8019 ///          instead of a quickfix command.
   8020 bool is_loclist_cmd(int cmdidx)
   8021  FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
   8022 {
   8023  if (cmdidx < 0 || cmdidx >= CMD_SIZE) {
   8024    return false;
   8025  }
   8026  return cmdnames[cmdidx].cmd_name[0] == 'l';
   8027 }
   8028 
   8029 bool get_pressedreturn(void)
   8030  FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
   8031 {
   8032  return ex_pressedreturn;
   8033 }
   8034 
   8035 void set_pressedreturn(bool val)
   8036 {
   8037  ex_pressedreturn = val;
   8038 }
   8039 
   8040 /// ":checkhealth [plugins]"
   8041 static void ex_checkhealth(exarg_T *eap)
   8042 {
   8043  Error err = ERROR_INIT;
   8044  MAXSIZE_TEMP_ARRAY(args, 2);
   8045 
   8046  char mods[1024];
   8047  size_t mods_len = 0;
   8048  mods[0] = NUL;
   8049 
   8050  if (cmdmod.cmod_tab > 0 || cmdmod.cmod_split != 0) {
   8051    bool multi_mods = false;
   8052    mods_len = add_win_cmd_modifiers(mods, &cmdmod, &multi_mods);
   8053    assert(mods_len < sizeof(mods));
   8054  }
   8055  ADD_C(args, STRING_OBJ(((String){ .data = mods, .size = mods_len })));
   8056  ADD_C(args, CSTR_AS_OBJ(eap->arg));
   8057 
   8058  NLUA_EXEC_STATIC("vim.health._check(...)", args, kRetNilBool, NULL, &err);
   8059  if (!ERROR_SET(&err)) {
   8060    return;
   8061  }
   8062 
   8063  char *vimruntime_env = os_getenv_noalloc("VIMRUNTIME");
   8064  if (!vimruntime_env) {
   8065    emsg(_("E5009: $VIMRUNTIME is empty or unset"));
   8066  } else {
   8067    bool rtp_ok = NULL != strstr(p_rtp, vimruntime_env);
   8068    if (rtp_ok) {
   8069      semsg(_("E5009: Invalid $VIMRUNTIME: %s"), vimruntime_env);
   8070    } else {
   8071      emsg(_("E5009: Invalid 'runtimepath'"));
   8072    }
   8073  }
   8074  semsg_multiline("emsg", err.msg);
   8075  api_clear_error(&err);
   8076 }
   8077 
   8078 static void ex_terminal(exarg_T *eap)
   8079 {
   8080  char ex_cmd[1024];
   8081  size_t len = 0;
   8082 
   8083  if (cmdmod.cmod_tab > 0 || cmdmod.cmod_split != 0) {
   8084    bool multi_mods = false;
   8085    // ex_cmd must be a null-terminated string before passing to add_win_cmd_modifiers
   8086    ex_cmd[0] = NUL;
   8087    len = add_win_cmd_modifiers(ex_cmd, &cmdmod, &multi_mods);
   8088    assert(len < sizeof(ex_cmd));
   8089    int result = snprintf(ex_cmd + len, sizeof(ex_cmd) - len, " new");
   8090    assert(result > 0);
   8091    len += (size_t)result;
   8092  } else {
   8093    int result = snprintf(ex_cmd, sizeof(ex_cmd), "enew%s", eap->forceit ? "!" : "");
   8094    assert(result > 0);
   8095    len += (size_t)result;
   8096  }
   8097 
   8098  assert(len < sizeof(ex_cmd));
   8099 
   8100  if (*eap->arg != NUL) {  // Run {cmd} in 'shell'.
   8101    char *name = vim_strsave_escaped(eap->arg, "\"\\");
   8102    snprintf(ex_cmd + len, sizeof(ex_cmd) - len,
   8103             " | call jobstart(\"%s\",{'term':v:true})", name);
   8104    xfree(name);
   8105  } else {  // No {cmd}: run the job with tokenized 'shell'.
   8106    if (*p_sh == NUL) {
   8107      emsg(_(e_shellempty));
   8108      return;
   8109    }
   8110 
   8111    char **argv = shell_build_argv(NULL, NULL);
   8112    char **p = argv;
   8113    char tempstring[512];
   8114    char shell_argv[512] = { 0 };
   8115 
   8116    while (*p != NULL) {
   8117      char *escaped = vim_strsave_escaped(*p, "\"\\");
   8118      snprintf(tempstring, sizeof(tempstring), ",\"%s\"", escaped);
   8119      xfree(escaped);
   8120      xstrlcat(shell_argv, tempstring, sizeof(shell_argv));
   8121      p++;
   8122    }
   8123    shell_free_argv(argv);
   8124 
   8125    snprintf(ex_cmd + len, sizeof(ex_cmd) - len,
   8126             " | call jobstart([%s], {'term':v:true})", shell_argv + 1);
   8127  }
   8128 
   8129  do_cmdline_cmd(ex_cmd);
   8130 }
   8131 
   8132 /// ":lsp {subcmd} {clients}"
   8133 static void ex_lsp(exarg_T *eap)
   8134 {
   8135  Error err = ERROR_INIT;
   8136  MAXSIZE_TEMP_ARRAY(args, 1);
   8137 
   8138  ADD_C(args, CSTR_AS_OBJ(eap->arg));
   8139 
   8140  NLUA_EXEC_STATIC("require'vim._core.ex_cmd'.ex_lsp(...)", args, kRetNilBool, NULL, &err);
   8141  if (ERROR_SET(&err)) {
   8142    emsg_multiline(err.msg, "lua_error", HLF_E, true);
   8143  }
   8144  api_clear_error(&err);
   8145 }
   8146 
   8147 /// ":fclose"
   8148 static void ex_fclose(exarg_T *eap)
   8149 {
   8150  win_float_remove(eap->forceit, eap->line1);
   8151 }
   8152 
   8153 void verify_command(char *cmd)
   8154 {
   8155  if (strcmp("smile", cmd) != 0) {
   8156    return;  // acceptable non-existing command
   8157  }
   8158  int a = HLF_E;
   8159  msg(" #xxn`          #xnxx`        ,+x@##@Mz;`        .xxx"
   8160      "xxxxxxnz+,      znnnnnnnnnnnnnnnn.", a);
   8161  msg(" n###z          x####`      :x##########W+`      ,###"
   8162      "##########M;    W################.", a);
   8163  msg(" n####;         x####`    `z##############W:     ,###"
   8164      "#############   W################.", a);
   8165  msg(" n####W.        x####`   ,W#################+    ,###"
   8166      "##############  W################.", a);
   8167  msg(" n#####n        x####`   @###################    ,###"
   8168      "##############i W################.", a);
   8169  msg(" n######i       x####`  .#########@W@########*   ,###"
   8170      "##############W`W################.", a);
   8171  msg(" n######@.      x####`  x######W*.  `;n#######:  ,###"
   8172      "#x,,,,:*M######iW###@:,,,,,,,,,,,`", a);
   8173  msg(" n#######n      x####` *######+`       :M#####M  ,###"
   8174      "#n      `x#####xW###@`", a);
   8175  msg(" n########*     x####``@####@;          `x#####i ,###"
   8176      "#n       ,#####@W###@`", a);
   8177  msg(" n########@     x####`*#####i            `M####M ,###"
   8178      "#n        x#########@`", a);
   8179  msg(" n#########     x####`M####z              :#####:,###"
   8180      "#n        z#########@`", a);
   8181  msg(" n#########*    x####,#####.               n####+,###"
   8182      "#n        n#########@`", a);
   8183  msg(" n####@####@,   x####i####x                ;####x,###"
   8184      "#n       `W#####@####+++++++++++i", a);
   8185  msg(" n####*#####M`  x#########*                `####@,###"
   8186      "#n       i#####MW###############W", a);
   8187  msg(" n####.######+  x####z####;                 W####,###"
   8188      "#n      i@######W###############W", a);
   8189  msg(" n####.`W#####: x####n####:                 M####:###"
   8190      "#@nnnnnW#######,W###############W", a);
   8191  msg(" n####. :#####M`x####z####;                 W####,###"
   8192      "##############z W###############W", a);
   8193  msg(" n####.  #######x#########*                `####W,###"
   8194      "#############W` W###############W", a);
   8195  msg(" n####.  `M#####W####i####x                ;####x,###"
   8196      "############W,  W####+**********i", a);
   8197  msg(" n####.   ,##########,#####.               n####+,###"
   8198      "###########n.   W###@`", a);
   8199  msg(" n####.    ##########`M####z              :#####:,###"
   8200      "########Wz:     W###@`", a);
   8201  msg(" n####.    x#########`*#####i            `M####M ,###"
   8202      "#x.....`        W###@`", a);
   8203  msg(" n####.    ,@########``@####@;          `x#####i ,###"
   8204      "#n              W###@`", a);
   8205  msg(" n####.     *########` *#####@+`       ,M#####M  ,###"
   8206      "#n              W###@`", a);
   8207  msg(" n####.      x#######`  x######W*.  `;n######@:  ,###"
   8208      "#n              W###@,,,,,,,,,,,,`", a);
   8209  msg(" n####.      .@######`  .#########@W@########*   ,###"
   8210      "#n              W################,", a);
   8211  msg(" n####.       i######`   @###################    ,###"
   8212      "#n              W################,", a);
   8213  msg(" n####.        n#####`   ,W#################+    ,###"
   8214      "#n              W################,", a);
   8215  msg(" n####.        .@####`    .n##############W;     ,###"
   8216      "#n              W################,", a);
   8217  msg(" n####.         i####`      :x##########W+`      ,###"
   8218      "#n              W################,", a);
   8219  msg(" +nnnn`          +nnn`        ,+x@##@Mz;`        .nnn"
   8220      "n+              zxxxxxxxxxxxxxxxx.", a);
   8221  msg(" ", a);
   8222  msg("                                                     "
   8223      "                              ,+M@#Mi", a);
   8224  msg("                                 "
   8225      "                                                .z########", a);
   8226  msg("                                 "
   8227      "                                               i@#########i", a);
   8228  msg("                                 "
   8229      "                                             `############W`", a);
   8230  msg("                                 "
   8231      "                                            `n#############i", a);
   8232  msg("                                 "
   8233      "                                           `n##############n", a);
   8234  msg("     ``                          "
   8235      "                                           z###############@`", a);
   8236  msg("    `W@z,                        "
   8237      "                                          ##################,", a);
   8238  msg("    *#####`                      "
   8239      "                                         i############@x@###i", a);
   8240  msg("    ######M.                     "
   8241      "                                        :#############n`,W##+", a);
   8242  msg("    +######@:                    "
   8243      "                                       .W#########M@##+  *##z", a);
   8244  msg("    :#######@:                   "
   8245      "                                      `x########@#x###*  ,##n", a);
   8246  msg("    `@#######@;                  "
   8247      "                                      z#########M*@nW#i  .##x", a);
   8248  msg("     z########@i                 "
   8249      "                                     *###########WM#@#,  `##x", a);
   8250  msg("     i##########+                "
   8251      "                                    ;###########*n###@   `##x", a);
   8252  msg("     `@#MM#######x,              "
   8253      "                                   ,@#########zM,`z##M   `@#x", a);
   8254  msg("      n##M#W#######n.            "
   8255      "   `.:i*+#zzzz##+i:.`             ,W#########Wii,`n@#@` n@##n", a);
   8256  msg("      ;###@#x#######n         `,i"
   8257      "#nW@#####@@WWW@@####@Mzi.        ,W##########@z.. ;zM#+i####z", a);
   8258  msg("       x####nz########    .;#x@##"
   8259      "@Wn#*;,.`      ``,:*#x@##M+,    ;@########xz@WM+#` `n@#######", a);
   8260  msg("       ,@####M########xi#@##@Mzi,"
   8261      "`                     .+x###Mi:n##########Mz```.:i  *@######*", a);
   8262  msg("        *#####W#########ix+:`    "
   8263      "                         :n#############z:       `*.`M######i", a);
   8264  msg("        i#W##nW@+@##@#M@;        "
   8265      "                           ;W@@##########W,        i`x@#####,", a);
   8266  msg("        `@@n@Wn#@iMW*#*:         "
   8267      "                            `iz#z@######x.           M######`", a);
   8268  msg("         z##zM###x`*, .`         "
   8269      "                                 `iW#####W;:`        +#####M", a);
   8270  msg("         ,###nn##n`              "
   8271      "                                  ,#####x;`        ,;@######", a);
   8272  msg("          x###xz#.               "
   8273      "                                    in###+        `:######@.", a);
   8274  msg("          ;####n+                "
   8275      "                                    `Mnx##xi`   , zM#######", a);
   8276  msg("          `W####+                "
   8277      "i.                                   `.+x###@#. :n,z######:", a);
   8278  msg("           z####@`              ;"
   8279      "#:                                     .ii@###@;.*M*z####@`", a);
   8280  msg("           i####M         `   `i@"
   8281      "#,           ::                           +#n##@+@##W####n", a);
   8282  msg("           :####x    ,i. ##xzM###"
   8283      "@`     i.   .@@,                           .z####x#######*", a);
   8284  msg("           ,###W;   i##Wz########"
   8285      "#     :##   z##n                           ,@########x###:", a);
   8286  msg("            n##n   `W###########M"
   8287      "`;n,  i#x  ,###@i                           *W########W#@`", a);
   8288  msg("           .@##+  `x###########@."
   8289      " z#+ .M#W``x#####n`                         `;#######@z#x", a);
   8290  msg("           n###z :W############@ "
   8291      " z#*  @##xM#######@n;                        `########nW+", a);
   8292  msg("          ;####nW##############W "
   8293      ":@#* `@#############*                        :########z@i`", a);
   8294  msg("          M##################### "
   8295      "M##:  @#############@:                       *W########M#", a);
   8296  msg("         ;#####################i."
   8297      "##x`  W#############W,                       :n########zx", a);
   8298  msg("         x####################@.`"
   8299      "x;    @#############z.                       .@########W#", a);
   8300  msg("        ,######################` "
   8301      "      W###############x*,`                    W######zM#i", a);
   8302  msg("        #######################: "
   8303      "      z##################@x+*#zzi            `@#########.", a);
   8304  msg("        W########W#z#M#########; "
   8305      "      *##########################z            :@#######@`", a);
   8306  msg("       `@#######x`;#z ,x#######; "
   8307      "      z###########M###xnM@########*            :M######@", a);
   8308  msg("       i########, x#@`  z######; "
   8309      "      *##########i *#@`  `+########+`            n######.", a);
   8310  msg("       n#######@` M##,  `W#####. "
   8311      "      *#########z  ###;    z########M:           :W####n", a);
   8312  msg("       M#######M  n##.   x####x  "
   8313      "      `x########:  z##+    M#########@;           .n###+", a);
   8314  msg("       W#######@` :#W   `@####:  "
   8315      "       `@######W   i###   ;###########@.            n##n", a);
   8316  msg("       W########z` ,,  .x####z   "
   8317      "        @######@`  `W#;  `W############*            *###;", a);
   8318  msg("      `@#########Mi,:*n@####W`   "
   8319      "        W#######*   ..  `n#############i            i###x", a);
   8320  msg("      .#####################z    "
   8321      "       `@#######@*`    .x############n:`            ;####.", a);
   8322  msg("      :####################x`,,` "
   8323      "       `W#########@x#+#@#############i              ,####:", a);
   8324  msg("      ;###################x#@###x"
   8325      "i`      *############################:              `####i", a);
   8326  msg("      i##################+#######"
   8327      "#M,      x##########################@`               W###i", a);
   8328  msg("      *################@; @######"
   8329      "##@,     .W#########################@                x###:", a);
   8330  msg("      .+M#############z.  M######"
   8331      "###x      ,W########################@`               ####.", a);
   8332  msg("      *M*;z@########x:    :W#####"
   8333      "##i        .M########################i               i###:", a);
   8334  msg("      *##@z;#@####x:        :z###"
   8335      "@i          `########################x               .###;", a);
   8336  msg("      *#####n;#@##            ;##"
   8337      "*             ,x#####################@`               W##*", a);
   8338  msg("      *#######n;*            :M##"
   8339      "W*,             *W####################`               n##z", a);
   8340  msg("      i########@.         ,*n####"
   8341      "###M*`           `###################M                *##M", a);
   8342  msg("      i########n        `z#####@@"
   8343      "#####Wi            ,M################;                ,##@`", a);
   8344  msg("      ;WMWW@###*       .x##@ni.``"
   8345      ".:+zW##z`           `n##############z                  @##,", a);
   8346  msg("      .*++*i;;;.      .M#@+`     "
   8347      "     .##n            `x############x`                  n##i", a);
   8348  msg("      :########*      x#W,       "
   8349      "       *#+            *###########M`                   +##+", a);
   8350  msg("      ,#########     :#@:        "
   8351      "        ##:           #nzzzzzzzzzz.                    :##x", a);
   8352  msg("      .#####Wz+`     ##+         "
   8353      "        `MM`          .znnnnnnnnn.                     `@#@`", a);
   8354  msg("      `@@ni;*nMz`    @W`         "
   8355      "         :#+           .x#######n                       x##,", a);
   8356  msg("       i;z@#####,   .#*          "
   8357      "          z#:           ;;;*zW##;                       ###i", a);
   8358  msg("       z########:   :#;          "
   8359      "          `Wx          +###Wni;n.                       ;##z", a);
   8360  msg("       n########W:  .#*          "
   8361      "           ,#,        ;#######@+                        `@#M", a);
   8362  msg("      .###########n;.MM          "
   8363      "            n*        ;iM#######*                        x#@`", a);
   8364  msg("      :#############@;;          "
   8365      "            .n`      ,#W*iW#####W`                       +##,", a);
   8366  msg("      ,##############.           "
   8367      "             ix.    `x###M;#######                       ,##i", a);
   8368  msg("      .#############@`           "
   8369      "              x@n**#W######z;M###@.                       W##", a);
   8370  msg("      .##############W:          "
   8371      "              .x############@*;zW#;                       z#x", a);
   8372  msg("      ,###############@;         "
   8373      "               `##############@n*;.                       i#@", a);
   8374  msg("      ,#################i        "
   8375      "                 :n##############W`                       .##,", a);
   8376  msg("      ,###################`      "
   8377      "                   .+W##########W,                        `##i", a);
   8378  msg("      :###################@zi,`  "
   8379      "                      ;zM@@@WMn*`                          @#z", a);
   8380  msg("      :#######################@x+"
   8381      "*i;;:i#M,                 ``                               M#W", a);
   8382  msg("      ;##########################"
   8383      "######@x.                                                  n##,", a);
   8384  msg("      i#####################@W@@@"
   8385      "@Wxz*:`                                                    *##+", a);
   8386  msg("      *######################+```"
   8387      "                                                           :##M", a);
   8388  msg("      ########################M; "
   8389      "                                                           `@##,", a);
   8390  msg("      z#########################x"
   8391      ",                                                           z###", a);
   8392  msg("      n##########################"
   8393      "#n:                                                         ;##W`", a);
   8394  msg("      x##########################"
   8395      "###Mz#++##*                                                 `W##i", a);
   8396  msg("      M##########################"
   8397      "##########@`                                                 ###x", a);
   8398  msg("      W##########################"
   8399      "###########`                                                 .###,", a);
   8400  msg("      @##########################"
   8401      "##########M                                                   n##z", a);
   8402  msg("      @##################z*i@WMMM"
   8403      "x#x@#####,.                                                   :##@.", a);
   8404  msg("     `#####################@xi`  "
   8405      "   `::,*                                                       x##+", a);
   8406  msg("     .#####################@#M.  "
   8407      "                                                               ;##@`", a);
   8408  msg("     ,#####################:.    "
   8409      "                                                                M##i", a);
   8410  msg("     ;###################ni`     "
   8411      "                                                                i##M", a);
   8412  msg("     *#################W#`       "
   8413      "                                                                `W##,", a);
   8414  msg("     z#################@Wx+.     "
   8415      "                                                                 +###", a);
   8416  msg("     x######################z.   "
   8417      "                                                                 .@#@`", a);
   8418  msg("    `@#######################@;  "
   8419      "                                                                  z##;", a);
   8420  msg("    :##########################: "
   8421      "                                                                  :##z", a);
   8422  msg("    +#########################W# "
   8423      "                                                                   M#W", a);
   8424  msg("    W################@n+*i;:,`                                "
   8425      "                                      +##,", a);
   8426  msg("   :##################WMxz+,                                  "
   8427      "                                      ,##i", a);
   8428  msg("   n#######################W..,                               "
   8429      "                                       W##", a);
   8430  msg("  +#########################WW@+. .:.                         "
   8431      "                                       z#x", a);
   8432  msg(" `@#############################@@###:                        "
   8433      "                                       *#W", a);
   8434  msg(" #################################Wz:                         "
   8435      "                                       :#@", a);
   8436  msg(",@###############################i                            "
   8437      "                                       .##", a);
   8438  msg("n@@@@@@@#########################+                            "
   8439      "                                       `##", a);
   8440  msg("`      `.:.`.,:iii;;;;;;;;iii;;;:`       `.``                 "
   8441      "                                       `nW", a);
   8442 }
   8443 
   8444 /// Get argt of command with id
   8445 uint32_t get_cmd_argt(cmdidx_T cmdidx)
   8446 {
   8447  return cmdnames[(int)cmdidx].cmd_argt;
   8448 }
   8449 
   8450 /// Check if a command is a :map/:abbrev command.
   8451 bool is_map_cmd(cmdidx_T cmdidx)
   8452 {
   8453  if (IS_USER_CMDIDX(cmdidx)) {
   8454    return false;
   8455  }
   8456 
   8457  ex_func_T func = cmdnames[cmdidx].cmd_func;
   8458  return func == ex_map           // :map, :nmap, :noremap, etc.
   8459         || func == ex_unmap         // :unmap, :nunmap, etc.
   8460         || func == ex_mapclear      // :mapclear, :nmapclear, etc.
   8461         || func == ex_abbreviate    // :abbreviate, :iabbrev, etc.
   8462         || func == ex_abclear;      // :abclear, :iabclear, etc.
   8463 }