neovim

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

main.c (72542B)


      1 // Make sure extern symbols are exported on Windows
      2 #ifdef WIN32
      3 # define EXTERN __declspec(dllexport)
      4 #else
      5 # define EXTERN
      6 #endif
      7 #include <assert.h>
      8 #include <limits.h>
      9 #include <stdbool.h>
     10 #include <stddef.h>
     11 #include <stdint.h>
     12 #include <stdio.h>
     13 #include <stdlib.h>
     14 #include <string.h>
     15 #ifdef ENABLE_ASAN_UBSAN
     16 # include <sanitizer/asan_interface.h>
     17 # ifndef MSWIN
     18 #  include <sanitizer/ubsan_interface.h>
     19 # endif
     20 #endif
     21 
     22 #include "auto/config.h"  // IWYU pragma: keep
     23 #include "klib/kvec.h"
     24 #include "nvim/api/extmark.h"
     25 #include "nvim/api/private/defs.h"
     26 #include "nvim/api/private/helpers.h"
     27 #include "nvim/api/ui.h"
     28 #include "nvim/arglist.h"
     29 #include "nvim/ascii_defs.h"
     30 #include "nvim/autocmd.h"
     31 #include "nvim/autocmd_defs.h"
     32 #include "nvim/buffer.h"
     33 #include "nvim/buffer_defs.h"
     34 #include "nvim/channel.h"
     35 #include "nvim/channel_defs.h"
     36 #include "nvim/decoration.h"
     37 #include "nvim/decoration_provider.h"
     38 #include "nvim/diff.h"
     39 #include "nvim/drawline.h"
     40 #include "nvim/drawscreen.h"
     41 #include "nvim/errors.h"
     42 #include "nvim/eval.h"
     43 #include "nvim/eval/typval.h"
     44 #include "nvim/eval/typval_defs.h"
     45 #include "nvim/eval/userfunc.h"
     46 #include "nvim/eval/vars.h"
     47 #include "nvim/event/loop.h"
     48 #include "nvim/event/multiqueue.h"
     49 #include "nvim/event/proc.h"
     50 #include "nvim/event/socket.h"
     51 #include "nvim/event/stream.h"
     52 #include "nvim/ex_cmds.h"
     53 #include "nvim/ex_docmd.h"
     54 #include "nvim/ex_getln.h"
     55 #include "nvim/extmark.h"
     56 #include "nvim/fileio.h"
     57 #include "nvim/fold.h"
     58 #include "nvim/garray.h"
     59 #include "nvim/getchar.h"
     60 #include "nvim/gettext_defs.h"
     61 #include "nvim/globals.h"
     62 #include "nvim/grid.h"
     63 #include "nvim/hashtab.h"
     64 #include "nvim/highlight.h"
     65 #include "nvim/highlight_group.h"
     66 #include "nvim/keycodes.h"
     67 #include "nvim/log.h"
     68 #include "nvim/lua/executor.h"
     69 #include "nvim/lua/secure.h"
     70 #include "nvim/lua/treesitter.h"
     71 #include "nvim/macros_defs.h"
     72 #include "nvim/main.h"
     73 #include "nvim/mark.h"
     74 #include "nvim/memline.h"
     75 #include "nvim/memory.h"
     76 #include "nvim/message.h"
     77 #include "nvim/mouse.h"
     78 #include "nvim/move.h"
     79 #include "nvim/msgpack_rpc/channel.h"
     80 #include "nvim/msgpack_rpc/server.h"
     81 #include "nvim/normal.h"
     82 #include "nvim/ops.h"
     83 #include "nvim/option.h"
     84 #include "nvim/option_defs.h"
     85 #include "nvim/option_vars.h"
     86 #include "nvim/os/fs.h"
     87 #include "nvim/os/input.h"
     88 #include "nvim/os/lang.h"
     89 #include "nvim/os/os.h"
     90 #include "nvim/os/os_defs.h"
     91 #include "nvim/os/signal.h"
     92 #include "nvim/os/stdpaths_defs.h"
     93 #include "nvim/path.h"
     94 #include "nvim/popupmenu.h"
     95 #include "nvim/profile.h"
     96 #include "nvim/quickfix.h"
     97 #include "nvim/register.h"
     98 #include "nvim/runtime.h"
     99 #include "nvim/runtime_defs.h"
    100 #include "nvim/shada.h"
    101 #include "nvim/statusline.h"
    102 #include "nvim/strings.h"
    103 #include "nvim/syntax.h"
    104 #include "nvim/terminal.h"
    105 #include "nvim/types_defs.h"
    106 #include "nvim/ui.h"
    107 #include "nvim/ui_client.h"
    108 #include "nvim/ui_compositor.h"
    109 #include "nvim/version.h"
    110 #include "nvim/vim_defs.h"
    111 #include "nvim/window.h"
    112 #include "nvim/winfloat.h"
    113 
    114 #ifdef MSWIN
    115 # include "nvim/os/os_win_console.h"
    116 # ifndef _UCRT
    117 #  error UCRT is the only supported C runtime on windows
    118 # endif
    119 #endif
    120 
    121 #if defined(MSWIN) && !defined(MAKE_LIB)
    122 # include "nvim/mbyte.h"
    123 #endif
    124 
    125 // values for "window_layout"
    126 enum {
    127  WIN_HOR = 1,   // "-o" horizontally split windows
    128  WIN_VER = 2,   // "-O" vertically split windows
    129  WIN_TABS = 3,  // "-p" windows on tab pages
    130 };
    131 
    132 // Values for edit_type.
    133 enum {
    134  EDIT_NONE = 0,   // no edit type yet
    135  EDIT_FILE = 1,   // file name argument[s] given, use argument list
    136  EDIT_STDIN = 2,  // read file from stdin
    137  EDIT_TAG = 3,    // tag name argument given, use tagname
    138  EDIT_QF = 4,     // start in quickfix mode
    139 };
    140 
    141 #include "main.c.generated.h"
    142 
    143 Loop main_loop;
    144 
    145 static char *argv0 = NULL;
    146 
    147 // Error messages
    148 static const char *err_arg_missing = N_("Argument missing after");
    149 static const char *err_opt_garbage = N_("Garbage after option argument");
    150 static const char *err_opt_unknown = N_("Unknown option argument");
    151 static const char *err_too_many_args = N_("Too many edit arguments");
    152 static const char *err_extra_cmd =
    153  N_("Too many \"+command\", \"-c command\" or \"--cmd command\" arguments");
    154 
    155 void event_init(void)
    156 {
    157  loop_init(&main_loop, NULL);
    158  env_init();
    159  resize_events = multiqueue_new_child(main_loop.events);
    160 
    161  autocmd_init();
    162  signal_init();
    163  // mspgack-rpc initialization
    164  channel_init();
    165  terminal_init();
    166  ui_init();
    167  TIME_MSG("event init");
    168 }
    169 
    170 /// @returns false if main_loop could not be closed gracefully
    171 static bool event_teardown(void)
    172 {
    173  if (!main_loop.events) {
    174    input_stop();
    175    return true;
    176  }
    177 
    178  multiqueue_process_events(main_loop.events);
    179  loop_poll_events(&main_loop, 0);  // Drain thread_events, fast_events.
    180  input_stop();
    181  channel_teardown();
    182  proc_teardown(&main_loop);
    183  timer_teardown();
    184  server_teardown();
    185  signal_teardown();
    186  terminal_teardown();
    187 
    188  return loop_close(&main_loop, true);
    189 }
    190 
    191 /// Performs early initialization.
    192 ///
    193 /// Needed for unit tests.
    194 void early_init(mparm_T *paramp)
    195 {
    196  os_hint_priority();
    197  estack_init();
    198  cmdline_init();
    199  eval_init();          // init global variables
    200  init_path(argv0 ? argv0 : "nvim");
    201  init_normal_cmds();   // Init the table of Normal mode commands.
    202  runtime_init();
    203  highlight_init();
    204 
    205 #ifdef MSWIN
    206  OSVERSIONINFO ovi;
    207  ovi.dwOSVersionInfoSize = sizeof(ovi);
    208  // Disable warning about GetVersionExA being deprecated. There doesn't seem to be a convenient
    209  // replacement that doesn't add a ton of extra code as of writing this.
    210 # ifdef _MSC_VER
    211 #  pragma warning(suppress : 4996)
    212  GetVersionEx(&ovi);
    213 # else
    214  GetVersionEx(&ovi);
    215 # endif
    216  snprintf(windowsVersion, sizeof(windowsVersion), "%d.%d",
    217           (int)ovi.dwMajorVersion, (int)ovi.dwMinorVersion);
    218 #endif
    219 
    220  TIME_MSG("early init");
    221 
    222  // Setup to use the current locale (for ctype() and many other things).
    223  // NOTE: Translated messages with encodings other than latin1 will not
    224  // work until set_init_1() has been called!
    225  init_locale();
    226 
    227  // tabpage local options (p_ch) must be set before allocating first tabpage.
    228  set_init_tablocal();
    229 
    230  // Allocate the first tabpage, window and buffer.
    231  win_alloc_first();
    232  TIME_MSG("init first window");
    233 
    234  alist_init(&global_alist);    // Init the argument list to empty.
    235  global_alist.id = 0;
    236 
    237  // Set the default values for the options.
    238  // First find out the home directory, needed to expand "~" in options.
    239  init_homedir();               // find real value of $HOME
    240  set_init_1(paramp != NULL ? paramp->clean : false);
    241  log_init();
    242  TIME_MSG("inits 1");
    243 
    244  set_lang_var();               // set v:lang and v:ctype
    245 
    246  // initialize quickfix list
    247  qf_init_stack();
    248 }
    249 
    250 #ifdef MAKE_LIB
    251 int nvim_main(int argc, char **argv);  // silence -Wmissing-prototypes
    252 int nvim_main(int argc, char **argv)
    253 #else
    254 int main(int argc, char **argv)
    255 #endif
    256 {
    257  argv0 = argv[0];
    258 
    259  if (!appname_is_valid()) {
    260    fprintf(stderr, "$NVIM_APPNAME must be a name or relative path.\n");
    261    exit(1);
    262  }
    263 
    264  if (argc > 1 && STRICMP(argv[1], "-ll") == 0) {
    265    if (argc == 2) {
    266      print_mainerr(err_arg_missing, argv[1], NULL);
    267      exit(1);
    268    }
    269    nlua_run_script(argv, argc, 3);
    270  }
    271 
    272  char *fname = NULL;     // file name from command line
    273  mparm_T params;         // various parameters passed between
    274                          // main() and other functions.
    275  char *cwd = NULL;       // current working dir on startup
    276 
    277  // Many variables are in `params` so that we can pass them around easily.
    278  // `argc` and `argv` are also copied, so that they can be changed.
    279  init_params(&params, argc, argv);
    280 
    281  init_startuptime(&params);
    282 
    283  // Need to find "--clean" before actually parsing arguments.
    284  for (int i = 1; i < params.argc; i++) {
    285    if (STRICMP(params.argv[i], "--clean") == 0) {
    286      params.clean = true;
    287      break;
    288    }
    289  }
    290 
    291  event_init();
    292 
    293  early_init(&params);
    294 
    295  set_argv_var(argv, argc);  // set v:argv
    296 
    297  // Check if we have an interactive window.
    298  check_and_set_isatty(&params);
    299 
    300  // Process the command line arguments.  File names are put in the global
    301  // argument list "global_alist".
    302  command_line_scan(&params);
    303 
    304  set_argf_var();
    305 
    306  nlua_init(argv, argc, params.lua_arg0);
    307  TIME_MSG("init lua interpreter");
    308 
    309  // On Windows, channel_from_stdio() replaces fd 2 with CONOUT$ (for ConPTY
    310  // support). Save a dup of the original stderr first so that if server_init()
    311  // fails, print_mainerr() can write through the pipe to the TUI client's relay.
    312 #ifdef MSWIN
    313  int startup_stderr_fd = -1;
    314  if (embedded_mode) {
    315    startup_stderr_fd = os_dup(STDERR_FILENO);
    316    if (startup_stderr_fd >= 0) {
    317      os_set_cloexec(startup_stderr_fd);
    318    }
    319  }
    320 #endif
    321 
    322  if (embedded_mode) {
    323    const char *err;
    324    if (!channel_from_stdio(true, CALLBACK_READER_INIT, &err)) {
    325      abort();
    326    }
    327  }
    328 
    329  if (GARGCOUNT > 0) {
    330    fname = get_fname(&params, cwd);
    331  }
    332 
    333  // Recovery mode without a file name: List swap files.
    334  // In this case, no UI is needed.
    335  if (recoverymode && fname == NULL) {
    336    headless_mode = true;
    337  }
    338 
    339 #ifdef MSWIN
    340  // on windows we use CONIN special file, thus we don't know this yet.
    341  bool has_term = true;
    342 #else
    343  bool has_term = (stdin_isatty || stdout_isatty || stderr_isatty);
    344 #endif
    345  bool use_builtin_ui = (has_term && !headless_mode && !embedded_mode && !silent_mode);
    346 
    347  if (params.remote) {
    348    remote_request(&params, params.remote, params.server_addr, argc, argv,
    349                   use_builtin_ui);
    350  }
    351 
    352  bool remote_ui = (ui_client_channel_id != 0);
    353 
    354  if (use_builtin_ui && !remote_ui) {
    355    ui_client_forward_stdin = true;
    356    uint64_t rv = ui_client_start_server(get_vim_var_str(VV_PROGPATH),
    357                                         (size_t)params.argc, params.argv);
    358    if (!rv) {
    359      fprintf(stderr, "Failed to start Nvim server!\n");
    360      os_exit(1);
    361    }
    362    ui_client_channel_id = rv;
    363  }
    364 
    365  // NORETURN: Start builtin UI client.
    366  if (ui_client_channel_id) {
    367    ui_client_run(remote_ui);  // NORETURN
    368  }
    369  assert(!ui_client_channel_id && !use_builtin_ui);
    370  // Nvim server...
    371 
    372  if (!server_init(params.listen_addr)) {
    373 #ifdef MSWIN
    374    // Restore the original stderr (pipe to TUI client) so print_mainerr()
    375    // output is visible in the TUI terminal via the relay in on_channel_output.
    376    if (startup_stderr_fd >= 0) {
    377      dup2(startup_stderr_fd, STDERR_FILENO);
    378      close(startup_stderr_fd);
    379      startup_stderr_fd = -1;
    380    }
    381 #endif
    382    mainerr(IObuff, NULL, NULL);
    383  }
    384 
    385 #ifdef MSWIN
    386  // Server started successfully. Close the saved fd so the pipe write end is
    387  // fully released — child processes inherit CONOUT$ (fd 2), not the pipe.
    388  if (startup_stderr_fd >= 0) {
    389    close(startup_stderr_fd);
    390    startup_stderr_fd = -1;
    391  }
    392 #endif
    393 
    394  TIME_MSG("expanding arguments");
    395 
    396  if (params.diff_mode && params.window_count == -1) {
    397    params.window_count = 0;            // open up to 3 windows
    398  }
    399  // Don't redraw until much later.
    400  RedrawingDisabled++;
    401 
    402  setbuf(stdout, NULL);  // NOLINT(bugprone-unsafe-functions)
    403 
    404  full_screen = !silent_mode;
    405 
    406  // Set the default values for the options that use Rows and Columns.
    407  win_init_size();
    408  // Set the 'diff' option now, so that it can be checked for in a vimrc
    409  // file.  There is no buffer yet though.
    410  if (params.diff_mode) {
    411    diff_win_options(firstwin, false);
    412  }
    413 
    414  assert(p_ch >= 0 && Rows >= p_ch && Rows - p_ch <= INT_MAX);
    415  cmdline_row = Rows - (int)p_ch;
    416  msg_row = cmdline_row;
    417  default_grid_alloc();  // allocate screen buffers
    418  set_init_2(headless_mode);
    419  TIME_MSG("inits 2");
    420 
    421  msg_scroll = true;
    422  no_wait_return = true;
    423 
    424  init_highlight(true, false);  // Default highlight groups.
    425  ui_comp_syn_init();
    426  TIME_MSG("init highlight");
    427 
    428  // Set the break level after the terminal is initialized.
    429  debug_break_level = params.use_debug_break_level;
    430 
    431  // Read ex-commands if invoked with "-es".
    432  if (!stdin_isatty && !params.input_istext && silent_mode && exmode_active) {
    433    input_start();
    434  }
    435 
    436  // Wait for UIs to set up Nvim or show early messages
    437  // and prompts (--cmd, swapfile dialog, …).
    438  bool use_remote_ui = (embedded_mode && !headless_mode);
    439  bool listen_and_embed = params.listen_addr != NULL;
    440  if (use_remote_ui) {
    441    TIME_MSG("waiting for UI");
    442    remote_ui_wait_for_attach(!listen_and_embed);
    443    TIME_MSG("done waiting for UI");
    444    firstwin->w_prev_height = firstwin->w_height;  // may have changed
    445  }
    446 
    447  // prepare screen now
    448  starting = NO_BUFFERS;
    449  screenclear();
    450  win_new_screensize();
    451  TIME_MSG("clear screen");
    452 
    453  // Handle "foo | nvim". EDIT_FILE may be overwritten now. #6299
    454  if (edit_stdin(&params)) {
    455    params.edit_type = EDIT_STDIN;
    456  }
    457 
    458  if (params.scriptin) {
    459    if (!open_scriptin(params.scriptin)) {
    460      os_exit(2);
    461    }
    462  }
    463  if (params.scriptout) {
    464    scriptout = os_fopen(params.scriptout, params.scriptout_append ? APPENDBIN : WRITEBIN);
    465    if (scriptout == NULL) {
    466      fprintf(stderr, _("Cannot open for script output: \""));
    467      fprintf(stderr, "%s\"\n", params.scriptout);
    468      os_exit(2);
    469    }
    470  }
    471 
    472  nlua_init_defaults();
    473 
    474  TIME_MSG("init default mappings & autocommands");
    475 
    476  bool vimrc_none = strequal(params.use_vimrc, "NONE");
    477 
    478  // Reset 'loadplugins' for "-u NONE" before "--cmd" arguments.
    479  // Allows for setting 'loadplugins' there.
    480  // For --clean we still want to load plugins.
    481  p_lpl = vimrc_none ? params.clean : p_lpl;
    482 
    483  // Execute --cmd arguments.
    484  exe_pre_commands(&params);
    485 
    486  if (!vimrc_none || params.clean) {
    487    // Sources ftplugin.vim and indent.vim. We do this *before* the user startup scripts to ensure
    488    // ftplugins run before FileType autocommands defined in the init file (which allows those
    489    // autocommands to overwrite settings from ftplugins).
    490    filetype_plugin_enable();
    491  }
    492 
    493  // Source startup scripts.
    494  source_startup_scripts(&params);
    495 
    496  // If using the runtime (-u is not NONE), enable syntax & filetype plugins.
    497  if (!vimrc_none || params.clean) {
    498    // Sources filetype.lua unless the user explicitly disabled it with :filetype off.
    499    filetype_maybe_enable();
    500    // Sources syntax/syntax.vim. We do this *after* the user startup scripts so that users can
    501    // disable syntax highlighting with `:syntax off` if they wish.
    502    syn_maybe_enable();
    503  }
    504 
    505  set_vim_var_nr(VV_VIM_DID_INIT, 1);
    506 
    507  // Read all the plugin files.
    508  load_plugins();
    509 
    510  // Decide about window layout for diff mode after reading vimrc.
    511  set_window_layout(&params);
    512 
    513  // Recovery mode without a file name: List swap files.
    514  // Uses the 'dir' option, therefore it must be after the initializations.
    515  if (recoverymode && fname == NULL) {
    516    recover_names(NULL, true, NULL, 0, NULL);
    517    os_exit(0);
    518  }
    519 
    520  // Set some option defaults after reading vimrc files.
    521  set_init_3();
    522  TIME_MSG("inits 3");
    523 
    524  // "-n" argument: Disable swap file by setting 'updatecount' to 0.
    525  // Note that this overrides anything from a vimrc file.
    526  if (params.no_swap_file) {
    527    p_uc = 0;
    528  }
    529 
    530  // XXX: Minimize 'updatetime' for -es/-Es. #7679
    531  if (silent_mode) {
    532    p_ut = 1;
    533  }
    534 
    535  // Read in registers, history etc, from the ShaDa file.
    536  // This is where v:oldfiles gets filled.
    537  if (*p_shada != NUL) {
    538    shada_read_everything(NULL, false, true);
    539    TIME_MSG("reading ShaDa");
    540  }
    541  // It's better to make v:oldfiles an empty list than NULL.
    542  if (get_vim_var_list(VV_OLDFILES) == NULL) {
    543    set_vim_var_list(VV_OLDFILES, tv_list_alloc(0));
    544  }
    545 
    546  // "-q errorfile": Load the error file now.
    547  // If the error file can't be read, exit before doing anything else.
    548  handle_quickfix(&params);
    549 
    550  //
    551  // Start putting things on the screen.
    552  // Scroll screen down before drawing over it
    553  // Clear screen now, so file message will not be cleared.
    554  //
    555  starting = NO_BUFFERS;
    556  no_wait_return = false;
    557  if (!exmode_active) {
    558    msg_scroll = false;
    559  }
    560 
    561  // Read file (text, not commands) from stdin if:
    562  //    - stdin is not a tty
    563  //    - and -e/-es was not given
    564  //
    565  // Do this before starting Raw mode, because it may change things that the
    566  // writing end of the pipe doesn't like, e.g., in case stdin and stderr
    567  // are the same terminal: "cat | vim -".
    568  // Using autocommands here may cause trouble...
    569  if (params.edit_type == EDIT_STDIN && !recoverymode) {
    570    read_stdin();
    571  }
    572 
    573  setmouse();  // may start using the mouse
    574 
    575  redraw_later(curwin, UPD_VALID);
    576 
    577  no_wait_return = true;
    578 
    579  // Create the requested number of windows and edit buffers in them.
    580  // Also does recovery if "recoverymode" set.
    581  create_windows(&params);
    582  TIME_MSG("opening buffers");
    583 
    584  // Clear v:swapcommand
    585  set_vim_var_string(VV_SWAPCOMMAND, NULL, -1);
    586 
    587  // Ex starts at last line of the file.
    588  if (exmode_active) {
    589    curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
    590  }
    591 
    592  apply_autocmds(EVENT_BUFENTER, NULL, NULL, false, curbuf);
    593  TIME_MSG("BufEnter autocommands");
    594  setpcmark();
    595 
    596  // When started with "-q errorfile" jump to first error now.
    597  if (params.edit_type == EDIT_QF) {
    598    qf_jump(NULL, 0, 0, false);
    599    TIME_MSG("jump to first error");
    600  }
    601 
    602  // If opened more than one window, start editing files in the other
    603  // windows.
    604  edit_buffers(&params, cwd);
    605  xfree(cwd);
    606 
    607  if (params.diff_mode) {
    608    // set options in each window for "nvim -d".
    609    FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
    610      if (!wp->w_arg_idx_invalid) {
    611        diff_win_options(wp, true);
    612      }
    613    }
    614  }
    615 
    616  // Shorten any of the filenames, but only when absolute.
    617  shorten_fnames(false);
    618 
    619  // Need to jump to the tag before executing the '-c command'.
    620  // Makes "vim -c '/return' -t main" work.
    621  handle_tag(params.tagname);
    622 
    623  // Execute any "+", "-c" and "-S" arguments.
    624  if (params.n_commands > 0) {
    625    exe_commands(&params);
    626  }
    627 
    628  starting = 0;
    629 
    630  RedrawingDisabled = 0;
    631  redraw_all_later(UPD_NOT_VALID);
    632  no_wait_return = false;
    633 
    634  // 'autochdir' has been postponed.
    635  do_autochdir();
    636 
    637  set_vim_var_nr(VV_VIM_DID_ENTER, 1);
    638  apply_autocmds(EVENT_VIMENTER, NULL, NULL, false, curbuf);
    639  TIME_MSG("VimEnter autocommands");
    640  if (use_remote_ui) {
    641    do_autocmd_uienter_all();
    642    TIME_MSG("UIEnter autocommands");
    643  }
    644 
    645 #ifdef MSWIN
    646  if (use_remote_ui) {
    647    os_icon_init();
    648  }
    649  os_title_save();
    650 #endif
    651 
    652  // Adjust default register name for "unnamed" in 'clipboard'. Can only be
    653  // done after the clipboard is available and all initial commands that may
    654  // modify the 'clipboard' setting have run; i.e. just before entering the
    655  // main loop.
    656  set_reg_var(get_default_register_name());
    657 
    658  // When a startup script or session file setup for diff'ing and
    659  // scrollbind, sync the scrollbind now.
    660  if (curwin->w_p_diff && curwin->w_p_scb) {
    661    update_topline(curwin);
    662    check_scrollbind(0, 0);
    663    TIME_MSG("diff scrollbinding");
    664  }
    665 
    666  // If ":startinsert" command used, stuff a dummy command to be able to
    667  // call normal_cmd(), which will then start Insert mode.
    668  if (restart_edit != 0) {
    669    stuffcharReadbuff(K_NOP);
    670  }
    671 
    672  // WORKAROUND(mhi): #3023
    673  if (cb_flags & (kOptCbFlagUnnamed | kOptCbFlagUnnamedplus)) {
    674    eval_has_provider("clipboard", false);
    675  }
    676 
    677  if (params.luaf != NULL) {
    678    // Like "--cmd", "+", "-c" and "-S", don't truncate messages.
    679    msg_scroll = true;
    680    DLOG("executing Lua -l script");
    681    bool lua_ok = nlua_exec_file(params.luaf);
    682    TIME_MSG("executing Lua -l script");
    683    if (msg_didout) {
    684      msg_putchar('\n');
    685      msg_didout = false;
    686    }
    687    getout(lua_ok ? 0 : 1);
    688  }
    689 
    690  TIME_MSG("before starting main loop");
    691  ILOG("starting main loop");
    692 
    693  // Main loop: never returns.
    694  normal_enter(false, false);
    695 
    696 #if defined(MSWIN) && !defined(MAKE_LIB)
    697  xfree(argv);
    698 #endif
    699  return 0;
    700 }
    701 
    702 void os_exit(int r)
    703  FUNC_ATTR_NORETURN
    704 {
    705  exiting = true;
    706 
    707  if (ui_client_channel_id) {
    708    ui_client_stop();
    709    if (r == 0) {
    710      r = ui_client_exit_status;
    711    }
    712  } else {
    713    ui_flush();
    714    ui_call_stop();
    715  }
    716 
    717  if (!event_teardown() && r == 0) {
    718    r = 1;  // Exit with error if main_loop did not teardown gracefully.
    719  }
    720  if (!ui_client_channel_id) {
    721    ml_close_all(true);  // remove all memfiles
    722  }
    723  if (used_stdin) {
    724    stream_set_blocking(STDIN_FILENO, true);  // normalize stream (#2598)
    725  }
    726 
    727  ILOG("Nvim exit: %d", r);
    728 
    729 #ifdef EXITFREE
    730  free_all_mem();
    731 #endif
    732 
    733  exit(r);
    734 }
    735 
    736 /// Exit properly
    737 void getout(int exitval)
    738  FUNC_ATTR_NORETURN
    739 {
    740  assert(!ui_client_channel_id);
    741  exiting = true;
    742 
    743  // make sure startuptimes have been flushed
    744  time_finish();
    745 
    746  // On error during Ex mode, exit with a non-zero code.
    747  // POSIX requires this, although it's not 100% clear from the standard.
    748  if (exmode_active) {
    749    exitval += ex_exitval;
    750  }
    751 
    752  set_vim_var_type(VV_EXITING, VAR_NUMBER);
    753  set_vim_var_nr(VV_EXITING, exitval);
    754 
    755  // Invoked all deferred functions in the function stack.
    756  invoke_all_defer();
    757 
    758  // Optionally print hashtable efficiency.
    759  hash_debug_results();
    760 
    761  if (v_dying <= 1) {
    762    const tabpage_T *next_tp;
    763 
    764    // Trigger BufWinLeave for all windows, but only once per buffer.
    765    for (const tabpage_T *tp = first_tabpage; tp != NULL; tp = next_tp) {
    766      next_tp = tp->tp_next;
    767      FOR_ALL_WINDOWS_IN_TAB(wp, tp) {
    768        if (wp->w_buffer == NULL || !buf_valid(wp->w_buffer)) {
    769          // Autocmd must have close the buffer already, skip.
    770          continue;
    771        }
    772 
    773        buf_T *buf = wp->w_buffer;
    774        if (buf_get_changedtick(buf) != -1) {
    775          bufref_T bufref;
    776 
    777          set_bufref(&bufref, buf);
    778          apply_autocmds(EVENT_BUFWINLEAVE, buf->b_fname, buf->b_fname, false, buf);
    779          if (bufref_valid(&bufref)) {
    780            buf_set_changedtick(buf, -1);  // note that we did it already
    781          }
    782          // start all over, autocommands may mess up the lists
    783          next_tp = first_tabpage;
    784          break;
    785        }
    786      }
    787    }
    788 
    789    // Trigger BufUnload for buffers that are loaded
    790    FOR_ALL_BUFFERS(buf) {
    791      if (buf->b_ml.ml_mfp != NULL) {
    792        bufref_T bufref;
    793        set_bufref(&bufref, buf);
    794        apply_autocmds(EVENT_BUFUNLOAD, buf->b_fname, buf->b_fname, false, buf);
    795        if (!bufref_valid(&bufref)) {
    796          // Autocmd deleted the buffer.
    797          break;
    798        }
    799      }
    800    }
    801 
    802    int unblock = 0;
    803    // deathtrap() blocks autocommands, but we do want to trigger
    804    // VimLeavePre.
    805    if (is_autocmd_blocked()) {
    806      unblock_autocmds();
    807      unblock++;
    808    }
    809    apply_autocmds(EVENT_VIMLEAVEPRE, NULL, NULL, false, curbuf);
    810    if (unblock) {
    811      block_autocmds();
    812    }
    813  }
    814 
    815  if (
    816 #ifdef EXITFREE
    817      !entered_free_all_mem &&
    818 #endif
    819      p_shada && *p_shada != NUL) {
    820    // Write out the registers, history, marks etc, to the ShaDa file
    821    shada_write_file(NULL, false);
    822  }
    823 
    824  if (v_dying <= 1) {
    825    int unblock = 0;
    826 
    827    // deathtrap() blocks autocommands, but we do want to trigger VimLeave.
    828    if (is_autocmd_blocked()) {
    829      unblock_autocmds();
    830      unblock++;
    831    }
    832    apply_autocmds(EVENT_VIMLEAVE, NULL, NULL, false, curbuf);
    833    if (unblock) {
    834      block_autocmds();
    835    }
    836  }
    837 
    838  profile_dump();
    839 
    840  if (did_emsg) {
    841    // give the user a chance to read the (error) message
    842    no_wait_return = false;
    843    // TODO(justinmk): this may call getout(0), clobbering exitval...
    844    wait_return(false);
    845  }
    846 
    847  // Apply 'titleold'.
    848  if (p_title && *p_titleold != NUL) {
    849    ui_call_set_title(cstr_as_string(p_titleold));
    850  }
    851 
    852  if (restarting) {
    853    Error err = ERROR_INIT;
    854    if (!remote_ui_restart(current_ui, &err)) {
    855      if (ERROR_SET(&err)) {
    856        ELOG("%s", err.msg);  // UI disappeared already?
    857        api_clear_error(&err);
    858      }
    859    }
    860    restarting = false;
    861  }
    862 
    863  if (garbage_collect_at_exit) {
    864    garbage_collect(false);
    865  }
    866 
    867 #ifdef MSWIN
    868  // Restore Windows console icon before exiting.
    869  os_icon_reset();
    870  os_title_reset();
    871 #endif
    872 
    873  os_exit(exitval);
    874 }
    875 
    876 /// Preserve files, print contents of `errmsg`, and exit 1.
    877 /// @param errmsg  If NULL, this function will not print anything.
    878 ///
    879 /// May be called from deadly_signal().
    880 void preserve_exit(const char *errmsg)
    881  FUNC_ATTR_NORETURN
    882 {
    883  // 'true' when we are sure to exit, e.g., after a deadly signal
    884  static bool really_exiting = false;
    885 
    886  // Prevent repeated calls into this method.
    887  if (really_exiting) {
    888    if (used_stdin) {
    889      // normalize stream (#2598)
    890      stream_set_blocking(STDIN_FILENO, true);
    891    }
    892    exit(2);
    893  }
    894 
    895  really_exiting = true;
    896  // Ignore SIGHUP while we are already exiting. #9274
    897  signal_reject_deadly();
    898 
    899  if (ui_client_channel_id) {
    900    // For TUI: exit alternate screen so that the error messages can be seen.
    901    ui_client_stop();
    902  }
    903  if (errmsg != NULL && errmsg[0] != NUL) {
    904    size_t has_eol = '\n' == errmsg[strlen(errmsg) - 1];
    905    fprintf(stderr, has_eol ? "%s" : "%s\n", errmsg);
    906  }
    907  if (ui_client_channel_id) {
    908    os_exit(1);
    909  }
    910 
    911  ml_close_notmod();                // close all not-modified buffers
    912 
    913  FOR_ALL_BUFFERS(buf) {
    914    if (buf->b_ml.ml_mfp != NULL && buf->b_ml.ml_mfp->mf_fname != NULL) {
    915      if (errmsg != NULL) {
    916        fprintf(stderr, "Nvim: preserving files...\n");
    917      }
    918      ml_sync_all(false, false, true);  // preserve all swap files
    919      break;
    920    }
    921  }
    922 
    923  ml_close_all(false);              // close all memfiles, without deleting
    924 
    925  if (errmsg != NULL) {
    926    fprintf(stderr, "Nvim: Finished.\n");
    927  }
    928 
    929  getout(1);
    930 }
    931 
    932 /// Gets the integer value of a numeric command line argument if given,
    933 /// such as '-o10'.
    934 ///
    935 /// @param[in] p         pointer to argument
    936 /// @param[in, out] idx  pointer to index in argument, is incremented
    937 /// @param[in] def       default value
    938 ///
    939 /// @return def unmodified if:
    940 ///   - argument isn't given
    941 ///   - argument is non-numeric
    942 ///
    943 /// @return argument's numeric value otherwise
    944 static int get_number_arg(const char *p, int *idx, int def)
    945  FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
    946 {
    947  if (ascii_isdigit(p[*idx])) {
    948    def = atoi(&(p[*idx]));
    949    while (ascii_isdigit(p[*idx])) {
    950      *idx = *idx + 1;
    951    }
    952  }
    953  return def;
    954 }
    955 
    956 static uint64_t server_connect(char *server_addr, const char **errmsg)
    957 {
    958  if (server_addr == NULL) {
    959    *errmsg = "no address specified";
    960    return 0;
    961  }
    962  CallbackReader on_data = CALLBACK_READER_INIT;
    963  const char *error = NULL;
    964  bool is_tcp = socket_address_tcp_host_end(server_addr) != NULL;
    965  // connected to channel
    966  uint64_t chan = channel_connect(is_tcp, server_addr, true, on_data, 500, &error);
    967  if (error) {
    968    *errmsg = error;
    969    return 0;
    970  }
    971  return chan;
    972 }
    973 
    974 /// Handle remote subcommands
    975 static void remote_request(mparm_T *params, int remote_args, char *server_addr, int argc,
    976                           char **argv, bool ui_only)
    977 {
    978  bool is_ui = strequal(argv[remote_args], "--remote-ui");
    979  if (ui_only && !is_ui) {
    980    // TODO(bfredl): this implies always starting the TUI.
    981    // if we be smart we could delay this past should_exit
    982    return;
    983  }
    984 
    985  const char *connect_error = NULL;
    986  uint64_t chan = server_connect(server_addr, &connect_error);
    987  Object rvobj = OBJECT_INIT;
    988 
    989  if (is_ui) {
    990    if (!chan) {
    991 #ifdef MSWIN
    992      // The TUI client is spawned in a ConPTY which only captures stdout.
    993      // Redirect stderr to stdout so this error appears in the terminal.
    994      dup2(STDOUT_FILENO, STDERR_FILENO);
    995 #endif
    996      fprintf(stderr, "Remote ui failed to start: %s\n", connect_error);
    997      os_exit(1);
    998    } else if (strequal(server_addr, os_getenv_noalloc("NVIM"))) {
    999      fprintf(stderr, "%s", "Cannot attach UI of :terminal child to its parent. ");
   1000      fprintf(stderr, "%s\n", "(Unset $NVIM to skip this check)");
   1001      os_exit(1);
   1002    }
   1003    ui_client_channel_id = chan;
   1004    return;
   1005  }
   1006 
   1007  Array args = ARRAY_DICT_INIT;
   1008  kv_resize(args, (size_t)(argc - remote_args));
   1009  for (int t_argc = remote_args; t_argc < argc; t_argc++) {
   1010    ADD_C(args, CSTR_AS_OBJ(argv[t_argc]));
   1011  }
   1012 
   1013  Error err = ERROR_INIT;
   1014  MAXSIZE_TEMP_ARRAY(a, 4);
   1015  ADD_C(a, INTEGER_OBJ((int)chan));
   1016  ADD_C(a, CSTR_AS_OBJ(server_addr));
   1017  ADD_C(a, CSTR_AS_OBJ(connect_error));
   1018  ADD_C(a, ARRAY_OBJ(args));
   1019  String s = STATIC_CSTR_AS_STRING("return vim._cs_remote(...)");
   1020  Object o = nlua_exec(s, NULL, a, kRetObject, NULL, &err);
   1021  kv_destroy(args);
   1022  if (ERROR_SET(&err)) {
   1023    fprintf(stderr, "%s\n", err.msg);
   1024    os_exit(2);
   1025  }
   1026 
   1027  if (o.type == kObjectTypeDict) {
   1028    rvobj.data.dict = o.data.dict;
   1029  } else {
   1030    fprintf(stderr, "vim._cs_remote returned unexpected value\n");
   1031    os_exit(2);
   1032  }
   1033 
   1034  TriState should_exit = kNone;
   1035  TriState tabbed = kNone;
   1036 
   1037  for (size_t i = 0; i < rvobj.data.dict.size; i++) {
   1038    if (strequal(rvobj.data.dict.items[i].key.data, "errmsg")) {
   1039      if (rvobj.data.dict.items[i].value.type != kObjectTypeString) {
   1040        fprintf(stderr, "vim._cs_remote returned an unexpected type for 'errmsg'\n");
   1041        os_exit(2);
   1042      }
   1043      fprintf(stderr, "%s\n", rvobj.data.dict.items[i].value.data.string.data);
   1044      os_exit(2);
   1045    } else if (strequal(rvobj.data.dict.items[i].key.data, "result")) {
   1046      if (rvobj.data.dict.items[i].value.type != kObjectTypeString) {
   1047        fprintf(stderr, "vim._cs_remote returned an unexpected type for 'result'\n");
   1048        os_exit(2);
   1049      }
   1050      printf("%s", rvobj.data.dict.items[i].value.data.string.data);
   1051    } else if (strequal(rvobj.data.dict.items[i].key.data, "tabbed")) {
   1052      if (rvobj.data.dict.items[i].value.type != kObjectTypeBoolean) {
   1053        fprintf(stderr, "vim._cs_remote returned an unexpected type for 'tabbed'\n");
   1054        os_exit(2);
   1055      }
   1056      tabbed = rvobj.data.dict.items[i].value.data.boolean ? kTrue : kFalse;
   1057    } else if (strequal(rvobj.data.dict.items[i].key.data, "should_exit")) {
   1058      if (rvobj.data.dict.items[i].value.type != kObjectTypeBoolean) {
   1059        fprintf(stderr, "vim._cs_remote returned an unexpected type for 'should_exit'\n");
   1060        os_exit(2);
   1061      }
   1062      should_exit = rvobj.data.dict.items[i].value.data.boolean ? kTrue : kFalse;
   1063    }
   1064  }
   1065  if (should_exit == kNone || tabbed == kNone) {
   1066    fprintf(stderr, "vim._cs_remote didn't return a value for should_exit or tabbed, bailing\n");
   1067    os_exit(2);
   1068  }
   1069  api_free_object(o);
   1070 
   1071  if (should_exit == kTrue) {
   1072    os_exit(0);
   1073  }
   1074  if (tabbed == kTrue) {
   1075    params->window_count = argc - remote_args - 1;
   1076    params->window_layout = WIN_TABS;
   1077  }
   1078 }
   1079 
   1080 /// Decides whether text (as opposed to commands) will be read from stdin.
   1081 /// @see EDIT_STDIN
   1082 static bool edit_stdin(mparm_T *parmp)
   1083 {
   1084  bool implicit = !headless_mode
   1085                  && !(embedded_mode && stdin_fd <= 0)
   1086                  && (!exmode_active || parmp->input_istext)
   1087                  && !stdin_isatty
   1088                  && parmp->edit_type <= EDIT_STDIN
   1089                  && parmp->scriptin == NULL;  // `-s -` was not given.
   1090  return parmp->had_stdin_file || implicit;
   1091 }
   1092 
   1093 /// Scan the command line arguments.
   1094 static void command_line_scan(mparm_T *parmp)
   1095 {
   1096  int argc = parmp->argc;
   1097  char **argv = parmp->argv;
   1098  int argv_idx;                         // index in argv[n][]
   1099  bool had_minmin = false;              // found "--" argument
   1100  bool want_argument;                   // option argument with argument
   1101  int n;
   1102 
   1103  argc--;
   1104  argv++;
   1105  argv_idx = 1;  // active option letter is argv[0][argv_idx]
   1106  while (argc > 0) {
   1107    // "+" or "+{number}" or "+/{pat}" or "+{command}" argument.
   1108    if (argv[0][0] == '+' && !had_minmin) {
   1109      if (parmp->n_commands >= MAX_ARG_CMDS) {
   1110        mainerr(err_extra_cmd, NULL, NULL);
   1111      }
   1112      argv_idx = -1;  // skip to next argument
   1113      if (argv[0][1] == NUL) {
   1114        parmp->commands[parmp->n_commands++] = "$";
   1115      } else {
   1116        parmp->commands[parmp->n_commands++] = &(argv[0][1]);
   1117      }
   1118    } else if (argv[0][0] == '-' && !had_minmin) {
   1119      // Optional argument.
   1120 
   1121      want_argument = false;
   1122      char c = argv[0][argv_idx++];
   1123      switch (c) {
   1124      case NUL:    // "nvim -"  read from stdin
   1125        if (exmode_active) {
   1126          // "nvim -e -" silent mode
   1127          silent_mode = true;
   1128          parmp->no_swap_file = true;
   1129        } else {
   1130          if (parmp->edit_type > EDIT_STDIN) {
   1131            mainerr(err_too_many_args, argv[0], NULL);
   1132          }
   1133          parmp->had_stdin_file = true;
   1134          parmp->edit_type = EDIT_STDIN;
   1135        }
   1136        argv_idx = -1;  // skip to next argument
   1137        break;
   1138      case '-':    // "--" No more option arguments.
   1139        // "--help" give help message
   1140        // "--version" give version message
   1141        // "--noplugin[s]" skip plugins
   1142        // "--cmd <cmd>" execute cmd before vimrc
   1143        // "--remote" execute commands remotey on a server
   1144        // "--server" name of vim server to send remote commands to
   1145        if (STRICMP(argv[0] + argv_idx, "help") == 0) {
   1146          usage();
   1147          os_exit(0);
   1148        } else if (STRICMP(argv[0] + argv_idx, "version") == 0) {
   1149          version();
   1150          os_exit(0);
   1151        } else if (STRICMP(argv[0] + argv_idx, "api-info") == 0) {
   1152 #ifdef MSWIN
   1153          // set stdout to binary to avoid crlf in --api-info output
   1154          _setmode(STDOUT_FILENO, _O_BINARY);
   1155 #endif
   1156 
   1157          String data = api_metadata_raw();
   1158          const ptrdiff_t written_bytes = os_write(STDOUT_FILENO, data.data, data.size, false);
   1159          if (written_bytes < 0) {
   1160            semsg(_("E5420: Failed to write to file: %s"), os_strerror((int)written_bytes));
   1161          }
   1162 
   1163          os_exit(0);
   1164        } else if (STRICMP(argv[0] + argv_idx, "headless") == 0) {
   1165          headless_mode = true;
   1166        } else if (STRICMP(argv[0] + argv_idx, "embed") == 0) {
   1167          embedded_mode = true;
   1168        } else if (STRNICMP(argv[0] + argv_idx, "listen", 6) == 0) {
   1169          want_argument = true;
   1170          argv_idx += 6;
   1171        } else if (STRNICMP(argv[0] + argv_idx, "literal", 7) == 0) {
   1172          // Do nothing: file args are always literal. #7679
   1173        } else if (STRNICMP(argv[0] + argv_idx, "remote", 6) == 0) {
   1174          parmp->remote = parmp->argc - argc;
   1175        } else if (STRNICMP(argv[0] + argv_idx, "server", 6) == 0) {
   1176          want_argument = true;
   1177          argv_idx += 6;
   1178        } else if (STRNICMP(argv[0] + argv_idx, "noplugin", 8) == 0) {
   1179          p_lpl = false;
   1180        } else if (STRNICMP(argv[0] + argv_idx, "cmd", 3) == 0) {
   1181          want_argument = true;
   1182          argv_idx += 3;
   1183        } else if (STRNICMP(argv[0] + argv_idx, "startuptime", 11) == 0) {
   1184          want_argument = true;
   1185          argv_idx += 11;
   1186        } else if (STRNICMP(argv[0] + argv_idx, "clean", 5) == 0) {
   1187          parmp->use_vimrc = "NONE";
   1188          parmp->clean = true;
   1189          set_option_value_give_err(kOptShadafile, STATIC_CSTR_AS_OPTVAL("NONE"), 0);
   1190        } else if (STRNICMP(argv[0] + argv_idx, "luamod-dev", 9) == 0) {
   1191          nlua_disable_preload = true;
   1192        } else {
   1193          if (argv[0][argv_idx]) {
   1194            mainerr(err_opt_unknown, argv[0], NULL);
   1195          }
   1196          had_minmin = true;
   1197        }
   1198        if (!want_argument) {
   1199          argv_idx = -1;  // skip to next argument
   1200        }
   1201        break;
   1202      case 'A':    // "-A" start in Arabic mode.
   1203        set_option_value_give_err(kOptArabic, BOOLEAN_OPTVAL(true), 0);
   1204        break;
   1205      case 'b':    // "-b" binary mode.
   1206        // Needs to be effective before expanding file names, because
   1207        // for Win32 this makes us edit a shortcut file itself,
   1208        // instead of the file it links to.
   1209        set_options_bin(curbuf->b_p_bin, 1, 0);
   1210        curbuf->b_p_bin = 1;  // Binary file I/O.
   1211        break;
   1212 
   1213      case 'D':    // "-D" Debugging
   1214        parmp->use_debug_break_level = 9999;
   1215        break;
   1216      case 'd':    // "-d" 'diff'
   1217        parmp->diff_mode = true;
   1218        break;
   1219      case 'e':    // "-e" Ex mode
   1220        exmode_active = true;
   1221        break;
   1222      case 'E':    // "-E" Ex mode
   1223        exmode_active = true;
   1224        parmp->input_istext = true;
   1225        break;
   1226      case 'f':    // "-f"  GUI: run in foreground.
   1227        break;
   1228      case '?':    // "-?" give help message (for MS-Windows)
   1229      case 'h':    // "-h" give help message
   1230        usage();
   1231        os_exit(0);
   1232      case 'H':    // "-H" start in Hebrew mode: rl + keymap=hebrew set.
   1233        set_option_value_give_err(kOptKeymap, STATIC_CSTR_AS_OPTVAL("hebrew"), 0);
   1234        set_option_value_give_err(kOptRightleft, BOOLEAN_OPTVAL(true), 0);
   1235        break;
   1236      case 'M':    // "-M"  no changes or writing of files
   1237        reset_modifiable();
   1238        FALLTHROUGH;
   1239      case 'm':    // "-m"  no writing of files
   1240        p_write = false;
   1241        break;
   1242 
   1243      case 'N':    // "-N"  Nocompatible
   1244      case 'X':    // "-X"  Do not connect to X server
   1245        // No-op
   1246        break;
   1247 
   1248      case 'n':    // "-n" no swap file
   1249        parmp->no_swap_file = true;
   1250        break;
   1251      case 'p':    // "-p[N]" open N tab pages
   1252        // default is 0: open window for each file
   1253        parmp->window_count = get_number_arg(argv[0], &argv_idx, 0);
   1254        parmp->window_layout = WIN_TABS;
   1255        break;
   1256      case 'o':    // "-o[N]" open N horizontal split windows
   1257        // default is 0: open window for each file
   1258        parmp->window_count = get_number_arg(argv[0], &argv_idx, 0);
   1259        parmp->window_layout = WIN_HOR;
   1260        break;
   1261      case 'O':    // "-O[N]" open N vertical split windows
   1262        // default is 0: open window for each file
   1263        parmp->window_count = get_number_arg(argv[0], &argv_idx, 0);
   1264        parmp->window_layout = WIN_VER;
   1265        break;
   1266      case 'q':    // "-q" QuickFix mode
   1267        if (parmp->edit_type != EDIT_NONE) {
   1268          mainerr(err_too_many_args, argv[0], NULL);
   1269        }
   1270        parmp->edit_type = EDIT_QF;
   1271        if (argv[0][argv_idx]) {  // "-q{errorfile}"
   1272          parmp->use_ef = argv[0] + argv_idx;
   1273          argv_idx = -1;
   1274        } else if (argc > 1) {    // "-q {errorfile}"
   1275          want_argument = true;
   1276        }
   1277        break;
   1278      case 'R':    // "-R" readonly mode
   1279        readonlymode = true;
   1280        curbuf->b_p_ro = true;
   1281        p_uc = 10000;  // don't update very often
   1282        break;
   1283      case 'r':    // "-r" recovery mode
   1284      case 'L':    // "-L" recovery mode
   1285        recoverymode = 1;
   1286        break;
   1287      case 's':
   1288        if (exmode_active) {    // "-es" silent (batch) Ex-mode
   1289          silent_mode = true;
   1290          parmp->no_swap_file = true;
   1291          if (p_shadafile == NULL || *p_shadafile == NUL) {
   1292            set_option_value_give_err(kOptShadafile, STATIC_CSTR_AS_OPTVAL("NONE"), 0);
   1293          }
   1294        } else {                // "-s {scriptin}" read from script file
   1295          want_argument = true;
   1296        }
   1297        break;
   1298      case 't':    // "-t {tag}" or "-t{tag}" jump to tag
   1299        if (parmp->edit_type != EDIT_NONE) {
   1300          mainerr(err_too_many_args, argv[0], NULL);
   1301        }
   1302        parmp->edit_type = EDIT_TAG;
   1303        if (argv[0][argv_idx]) {  // "-t{tag}"
   1304          parmp->tagname = argv[0] + argv_idx;
   1305          argv_idx = -1;
   1306        } else {  // "-t {tag}"
   1307          want_argument = true;
   1308        }
   1309        break;
   1310      case 'v':
   1311        version();
   1312        os_exit(0);
   1313      case 'V':    // "-V{N}" Verbose level
   1314        // default is 10: a little bit verbose
   1315        p_verbose = get_number_arg(argv[0], &argv_idx, 10);
   1316        if (argv[0][argv_idx] != NUL) {
   1317          set_option_value_give_err(kOptVerbosefile, CSTR_AS_OPTVAL(argv[0] + argv_idx), 0);
   1318          argv_idx = (int)strlen(argv[0]);
   1319        }
   1320        break;
   1321      case 'w':    // "-w{number}" set window height
   1322        // "-w {scriptout}" write to script
   1323        if (ascii_isdigit((argv[0])[argv_idx])) {
   1324          n = get_number_arg(argv[0], &argv_idx, 10);
   1325          set_option_value_give_err(kOptWindow, NUMBER_OPTVAL((OptInt)n), 0);
   1326          break;
   1327        }
   1328        want_argument = true;
   1329        break;
   1330 
   1331      case 'c':    // "-c{command}" or "-c {command}" exec command
   1332        if (argv[0][argv_idx] != NUL) {
   1333          if (parmp->n_commands >= MAX_ARG_CMDS) {
   1334            mainerr(err_extra_cmd, NULL, NULL);
   1335          }
   1336          parmp->commands[parmp->n_commands++] = argv[0] + argv_idx;
   1337          argv_idx = -1;
   1338          break;
   1339        }
   1340        FALLTHROUGH;
   1341      case 'S':    // "-S {file}" execute Vim script
   1342      case 'i':    // "-i {shada}" use for ShaDa file
   1343      case 'l':    // "-l {file}" Lua mode
   1344      case 'u':    // "-u {vimrc}" vim inits file
   1345      case 'U':    // "-U {gvimrc}" gvim inits file
   1346      case 'W':    // "-W {scriptout}" overwrite
   1347        want_argument = true;
   1348        break;
   1349 
   1350      default:
   1351        mainerr(err_opt_unknown, argv[0], NULL);
   1352      }
   1353 
   1354      // Handle option arguments with argument.
   1355      if (want_argument) {
   1356        // Check for garbage immediately after the option letter.
   1357        if (argv[0][argv_idx] != NUL) {
   1358          mainerr(err_opt_garbage, argv[0], NULL);
   1359        }
   1360 
   1361        argc--;
   1362        if (argc < 1 && c != 'S') {  // -S has an optional argument
   1363          mainerr(err_arg_missing, argv[0], NULL);
   1364        }
   1365        argv++;
   1366        argv_idx = -1;
   1367 
   1368        switch (c) {
   1369        case 'c':    // "-c {command}" execute command
   1370        case 'S':    // "-S {file}" execute Vim script
   1371          if (parmp->n_commands >= MAX_ARG_CMDS) {
   1372            mainerr(err_extra_cmd, NULL, NULL);
   1373          }
   1374          if (c == 'S') {
   1375            char *a;
   1376 
   1377            if (argc < 1) {
   1378              // "-S" without argument: use default session file name.
   1379              a = SESSION_FILE;
   1380            } else if (argv[0][0] == '-') {
   1381              // "-S" followed by another option: use default session file.
   1382              a = SESSION_FILE;
   1383              argc++;
   1384              argv--;
   1385            } else {
   1386              a = argv[0];
   1387            }
   1388 
   1389            size_t s_size = strlen(a) + 9;
   1390            char *s = xmalloc(s_size);
   1391            snprintf(s, s_size, "so %s", a);
   1392            parmp->cmds_tofree[parmp->n_commands] = true;
   1393            parmp->commands[parmp->n_commands++] = s;
   1394          } else {
   1395            parmp->commands[parmp->n_commands++] = argv[0];
   1396          }
   1397          break;
   1398 
   1399        case '-':
   1400          if (strequal(argv[-1], "--cmd")) {
   1401            // "--cmd {command}" execute command
   1402            if (parmp->n_pre_commands >= MAX_ARG_CMDS) {
   1403              mainerr(err_extra_cmd, NULL, NULL);
   1404            }
   1405            parmp->pre_commands[parmp->n_pre_commands++] = argv[0];
   1406          } else if (strequal(argv[-1], "--listen")) {
   1407            // "--listen {address}"
   1408            parmp->listen_addr = argv[0];
   1409          } else if (strequal(argv[-1], "--server")) {
   1410            // "--server {address}"
   1411            parmp->server_addr = argv[0];
   1412          }
   1413          // "--startuptime <file>" already handled
   1414          break;
   1415 
   1416        case 'q':    // "-q {errorfile}" QuickFix mode
   1417          parmp->use_ef = argv[0];
   1418          break;
   1419 
   1420        case 'i':    // "-i {shada}" use for shada
   1421          set_option_value_give_err(kOptShadafile, CSTR_AS_OPTVAL(argv[0]), 0);
   1422          break;
   1423 
   1424        case 'l':    // "-l" Lua script: args after "-l".
   1425          headless_mode = true;
   1426          silent_mode = true;
   1427          p_verbose = 1;
   1428          parmp->no_swap_file = true;
   1429          parmp->use_vimrc = parmp->use_vimrc ? parmp->use_vimrc : "NONE";
   1430          if (p_shadafile == NULL || *p_shadafile == NUL) {
   1431            set_option_value_give_err(kOptShadafile, STATIC_CSTR_AS_OPTVAL("NONE"), 0);
   1432          }
   1433          parmp->luaf = argv[0];
   1434          argc--;
   1435          if (argc >= 0) {  // Lua args after "-l <file>".
   1436            parmp->lua_arg0 = parmp->argc - argc;
   1437            argc = 0;
   1438          }
   1439          break;
   1440 
   1441        case 's':    // "-s {scriptin}" read from script file
   1442          if (parmp->scriptin != NULL) {
   1443 scripterror:
   1444            vim_snprintf(IObuff, IOSIZE,
   1445                         _("Attempt to open script file again: \"%s %s\"\n"),
   1446                         argv[-1], argv[0]);
   1447            fprintf(stderr, "%s", IObuff);
   1448            os_exit(2);
   1449          }
   1450          parmp->scriptin = argv[0];
   1451          break;
   1452 
   1453        case 't':    // "-t {tag}"
   1454          parmp->tagname = argv[0];
   1455          break;
   1456        case 'u':    // "-u {vimrc}" vim inits file
   1457          parmp->use_vimrc = argv[0];
   1458          break;
   1459        case 'U':    // "-U {gvimrc}" gvim inits file
   1460          break;
   1461 
   1462        case 'w':    // "-w {nr}" 'window' value
   1463          // "-w {scriptout}" append to script file
   1464          if (ascii_isdigit(*(argv[0]))) {
   1465            argv_idx = 0;
   1466            n = get_number_arg(argv[0], &argv_idx, 10);
   1467            set_option_value_give_err(kOptWindow, NUMBER_OPTVAL((OptInt)n), 0);
   1468            argv_idx = -1;
   1469            break;
   1470          }
   1471          FALLTHROUGH;
   1472        case 'W':    // "-W {scriptout}" overwrite script file
   1473          if (parmp->scriptout != NULL) {
   1474            goto scripterror;
   1475          }
   1476          parmp->scriptout = argv[0];
   1477          parmp->scriptout_append = (c == 'w');
   1478        }
   1479      }
   1480    } else {  // File name argument.
   1481      argv_idx = -1;  // skip to next argument
   1482 
   1483      // Check for only one type of editing.
   1484      if (parmp->edit_type > EDIT_STDIN) {
   1485        mainerr(err_too_many_args, argv[0], NULL);
   1486      }
   1487      parmp->edit_type = EDIT_FILE;
   1488 
   1489      // Add the file to the global argument list.
   1490      ga_grow(&global_alist.al_ga, 1);
   1491      char *p = xstrdup(argv[0]);
   1492 
   1493      // On Windows expand "~\" or "~/" prefix in file names to profile directory.
   1494 #ifdef MSWIN
   1495      if (*p == '~' && (p[1] == '\\' || p[1] == '/')) {
   1496        size_t size = strlen(os_homedir()) + strlen(p);
   1497        char *tilde_expanded = xmalloc(size);
   1498        snprintf(tilde_expanded, size, "%s%s", os_homedir(), p + 1);
   1499        xfree(p);
   1500        p = tilde_expanded;
   1501      }
   1502 #endif
   1503 
   1504      if (parmp->diff_mode && os_isdir(p) && GARGCOUNT > 0
   1505          && !os_isdir(alist_name(&GARGLIST[0]))) {
   1506        char *r = concat_fnames(p, path_tail(alist_name(&GARGLIST[0])), true);
   1507        xfree(p);
   1508        p = r;
   1509      }
   1510 
   1511 #ifdef CASE_INSENSITIVE_FILENAME
   1512      // Make the case of the file name match the actual file.
   1513      path_fix_case(p);
   1514 #endif
   1515 
   1516      int alist_fnum_flag = edit_stdin(parmp)
   1517                            ? 1   // add buffer nr after exp.
   1518                            : 2;  // add buffer number now and use curbuf
   1519      alist_add(&global_alist, p, alist_fnum_flag);
   1520    }
   1521 
   1522    // If there are no more letters after the current "-", go to next argument.
   1523    // argv_idx is set to -1 when the current argument is to be skipped.
   1524    if (argv_idx <= 0 || argv[0][argv_idx] == NUL) {
   1525      argc--;
   1526      argv++;
   1527      argv_idx = 1;
   1528    }
   1529  }
   1530 
   1531  if (embedded_mode && (silent_mode || parmp->luaf)) {
   1532    mainerr(_("--embed conflicts with -es/-Es/-l"), NULL, NULL);
   1533  }
   1534 
   1535  // If there is a "+123" or "-c" command, set v:swapcommand to the first one.
   1536  if (parmp->n_commands > 0) {
   1537    const size_t swcmd_len = strlen(parmp->commands[0]) + 3;
   1538    char *const swcmd = xmalloc(swcmd_len);
   1539    snprintf(swcmd, swcmd_len, ":%s\r", parmp->commands[0]);
   1540    set_vim_var_string(VV_SWAPCOMMAND, swcmd, -1);
   1541    xfree(swcmd);
   1542  }
   1543 
   1544  TIME_MSG("parsing arguments");
   1545 }
   1546 
   1547 static void set_argf_var(void)
   1548 {
   1549  list_T *list = tv_list_alloc(kListLenMayKnow);
   1550 
   1551  for (int i = 0; i < GARGCOUNT; i++) {
   1552    char *fname = alist_name(&GARGLIST[i]);
   1553    if (fname != NULL) {
   1554      (void)vim_FullName(fname, NameBuff, sizeof(NameBuff), false);
   1555      tv_list_append_string(list, NameBuff, -1);
   1556    }
   1557  }
   1558 
   1559  tv_list_set_lock(list, VAR_FIXED);
   1560  set_vim_var_list(VV_ARGF, list);
   1561 }
   1562 
   1563 // Many variables are in "params" so that we can pass them to invoked
   1564 // functions without a lot of arguments.  "argc" and "argv" are also
   1565 // copied, so that they can be changed.
   1566 static void init_params(mparm_T *paramp, int argc, char **argv)
   1567 {
   1568  CLEAR_POINTER(paramp);
   1569  paramp->argc = argc;
   1570  paramp->argv = argv;
   1571  paramp->use_debug_break_level = -1;
   1572  paramp->window_count = -1;
   1573  paramp->listen_addr = NULL;
   1574  paramp->server_addr = NULL;
   1575  paramp->remote = 0;
   1576  paramp->luaf = NULL;
   1577  paramp->lua_arg0 = -1;
   1578 }
   1579 
   1580 /// Initialize global startuptime file if "--startuptime" passed as an argument.
   1581 static void init_startuptime(mparm_T *paramp)
   1582 {
   1583  bool is_embed = false;
   1584  for (int i = 1; i < paramp->argc - 1; i++) {
   1585    if (STRICMP(paramp->argv[i], "--embed") == 0) {
   1586      is_embed = true;
   1587      break;
   1588    }
   1589  }
   1590  for (int i = 1; i < paramp->argc - 1; i++) {
   1591    if (STRICMP(paramp->argv[i], "--startuptime") == 0) {
   1592      time_init(paramp->argv[i + 1], is_embed ? "Embedded" : "Primary (or UI client)");
   1593      time_start("--- NVIM STARTING ---");
   1594      break;
   1595    }
   1596  }
   1597 }
   1598 
   1599 static void check_and_set_isatty(mparm_T *paramp)
   1600 {
   1601  stdin_isatty = os_isatty(STDIN_FILENO);
   1602  stdout_isatty = os_isatty(STDOUT_FILENO);
   1603  stderr_isatty = os_isatty(STDERR_FILENO);
   1604  TIME_MSG("window checked");
   1605 }
   1606 
   1607 // Sets v:progname and v:progpath. Also modifies $PATH on Windows.
   1608 static void init_path(const char *exename)
   1609  FUNC_ATTR_NONNULL_ALL
   1610 {
   1611  char exepath[MAXPATHL] = { 0 };
   1612  size_t exepathlen = MAXPATHL;
   1613  // Make v:progpath absolute.
   1614  if (os_exepath(exepath, &exepathlen) != 0) {
   1615    // Fall back to argv[0]. Missing procfs? #6734
   1616    path_guess_exepath(exename, exepath, sizeof(exepath));
   1617  }
   1618  set_vim_var_string(VV_PROGPATH, exepath, -1);
   1619  set_vim_var_string(VV_PROGNAME, path_tail(exename), -1);
   1620 
   1621 #ifdef MSWIN
   1622  // Append the process start directory to $PATH, so that ":!foo" finds tools
   1623  // shipped with Windows package. This also mimics SearchPath().
   1624  os_setenv_append_path(exepath);
   1625 #endif
   1626 }
   1627 
   1628 /// Get filename from command line, if any.
   1629 static char *get_fname(mparm_T *parmp, char *cwd)
   1630 {
   1631  return alist_name(&GARGLIST[0]);
   1632 }
   1633 
   1634 // Decide about window layout for diff mode after reading vimrc.
   1635 static void set_window_layout(mparm_T *paramp)
   1636 {
   1637  if (paramp->diff_mode && paramp->window_layout == 0) {
   1638    if (diffopt_horizontal()) {
   1639      paramp->window_layout = WIN_HOR;             // use horizontal split
   1640    } else {
   1641      paramp->window_layout = WIN_VER;             // use vertical split
   1642    }
   1643  }
   1644 }
   1645 
   1646 // "-q errorfile": Load the error file now.
   1647 // If the error file can't be read, exit before doing anything else.
   1648 static void handle_quickfix(mparm_T *paramp)
   1649 {
   1650  if (paramp->edit_type == EDIT_QF) {
   1651    if (paramp->use_ef != NULL) {
   1652      set_option_direct(kOptErrorfile, CSTR_AS_OPTVAL(paramp->use_ef), 0, SID_CARG);
   1653    }
   1654    vim_snprintf(IObuff, IOSIZE, "cfile %s", p_ef);
   1655    if (qf_init(NULL, p_ef, p_efm, true, IObuff, p_menc) < 0) {
   1656      msg_putchar('\n');
   1657      os_exit(3);
   1658    }
   1659    TIME_MSG("reading errorfile");
   1660  }
   1661 }
   1662 
   1663 // Need to jump to the tag before executing the '-c command'.
   1664 // Makes "vim -c '/return' -t main" work.
   1665 static void handle_tag(char *tagname)
   1666 {
   1667  if (tagname != NULL) {
   1668    swap_exists_did_quit = false;
   1669 
   1670    vim_snprintf(IObuff, IOSIZE, "ta %s", tagname);
   1671    do_cmdline_cmd(IObuff);
   1672    TIME_MSG("jumping to tag");
   1673 
   1674    // If the user doesn't want to edit the file then we quit here.
   1675    if (swap_exists_did_quit) {
   1676      ui_call_error_exit(1);
   1677      getout(1);
   1678    }
   1679  }
   1680 }
   1681 
   1682 /// Read text from stdin.
   1683 static void read_stdin(void)
   1684 {
   1685  // When getting the ATTENTION prompt here, use a dialog.
   1686  swap_exists_action = SEA_DIALOG;
   1687  no_wait_return = true;
   1688  bool save_msg_didany = msg_didany;
   1689 
   1690  if (curbuf->b_ffname) {
   1691    // curbuf is already opened for a file, create a new buffer for stdin. #35269
   1692    buf_T *stdin_buf = buflist_new(NULL, NULL, 0, BLN_LISTED);
   1693    if (stdin_buf == NULL) {
   1694      semsg("Failed to create buffer for stdin");
   1695      return;
   1696    }
   1697 
   1698    // remember the current buffer number so we can go back to it
   1699    handle_T initial_buf_handle = curbuf->handle;
   1700 
   1701    // set the buffer we just created as curbuf so we can read stdin into it
   1702    set_curbuf(stdin_buf, 0, false);
   1703    readfile(NULL, NULL, 0, 0, (linenr_T)MAXLNUM, NULL, READ_NEW + READ_STDIN, true);
   1704 
   1705    // remember stdin_buf_handle so we can close it if stdin_buf ends up empty
   1706    handle_T stdin_buf_handle = stdin_buf->handle;
   1707    bool stdin_buf_empty = buf_is_empty(curbuf);
   1708 
   1709    // switch back to the original starting buffer
   1710    char buf[100];
   1711    vim_snprintf(buf, sizeof(buf), "silent! buffer %d", initial_buf_handle);
   1712    do_cmdline_cmd(buf);
   1713 
   1714    if (stdin_buf_empty) {
   1715      // stdin buffer may be first or last ("echo foo | nvim file1 -"). #35269
   1716      // only wipe buffer after having switched to original starting buffer. #35681
   1717      vim_snprintf(buf, sizeof(buf), "silent! bwipeout! %d", stdin_buf_handle);
   1718      do_cmdline_cmd(buf);
   1719    }
   1720  } else {
   1721    // stdin buffer is first so we can just use curbuf
   1722    set_buflisted(true);
   1723    // Create memfile and read from stdin.
   1724    open_buffer(true, NULL, 0);
   1725    // stdin was empty so we should wipe it (e.g. "echo file1 | xargs nvim"). #8561
   1726    if (buf_is_empty(curbuf) && curbuf->b_next != NULL) {
   1727      do_cmdline_cmd("silent! bnext");
   1728      do_cmdline_cmd("silent! bwipeout 1");
   1729    }
   1730  }
   1731 
   1732  no_wait_return = false;
   1733  msg_didany = save_msg_didany;
   1734  TIME_MSG("reading stdin");
   1735  check_swap_exists_action();
   1736 }
   1737 
   1738 // Create the requested number of windows and edit buffers in them.
   1739 // Also does recovery if "recoverymode" set.
   1740 static void create_windows(mparm_T *parmp)
   1741 {
   1742  // Create the number of windows that was requested.
   1743  if (parmp->window_count == -1) {      // was not set
   1744    parmp->window_count = 1;
   1745  }
   1746  if (parmp->window_count == 0) {
   1747    parmp->window_count = GARGCOUNT;
   1748  }
   1749  if (parmp->window_count > 1) {
   1750    // Don't change the windows if there was a command in vimrc that
   1751    // already split some windows
   1752    if (parmp->window_layout == 0) {
   1753      parmp->window_layout = WIN_HOR;
   1754    }
   1755    if (parmp->window_layout == WIN_TABS) {
   1756      parmp->window_count = make_tabpages(parmp->window_count);
   1757      TIME_MSG("making tab pages");
   1758    } else if (firstwin->w_next == NULL || firstwin->w_next->w_floating) {
   1759      parmp->window_count = make_windows(parmp->window_count, parmp->window_layout == WIN_VER);
   1760      TIME_MSG("making windows");
   1761    } else {
   1762      parmp->window_count = win_count();
   1763    }
   1764  } else {
   1765    parmp->window_count = 1;
   1766  }
   1767 
   1768  if (recoverymode) {                   // do recover
   1769    msg_scroll = true;                  // scroll message up
   1770    ml_recover(true);
   1771    if (curbuf->b_ml.ml_mfp == NULL) {   // failed
   1772      getout(1);
   1773    }
   1774    do_modelines(0);                    // do modelines
   1775  } else {
   1776    int done = 0;
   1777    // Open a buffer for windows that don't have one yet.
   1778    // Commands in the vimrc might have loaded a file or split the window.
   1779    // Watch out for autocommands that delete a window.
   1780    //
   1781    // Don't execute Win/Buf Enter/Leave autocommands here
   1782    autocmd_no_enter++;
   1783    autocmd_no_leave++;
   1784    bool dorewind = true;
   1785    while (done++ < 1000) {
   1786      if (dorewind) {
   1787        if (parmp->window_layout == WIN_TABS) {
   1788          goto_tabpage(1);
   1789        } else {
   1790          curwin = firstwin;
   1791        }
   1792      } else if (parmp->window_layout == WIN_TABS) {
   1793        if (curtab->tp_next == NULL) {
   1794          break;
   1795        }
   1796        goto_tabpage(0);
   1797      } else {
   1798        if (curwin->w_next == NULL) {
   1799          break;
   1800        }
   1801        curwin = curwin->w_next;
   1802      }
   1803      dorewind = false;
   1804      curbuf = curwin->w_buffer;
   1805      if (curbuf->b_ml.ml_mfp == NULL) {
   1806        // Set 'foldlevel' to 'foldlevelstart' if it's not negative..
   1807        if (p_fdls >= 0) {
   1808          curwin->w_p_fdl = p_fdls;
   1809        }
   1810        // When getting the ATTENTION prompt here, use a dialog.
   1811        swap_exists_action = SEA_DIALOG;
   1812        set_buflisted(true);
   1813 
   1814        // create memfile, read file
   1815        open_buffer(false, NULL, 0);
   1816 
   1817        if (swap_exists_action == SEA_QUIT) {
   1818          if (got_int || only_one_window()) {
   1819            // abort selected or quit and only one window
   1820            did_emsg = false;               // avoid hit-enter prompt
   1821            ui_call_error_exit(1);
   1822            getout(1);
   1823          }
   1824          // We can't close the window, it would disturb what
   1825          // happens next.  Clear the file name and set the arg
   1826          // index to -1 to delete it later.
   1827          setfname(curbuf, NULL, NULL, false);
   1828          curwin->w_arg_idx = -1;
   1829          swap_exists_action = SEA_NONE;
   1830        } else {
   1831          handle_swap_exists(NULL);
   1832        }
   1833        dorewind = true;                        // start again
   1834      }
   1835      os_breakcheck();
   1836      if (got_int) {
   1837        vgetc();          // only break the file loading, not the rest
   1838        break;
   1839      }
   1840    }
   1841    if (parmp->window_layout == WIN_TABS) {
   1842      goto_tabpage(1);
   1843    } else {
   1844      curwin = firstwin;
   1845    }
   1846    curbuf = curwin->w_buffer;
   1847    autocmd_no_enter--;
   1848    autocmd_no_leave--;
   1849  }
   1850 }
   1851 
   1852 /// If opened more than one window, start editing files in the other
   1853 /// windows. make_windows() has already opened the windows.
   1854 static void edit_buffers(mparm_T *parmp, char *cwd)
   1855 {
   1856  int arg_idx;                          // index in argument list
   1857  bool advance = true;
   1858  win_T *win;
   1859  char *p_shm_save = NULL;
   1860 
   1861  // Don't execute Win/Buf Enter/Leave autocommands here
   1862  autocmd_no_enter++;
   1863  autocmd_no_leave++;
   1864 
   1865  // When w_arg_idx is -1 remove the window (see create_windows()).
   1866  if (curwin->w_arg_idx == -1) {
   1867    win_close(curwin, true, false);
   1868    advance = false;
   1869  }
   1870 
   1871  arg_idx = 1;
   1872  for (int i = 1; i < parmp->window_count; i++) {
   1873    if (cwd != NULL) {
   1874      os_chdir(cwd);
   1875    }
   1876    // When w_arg_idx is -1 remove the window (see create_windows()).
   1877    if (curwin->w_arg_idx == -1) {
   1878      arg_idx++;
   1879      win_close(curwin, true, false);
   1880      advance = false;
   1881      continue;
   1882    }
   1883 
   1884    if (advance) {
   1885      if (parmp->window_layout == WIN_TABS) {
   1886        if (curtab->tp_next == NULL) {          // just checking
   1887          break;
   1888        }
   1889        goto_tabpage(0);
   1890        // Temporarily reset 'shm' option to not print fileinfo when
   1891        // loading the other buffers. This would overwrite the already
   1892        // existing fileinfo for the first tab.
   1893        if (i == 1) {
   1894          char buf[100];
   1895 
   1896          p_shm_save = xstrdup(p_shm);
   1897          snprintf(buf, sizeof(buf), "F%s", p_shm);
   1898          set_option_value_give_err(kOptShortmess, CSTR_AS_OPTVAL(buf), 0);
   1899        }
   1900      } else {
   1901        if (curwin->w_next == NULL) {           // just checking
   1902          break;
   1903        }
   1904        win_enter(curwin->w_next, false);
   1905      }
   1906    }
   1907    advance = true;
   1908 
   1909    // Only open the file if there is no file in this window yet (that can
   1910    // happen when vimrc contains ":sall").
   1911    if (curbuf == firstwin->w_buffer || curbuf->b_ffname == NULL) {
   1912      curwin->w_arg_idx = arg_idx;
   1913      // Edit file from arg list, if there is one.  When "Quit" selected
   1914      // at the ATTENTION prompt close the window.
   1915      swap_exists_did_quit = false;
   1916      do_ecmd(0, arg_idx < GARGCOUNT
   1917              ? alist_name(&GARGLIST[arg_idx])
   1918              : NULL, NULL, NULL, ECMD_LASTL, ECMD_HIDE, curwin);
   1919      if (swap_exists_did_quit) {
   1920        // abort or quit selected
   1921        if (got_int || only_one_window()) {
   1922          // abort selected and only one window
   1923          did_emsg = false;             // avoid hit-enter prompt
   1924          ui_call_error_exit(1);
   1925          getout(1);
   1926        }
   1927        win_close(curwin, true, false);
   1928        advance = false;
   1929      }
   1930      if (arg_idx == GARGCOUNT - 1) {
   1931        arg_had_last = true;
   1932      }
   1933      arg_idx++;
   1934    }
   1935    os_breakcheck();
   1936    if (got_int) {
   1937      vgetc();            // only break the file loading, not the rest
   1938      break;
   1939    }
   1940  }
   1941 
   1942  if (p_shm_save != NULL) {
   1943    set_option_value_give_err(kOptShortmess, CSTR_AS_OPTVAL(p_shm_save), 0);
   1944    xfree(p_shm_save);
   1945  }
   1946 
   1947  if (parmp->window_layout == WIN_TABS) {
   1948    goto_tabpage(1);
   1949  }
   1950  autocmd_no_enter--;
   1951 
   1952  // make the first window the current window
   1953  win = firstwin;
   1954  // Avoid making a preview window the current window.
   1955  while (win->w_p_pvw) {
   1956    win = win->w_next;
   1957    if (win == NULL) {
   1958      win = firstwin;
   1959      break;
   1960    }
   1961  }
   1962  win_enter(win, false);
   1963 
   1964  autocmd_no_leave--;
   1965  TIME_MSG("editing files in windows");
   1966  if (parmp->window_count > 1 && parmp->window_layout != WIN_TABS) {
   1967    win_equal(curwin, false, 'b');      // adjust heights
   1968  }
   1969 }
   1970 
   1971 // Execute the commands from --cmd arguments "cmds[cnt]".
   1972 static void exe_pre_commands(mparm_T *parmp)
   1973 {
   1974  char **cmds = parmp->pre_commands;
   1975  int cnt = parmp->n_pre_commands;
   1976  ESTACK_CHECK_DECLARATION;
   1977 
   1978  if (cnt <= 0) {
   1979    return;
   1980  }
   1981 
   1982  curwin->w_cursor.lnum = 0;     // just in case..
   1983  estack_push(ETYPE_ARGS, _("pre-vimrc command line"), 0);
   1984  ESTACK_CHECK_SETUP;
   1985  current_sctx.sc_sid = SID_CMDARG;
   1986  for (int i = 0; i < cnt; i++) {
   1987    do_cmdline_cmd(cmds[i]);
   1988  }
   1989  ESTACK_CHECK_NOW;
   1990  estack_pop();
   1991  current_sctx.sc_sid = 0;
   1992  TIME_MSG("--cmd commands");
   1993 }
   1994 
   1995 // Execute "+", "-c" and "-S" arguments.
   1996 static void exe_commands(mparm_T *parmp)
   1997 {
   1998  ESTACK_CHECK_DECLARATION;
   1999 
   2000  // We start commands on line 0, make "vim +/pat file" match a
   2001  // pattern on line 1.  But don't move the cursor when an autocommand
   2002  // with g`" was used.
   2003  msg_scroll = true;
   2004  if (parmp->tagname == NULL && curwin->w_cursor.lnum <= 1) {
   2005    curwin->w_cursor.lnum = 0;
   2006  }
   2007  estack_push(ETYPE_ARGS, "command line", 0);
   2008  ESTACK_CHECK_SETUP;
   2009  current_sctx.sc_sid = SID_CARG;
   2010  current_sctx.sc_seq = 0;
   2011  for (int i = 0; i < parmp->n_commands; i++) {
   2012    do_cmdline_cmd(parmp->commands[i]);
   2013    if (parmp->cmds_tofree[i]) {
   2014      xfree(parmp->commands[i]);
   2015    }
   2016  }
   2017  ESTACK_CHECK_NOW;
   2018  estack_pop();
   2019  current_sctx.sc_sid = 0;
   2020  if (curwin->w_cursor.lnum == 0) {
   2021    curwin->w_cursor.lnum = 1;
   2022  }
   2023 
   2024  if (!exmode_active) {
   2025    msg_scroll = false;
   2026  }
   2027 
   2028  // When started with "-q errorfile" jump to first error again.
   2029  if (parmp->edit_type == EDIT_QF) {
   2030    qf_jump(NULL, 0, 0, false);
   2031  }
   2032  TIME_MSG("executing command arguments");
   2033 }
   2034 
   2035 /// Source system-wide vimrc if built with one defined
   2036 ///
   2037 /// Does one of the following things, stops after whichever succeeds:
   2038 ///
   2039 /// 1. Source system vimrc file from $XDG_CONFIG_DIRS/$NVIM_APPNAME/sysinit.vim
   2040 /// 2. Source system vimrc file from $VIM
   2041 static void do_system_initialization(void)
   2042 {
   2043  char *const config_dirs = stdpaths_get_xdg_var(kXDGConfigDirs);
   2044  if (config_dirs != NULL) {
   2045    const void *iter = NULL;
   2046    const char *appname = get_appname(false);
   2047    size_t appname_len = strlen(appname);
   2048    const char sysinit_suffix[] = {
   2049      PATHSEP,
   2050      's', 'y', 's', 'i', 'n', 'i', 't', '.', 'v', 'i', 'm', NUL
   2051    };
   2052    do {
   2053      const char *dir;
   2054      size_t dir_len;
   2055      iter = vim_env_iter(':', config_dirs, iter, &dir, &dir_len);
   2056      if (dir == NULL || dir_len == 0) {
   2057        break;
   2058      }
   2059      size_t path_len = dir_len + 1 + appname_len + sizeof(sysinit_suffix);
   2060      char *vimrc = xmalloc(path_len);
   2061      memcpy(vimrc, dir, dir_len);
   2062      if (vimrc[dir_len - 1] != PATHSEP) {
   2063        vimrc[dir_len] = PATHSEP;
   2064        dir_len += 1;
   2065      }
   2066      memcpy(vimrc + dir_len, appname, appname_len);
   2067      memcpy(vimrc + dir_len + appname_len, sysinit_suffix, sizeof(sysinit_suffix));
   2068      if (do_source(vimrc, false, DOSO_NONE, NULL) != FAIL) {
   2069        xfree(vimrc);
   2070        xfree(config_dirs);
   2071        return;
   2072      }
   2073      xfree(vimrc);
   2074    } while (iter != NULL);
   2075    xfree(config_dirs);
   2076  }
   2077 
   2078 #ifdef SYS_VIMRC_FILE
   2079  // Get system wide defaults, if the file name is defined.
   2080  do_source(SYS_VIMRC_FILE, false, DOSO_NONE, NULL);
   2081 #endif
   2082 }
   2083 
   2084 /// Source vimrc or do other user initialization
   2085 ///
   2086 /// Does one of the following things, stops after whichever succeeds:
   2087 ///
   2088 /// 1. Execution of VIMINIT environment variable.
   2089 /// 2. Sourcing user config file ($XDG_CONFIG_HOME/$NVIM_APPNAME/init.lua or init.vim).
   2090 /// 3. Sourcing other config files ($XDG_CONFIG_DIRS[1]/$NVIM_APPNAME/init.lua or init.vim, …).
   2091 /// 4. Execution of EXINIT environment variable.
   2092 ///
   2093 /// @return True if it is needed to attempt to source exrc file according to
   2094 ///         'exrc' option definition.
   2095 static bool do_user_initialization(void)
   2096  FUNC_ATTR_WARN_UNUSED_RESULT
   2097 {
   2098  bool do_exrc = p_exrc;
   2099  if (execute_env("VIMINIT") == OK) {
   2100    do_exrc = p_exrc;
   2101    return do_exrc;
   2102  }
   2103 
   2104  char *init_lua_path = stdpaths_user_conf_subpath("init.lua");
   2105  char *user_vimrc = stdpaths_user_conf_subpath("init.vim");
   2106 
   2107  // init.lua
   2108  if (os_path_exists(init_lua_path)
   2109      && do_source(init_lua_path, true, DOSO_VIMRC, NULL)) {
   2110    if (os_path_exists(user_vimrc)) {
   2111      semsg(e_conflicting_configs, init_lua_path, user_vimrc);
   2112    }
   2113 
   2114    xfree(user_vimrc);
   2115    xfree(init_lua_path);
   2116    do_exrc = p_exrc;
   2117    return do_exrc;
   2118  }
   2119  xfree(init_lua_path);
   2120 
   2121  // init.vim
   2122  if (do_source(user_vimrc, true, DOSO_VIMRC, NULL) != FAIL) {
   2123    do_exrc = p_exrc;
   2124    if (do_exrc) {
   2125      do_exrc = (path_full_compare(VIMRC_FILE, user_vimrc, false, true) != kEqualFiles);
   2126    }
   2127    xfree(user_vimrc);
   2128    return do_exrc;
   2129  }
   2130  xfree(user_vimrc);
   2131 
   2132  char *const config_dirs = stdpaths_get_xdg_var(kXDGConfigDirs);
   2133  if (config_dirs != NULL) {
   2134    const char *appname = get_appname(false);
   2135    size_t appname_len = strlen(appname);
   2136 
   2137    const void *iter = NULL;
   2138    do {
   2139      const char *dir;
   2140      size_t dir_len;
   2141      iter = vim_env_iter(':', config_dirs, iter, &dir, &dir_len);
   2142      if (dir == NULL || dir_len == 0) {
   2143        break;
   2144      }
   2145 
   2146      // Build: <xdg_dir>/<appname>/init.lua
   2147      const char init_lua_suffix[] = { PATHSEP, 'i', 'n', 'i', 't', '.', 'l', 'u', 'a', NUL };
   2148      size_t init_lua_len = dir_len + 1 + appname_len + sizeof(init_lua_suffix);
   2149      char *init_lua = xmalloc(init_lua_len);
   2150      memcpy(init_lua, dir, dir_len);
   2151      init_lua[dir_len] = PATHSEP;
   2152      memcpy(init_lua + dir_len + 1, appname, appname_len);
   2153      memcpy(init_lua + dir_len + 1 + appname_len, init_lua_suffix, sizeof(init_lua_suffix));
   2154 
   2155      // Build: <xdg_dir>/<appname>/init.vim
   2156      const char init_vim_suffix[] = { PATHSEP, 'i', 'n', 'i', 't', '.', 'v', 'i', 'm', NUL };
   2157      size_t init_vim_len = dir_len + 1 + appname_len + sizeof(init_vim_suffix);
   2158      char *init_vim = xmalloc(init_vim_len);
   2159      memcpy(init_vim, dir, dir_len);
   2160      init_vim[dir_len] = PATHSEP;
   2161      memcpy(init_vim + dir_len + 1, appname, appname_len);
   2162      memcpy(init_vim + dir_len + 1 + appname_len, init_vim_suffix, sizeof(init_vim_suffix));
   2163 
   2164      if (os_path_exists(init_lua)
   2165          && do_source(init_lua, true, DOSO_VIMRC, NULL)) {
   2166        if (os_path_exists(init_vim)) {
   2167          semsg(e_conflicting_configs, init_lua, init_vim);
   2168        }
   2169 
   2170        xfree(init_vim);
   2171        xfree(init_lua);
   2172        xfree(config_dirs);
   2173        do_exrc = p_exrc;
   2174        return do_exrc;
   2175      }
   2176      xfree(init_lua);
   2177 
   2178      if (do_source(init_vim, true, DOSO_VIMRC, NULL) != FAIL) {
   2179        do_exrc = p_exrc;
   2180        if (do_exrc) {
   2181          do_exrc = (path_full_compare(VIMRC_FILE, init_vim, false, true) != kEqualFiles);
   2182        }
   2183        xfree(init_vim);
   2184        xfree(config_dirs);
   2185        return do_exrc;
   2186      }
   2187      xfree(init_vim);
   2188    } while (iter != NULL);
   2189    xfree(config_dirs);
   2190  }
   2191 
   2192  if (execute_env("EXINIT") == OK) {
   2193    do_exrc = p_exrc;
   2194    return do_exrc;
   2195  }
   2196  return do_exrc;
   2197 }
   2198 
   2199 // Read initialization commands from ".nvim.lua", ".nvimrc", or ".exrc" in
   2200 // current directory and all parent directories.  This is only done if the 'exrc'
   2201 // option is set. Only do this if VIMRC_FILE is not the same as vimrc file
   2202 // sourced in do_user_initialization.
   2203 static void do_exrc_initialization(void)
   2204 {
   2205  lua_State *const L = get_global_lstate();
   2206  assert(L);
   2207 
   2208  lua_getglobal(L, "require");
   2209  lua_pushstring(L, "vim._core.exrc");
   2210  if (nlua_pcall(L, 1, 0)) {
   2211    fprintf(stderr, "%s\n", lua_tostring(L, -1));
   2212  }
   2213 }
   2214 
   2215 /// Source startup scripts
   2216 static void source_startup_scripts(const mparm_T *const parmp)
   2217  FUNC_ATTR_NONNULL_ALL
   2218 {
   2219  // If -u given, use only the initializations from that file and nothing else.
   2220  if (parmp->use_vimrc != NULL) {
   2221    if (strequal(parmp->use_vimrc, "NONE") || strequal(parmp->use_vimrc, "NORC")) {
   2222      // Do nothing.
   2223    } else {
   2224      if (do_source(parmp->use_vimrc, false, DOSO_NONE, NULL) != OK) {
   2225        semsg(_(e_cannot_read_from_str_2), parmp->use_vimrc);
   2226      }
   2227    }
   2228  } else if (!silent_mode) {
   2229    do_system_initialization();
   2230 
   2231    if (do_user_initialization()) {
   2232      do_exrc_initialization();
   2233    }
   2234  }
   2235  TIME_MSG("sourcing vimrc file(s)");
   2236 }
   2237 
   2238 /// Get an environment variable, and execute it as Ex commands.
   2239 ///
   2240 /// @param env         environment variable to execute
   2241 ///
   2242 /// @return FAIL if the environment variable was not executed,
   2243 ///         OK otherwise.
   2244 static int execute_env(char *env)
   2245  FUNC_ATTR_NONNULL_ALL
   2246 {
   2247  ESTACK_CHECK_DECLARATION;
   2248  char *initstr = os_getenv(env);
   2249  if (initstr == NULL) {
   2250    return FAIL;
   2251  }
   2252 
   2253  estack_push(ETYPE_ENV, env, 0);
   2254  ESTACK_CHECK_SETUP;
   2255  const sctx_T save_current_sctx = current_sctx;
   2256  current_sctx.sc_sid = SID_ENV;
   2257  current_sctx.sc_seq = 0;
   2258  current_sctx.sc_lnum = 0;
   2259  do_cmdline_cmd(initstr);
   2260 
   2261  ESTACK_CHECK_NOW;
   2262  estack_pop();
   2263  current_sctx = save_current_sctx;
   2264 
   2265  xfree(initstr);
   2266  return OK;
   2267 }
   2268 
   2269 /// Prints a message of the form "{msg1}: {msg2}: {msg3}", then exits with code 1.
   2270 ///
   2271 /// @param msg1  error message
   2272 /// @param msg2  extra message, or NULL
   2273 /// @param msg3  extra message, or NULL
   2274 static void mainerr(const char *msg1, const char *msg2, const char *msg3)
   2275  FUNC_ATTR_NORETURN
   2276 {
   2277  print_mainerr(msg1, msg2, msg3);
   2278  os_exit(1);
   2279 }
   2280 
   2281 static void print_mainerr(const char *msg1, const char *msg2, const char *msg3)
   2282 {
   2283  char *prgname = path_tail(argv0);
   2284 
   2285  signal_stop();              // kill us with CTRL-C here, if you like
   2286 
   2287  fprintf(stderr, "%s: %s", prgname, _(msg1));
   2288  if (msg2 != NULL) {
   2289    fprintf(stderr, ": \"%s\"", msg2);
   2290  }
   2291  if (msg3 != NULL) {
   2292    fprintf(stderr, ": \"%s\"", msg3);
   2293  }
   2294  fprintf(stderr, _("\nMore info with \""));
   2295  fprintf(stderr, "%s -h\"\n", prgname);
   2296 }
   2297 
   2298 /// Prints version information for "nvim -v" or "nvim --version".
   2299 static void version(void)
   2300 {
   2301  // TODO(bfred): not like this?
   2302  nlua_init(NULL, 0, -1);
   2303  info_message = true;  // use stdout, not stderr
   2304  list_version();
   2305  msg_putchar('\n');
   2306  msg_didout = false;
   2307 }
   2308 
   2309 /// Prints help message for "nvim -h" or "nvim --help".
   2310 static void usage(void)
   2311 {
   2312  signal_stop();              // kill us with CTRL-C here, if you like
   2313 
   2314  printf(_("Usage:\n"));
   2315  printf(_("  nvim [options] [file ...]\n"));
   2316  printf(_("\nOptions:\n"));
   2317  printf(_("  --cmd <cmd>           Execute <cmd> before any config\n"));
   2318  printf(_("  +<cmd>, -c <cmd>      Execute <cmd> after config and first file\n"));
   2319  printf(_("  -l <script> [args...] Execute Lua <script> (with optional args)\n"));
   2320  printf(_("  -S <session>          Source <session> after loading the first file\n"));
   2321  printf(_("  -s <scriptin>         Read Normal mode commands from <scriptin>\n"));
   2322  printf(_("  -u <config>           Use this config file\n"));
   2323  printf("\n");
   2324  printf(_("  -d                    Diff mode\n"));
   2325  printf(_("  -es, -Es              Silent (batch) mode\n"));
   2326  printf(_("  -h, --help            Print this help message\n"));
   2327  printf(_("  -i <shada>            Use this shada file\n"));
   2328  printf(_("  -n                    No swap file, use memory only\n"));
   2329  printf(_("  -o[N]                 Open N windows (default: one per file)\n"));
   2330  printf(_("  -O[N]                 Open N vertical windows (default: one per file)\n"));
   2331  printf(_("  -p[N]                 Open N tab pages (default: one per file)\n"));
   2332  printf(_("  -R                    Read-only (view) mode\n"));
   2333  printf(_("  -v, --version         Print version information\n"));
   2334  printf(_("  -V[N][file]           Verbose [level][file]\n"));
   2335  printf("\n");
   2336  printf(_("  --                    Only file names after this\n"));
   2337  printf(_("  --api-info            Write msgpack-encoded API metadata to stdout\n"));
   2338  printf(_("  --clean               \"Factory defaults\" (skip user config and plugins, shada)\n"));
   2339  printf(_("  --embed               Use stdin/stdout as a msgpack-rpc channel\n"));
   2340  printf(_("  --headless            Don't start a user interface\n"));
   2341  printf(_("  --listen <address>    Serve RPC API from this address\n"));
   2342  printf(_("  --remote[-subcommand] Execute commands remotely on a server\n"));
   2343  printf(_("  --server <address>    Connect to this Nvim server\n"));
   2344  printf(_("  --startuptime <file>  Write startup timing messages to <file>\n"));
   2345  printf(_("\nSee \":help startup-options\" for all options.\n"));
   2346 }
   2347 
   2348 // Check the result of the ATTENTION dialog:
   2349 // When "Quit" selected, exit Vim.
   2350 // When "Recover" selected, recover the file.
   2351 static void check_swap_exists_action(void)
   2352 {
   2353  if (swap_exists_action == SEA_QUIT) {
   2354    ui_call_error_exit(1);
   2355    getout(1);
   2356  }
   2357  handle_swap_exists(NULL);
   2358 }
   2359 
   2360 #ifdef ENABLE_ASAN_UBSAN
   2361 const char *__ubsan_default_options(void)
   2362 {
   2363  return "print_stacktrace=1";
   2364 }
   2365 
   2366 const char *__asan_default_options(void)
   2367 {
   2368  return "handle_abort=1,handle_sigill=1";
   2369 }
   2370 #endif