diff options
Diffstat (limited to 'src/nvim/main.c')
-rw-r--r-- | src/nvim/main.c | 1266 |
1 files changed, 699 insertions, 567 deletions
diff --git a/src/nvim/main.c b/src/nvim/main.c index 64b5de8663..17f3a894d4 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -1,3 +1,6 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check +// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + #define EXTERN #include <assert.h> #include <stdint.h> @@ -20,6 +23,7 @@ #include "nvim/fold.h" #include "nvim/getchar.h" #include "nvim/hashtab.h" +#include "nvim/highlight.h" #include "nvim/iconv.h" #include "nvim/if_cscope.h" #ifdef HAVE_LOCALE_H @@ -30,7 +34,6 @@ #include "nvim/memline.h" #include "nvim/message.h" #include "nvim/misc1.h" -#include "nvim/misc2.h" #include "nvim/garray.h" #include "nvim/log.h" #include "nvim/memory.h" @@ -55,58 +58,59 @@ #include "nvim/os/input.h" #include "nvim/os/os.h" #include "nvim/os/time.h" +#include "nvim/os/fileio.h" #include "nvim/event/loop.h" #include "nvim/os/signal.h" #include "nvim/event/process.h" -#include "nvim/msgpack_rpc/defs.h" #include "nvim/msgpack_rpc/helpers.h" #include "nvim/msgpack_rpc/server.h" #include "nvim/msgpack_rpc/channel.h" +#include "nvim/api/ui.h" #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" #include "nvim/api/private/handle.h" +#include "nvim/api/private/dispatch.h" +#ifndef WIN32 +# include "nvim/os/pty_process_unix.h" +#endif +#include "nvim/api/vim.h" -/* Maximum number of commands from + or -c arguments. */ +// Maximum number of commands from + or -c arguments. #define MAX_ARG_CMDS 10 -/* values for "window_layout" */ -#define WIN_HOR 1 /* "-o" horizontally split windows */ -#define WIN_VER 2 /* "-O" vertically split windows */ -#define WIN_TABS 3 /* "-p" windows on tab pages */ +// values for "window_layout" +#define WIN_HOR 1 // "-o" horizontally split windows +#define WIN_VER 2 // "-O" vertically split windows +#define WIN_TABS 3 // "-p" windows on tab pages -/* Struct for various parameters passed between main() and other functions. */ +// Struct for various parameters passed between main() and other functions. typedef struct { int argc; char **argv; char *use_vimrc; // vimrc from -u argument - int n_commands; /* no. of commands from + or -c */ + int n_commands; // no. of commands from + or -c char *commands[MAX_ARG_CMDS]; // commands from + or -c arg - char_u cmds_tofree[MAX_ARG_CMDS]; /* commands that need free() */ - int n_pre_commands; /* no. of commands from --cmd */ + char_u cmds_tofree[MAX_ARG_CMDS]; // commands that need free() + int n_pre_commands; // no. of commands from --cmd char *pre_commands[MAX_ARG_CMDS]; // commands from --cmd argument - int edit_type; /* type of editing to do */ - char_u *tagname; /* tag from -t argument */ - char_u *use_ef; /* 'errorfile' from -q argument */ + int edit_type; // type of editing to do + char_u *tagname; // tag from -t argument + char_u *use_ef; // 'errorfile' from -q argument - int want_full_screen; bool input_isatty; // stdin is a terminal bool output_isatty; // stdout is a terminal bool err_isatty; // stderr is a terminal - bool headless; // Dont try to start an user interface - // or read/write to stdio(unless - // embedding) - int no_swap_file; /* "-n" argument used */ + int no_swap_file; // "-n" argument used int use_debug_break_level; - int window_count; /* number of windows to use */ - int window_layout; /* 0, WIN_HOR, WIN_VER or WIN_TABS */ + int window_count; // number of windows to use + int window_layout; // 0, WIN_HOR, WIN_VER or WIN_TABS -#if !defined(UNIX) - int literal; /* don't expand file names */ -#endif - int diff_mode; /* start with 'diff' set */ + int diff_mode; // start with 'diff' set + + char *listen_addr; // --listen {address} } mparm_T; /* Values for edit_type. */ @@ -122,7 +126,7 @@ typedef struct { Loop main_loop; -static char *argv0; +static char *argv0 = NULL; // Error messages static const char *err_arg_missing = N_("Argument missing after"); @@ -147,17 +151,21 @@ void event_init(void) signal_init(); // finish mspgack-rpc initialization channel_init(); - server_init(); + remote_ui_init(); + api_vim_init(); terminal_init(); } -void event_teardown(void) +/// @returns false if main_loop could not be closed gracefully +bool event_teardown(void) { if (!main_loop.events) { - return; + input_stop(); + return true; } - queue_process_events(main_loop.events); + multiqueue_process_events(main_loop.events); + loop_poll_events(&main_loop, 0); // Drain thread_events, fast_events. input_stop(); channel_teardown(); process_teardown(&main_loop); @@ -166,7 +174,7 @@ void event_teardown(void) signal_teardown(); terminal_teardown(); - loop_close(&main_loop); + return loop_close(&main_loop, true); } /// Performs early initialization. @@ -177,12 +185,10 @@ void early_init(void) log_init(); fs_init(); handle_init(); - - (void)mb_init(); // init mb_bytelen_tab[] to ones eval_init(); // init global variables - - // Init the table of Normal mode commands. - init_normal_cmds(); + init_path(argv0 ? argv0 : "nvim"); + init_normal_cmds(); // Init the table of Normal mode commands. + highlight_init(); #if defined(HAVE_LOCALE_H) // Setup to use the current locale (for ctype() and many other things). @@ -216,21 +222,32 @@ void early_init(void) #ifdef MAKE_LIB int nvim_main(int argc, char **argv) +#elif defined(WIN32) +int wmain(int argc, wchar_t **argv_w) // multibyte args on Windows. #7060 #else int main(int argc, char **argv) #endif { - argv0 = (char *)path_tail((char_u *)argv[0]); +#if defined(WIN32) && !defined(MAKE_LIB) + char **argv = xmalloc((size_t)argc * sizeof(char *)); + for (int i = 0; i < argc; i++) { + char *buf = NULL; + utf16_to_utf8(argv_w[i], &buf); + assert(buf); + argv[i] = buf; + } +#endif + + argv0 = argv[0]; char_u *fname = NULL; // file name from command line mparm_T params; // various parameters passed between // main() and other functions. - char_u *cwd = NULL; // current workding dir on startup + char_u *cwd = NULL; // current working dir on startup time_init(); - /* Many variables are in "params" so that we can pass them to invoked - * functions without a lot of arguments. "argc" and "argv" are also - * copied, so that they can be changed. */ + // Many variables are in `params` so that we can pass them around easily. + // `argc` and `argv` are also copied, so that they can be changed. init_params(¶ms, argc, argv); init_startuptime(¶ms); @@ -240,19 +257,23 @@ int main(int argc, char **argv) // Check if we have an interactive window. check_and_set_isatty(¶ms); - // Get the name with which Nvim was invoked, with and without path. - set_vim_var_string(VV_PROGPATH, argv[0], -1); - set_vim_var_string(VV_PROGNAME, (char *) path_tail((char_u *) argv[0]), -1); - event_init(); - /* - * Process the command line arguments. File names are put in the global - * argument list "global_alist". - */ + // Process the command line arguments. File names are put in the global + // argument list "global_alist". command_line_scan(¶ms); - if (GARGCOUNT > 0) - fname = get_fname(¶ms); + if (embedded_mode) { + const char *err; + if (!channel_from_stdio(true, CALLBACK_READER_INIT, &err)) { + abort(); + } + } + + server_init(params.listen_addr); + + if (GARGCOUNT > 0) { + fname = get_fname(¶ms, cwd); + } TIME_MSG("expanding arguments"); @@ -262,57 +283,53 @@ int main(int argc, char **argv) /* Don't redraw until much later. */ ++RedrawingDisabled; - /* - * When listing swap file names, don't do cursor positioning et. al. - */ - if (recoverymode && fname == NULL) - params.want_full_screen = FALSE; - setbuf(stdout, NULL); - /* This message comes before term inits, but after setting "silent_mode" - * when the input is not a tty. */ - if (GARGCOUNT > 1 && !silent_mode) - printf(_("%d files to edit\n"), GARGCOUNT); + full_screen = !silent_mode; - full_screen = true; - check_tty(¶ms); - - /* - * Set the default values for the options that use Rows and Columns. - */ + // Set the default values for the options that use Rows and Columns. win_init_size(); // Set the 'diff' option now, so that it can be checked for in a vimrc // file. There is no buffer yet though. - if (params.diff_mode) - diff_win_options(firstwin, FALSE); + if (params.diff_mode) { + diff_win_options(firstwin, false); + } assert(p_ch >= 0 && Rows >= p_ch && Rows - p_ch <= INT_MAX); cmdline_row = (int)(Rows - p_ch); msg_row = cmdline_row; - screenalloc(false); /* allocate screen buffers */ - set_init_2(); + screenalloc(false); // allocate screen buffers + set_init_2(headless_mode); TIME_MSG("inits 2"); - msg_scroll = TRUE; - no_wait_return = TRUE; + msg_scroll = true; + no_wait_return = true; - init_highlight(TRUE, FALSE); /* set the default highlight groups */ + init_highlight(true, false); // Default highlight groups. TIME_MSG("init highlight"); - /* Set the break level after the terminal is initialized. */ + // Set the break level after the terminal is initialized. debug_break_level = params.use_debug_break_level; - bool reading_input = !params.headless && (params.input_isatty - || params.output_isatty || params.err_isatty); - - if (reading_input) { + // + // Read user-input if any TTY is connected. + // Read ex-commands if invoked with "-es". + // + bool reading_tty = !headless_mode + && !embedded_mode + && !silent_mode + && (params.input_isatty || params.output_isatty + || params.err_isatty); + bool reading_excmds = !params.input_isatty + && silent_mode + && exmode_active == EXMODE_NORMAL; + if (reading_tty || reading_excmds) { // One of the startup commands (arguments, sourced scripts or plugins) may // prompt the user, so start reading from a tty now. - int fd = fileno(stdin); - if (!params.input_isatty || params.edit_type == EDIT_STDIN) { - // Use stderr or stdout since stdin is not a tty and/or could be used to - // read the "-" file (eg: cat file | nvim -) + int fd = STDIN_FILENO; + if (!silent_mode + && (!params.input_isatty || params.edit_type == EDIT_STDIN)) { + // Use stderr or stdout since stdin is being used to read commands. fd = params.err_isatty ? fileno(stderr) : fileno(stdout); } input_start(fd); @@ -320,24 +337,50 @@ int main(int argc, char **argv) // open terminals when opening files that start with term:// #define PROTO "term://" + do_cmdline_cmd("augroup nvim_terminal"); + do_cmdline_cmd("autocmd!"); do_cmdline_cmd("autocmd BufReadCmd " PROTO "* nested " - ":call termopen( " + ":if !exists('b:term_title')|call termopen( " // Capture the command string "matchstr(expand(\"<amatch>\"), " "'\\c\\m" PROTO "\\%(.\\{-}//\\%(\\d\\+:\\)\\?\\)\\?\\zs.*'), " // capture the working directory "{'cwd': get(matchlist(expand(\"<amatch>\"), " - "'\\c\\m" PROTO "\\(.\\{-}\\)//'), 1, '')})"); + "'\\c\\m" PROTO "\\(.\\{-}\\)//'), 1, '')})" + "|endif"); + do_cmdline_cmd("augroup END"); #undef PROTO - /* Execute --cmd arguments. */ + // Reset 'loadplugins' for "-u NONE" before "--cmd" arguments. + // Allows for setting 'loadplugins' there. + if (params.use_vimrc != NULL && strequal(params.use_vimrc, "NONE")) { + p_lpl = false; + } + + // give embedders a chance to set up nvim, by processing a request before + // startup. This allows an external UI to show messages and prompts from + // --cmd and buffer loading (e.g. swap files) + bool early_ui = false; + if (embedded_mode && !headless_mode) { + TIME_MSG("waiting for embedder to make request"); + remote_ui_wait_for_attach(); + TIME_MSG("done waiting for embedder"); + + // prepare screen now, so external UIs can display messages + starting = NO_BUFFERS; + screenclear(); + early_ui = true; + TIME_MSG("initialized screen early for embedder"); + } + + // Execute --cmd arguments. exe_pre_commands(¶ms); - /* Source startup scripts. */ + // Source startup scripts. source_startup_scripts(¶ms); // If using the runtime (-u is not NONE), enable syntax & filetype plugins. - if (params.use_vimrc == NULL || strcmp(params.use_vimrc, "NONE") != 0) { + if (params.use_vimrc == NULL || !strequal(params.use_vimrc, "NONE")) { // Does ":filetype plugin indent on". filetype_maybe_enable(); // Sources syntax/syntax.vim, which calls `:filetype on`. @@ -363,17 +406,21 @@ int main(int argc, char **argv) mch_exit(0); } - // Set a few option defaults after reading vimrc files: - // 'title' and 'icon', Unix: 'shellpipe' and 'shellredir'. + // Set a few option defaults after reading vimrc files: 'title', 'icon', + // 'shellpipe', 'shellredir'. set_init_3(); TIME_MSG("inits 3"); - /* - * "-n" argument: Disable swap file by setting 'updatecount' to 0. - * Note that this overrides anything from a vimrc file. - */ - if (params.no_swap_file) + // "-n" argument: Disable swap file by setting 'updatecount' to 0. + // Note that this overrides anything from a vimrc file. + if (params.no_swap_file) { p_uc = 0; + } + + // XXX: Minimize 'updatetime' for -es/-Es. #7679 + if (silent_mode) { + p_ut = 1; + } if (curwin->w_p_rl && p_altkeymap) { p_hkmap = FALSE; /* Reset the Hebrew keymap mode */ @@ -389,9 +436,10 @@ int main(int argc, char **argv) shada_read_everything(NULL, false, true); TIME_MSG("reading ShaDa"); } - /* It's better to make v:oldfiles an empty list than NULL. */ - if (get_vim_var_list(VV_OLDFILES) == NULL) - set_vim_var_list(VV_OLDFILES, list_alloc()); + // It's better to make v:oldfiles an empty list than NULL. + if (get_vim_var_list(VV_OLDFILES) == NULL) { + set_vim_var_list(VV_OLDFILES, tv_list_alloc(0)); + } /* * "-q errorfile": Load the error file now. @@ -405,48 +453,48 @@ int main(int argc, char **argv) * Clear screen now, so file message will not be cleared. */ starting = NO_BUFFERS; - no_wait_return = FALSE; - if (!exmode_active) - msg_scroll = FALSE; + no_wait_return = false; + if (!exmode_active) { + msg_scroll = false; + } - /* - * If "-" argument given: Read file from stdin. - * Do this before starting Raw mode, because it may change things that the - * writing end of the pipe doesn't like, e.g., in case stdin and stderr - * are the same terminal: "cat | vim -". - * Using autocommands here may cause trouble... - */ - if (params.edit_type == EDIT_STDIN && !recoverymode) + // Read file (text, not commands) from stdin if: + // - stdin is not a tty + // - and -e/-es was not given + // + // Do this before starting Raw mode, because it may change things that the + // writing end of the pipe doesn't like, e.g., in case stdin and stderr + // are the same terminal: "cat | vim -". + // Using autocommands here may cause trouble... + if (params.edit_type == EDIT_STDIN && !recoverymode) { read_stdin(); + } - - if (reading_input && (need_wait_return || msg_didany)) { - // Since at this point there's no UI instance running yet, error messages - // would have been printed to stdout. Before starting (which can result in - // a alternate screen buffer being shown) we need confirmation that the - // user has seen the messages and that is done with a call to wait_return. + if (reading_tty && (need_wait_return || msg_didany)) { + // Because there's no UI yet, error messages would have been printed to + // stdout. Before starting we need confirmation that the user has seen the + // messages and that is done with a call to wait_return. TIME_MSG("waiting for return"); - wait_return(TRUE); + wait_return(true); } - if (!params.headless) { - // Stop reading from input stream, the UI layer will take over now. - input_stop(); + if (!headless_mode && !embedded_mode && !silent_mode) { + input_stop(); // Stop reading input, let the UI take over. ui_builtin_start(); } setmouse(); // may start using the mouse - ui_reset_scroll_region(); // In case Rows changed - // Don't clear the screen when starting in Ex mode, unless using the GUI. - if (exmode_active) + if (exmode_active || early_ui) { + // Don't clear the screen when starting in Ex mode, or when an + // embedding UI might have displayed messages must_redraw = CLEAR; - else { - screenclear(); /* clear screen */ + } else { + screenclear(); // clear screen TIME_MSG("clearing screen"); } - no_wait_return = TRUE; + no_wait_return = true; /* * Create the requested number of windows and edit buffers in them. @@ -513,9 +561,16 @@ int main(int argc, char **argv) if (p_im) need_start_insertmode = TRUE; - apply_autocmds(EVENT_VIMENTER, NULL, NULL, FALSE, curbuf); + set_vim_var_nr(VV_VIM_DID_ENTER, 1L); + apply_autocmds(EVENT_VIMENTER, NULL, NULL, false, curbuf); TIME_MSG("VimEnter autocommands"); + // Adjust default register name for "unnamed" in 'clipboard'. Can only be + // done after the clipboard is available and all initial commands that may + // modify the 'clipboard' setting have run; i.e. just before entering the + // main loop. + set_reg_var(get_default_register_name()); + /* When a startup script or session file setup for diff'ing and * scrollbind, sync the scrollbind now. */ if (curwin->w_p_diff && curwin->w_p_scb) { @@ -535,18 +590,22 @@ int main(int argc, char **argv) } TIME_MSG("before starting main loop"); - ILOG("Starting Neovim main loop."); + ILOG("starting main loop"); /* * Call the main command loop. This never returns. */ normal_enter(false, false); +#if defined(WIN32) && !defined(MAKE_LIB) + xfree(argv); +#endif return 0; } -/* Exit properly */ +/// Exit properly void getout(int exitval) + FUNC_ATTR_NORETURN { tabpage_T *tp, *next_tp; @@ -558,6 +617,8 @@ void getout(int exitval) if (exmode_active) exitval += ex_exitval; + set_vim_var_nr(VV_EXITING, exitval); + /* Position the cursor on the last screen line, below all the text */ ui_cursor_goto((int)Rows - 1, 0); @@ -575,11 +636,16 @@ void getout(int exitval) } buf_T *buf = wp->w_buffer; - if (buf->b_changedtick != -1) { + if (buf_get_changedtick(buf) != -1) { + bufref_T bufref; + + set_bufref(&bufref, buf); apply_autocmds(EVENT_BUFWINLEAVE, buf->b_fname, - buf->b_fname, FALSE, buf); - buf->b_changedtick = -1; /* note that we did it already */ - /* start all over, autocommands may mess up the lists */ + buf->b_fname, false, buf); + if (bufref_valid(&bufref)) { + buf_set_changedtick(buf, -1); // note that we did it already + } + // start all over, autocommands may mess up the lists next_tp = first_tabpage; break; } @@ -589,10 +655,13 @@ void getout(int exitval) /* Trigger BufUnload for buffers that are loaded */ FOR_ALL_BUFFERS(buf) { if (buf->b_ml.ml_mfp != NULL) { - apply_autocmds(EVENT_BUFUNLOAD, buf->b_fname, buf->b_fname, - FALSE, buf); - if (!buf_valid(buf)) /* autocmd may delete the buffer */ + bufref_T bufref; + set_bufref(&bufref, buf); + apply_autocmds(EVENT_BUFUNLOAD, buf->b_fname, buf->b_fname, false, buf); + if (!bufref_valid(&bufref)) { + // Autocmd deleted the buffer. break; + } } } apply_autocmds(EVENT_VIMLEAVEPRE, NULL, NULL, FALSE, curbuf); @@ -618,12 +687,18 @@ void getout(int exitval) /* Position the cursor again, the autocommands may have moved it */ ui_cursor_goto((int)Rows - 1, 0); + // Apply 'titleold'. + if (p_title && *p_titleold != NUL) { + ui_call_set_title(cstr_as_string((char *)p_titleold)); + } + #if defined(USE_ICONV) && defined(DYNAMIC_ICONV) iconv_end(); #endif cs_end(); - if (garbage_collect_at_exit) - garbage_collect(); + if (garbage_collect_at_exit) { + garbage_collect(false); + } mch_exit(exitval); } @@ -641,8 +716,9 @@ void getout(int exitval) /// /// @return argument's numeric value otherwise static int get_number_arg(const char *p, int *idx, int def) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { - if (ascii_isdigit(p[*idx])) { + if (ascii_isdigit(p[*idx])) { // -V522 def = atoi(&(p[*idx])); while (ascii_isdigit(p[*idx])) { *idx = *idx + 1; @@ -652,9 +728,7 @@ static int get_number_arg(const char *p, int *idx, int def) } #if defined(HAVE_LOCALE_H) -/* - * Setup to use the current locale (for ctype() and many other things). - */ +/// Setup to use the current locale (for ctype() and many other things). static void init_locale(void) { setlocale(LC_ALL, ""); @@ -670,8 +744,8 @@ static void init_locale(void) { char_u *p; - /* expand_env() doesn't work yet, because chartab[] is not initialized - * yet, call vim_getenv() directly */ + // expand_env() doesn't work yet, because g_chartab[] is not + // initialized yet, call vim_getenv() directly p = (char_u *)vim_getenv("VIMRUNTIME"); if (p != NULL && *p != NUL) { vim_snprintf((char *)NameBuff, MAXPATHL, "%s/lang", p); @@ -685,63 +759,72 @@ static void init_locale(void) } #endif +/// Decides whether text (as opposed to commands) will be read from stdin. +/// @see EDIT_STDIN +static bool edit_stdin(bool explicit, mparm_T *parmp) +{ + bool implicit = !headless_mode + && !embedded_mode + && exmode_active != EXMODE_NORMAL // -E/-Es but not -e/-es. + && !parmp->input_isatty + && scriptin[0] == NULL; // `-s -` was not given. + return explicit || implicit; +} -/* - * Scan the command line arguments. - */ +/// Scan the command line arguments. static void command_line_scan(mparm_T *parmp) { int argc = parmp->argc; - char **argv = parmp->argv; - int argv_idx; /* index in argv[n][] */ - int had_minmin = FALSE; /* found "--" argument */ - int want_argument; /* option argument with argument */ + char **argv = parmp->argv; + int argv_idx; // index in argv[n][] + bool had_stdin_file = false; // found explicit "-" argument + bool had_minmin = false; // found "--" argument + int want_argument; // option argument with argument int c; - char_u *p = NULL; long n; - --argc; - ++argv; - argv_idx = 1; /* active option letter is argv[0][argv_idx] */ + argc--; + argv++; + argv_idx = 1; // active option letter is argv[0][argv_idx] while (argc > 0) { - /* - * "+" or "+{number}" or "+/{pat}" or "+{command}" argument. - */ + // "+" or "+{number}" or "+/{pat}" or "+{command}" argument. if (argv[0][0] == '+' && !had_minmin) { - if (parmp->n_commands >= MAX_ARG_CMDS) + if (parmp->n_commands >= MAX_ARG_CMDS) { mainerr(err_extra_cmd, NULL); - argv_idx = -1; /* skip to next argument */ - if (argv[0][1] == NUL) + } + argv_idx = -1; // skip to next argument + if (argv[0][1] == NUL) { parmp->commands[parmp->n_commands++] = "$"; - else + } else { parmp->commands[parmp->n_commands++] = &(argv[0][1]); - } - /* - * Optional argument. - */ - else if (argv[0][0] == '-' && !had_minmin) { - want_argument = FALSE; + } + + // Optional argument. + } else if (argv[0][0] == '-' && !had_minmin) { + want_argument = false; c = argv[0][argv_idx++]; switch (c) { - case NUL: /* "vim -" read from stdin */ + case NUL: { // "nvim -" read from stdin if (exmode_active) { - // "ex -" silent mode - silent_mode = TRUE; + // "nvim -e -" silent mode + silent_mode = true; } else { - if (parmp->edit_type != EDIT_NONE) { + if (parmp->edit_type != EDIT_NONE + && parmp->edit_type != EDIT_FILE + && parmp->edit_type != EDIT_STDIN) { mainerr(err_too_many_args, argv[0]); } + had_stdin_file = true; parmp->edit_type = EDIT_STDIN; } - argv_idx = -1; /* skip to next argument */ + argv_idx = -1; // skip to next argument break; - - case '-': /* "--" don't take any more option arguments */ - /* "--help" give help message */ - /* "--version" give version message */ - /* "--literal" take files literally */ - /* "--noplugin[s]" skip plugins */ - /* "--cmd <cmd>" execute cmd before vimrc */ + } + case '-': { // "--" don't take any more option arguments + // "--help" give help message + // "--version" give version message + // "--noplugin[s]" skip plugins + // "--cmd <cmd>" execute cmd before vimrc if (STRICMP(argv[0] + argv_idx, "help") == 0) { usage(); mch_exit(0); @@ -749,337 +832,376 @@ static void command_line_scan(mparm_T *parmp) version(); mch_exit(0); } else if (STRICMP(argv[0] + argv_idx, "api-info") == 0) { - msgpack_sbuffer* b = msgpack_sbuffer_new(); - msgpack_packer* p = msgpack_packer_new(b, msgpack_sbuffer_write); - Object md = DICTIONARY_OBJ(api_metadata()); - msgpack_rpc_from_object(md, p); + FileDescriptor fp; + const int fof_ret = file_open_fd(&fp, STDOUT_FILENO, + kFileWriteOnly); + msgpack_packer *p = msgpack_packer_new(&fp, msgpack_file_write); - for (size_t i = 0; i < b->size; i++) { - putchar(b->data[i]); + if (fof_ret != 0) { + emsgf(_("E5421: Failed to open stdin: %s"), os_strerror(fof_ret)); } + if (p == NULL) { + EMSG(_(e_outofmem)); + } + + Object md = DICTIONARY_OBJ(api_metadata()); + msgpack_rpc_from_object(md, p); + msgpack_packer_free(p); + const int ff_ret = file_flush(&fp); + if (ff_ret < 0) { + msgpack_file_write_error(ff_ret); + } mch_exit(0); } else if (STRICMP(argv[0] + argv_idx, "headless") == 0) { - parmp->headless = true; + headless_mode = true; } else if (STRICMP(argv[0] + argv_idx, "embed") == 0) { embedded_mode = true; - parmp->headless = true; - channel_from_stdio(); + } else if (STRNICMP(argv[0] + argv_idx, "listen", 6) == 0) { + want_argument = true; + argv_idx += 6; } else if (STRNICMP(argv[0] + argv_idx, "literal", 7) == 0) { -#if !defined(UNIX) - parmp->literal = TRUE; -#endif - } else if (STRNICMP(argv[0] + argv_idx, "noplugin", 8) == 0) - p_lpl = FALSE; - else if (STRNICMP(argv[0] + argv_idx, "cmd", 3) == 0) { - want_argument = TRUE; + // Do nothing: file args are always literal. #7679 + } else if (STRNICMP(argv[0] + argv_idx, "noplugin", 8) == 0) { + p_lpl = false; + } else if (STRNICMP(argv[0] + argv_idx, "cmd", 3) == 0) { + want_argument = true; argv_idx += 3; } else if (STRNICMP(argv[0] + argv_idx, "startuptime", 11) == 0) { - want_argument = TRUE; + want_argument = true; argv_idx += 11; } else { if (argv[0][argv_idx]) mainerr(err_opt_unknown, argv[0]); - had_minmin = TRUE; + had_minmin = true; + } + if (!want_argument) { + argv_idx = -1; // skip to next argument } - if (!want_argument) - argv_idx = -1; /* skip to next argument */ break; - - case 'A': /* "-A" start in Arabic mode */ - set_option_value((char_u *)"arabic", 1L, NULL, 0); + } + case 'A': { // "-A" start in Arabic mode. + set_option_value("arabic", 1L, NULL, 0); break; - - case 'b': /* "-b" binary mode */ - /* Needs to be effective before expanding file names, because - * for Win32 this makes us edit a shortcut file itself, - * instead of the file it links to. */ + } + case 'b': { // "-b" binary mode. + // Needs to be effective before expanding file names, because + // for Win32 this makes us edit a shortcut file itself, + // instead of the file it links to. set_options_bin(curbuf->b_p_bin, 1, 0); - curbuf->b_p_bin = 1; /* binary file I/O */ + curbuf->b_p_bin = 1; // Binary file I/O. break; + } - case 'e': /* "-e" Ex mode */ + case 'D': { // "-D" Debugging + parmp->use_debug_break_level = 9999; + break; + } + case 'd': { // "-d" 'diff' + parmp->diff_mode = true; + break; + } + case 'e': { // "-e" Ex mode exmode_active = EXMODE_NORMAL; break; - - case 'E': /* "-E" Improved Ex mode */ + } + case 'E': { // "-E" Ex mode exmode_active = EXMODE_VIM; break; - - case 'f': /* "-f" GUI: run in foreground. */ - break; - - case 'g': /* "-g" start GUI */ - main_start_gui(); + } + case 'f': { // "-f" GUI: run in foreground. break; - - case 'F': /* "-F" start in Farsi mode: rl + fkmap set */ - p_fkmap = TRUE; - set_option_value((char_u *)"rl", 1L, NULL, 0); + } + case 'F': { // "-F" start in Farsi mode: rl + fkmap set. + p_fkmap = true; + set_option_value("rl", 1L, NULL, 0); break; - - case 'h': /* "-h" give help message */ + } + case '?': // "-?" give help message (for MS-Windows) + case 'h': { // "-h" give help message usage(); mch_exit(0); - - case 'H': /* "-H" start in Hebrew mode: rl + hkmap set */ - p_hkmap = TRUE; - set_option_value((char_u *)"rl", 1L, NULL, 0); + } + case 'H': { // "-H" start in Hebrew mode: rl + hkmap set. + p_hkmap = true; + set_option_value("rl", 1L, NULL, 0); break; - - case 'l': /* "-l" lisp mode, 'lisp' and 'showmatch' on */ - set_option_value((char_u *)"lisp", 1L, NULL, 0); - p_sm = TRUE; + } + case 'l': { // "-l" lisp mode, 'lisp' and 'showmatch' on. + set_option_value("lisp", 1L, NULL, 0); + p_sm = true; break; - - case 'M': /* "-M" no changes or writing of files */ + } + case 'M': { // "-M" no changes or writing of files reset_modifiable(); - /* FALLTHROUGH */ - - case 'm': /* "-m" no writing of files */ - p_write = FALSE; + FALLTHROUGH; + } + case 'm': { // "-m" no writing of files + p_write = false; break; + } - case 'N': /* "-N" Nocompatible */ - /* No-op */ + case 'N': // "-N" Nocompatible + case 'X': // "-X" Do not connect to X server + // No-op break; - case 'n': /* "-n" no swap file */ - parmp->no_swap_file = TRUE; + case 'n': { // "-n" no swap file + parmp->no_swap_file = true; break; - - case 'p': /* "-p[N]" open N tab pages */ -#ifdef TARGET_API_MAC_OSX - /* For some reason on MacOS X, an argument like: - -psn_0_10223617 is passed in when invoke from Finder - or with the 'open' command */ - if (argv[0][argv_idx] == 's') { - argv_idx = -1; /* bypass full -psn */ - main_start_gui(); - break; - } -#endif - /* default is 0: open window for each file */ + } + case 'p': { // "-p[N]" open N tab pages + // default is 0: open window for each file parmp->window_count = get_number_arg(argv[0], &argv_idx, 0); parmp->window_layout = WIN_TABS; break; - - case 'o': /* "-o[N]" open N horizontal split windows */ - /* default is 0: open window for each file */ + } + case 'o': { // "-o[N]" open N horizontal split windows + // default is 0: open window for each file parmp->window_count = get_number_arg(argv[0], &argv_idx, 0); parmp->window_layout = WIN_HOR; break; - - case 'O': /* "-O[N]" open N vertical split windows */ - /* default is 0: open window for each file */ + } + case 'O': { // "-O[N]" open N vertical split windows + // default is 0: open window for each file parmp->window_count = get_number_arg(argv[0], &argv_idx, 0); parmp->window_layout = WIN_VER; break; - - case 'q': /* "-q" QuickFix mode */ - if (parmp->edit_type != EDIT_NONE) + } + case 'q': { // "-q" QuickFix mode + if (parmp->edit_type != EDIT_NONE) { mainerr(err_too_many_args, argv[0]); + } parmp->edit_type = EDIT_QF; - if (argv[0][argv_idx]) { /* "-q{errorfile}" */ + if (argv[0][argv_idx]) { // "-q{errorfile}" parmp->use_ef = (char_u *)argv[0] + argv_idx; argv_idx = -1; - } else if (argc > 1) /* "-q {errorfile}" */ - want_argument = TRUE; + } else if (argc > 1) { // "-q {errorfile}" + want_argument = true; + } break; - - case 'R': /* "-R" readonly mode */ - readonlymode = TRUE; - curbuf->b_p_ro = TRUE; - p_uc = 10000; /* don't update very often */ + } + case 'R': { // "-R" readonly mode + readonlymode = true; + curbuf->b_p_ro = true; + p_uc = 10000; // don't update very often break; - - case 'r': /* "-r" recovery mode */ - case 'L': /* "-L" recovery mode */ + } + case 'r': // "-r" recovery mode + case 'L': { // "-L" recovery mode recoverymode = 1; break; - - case 's': - if (exmode_active) /* "-s" silent (batch) mode */ - silent_mode = TRUE; - else /* "-s {scriptin}" read from script file */ - want_argument = TRUE; + } + case 's': { + if (exmode_active) { // "-es" silent (batch) Ex-mode + silent_mode = true; + } else { // "-s {scriptin}" read from script file + want_argument = true; + } break; - - case 't': /* "-t {tag}" or "-t{tag}" jump to tag */ - if (parmp->edit_type != EDIT_NONE) + } + case 't': { // "-t {tag}" or "-t{tag}" jump to tag + if (parmp->edit_type != EDIT_NONE) { mainerr(err_too_many_args, argv[0]); + } parmp->edit_type = EDIT_TAG; - if (argv[0][argv_idx]) { /* "-t{tag}" */ + if (argv[0][argv_idx]) { // "-t{tag}" parmp->tagname = (char_u *)argv[0] + argv_idx; argv_idx = -1; - } else /* "-t {tag}" */ - want_argument = TRUE; - break; - - case 'D': /* "-D" Debugging */ - parmp->use_debug_break_level = 9999; - break; - case 'd': /* "-d" 'diff' */ - parmp->diff_mode = TRUE; + } else { // "-t {tag}" + want_argument = true; + } break; - case 'v': + } + case 'v': { version(); mch_exit(0); - case 'V': /* "-V{N}" Verbose level */ - /* default is 10: a little bit verbose */ + } + case 'V': { // "-V{N}" Verbose level + // default is 10: a little bit verbose p_verbose = get_number_arg(argv[0], &argv_idx, 10); if (argv[0][argv_idx] != NUL) { - set_option_value((char_u *)"verbosefile", 0L, - (char_u *)argv[0] + argv_idx, 0); + set_option_value("verbosefile", 0L, argv[0] + argv_idx, 0); argv_idx = (int)STRLEN(argv[0]); } break; - - case 'w': /* "-w{number}" set window height */ - /* "-w {scriptout}" write to script */ + } + case 'w': { // "-w{number}" set window height + // "-w {scriptout}" write to script if (ascii_isdigit(((char_u *)argv[0])[argv_idx])) { n = get_number_arg(argv[0], &argv_idx, 10); - set_option_value((char_u *)"window", n, NULL, 0); + set_option_value("window", n, NULL, 0); break; } - want_argument = TRUE; + want_argument = true; break; - - case 'Z': /* "-Z" restricted mode */ - restricted = TRUE; + } + case 'Z': { // "-Z" restricted mode + restricted = true; break; + } - case 'c': /* "-c{command}" or "-c {command}" execute - command */ + case 'c': { // "-c{command}" or "-c {command}" exec command if (argv[0][argv_idx] != NUL) { - if (parmp->n_commands >= MAX_ARG_CMDS) + if (parmp->n_commands >= MAX_ARG_CMDS) { mainerr(err_extra_cmd, NULL); - parmp->commands[parmp->n_commands++] = argv[0] - + argv_idx; + } + parmp->commands[parmp->n_commands++] = argv[0] + argv_idx; argv_idx = -1; break; } - /*FALLTHROUGH*/ - case 'S': /* "-S {file}" execute Vim script */ - case 'i': /* "-i {shada}" use for ShaDa file */ - case 'u': /* "-u {vimrc}" vim inits file */ - case 'U': /* "-U {gvimrc}" gvim inits file */ - case 'W': /* "-W {scriptout}" overwrite */ - want_argument = TRUE; + FALLTHROUGH; + } + case 'S': // "-S {file}" execute Vim script + case 'i': // "-i {shada}" use for ShaDa file + case 'u': // "-u {vimrc}" vim inits file + case 'U': // "-U {gvimrc}" gvim inits file + case 'W': { // "-W {scriptout}" overwrite + want_argument = true; break; + } - default: + default: { mainerr(err_opt_unknown, argv[0]); + } } - /* - * Handle option arguments with argument. - */ + // Handle option arguments with argument. if (want_argument) { - /* - * Check for garbage immediately after the option letter. - */ - if (argv[0][argv_idx] != NUL) + // Check for garbage immediately after the option letter. + if (argv[0][argv_idx] != NUL) { mainerr(err_opt_garbage, argv[0]); + } - --argc; - if (argc < 1 && c != 'S') /* -S has an optional argument */ + argc--; + if (argc < 1 && c != 'S') { // -S has an optional argument mainerr(err_arg_missing, argv[0]); - ++argv; + } + argv++; argv_idx = -1; switch (c) { - case 'c': /* "-c {command}" execute command */ - case 'S': /* "-S {file}" execute Vim script */ - if (parmp->n_commands >= MAX_ARG_CMDS) + case 'c': // "-c {command}" execute command + case 'S': { // "-S {file}" execute Vim script + if (parmp->n_commands >= MAX_ARG_CMDS) { mainerr(err_extra_cmd, NULL); + } if (c == 'S') { - char *a; + char *a; - if (argc < 1) - /* "-S" without argument: use default session file - * name. */ + if (argc < 1) { + // "-S" without argument: use default session file name. a = SESSION_FILE; - else if (argv[0][0] == '-') { - /* "-S" followed by another option: use default - * session file name. */ + } else if (argv[0][0] == '-') { + // "-S" followed by another option: use default session file. a = SESSION_FILE; ++argc; --argv; } else { a = argv[0]; } - char *s = xmalloc(STRLEN(a) + 4); - sprintf(s, "so %s", a); - parmp->cmds_tofree[parmp->n_commands] = TRUE; + size_t s_size = STRLEN(a) + 4; + char *s = xmalloc(s_size); + snprintf(s, s_size, "so %s", a); + parmp->cmds_tofree[parmp->n_commands] = true; parmp->commands[parmp->n_commands++] = s; } else { parmp->commands[parmp->n_commands++] = argv[0]; } break; + } - case '-': - if (argv[-1][2] == 'c') { - /* "--cmd {command}" execute command */ - if (parmp->n_pre_commands >= MAX_ARG_CMDS) + case '-': { + if (strequal(argv[-1], "--cmd")) { + // "--cmd {command}" execute command + if (parmp->n_pre_commands >= MAX_ARG_CMDS) { mainerr(err_extra_cmd, NULL); + } parmp->pre_commands[parmp->n_pre_commands++] = argv[0]; + } else if (strequal(argv[-1], "--listen")) { + // "--listen {address}" + parmp->listen_addr = argv[0]; } - /* "--startuptime <file>" already handled */ + // "--startuptime <file>" already handled break; + } - case 'q': /* "-q {errorfile}" QuickFix mode */ + case 'q': { // "-q {errorfile}" QuickFix mode parmp->use_ef = (char_u *)argv[0]; break; + } - case 'i': /* "-i {shada}" use for shada */ + case 'i': { // "-i {shada}" use for shada used_shada_file = argv[0]; break; + } - case 's': /* "-s {scriptin}" read from script file */ + case 's': { // "-s {scriptin}" read from script file if (scriptin[0] != NULL) { scripterror: - mch_errmsg(_("Attempt to open script file again: \"")); - mch_errmsg(argv[-1]); - mch_errmsg(" "); - mch_errmsg(argv[0]); - mch_errmsg("\"\n"); + vim_snprintf((char *)IObuff, IOSIZE, + _("Attempt to open script file again: \"%s %s\"\n"), + argv[-1], argv[0]); + mch_errmsg((const char *)IObuff); mch_exit(2); } - if ((scriptin[0] = mch_fopen(argv[0], READBIN)) == NULL) { - mch_errmsg(_("Cannot open for reading: \"")); - mch_errmsg(argv[0]); - mch_errmsg("\"\n"); + int error; + if (strequal(argv[0], "-")) { + const int stdin_dup_fd = os_dup(STDIN_FILENO); +#ifdef WIN32 + // Replace the original stdin with the console input handle. + close(STDIN_FILENO); + const HANDLE conin_handle = + CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ, (LPSECURITY_ATTRIBUTES)NULL, + OPEN_EXISTING, 0, (HANDLE)NULL); + const int conin_fd = _open_osfhandle(conin_handle, _O_RDONLY); + assert(conin_fd == STDIN_FILENO); +#endif + FileDescriptor *const stdin_dup = file_open_fd_new( + &error, stdin_dup_fd, kFileReadOnly|kFileNonBlocking); + assert(stdin_dup != NULL); + scriptin[0] = stdin_dup; + } else if ((scriptin[0] = file_open_new( + &error, argv[0], kFileReadOnly|kFileNonBlocking, 0)) == NULL) { + vim_snprintf((char *)IObuff, IOSIZE, + _("Cannot open for reading: \"%s\": %s\n"), + argv[0], os_strerror(error)); + mch_errmsg((const char *)IObuff); mch_exit(2); } save_typebuf(); break; + } - case 't': /* "-t {tag}" */ + case 't': { // "-t {tag}" parmp->tagname = (char_u *)argv[0]; break; - - case 'u': /* "-u {vimrc}" vim inits file */ + } + case 'u': { // "-u {vimrc}" vim inits file parmp->use_vimrc = argv[0]; break; - - case 'U': /* "-U {gvimrc}" gvim inits file */ + } + case 'U': { // "-U {gvimrc}" gvim inits file break; + } - case 'w': /* "-w {nr}" 'window' value */ - /* "-w {scriptout}" append to script file */ + case 'w': { // "-w {nr}" 'window' value + // "-w {scriptout}" append to script file if (ascii_isdigit(*((char_u *)argv[0]))) { argv_idx = 0; n = get_number_arg(argv[0], &argv_idx, 10); - set_option_value((char_u *)"window", n, NULL, 0); + set_option_value("window", n, NULL, 0); argv_idx = -1; break; } - /*FALLTHROUGH*/ - case 'W': /* "-W {scriptout}" overwrite script file */ - if (scriptout != NULL) + FALLTHROUGH; + } + case 'W': { // "-W {scriptout}" overwrite script file + if (scriptout != NULL) { goto scripterror; + } if ((scriptout = mch_fopen(argv[0], c == 'w' ? APPENDBIN : WRITEBIN)) == NULL) { mch_errmsg(_("Cannot open for script output: \"")); @@ -1088,30 +1210,28 @@ scripterror: mch_exit(2); } break; - + } } } - } - /* - * File name argument. - */ - else { - argv_idx = -1; /* skip to next argument */ - - /* Check for only one type of editing. */ - if (parmp->edit_type != EDIT_NONE && parmp->edit_type != EDIT_FILE) + } else { // File name argument. + argv_idx = -1; // skip to next argument + + // Check for only one type of editing. + if (parmp->edit_type != EDIT_NONE + && parmp->edit_type != EDIT_FILE + && parmp->edit_type != EDIT_STDIN) { mainerr(err_too_many_args, argv[0]); + } parmp->edit_type = EDIT_FILE; - /* Add the file to the global argument list. */ + // Add the file to the global argument list. ga_grow(&global_alist.al_ga, 1); - p = vim_strsave((char_u *)argv[0]); + char_u *p = vim_strsave((char_u *)argv[0]); if (parmp->diff_mode && os_isdir(p) && GARGCOUNT > 0 && !os_isdir(alist_name(&GARGLIST[0]))) { - char_u *r; - - r = (char_u *)concat_fnames((char *)p, (char *)path_tail(alist_name(&GARGLIST[0])), TRUE); + char_u *r = (char_u *)concat_fnames((char *)p, + (char *)path_tail(alist_name(&GARGLIST[0])), true); xfree(p); p = r; } @@ -1121,30 +1241,22 @@ scripterror: path_fix_case(p); #endif - alist_add(&global_alist, p, -#if !defined(UNIX) - parmp->literal ? 2 : 0 /* add buffer nr after exp. */ -#else - 2 /* add buffer number now and use curbuf */ -#endif - ); - + int alist_fnum_flag = edit_stdin(had_stdin_file, parmp) + ? 1 // add buffer nr after exp. + : 2; // add buffer number now and use curbuf + alist_add(&global_alist, p, alist_fnum_flag); } - /* - * If there are no more letters after the current "-", go to next - * argument. argv_idx is set to -1 when the current argument is to be - * skipped. - */ + // If there are no more letters after the current "-", go to next argument. + // argv_idx is set to -1 when the current argument is to be skipped. if (argv_idx <= 0 || argv[0][argv_idx] == NUL) { - --argc; - ++argv; + argc--; + argv++; argv_idx = 1; } } - /* If there is a "+123" or "-c" command, set v:swapcommand to the first - * one. */ + // If there is a "+123" or "-c" command, set v:swapcommand to the first one. if (parmp->n_commands > 0) { const size_t swcmd_len = STRLEN(parmp->commands[0]) + 3; char *const swcmd = xmalloc(swcmd_len); @@ -1152,6 +1264,12 @@ scripterror: set_vim_var_string(VV_SWAPCOMMAND, swcmd, -1); xfree(swcmd); } + + // Handle "foo | nvim". EDIT_FILE may be overwritten now. #6299 + if (edit_stdin(had_stdin_file, parmp)) { + parmp->edit_type = EDIT_STDIN; + } + TIME_MSG("parsing arguments"); } @@ -1164,15 +1282,12 @@ static void init_params(mparm_T *paramp, int argc, char **argv) memset(paramp, 0, sizeof(*paramp)); paramp->argc = argc; paramp->argv = argv; - paramp->headless = false; - paramp->want_full_screen = true; paramp->use_debug_break_level = -1; paramp->window_count = -1; + paramp->listen_addr = NULL; } -/* - * Initialize global startuptime file if "--startuptime" passed as an argument. - */ +/// Initialize global startuptime file if "--startuptime" passed as an argument. static void init_startuptime(mparm_T *paramp) { for (int i = 1; i < paramp->argc; i++) { @@ -1188,36 +1303,46 @@ static void init_startuptime(mparm_T *paramp) static void check_and_set_isatty(mparm_T *paramp) { - paramp->input_isatty = os_isatty(fileno(stdin)); - paramp->output_isatty = os_isatty(fileno(stdout)); - paramp->err_isatty = os_isatty(fileno(stderr)); + stdin_isatty + = paramp->input_isatty = os_isatty(STDIN_FILENO); + stdout_isatty + = paramp->output_isatty = os_isatty(STDOUT_FILENO); + paramp->err_isatty = os_isatty(STDERR_FILENO); +#ifndef WIN32 + int tty_fd = paramp->input_isatty + ? STDIN_FILENO + : (paramp->output_isatty + ? STDOUT_FILENO + : (paramp->err_isatty ? STDERR_FILENO : -1)); + pty_process_save_termios(tty_fd); +#endif TIME_MSG("window checked"); } -/* - * Get filename from command line, given that there is one. - */ -static char_u *get_fname(mparm_T *parmp) + +// Sets v:progname and v:progpath. Also modifies $PATH on Windows. +static void init_path(const char *exename) + FUNC_ATTR_NONNULL_ALL { -#if !defined(UNIX) - /* - * Expand wildcards in file names. - */ - if (!parmp->literal) { - cwd = xmalloc(MAXPATHL); - if (cwd != NULL) { - os_dirname(cwd, MAXPATHL); - } - // Temporarily add '(' and ')' to 'isfname'. These are valid - // filename characters but are excluded from 'isfname' to make - // "gf" work on a file name in parenthesis (e.g.: see vim.h). - do_cmdline_cmd(":set isf+=(,)"); - alist_expand(NULL, 0); - do_cmdline_cmd(":set isf&"); - if (cwd != NULL) { - os_chdir((char *)cwd); - } + char exepath[MAXPATHL] = { 0 }; + size_t exepathlen = MAXPATHL; + // Make v:progpath absolute. + if (os_exepath(exepath, &exepathlen) != 0) { + // Fall back to argv[0]. Missing procfs? #6734 + path_guess_exepath(exename, exepath, sizeof(exepath)); } + set_vim_var_string(VV_PROGPATH, exepath, -1); + set_vim_var_string(VV_PROGNAME, (char *)path_tail((char_u *)exename), -1); + +#ifdef WIN32 + // Append the process start directory to $PATH, so that ":!foo" finds tools + // shipped with Windows package. This also mimics SearchPath(). + os_setenv_append_path(exepath); #endif +} + +/// Get filename from command line, if any. +static char_u *get_fname(mparm_T *parmp, char_u *cwd) +{ return alist_name(&GARGLIST[0]); } @@ -1241,11 +1366,33 @@ static void set_window_layout(mparm_T *paramp) static void load_plugins(void) { if (p_lpl) { - source_runtime((char_u *)"plugin/**/*.vim", DIP_ALL); // NOLINT + char_u *rtp_copy = NULL; + + // First add all package directories to 'runtimepath', so that their + // autoload directories can be found. Only if not done already with a + // :packloadall command. + // Make a copy of 'runtimepath', so that source_runtime does not use the + // pack directories. + if (!did_source_packages) { + rtp_copy = vim_strsave(p_rtp); + add_pack_start_dirs(); + } + + source_in_path(rtp_copy == NULL ? p_rtp : rtp_copy, + (char_u *)"plugin/**/*.vim", // NOLINT + DIP_ALL | DIP_NOAFTER); TIME_MSG("loading plugins"); + xfree(rtp_copy); - ex_packloadall(NULL); + // Only source "start" packages if not done already with a :packloadall + // command. + if (!did_source_packages) { + load_start_packages(); + } TIME_MSG("loading packages"); + + source_runtime((char_u *)"plugin/**/*.vim", DIP_ALL | DIP_AFTER); + TIME_MSG("loading after plugins"); } } @@ -1260,8 +1407,8 @@ static void handle_quickfix(mparm_T *paramp) set_string_option_direct((char_u *)"ef", -1, paramp->use_ef, OPT_FREE, SID_CARG); vim_snprintf((char *)IObuff, IOSIZE, "cfile %s", p_ef); - if (qf_init(NULL, p_ef, p_efm, TRUE, IObuff) < 0) { - ui_putc('\n'); + if (qf_init(NULL, p_ef, p_efm, true, IObuff, p_menc) < 0) { + msg_putchar('\n'); mch_exit(3); } TIME_MSG("reading errorfile"); @@ -1287,55 +1434,23 @@ static void handle_tag(char_u *tagname) } } -// Print a warning if stdout is not a terminal. -// When starting in Ex mode and commands come from a file, set Silent mode. -static void check_tty(mparm_T *parmp) -{ - if (parmp->headless) { - return; - } - - // is active input a terminal? - if (exmode_active) { - if (!parmp->input_isatty) { - silent_mode = true; - } - } else if (parmp->want_full_screen && (!parmp->err_isatty - && (!parmp->output_isatty || !parmp->input_isatty))) { - - if (!parmp->output_isatty) { - mch_errmsg(_("Vim: Warning: Output is not to a terminal\n")); - } - - if (!parmp->input_isatty) { - mch_errmsg(_("Vim: Warning: Input is not from a terminal\n")); - } - - ui_flush(); - - if (scriptin[0] == NULL) { - os_delay(2000L, true); - } - - TIME_MSG("Warning delay"); - } -} - -/* - * Read text from stdin. - */ +/// Read text from stdin. static void read_stdin(void) { - int i; - - /* When getting the ATTENTION prompt here, use a dialog */ + // When getting the ATTENTION prompt here, use a dialog. swap_exists_action = SEA_DIALOG; - no_wait_return = TRUE; - i = msg_didany; - set_buflisted(TRUE); - (void)open_buffer(TRUE, NULL, 0); /* create memfile and read file */ - no_wait_return = FALSE; - msg_didany = i; + no_wait_return = true; + int save_msg_didany = msg_didany; + set_buflisted(true); + (void)open_buffer(true, NULL, 0); // create memfile and read file + if (BUFEMPTY() && curbuf->b_next != NULL) { + // stdin was empty, go to buffer 2 (e.g. "echo file1 | xargs nvim"). #8561 + do_cmdline_cmd("silent! bnext"); + // Delete the empty stdin buffer. + do_cmdline_cmd("bwipeout 1"); + } + no_wait_return = false; + msg_didany = save_msg_didany; TIME_MSG("reading stdin"); check_swap_exists_action(); } @@ -1454,7 +1569,7 @@ static void edit_buffers(mparm_T *parmp, char_u *cwd) { int arg_idx; /* index in argument list */ int i; - int advance = TRUE; + bool advance = true; win_T *win; /* @@ -1465,8 +1580,8 @@ static void edit_buffers(mparm_T *parmp, char_u *cwd) /* When w_arg_idx is -1 remove the window (see create_windows()). */ if (curwin->w_arg_idx == -1) { - win_close(curwin, TRUE); - advance = FALSE; + win_close(curwin, true); + advance = false; } arg_idx = 1; @@ -1476,9 +1591,9 @@ static void edit_buffers(mparm_T *parmp, char_u *cwd) } // When w_arg_idx is -1 remove the window (see create_windows()). if (curwin->w_arg_idx == -1) { - ++arg_idx; - win_close(curwin, TRUE); - advance = FALSE; + arg_idx++; + win_close(curwin, true); + advance = false; continue; } @@ -1493,7 +1608,7 @@ static void edit_buffers(mparm_T *parmp, char_u *cwd) win_enter(curwin->w_next, false); } } - advance = TRUE; + advance = true; // Only open the file if there is no file in this window yet (that can // happen when vimrc contains ":sall"). @@ -1512,12 +1627,13 @@ static void edit_buffers(mparm_T *parmp, char_u *cwd) did_emsg = FALSE; /* avoid hit-enter prompt */ getout(1); } - win_close(curwin, TRUE); - advance = FALSE; + win_close(curwin, true); + advance = false; } - if (arg_idx == GARGCOUNT - 1) - arg_had_last = TRUE; - ++arg_idx; + if (arg_idx == GARGCOUNT - 1) { + arg_had_last = true; + } + arg_idx++; } os_breakcheck(); if (got_int) { @@ -1605,6 +1721,48 @@ static void exe_commands(mparm_T *parmp) TIME_MSG("executing command arguments"); } +/// Source system-wide vimrc if built with one defined +/// +/// Does one of the following things, stops after whichever succeeds: +/// +/// 1. Source system vimrc file from $XDG_CONFIG_DIRS/nvim/sysinit.vim +/// 2. Source system vimrc file from $VIM +static void do_system_initialization(void) +{ + char *const config_dirs = stdpaths_get_xdg_var(kXDGConfigDirs); + if (config_dirs != NULL) { + const void *iter = NULL; + const char path_tail[] = { + 'n', 'v', 'i', 'm', PATHSEP, + 's', 'y', 's', 'i', 'n', 'i', 't', '.', 'v', 'i', 'm', NUL + }; + do { + const char *dir; + size_t dir_len; + iter = vim_env_iter(':', config_dirs, iter, &dir, &dir_len); + if (dir == NULL || dir_len == 0) { + break; + } + char *vimrc = xmalloc(dir_len + sizeof(path_tail) + 1); + memcpy(vimrc, dir, dir_len); + vimrc[dir_len] = PATHSEP; + memcpy(vimrc + dir_len + 1, path_tail, sizeof(path_tail)); + if (do_source((char_u *)vimrc, false, DOSO_NONE) != FAIL) { + xfree(vimrc); + xfree(config_dirs); + return; + } + xfree(vimrc); + } while (iter != NULL); + xfree(config_dirs); + } + +#ifdef SYS_VIMRC_FILE + // Get system wide defaults, if the file name is defined. + (void)do_source((char_u *)SYS_VIMRC_FILE, false, DOSO_NONE); +#endif +} + /// Source vimrc or do other user initialization /// /// Does one of the following things, stops after whichever succeeds: @@ -1620,7 +1778,7 @@ static bool do_user_initialization(void) FUNC_ATTR_WARN_UNUSED_RESULT { bool do_exrc = p_exrc; - if (process_env("VIMINIT", true) == OK) { + if (process_env("VIMINIT") == OK) { do_exrc = p_exrc; return do_exrc; } @@ -1641,7 +1799,7 @@ static bool do_user_initialization(void) do { const char *dir; size_t dir_len; - iter = vim_colon_env_iter(config_dirs, iter, &dir, &dir_len); + iter = vim_env_iter(':', config_dirs, iter, &dir, &dir_len); if (dir == NULL || dir_len == 0) { break; } @@ -1665,7 +1823,7 @@ static bool do_user_initialization(void) } while (iter != NULL); xfree(config_dirs); } - if (process_env("EXINIT", false) == OK) { + if (process_env("EXINIT") == OK) { do_exrc = p_exrc; return do_exrc; } @@ -1676,22 +1834,18 @@ static bool do_user_initialization(void) static void source_startup_scripts(const mparm_T *const parmp) FUNC_ATTR_NONNULL_ALL { - // If -u argument given, use only the initializations from that file and - // nothing else. + // If -u given, use only the initializations from that file and nothing else. if (parmp->use_vimrc != NULL) { - if (strcmp(parmp->use_vimrc, "NONE") == 0 - || strcmp(parmp->use_vimrc, "NORC") == 0) { - if (parmp->use_vimrc[2] == 'N') - p_lpl = false; // don't load plugins either + if (strequal(parmp->use_vimrc, "NONE") + || strequal(parmp->use_vimrc, "NORC")) { + // Do nothing. } else { - if (do_source((char_u *)parmp->use_vimrc, FALSE, DOSO_NONE) != OK) + if (do_source((char_u *)parmp->use_vimrc, false, DOSO_NONE) != OK) { EMSG2(_("E282: Cannot read from \"%s\""), parmp->use_vimrc); + } } } else if (!silent_mode) { -#ifdef SYS_VIMRC_FILE - // Get system wide defaults, if the file name is defined. - (void) do_source((char_u *)SYS_VIMRC_FILE, false, DOSO_NONE); -#endif + do_system_initialization(); if (do_user_initialization()) { // Read initialization commands from ".vimrc" or ".exrc" in current @@ -1724,35 +1878,20 @@ static void source_startup_scripts(const mparm_T *const parmp) } secure = 0; } - did_source_startup_scripts = true; TIME_MSG("sourcing vimrc file(s)"); } -/* - * Setup to start using the GUI. Exit with an error when not available. - */ -static void main_start_gui(void) -{ - mch_errmsg(_(e_nogvim)); - mch_errmsg("\n"); - mch_exit(2); -} - - /// Get an environment variable, and execute it as Ex commands. /// /// @param env environment variable to execute -/// @param is_viminit when true, called for VIMINIT /// /// @return FAIL if the environment variable was not executed, /// OK otherwise. -static int process_env(char *env, bool is_viminit) +static int process_env(char *env) + FUNC_ATTR_NONNULL_ALL { const char *initstr = os_getenv(env); if (initstr != NULL) { - if (is_viminit) { - vimrc_found(NULL, NULL); - } char_u *save_sourcing_name = sourcing_name; linenr_T save_sourcing_lnum = sourcing_lnum; sourcing_name = (char_u *)env; @@ -1762,7 +1901,7 @@ static int process_env(char *env, bool is_viminit) do_cmdline_cmd((char *)initstr); sourcing_name = save_sourcing_name; sourcing_lnum = save_sourcing_lnum; - current_SID = save_sid;; + current_SID = save_sid; return OK; } return FAIL; @@ -1774,6 +1913,7 @@ static int process_env(char *env, bool is_viminit) /// os_fileinfo_link() respectively for extra security. static bool file_owned(const char *fname) { + assert(fname != NULL); uid_t uid = getuid(); FileInfo file_info; bool file_owned = os_fileinfo(fname, &file_info) @@ -1792,9 +1932,11 @@ static bool file_owned(const char *fname) /// @param str string to append to the primary error message, or NULL static void mainerr(const char *errstr, const char *str) { + char *prgname = (char *)path_tail((char_u *)argv0); + signal_stop(); // kill us with CTRL-C here, if you like - mch_errmsg(argv0); + mch_errmsg(prgname); mch_errmsg(": "); mch_errmsg(_(errstr)); if (str != NULL) { @@ -1803,7 +1945,7 @@ static void mainerr(const char *errstr, const char *str) mch_errmsg("\""); } mch_errmsg(_("\nMore info with \"")); - mch_errmsg(argv0); + mch_errmsg(prgname); mch_errmsg(" -h\"\n"); mch_exit(1); @@ -1824,54 +1966,44 @@ static void usage(void) signal_stop(); // kill us with CTRL-C here, if you like mch_msg(_("Usage:\n")); - mch_msg(_(" nvim [arguments] [file ...] Edit specified file(s)\n")); - mch_msg(_(" nvim [arguments] - Read text from stdin\n")); - mch_msg(_(" nvim [arguments] -t <tag> Edit file where tag is defined\n")); - mch_msg(_(" nvim [arguments] -q [errorfile] Edit file with first error\n")); - mch_msg(_("\nArguments:\n")); + mch_msg(_(" nvim [options] [file ...] Edit file(s)\n")); + mch_msg(_(" nvim [options] -t <tag> Edit file where tag is defined\n")); + mch_msg(_(" nvim [options] -q [errorfile] Edit file with first error\n")); + mch_msg(_("\nOptions:\n")); mch_msg(_(" -- Only file names after this\n")); -#if !defined(UNIX) - mch_msg(_(" --literal Don't expand wildcards\n")); -#endif - mch_msg(_(" -e Ex mode\n")); - mch_msg(_(" -E Improved Ex mode\n")); - mch_msg(_(" -s Silent (batch) mode (only for ex mode)\n")); + mch_msg(_(" + Start at end of file\n")); + mch_msg(_(" --cmd <cmd> Execute <cmd> before any config\n")); + mch_msg(_(" +<cmd>, -c <cmd> Execute <cmd> after config and first file\n")); + mch_msg("\n"); + mch_msg(_(" -b Binary mode\n")); mch_msg(_(" -d Diff mode\n")); - mch_msg(_(" -R Read-only mode\n")); - mch_msg(_(" -Z Restricted mode\n")); + mch_msg(_(" -e, -E Ex mode\n")); + mch_msg(_(" -es, -Es Silent (batch) mode\n")); + mch_msg(_(" -h, --help Print this help message\n")); + mch_msg(_(" -i <shada> Use this shada file\n")); mch_msg(_(" -m Modifications (writing files) not allowed\n")); mch_msg(_(" -M Modifications in text not allowed\n")); - mch_msg(_(" -b Binary mode\n")); - mch_msg(_(" -l Lisp mode\n")); - mch_msg(_(" -A Arabic mode\n")); - mch_msg(_(" -F Farsi mode\n")); - mch_msg(_(" -H Hebrew mode\n")); - mch_msg(_(" -V[N][file] Be verbose [level N][log messages to file]\n")); - mch_msg(_(" -D Debugging mode\n")); mch_msg(_(" -n No swap file, use memory only\n")); - mch_msg(_(" -r, -L List swap files and exit\n")); - mch_msg(_(" -r <file> Recover crashed session\n")); - mch_msg(_(" -u <vimrc> Use <vimrc> instead of the default\n")); - mch_msg(_(" -i <shada> Use <shada> instead of the default\n")); - mch_msg(_(" --noplugin Don't load plugin scripts\n")); - mch_msg(_(" -o[N] Open N windows (default: one for each file)\n")); - mch_msg(_(" -O[N] Like -o but split vertically\n")); - mch_msg(_(" -p[N] Open N tab pages (default: one for each file)\n")); - mch_msg(_(" + Start at end of file\n")); - mch_msg(_(" +<linenum> Start at line <linenum>\n")); - mch_msg(_(" +/<pattern> Start at first occurrence of <pattern>\n")); - mch_msg(_(" --cmd <command> Execute <command> before loading any vimrc\n")); - mch_msg(_(" -c <command> Execute <command> after loading the first file\n")); + mch_msg(_(" -o[N] Open N windows (default: one per file)\n")); + mch_msg(_(" -O[N] Open N vertical windows (default: one per file)\n")); + mch_msg(_(" -p[N] Open N tab pages (default: one per file)\n")); + mch_msg(_(" -r, -L List swap files\n")); + mch_msg(_(" -r <file> Recover edit state for this file\n")); + mch_msg(_(" -R Read-only mode\n")); mch_msg(_(" -S <session> Source <session> after loading the first file\n")); mch_msg(_(" -s <scriptin> Read Normal mode commands from <scriptin>\n")); - mch_msg(_(" -w <scriptout> Append all typed characters to <scriptout>\n")); - mch_msg(_(" -W <scriptout> Write all typed characters to <scriptout>\n")); - mch_msg(_(" --startuptime <file> Write startup timing messages to <file>\n")); - mch_msg(_(" --api-info Dump API metadata serialized to msgpack and exit\n")); + mch_msg(_(" -u <config> Use this config file\n")); + mch_msg(_(" -v, --version Print version information\n")); + mch_msg(_(" -V[N][file] Verbose [level][file]\n")); + mch_msg(_(" -Z Restricted mode\n")); + mch_msg("\n"); + mch_msg(_(" --api-info Write msgpack-encoded API metadata to stdout\n")); mch_msg(_(" --embed Use stdin/stdout as a msgpack-rpc channel\n")); mch_msg(_(" --headless Don't start a user interface\n")); - mch_msg(_(" -v, --version Print version information and exit\n")); - mch_msg(_(" -h, --help Print this help message and exit\n")); + mch_msg(_(" --listen <address> Serve RPC API from this address\n")); + mch_msg(_(" --noplugin Don't load plugins\n")); + mch_msg(_(" --startuptime <file> Write startup timing messages to <file>\n")); + mch_msg(_("\nSee \":help startup-options\" for all options.\n")); } |