diff options
Diffstat (limited to 'src/nvim/main.c')
-rw-r--r-- | src/nvim/main.c | 652 |
1 files changed, 365 insertions, 287 deletions
diff --git a/src/nvim/main.c b/src/nvim/main.c index 5687e0a6a9..bbe877356d 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -3,43 +3,51 @@ #define EXTERN #include <assert.h> -#include <msgpack.h> +#include <limits.h> +#include <msgpack/pack.h> #include <stdbool.h> #include <stdint.h> +#include <stdio.h> +#include <stdlib.h> #include <string.h> +#include "auto/config.h" #include "nvim/arglist.h" #include "nvim/ascii.h" #include "nvim/autocmd.h" #include "nvim/buffer.h" +#include "nvim/buffer_defs.h" #include "nvim/channel.h" -#include "nvim/charset.h" #include "nvim/decoration.h" #include "nvim/decoration_provider.h" #include "nvim/diff.h" #include "nvim/drawscreen.h" #include "nvim/eval.h" +#include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" +#include "nvim/event/multiqueue.h" +#include "nvim/event/stream.h" #include "nvim/ex_cmds.h" -#include "nvim/ex_cmds2.h" #include "nvim/ex_docmd.h" #include "nvim/ex_getln.h" #include "nvim/fileio.h" #include "nvim/fold.h" #include "nvim/garray.h" +#include "nvim/getchar.h" +#include "nvim/gettext.h" +#include "nvim/globals.h" #include "nvim/grid.h" #include "nvim/hashtab.h" #include "nvim/highlight.h" #include "nvim/highlight_group.h" -#include "nvim/iconv.h" -#include "nvim/if_cscope.h" -#include "nvim/insexpand.h" +#include "nvim/keycodes.h" #include "nvim/locale.h" #include "nvim/log.h" #include "nvim/lua/executor.h" +#include "nvim/macros.h" #include "nvim/main.h" -#include "nvim/mapping.h" #include "nvim/mark.h" -#include "nvim/mbyte.h" +#include "nvim/memfile_defs.h" #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" @@ -48,23 +56,26 @@ #include "nvim/normal.h" #include "nvim/ops.h" #include "nvim/option.h" +#include "nvim/option_defs.h" #include "nvim/optionstr.h" #include "nvim/os/fileio.h" #include "nvim/os/input.h" #include "nvim/os/os.h" -#include "nvim/os/os_defs.h" +#include "nvim/os/stdpaths_defs.h" #include "nvim/os/time.h" -#include "nvim/os_unix.h" #include "nvim/path.h" #include "nvim/popupmenu.h" +#include "nvim/pos.h" #include "nvim/profile.h" #include "nvim/quickfix.h" #include "nvim/runtime.h" #include "nvim/shada.h" #include "nvim/sign.h" -#include "nvim/state.h" +#include "nvim/statusline.h" #include "nvim/strings.h" #include "nvim/syntax.h" +#include "nvim/terminal.h" +#include "nvim/types.h" #include "nvim/ui.h" #include "nvim/ui_client.h" #include "nvim/ui_compositor.h" @@ -74,8 +85,8 @@ #ifdef MSWIN # include "nvim/os/os_win_console.h" #endif +#include "nvim/api/extmark.h" #include "nvim/api/private/defs.h" -#include "nvim/api/private/dispatch.h" #include "nvim/api/private/helpers.h" #include "nvim/api/ui.h" #include "nvim/event/loop.h" @@ -84,22 +95,22 @@ #include "nvim/msgpack_rpc/helpers.h" #include "nvim/msgpack_rpc/server.h" #include "nvim/os/signal.h" -#ifndef MSWIN -# include "nvim/os/pty_process_unix.h" -#endif -#include "nvim/api/extmark.h" // 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 +enum { + WIN_HOR = 1, // "-o" horizontally split windows + WIN_VER = 2, // "-O" vertically split windows + WIN_TABS = 3, // "-p" windows on tab pages +}; // Values for edit_type. -#define EDIT_NONE 0 // no edit type yet -#define EDIT_FILE 1 // file name argument[s] given, use argument list -#define EDIT_STDIN 2 // read file from stdin -#define EDIT_TAG 3 // tag name argument given, use tagname -#define EDIT_QF 4 // start in quickfix mode +enum { + EDIT_NONE = 0, // no edit type yet + EDIT_FILE = 1, // file name argument[s] given, use argument list + EDIT_STDIN = 2, // read file from stdin + EDIT_TAG = 3, // tag name argument given, use tagname + EDIT_QF = 4, // start in quickfix mode +}; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "main.c.generated.h" @@ -228,10 +239,10 @@ int main(int argc, char **argv) argv0 = argv[0]; - char_u *fname = NULL; // file name from command line + char *fname = NULL; // file name from command line mparm_T params; // various parameters passed between // main() and other functions. - char_u *cwd = NULL; // current working dir on startup + char *cwd = NULL; // current working dir on startup time_init(); // Many variables are in `params` so that we can pass them around easily. @@ -265,8 +276,7 @@ int main(int argc, char **argv) // argument list "global_alist". command_line_scan(¶ms); - nlua_init(); - + nlua_init(argv, argc, params.lua_arg0); TIME_MSG("init lua interpreter"); if (embedded_mode) { @@ -276,9 +286,35 @@ int main(int argc, char **argv) } } - server_init(params.listen_addr); +#ifdef MSWIN + // on windows we use CONIN special file, thus we don't know this yet. + bool has_term = true; +#else + bool has_term = (stdin_isatty || stdout_isatty || stderr_isatty); +#endif + bool use_builtin_ui = (has_term && !headless_mode && !embedded_mode && !silent_mode); + + // don't bind the server yet, if we are using builtin ui. + // This will be done when nvim server has been forked from the ui process + if (!use_builtin_ui) { + server_init(params.listen_addr); + } + if (params.remote) { - remote_request(¶ms, params.remote, params.server_addr, argc, argv); + remote_request(¶ms, params.remote, params.server_addr, argc, argv, + use_builtin_ui); + } + + bool remote_ui = (ui_client_channel_id != 0); + + if (use_builtin_ui && !remote_ui) { + ui_client_forward_stdin = !stdin_isatty; + uint64_t rv = ui_client_start_server(params.argc, params.argv); + if (!rv) { + os_errmsg("Failed to start Nvim server!\n"); + getout(1); + } + ui_client_channel_id = rv; } if (GARGCOUNT > 0) { @@ -329,22 +365,20 @@ int main(int argc, char **argv) debug_break_level = params.use_debug_break_level; // Read ex-commands if invoked with "-es". - if (!params.input_isatty && !params.input_neverscript - && silent_mode && exmode_active) { - input_start(STDIN_FILENO); + if (!stdin_isatty && !params.input_istext && silent_mode && exmode_active) { + input_start(); + } + + if (ui_client_channel_id) { + ui_client_run(remote_ui); // NORETURN } // Wait for UIs to set up Nvim or show early messages // and prompts (--cmd, swapfile dialog, …). bool use_remote_ui = (embedded_mode && !headless_mode); - bool use_builtin_ui = (!headless_mode && !embedded_mode && !silent_mode); - if (use_remote_ui || use_builtin_ui) { + if (use_remote_ui) { TIME_MSG("waiting for UI"); - if (use_remote_ui) { - remote_ui_wait_for_attach(); - } else { - ui_builtin_start(); - } + remote_ui_wait_for_attach(); TIME_MSG("done waiting for UI"); firstwin->w_prev_height = firstwin->w_height; // may have changed } @@ -355,12 +389,13 @@ int main(int argc, char **argv) win_new_screensize(); TIME_MSG("clear screen"); - if (ui_client_channel_id) { - ui_client_init(ui_client_channel_id); - ui_client_execute(ui_client_channel_id); - abort(); // unreachable + // Handle "foo | nvim". EDIT_FILE may be overwritten now. #6299 + if (edit_stdin(¶ms)) { + params.edit_type = EDIT_STDIN; } + open_script_files(¶ms); + // Default mappings (incl. menus) Error err = ERROR_INIT; Object o = NLUA_EXEC_STATIC("return vim._init_default_mappings()", @@ -369,19 +404,18 @@ int main(int argc, char **argv) api_clear_error(&err); assert(o.type == kObjectTypeNil); api_free_object(o); + TIME_MSG("init default mappings"); init_default_autocmds(); TIME_MSG("init default autocommands"); - bool vimrc_none = params.use_vimrc != NULL && strequal(params.use_vimrc, "NONE"); + bool vimrc_none = strequal(params.use_vimrc, "NONE"); // Reset 'loadplugins' for "-u NONE" before "--cmd" arguments. // Allows for setting 'loadplugins' there. - if (vimrc_none) { - // When using --clean we still want to load plugins - p_lpl = params.clean; - } + // For --clean we still want to load plugins. + p_lpl = vimrc_none ? params.clean : p_lpl; // Execute --cmd arguments. exe_pre_commands(¶ms); @@ -468,7 +502,7 @@ int main(int argc, char **argv) // 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 || stdin_fd >= 0) && !recoverymode) { + if (params.edit_type == EDIT_STDIN && !recoverymode) { read_stdin(); } @@ -511,7 +545,9 @@ int main(int argc, char **argv) if (params.diff_mode) { // set options in each window for "nvim -d". FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - diff_win_options(wp, true); + if (!wp->w_arg_idx_invalid) { + diff_win_options(wp, true); + } } } @@ -520,7 +556,7 @@ int main(int argc, char **argv) // Need to jump to the tag before executing the '-c command'. // Makes "vim -c '/return' -t main" work. - handle_tag((char_u *)params.tagname); + handle_tag(params.tagname); // Execute any "+", "-c" and "-S" arguments. if (params.n_commands > 0) { @@ -544,6 +580,13 @@ int main(int argc, char **argv) TIME_MSG("UIEnter autocommands"); } +#ifdef MSWIN + if (use_builtin_ui) { + os_icon_init(); + } + os_title_save(); +#endif + // 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 @@ -569,6 +612,12 @@ int main(int argc, char **argv) (void)eval_has_provider("clipboard"); } + if (params.luaf != NULL) { + bool lua_ok = nlua_exec_file(params.luaf); + TIME_MSG("executing Lua -l script"); + getout(lua_ok ? 0 : 1); + } + TIME_MSG("before starting main loop"); ILOG("starting main loop"); @@ -586,15 +635,19 @@ void os_exit(int r) { exiting = true; - ui_flush(); - ui_call_stop(); - ml_close_all(true); // remove all memfiles + if (ui_client_channel_id) { + ui_client_stop(); + } else { + ui_flush(); + ui_call_stop(); + ml_close_all(true); // remove all memfiles + } if (!event_teardown() && r == 0) { r = 1; // Exit with error if main_loop did not teardown gracefully. } - if (input_global_fd() >= 0) { - stream_set_blocking(input_global_fd(), true); // normalize stream (#2598) + if (used_stdin) { + stream_set_blocking(STDIN_FILENO, true); // normalize stream (#2598) } ILOG("Nvim exit: %d", r); @@ -612,9 +665,8 @@ void getout(int exitval) { exiting = true; - // When running in Ex mode an error causes us to exit with a non-zero exit - // code. POSIX requires this, although it's not 100% clear from the - // standard. + // On error during Ex mode, exit with a non-zero code. + // POSIX requires this, although it's not 100% clear from the standard. if (exmode_active) { exitval += ex_exitval; } @@ -705,6 +757,7 @@ void getout(int exitval) if (did_emsg) { // give the user a chance to read the (error) message no_wait_return = false; + // TODO(justinmk): this may call getout(0), clobbering exitval... wait_return(false); } @@ -713,21 +766,25 @@ void getout(int exitval) // Apply 'titleold'. if (p_title && *p_titleold != NUL) { - ui_call_set_title(cstr_as_string((char *)p_titleold)); + ui_call_set_title(cstr_as_string(p_titleold)); } - cs_end(); if (garbage_collect_at_exit) { garbage_collect(false); } +#ifdef MSWIN + // Restore Windows console icon before exiting. + os_icon_set(NULL, NULL); + os_title_reset(); +#endif + os_exit(exitval); } -/// Preserve files and exit. -/// @note IObuff must contain a message. -/// @note This may be called from deadly_signal() in a signal handler, avoid -/// unsafe functions, such as allocating memory. +/// Preserve files, print contents of `IObuff`, and exit 1. +/// +/// May be called from deadly_signal(). void preserve_exit(void) FUNC_ATTR_NORETURN { @@ -736,9 +793,9 @@ void preserve_exit(void) // Prevent repeated calls into this method. if (really_exiting) { - if (input_global_fd() >= 0) { + if (used_stdin) { // normalize stream (#2598) - stream_set_blocking(input_global_fd(), true); + stream_set_blocking(STDIN_FILENO, true); } exit(2); } @@ -746,15 +803,15 @@ void preserve_exit(void) really_exiting = true; // Ignore SIGHUP while we are already exiting. #9274 signal_reject_deadly(); - mch_errmsg(IObuff); - mch_errmsg("\n"); + os_errmsg(IObuff); + os_errmsg("\n"); ui_flush(); ml_close_notmod(); // close all not-modified buffers FOR_ALL_BUFFERS(buf) { if (buf->b_ml.ml_mfp != NULL && buf->b_ml.ml_mfp->mf_fname != NULL) { - mch_errmsg("Vim: preserving files...\r\n"); + os_errmsg("Vim: preserving files...\r\n"); ui_flush(); ml_sync_all(false, false, true); // preserve all swap files break; @@ -763,7 +820,7 @@ void preserve_exit(void) ml_close_all(false); // close all memfiles, without deleting - mch_errmsg("Vim: Finished.\r\n"); + os_errmsg("Vim: Finished.\r\n"); getout(1); } @@ -812,16 +869,25 @@ static uint64_t server_connect(char *server_addr, const char **errmsg) /// Handle remote subcommands static void remote_request(mparm_T *params, int remote_args, char *server_addr, int argc, - char **argv) + char **argv, bool ui_only) { + bool is_ui = strequal(argv[remote_args], "--remote-ui"); + if (ui_only && !is_ui) { + // TODO(bfredl): this implies always starting the TUI. + // if we be smart we could delay this past should_exit + return; + } + const char *connect_error = NULL; uint64_t chan = server_connect(server_addr, &connect_error); Object rvobj = OBJECT_INIT; - if (strequal(argv[remote_args], "--remote-ui-test")) { + if (is_ui) { if (!chan) { - emsg(connect_error); - exit(1); + os_errmsg("Remote ui failed to start: "); + os_errmsg(connect_error); + os_errmsg("\n"); + os_exit(1); } ui_client_channel_id = chan; @@ -845,15 +911,15 @@ static void remote_request(mparm_T *params, int remote_args, char *server_addr, Object o = nlua_exec(s, a, &err); api_free_array(a); if (ERROR_SET(&err)) { - mch_errmsg(err.msg); - mch_errmsg("\n"); + os_errmsg(err.msg); + os_errmsg("\n"); os_exit(2); } if (o.type == kObjectTypeDictionary) { rvobj.data.dictionary = o.data.dictionary; } else { - mch_errmsg("vim._cs_remote returned unexpected value\n"); + os_errmsg("vim._cs_remote returned unexpected value\n"); os_exit(2); } @@ -863,28 +929,28 @@ static void remote_request(mparm_T *params, int remote_args, char *server_addr, for (size_t i = 0; i < rvobj.data.dictionary.size; i++) { if (strcmp(rvobj.data.dictionary.items[i].key.data, "errmsg") == 0) { if (rvobj.data.dictionary.items[i].value.type != kObjectTypeString) { - mch_errmsg("vim._cs_remote returned an unexpected type for 'errmsg'\n"); + os_errmsg("vim._cs_remote returned an unexpected type for 'errmsg'\n"); os_exit(2); } - mch_errmsg(rvobj.data.dictionary.items[i].value.data.string.data); - mch_errmsg("\n"); + os_errmsg(rvobj.data.dictionary.items[i].value.data.string.data); + os_errmsg("\n"); os_exit(2); } else if (strcmp(rvobj.data.dictionary.items[i].key.data, "tabbed") == 0) { if (rvobj.data.dictionary.items[i].value.type != kObjectTypeBoolean) { - mch_errmsg("vim._cs_remote returned an unexpected type for 'tabbed'\n"); + os_errmsg("vim._cs_remote returned an unexpected type for 'tabbed'\n"); os_exit(2); } tabbed = rvobj.data.dictionary.items[i].value.data.boolean ? kTrue : kFalse; } else if (strcmp(rvobj.data.dictionary.items[i].key.data, "should_exit") == 0) { if (rvobj.data.dictionary.items[i].value.type != kObjectTypeBoolean) { - mch_errmsg("vim._cs_remote returned an unexpected type for 'should_exit'\n"); + os_errmsg("vim._cs_remote returned an unexpected type for 'should_exit'\n"); os_exit(2); } should_exit = rvobj.data.dictionary.items[i].value.data.boolean ? kTrue : kFalse; } } if (should_exit == kNone || tabbed == kNone) { - mch_errmsg("vim._cs_remote didn't return a value for should_exit or tabbed, bailing\n"); + os_errmsg("vim._cs_remote didn't return a value for should_exit or tabbed, bailing\n"); os_exit(2); } api_free_object(o); @@ -900,14 +966,14 @@ static void remote_request(mparm_T *params, int remote_args, char *server_addr, /// Decides whether text (as opposed to commands) will be read from stdin. /// @see EDIT_STDIN -static bool edit_stdin(bool explicit, mparm_T *parmp) +static bool edit_stdin(mparm_T *parmp) { bool implicit = !headless_mode - && !embedded_mode - && (!exmode_active || parmp->input_neverscript) - && !parmp->input_isatty - && scriptin[0] == NULL; // `-s -` was not given. - return explicit || implicit; + && !(embedded_mode && stdin_fd <= 0) + && (!exmode_active || parmp->input_istext) + && !stdin_isatty + && parmp->scriptin == NULL; // `-s -` was not given. + return parmp->had_stdin_file || implicit; } /// Scan the command line arguments. @@ -916,7 +982,6 @@ static void command_line_scan(mparm_T *parmp) int argc = parmp->argc; 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 long n; @@ -936,9 +1001,9 @@ static void command_line_scan(mparm_T *parmp) } else { parmp->commands[parmp->n_commands++] = &(argv[0][1]); } - - // Optional argument. } else if (argv[0][0] == '-' && !had_minmin) { + // Optional argument. + want_argument = false; char c = argv[0][argv_idx++]; switch (c) { @@ -948,17 +1013,15 @@ static void command_line_scan(mparm_T *parmp) silent_mode = true; parmp->no_swap_file = true; } else { - if (parmp->edit_type != EDIT_NONE - && parmp->edit_type != EDIT_FILE - && parmp->edit_type != EDIT_STDIN) { + if (parmp->edit_type > EDIT_STDIN) { mainerr(err_too_many_args, argv[0]); } - had_stdin_file = true; + parmp->had_stdin_file = true; parmp->edit_type = EDIT_STDIN; } argv_idx = -1; // skip to next argument break; - case '-': // "--" don't take any more option arguments + case '-': // "--" No more option arguments. // "--help" give help message // "--version" give version message // "--noplugin[s]" skip plugins @@ -1054,7 +1117,7 @@ static void command_line_scan(mparm_T *parmp) break; case 'E': // "-E" Ex mode exmode_active = true; - parmp->input_neverscript = true; + parmp->input_istext = true; break; case 'f': // "-f" GUI: run in foreground. break; @@ -1066,10 +1129,6 @@ static void command_line_scan(mparm_T *parmp) p_hkmap = true; set_option_value_give_err("rl", 1L, NULL, 0); break; - case 'l': // "-l" lisp mode, 'lisp' and 'showmatch' on. - set_option_value_give_err("lisp", 1L, NULL, 0); - p_sm = true; - break; case 'M': // "-M" no changes or writing of files reset_modifiable(); FALLTHROUGH; @@ -1154,7 +1213,7 @@ static void command_line_scan(mparm_T *parmp) break; case 'w': // "-w{number}" set window height // "-w {scriptout}" write to script - if (ascii_isdigit(((char_u *)argv[0])[argv_idx])) { + if (ascii_isdigit((argv[0])[argv_idx])) { n = get_number_arg(argv[0], &argv_idx, 10); set_option_value_give_err("window", n, NULL, 0); break; @@ -1174,6 +1233,7 @@ static void command_line_scan(mparm_T *parmp) FALLTHROUGH; case 'S': // "-S {file}" execute Vim script case 'i': // "-i {shada}" use for ShaDa file + case 'l': // "-l {file}" Lua mode case 'u': // "-u {vimrc}" vim inits file case 'U': // "-U {gvimrc}" gvim inits file case 'W': // "-W {scriptout}" overwrite @@ -1254,38 +1314,34 @@ static void command_line_scan(mparm_T *parmp) set_option_value_give_err("shadafile", 0L, argv[0], 0); break; - case 's': { // "-s {scriptin}" read from script file - if (scriptin[0] != NULL) { + case 'l': // "-l" Lua script: args after "-l". + headless_mode = true; + silent_mode = true; + p_verbose = 1; + parmp->no_swap_file = true; + parmp->use_vimrc = parmp->use_vimrc ? parmp->use_vimrc : "NONE"; + if (p_shadafile == NULL || *p_shadafile == NUL) { + set_option_value_give_err("shadafile", 0L, "NONE", 0); + } + parmp->luaf = argv[0]; + argc--; + if (argc > 0) { // Lua args after "-l <file>". + parmp->lua_arg0 = parmp->argc - argc; + argc = 0; + } + break; + + case 's': // "-s {scriptin}" read from script file + if (parmp->scriptin != NULL) { scripterror: - vim_snprintf((char *)IObuff, IOSIZE, + vim_snprintf(IObuff, IOSIZE, _("Attempt to open script file again: \"%s %s\"\n"), argv[-1], argv[0]); - mch_errmsg((const char *)IObuff); + os_errmsg(IObuff); os_exit(2); } - int error; - if (strequal(argv[0], "-")) { - const int stdin_dup_fd = os_dup(STDIN_FILENO); -#ifdef MSWIN - // Replace the original stdin with the console input handle. - os_replace_stdin_to_conin(); -#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); - os_exit(2); - } - save_typebuf(); + parmp->scriptin = argv[0]; break; - } case 't': // "-t {tag}" parmp->tagname = argv[0]; @@ -1298,7 +1354,7 @@ scripterror: case 'w': // "-w {nr}" 'window' value // "-w {scriptout}" append to script file - if (ascii_isdigit(*((char_u *)argv[0]))) { + if (ascii_isdigit(*(argv[0]))) { argv_idx = 0; n = get_number_arg(argv[0], &argv_idx, 10); set_option_value_give_err("window", n, NULL, 0); @@ -1307,26 +1363,18 @@ scripterror: } FALLTHROUGH; case 'W': // "-W {scriptout}" overwrite script file - if (scriptout != NULL) { + if (parmp->scriptout != NULL) { goto scripterror; } - if ((scriptout = os_fopen(argv[0], c == 'w' ? APPENDBIN : WRITEBIN)) - == NULL) { - mch_errmsg(_("Cannot open for script output: \"")); - mch_errmsg(argv[0]); - mch_errmsg("\"\n"); - os_exit(2); - } - break; + parmp->scriptout = argv[0]; + parmp->scriptout_append = (c == 'w'); } } } 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) { + if (parmp->edit_type > EDIT_STDIN) { mainerr(err_too_many_args, argv[0]); } parmp->edit_type = EDIT_FILE; @@ -1347,7 +1395,7 @@ scripterror: path_fix_case(p); #endif - int alist_fnum_flag = edit_stdin(had_stdin_file, parmp) + int alist_fnum_flag = edit_stdin(parmp) ? 1 // add buffer nr after exp. : 2; // add buffer number now and use curbuf alist_add(&global_alist, p, alist_fnum_flag); @@ -1362,8 +1410,8 @@ scripterror: } } - if (embedded_mode && silent_mode) { - mainerr(_("--embed conflicts with -es/-Es"), NULL); + if (embedded_mode && (silent_mode || parmp->luaf)) { + mainerr(_("--embed conflicts with -es/-Es/-l"), NULL); } // If there is a "+123" or "-c" command, set v:swapcommand to the first one. @@ -1375,11 +1423,6 @@ scripterror: 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"); } @@ -1396,6 +1439,8 @@ static void init_params(mparm_T *paramp, int argc, char **argv) paramp->listen_addr = NULL; paramp->server_addr = NULL; paramp->remote = 0; + paramp->luaf = NULL; + paramp->lua_arg0 = -1; } /// Initialize global startuptime file if "--startuptime" passed as an argument. @@ -1408,25 +1453,13 @@ static void init_startuptime(mparm_T *paramp) break; } } - - starttime = time(NULL); } static void check_and_set_isatty(mparm_T *paramp) { - 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 MSWIN - 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 + stdin_isatty = os_isatty(STDIN_FILENO); + stdout_isatty = os_isatty(STDOUT_FILENO); + stderr_isatty = os_isatty(STDERR_FILENO); TIME_MSG("window checked"); } @@ -1452,9 +1485,9 @@ static void init_path(const char *exename) } /// Get filename from command line, if any. -static char_u *get_fname(mparm_T *parmp, char_u *cwd) +static char *get_fname(mparm_T *parmp, char *cwd) { - return (char_u *)alist_name(&GARGLIST[0]); + return alist_name(&GARGLIST[0]); } // Decide about window layout for diff mode after reading vimrc. @@ -1477,8 +1510,8 @@ static void handle_quickfix(mparm_T *paramp) if (paramp->use_ef != NULL) { set_string_option_direct("ef", -1, paramp->use_ef, OPT_FREE, SID_CARG); } - vim_snprintf((char *)IObuff, IOSIZE, "cfile %s", p_ef); - if (qf_init(NULL, (char *)p_ef, p_efm, true, (char *)IObuff, p_menc) < 0) { + vim_snprintf(IObuff, IOSIZE, "cfile %s", p_ef); + if (qf_init(NULL, p_ef, p_efm, true, IObuff, p_menc) < 0) { msg_putchar('\n'); os_exit(3); } @@ -1488,13 +1521,13 @@ static void handle_quickfix(mparm_T *paramp) // Need to jump to the tag before executing the '-c command'. // Makes "vim -c '/return' -t main" work. -static void handle_tag(char_u *tagname) +static void handle_tag(char *tagname) { if (tagname != NULL) { swap_exists_did_quit = false; - vim_snprintf((char *)IObuff, IOSIZE, "ta %s", tagname); - do_cmdline_cmd((char *)IObuff); + vim_snprintf(IObuff, IOSIZE, "ta %s", tagname); + do_cmdline_cmd(IObuff); TIME_MSG("jumping to tag"); // If the user doesn't want to edit the file then we quit here. @@ -1526,6 +1559,38 @@ static void read_stdin(void) check_swap_exists_action(); } +static void open_script_files(mparm_T *parmp) +{ + if (parmp->scriptin) { + int error; + if (strequal(parmp->scriptin, "-")) { + FileDescriptor *stdin_dup = file_open_stdin(); + scriptin[0] = stdin_dup; + } else { + scriptin[0] = file_open_new(&error, parmp->scriptin, + kFileReadOnly|kFileNonBlocking, 0); + if (scriptin[0] == NULL) { + vim_snprintf((char *)IObuff, IOSIZE, + _("Cannot open for reading: \"%s\": %s\n"), + parmp->scriptin, os_strerror(error)); + os_errmsg(IObuff); + os_exit(2); + } + } + save_typebuf(); + } + + if (parmp->scriptout) { + scriptout = os_fopen(parmp->scriptout, parmp->scriptout_append ? APPENDBIN : WRITEBIN); + if (scriptout == NULL) { + os_errmsg(_("Cannot open for script output: \"")); + os_errmsg(parmp->scriptout); + os_errmsg("\"\n"); + os_exit(2); + } + } +} + // Create the requested number of windows and edit buffers in them. // Also does recovery if "recoverymode" set. static void create_windows(mparm_T *parmp) @@ -1644,7 +1709,7 @@ static void create_windows(mparm_T *parmp) /// If opened more than one window, start editing files in the other /// windows. make_windows() has already opened the windows. -static void edit_buffers(mparm_T *parmp, char_u *cwd) +static void edit_buffers(mparm_T *parmp, char *cwd) { int arg_idx; // index in argument list int i; @@ -1665,7 +1730,7 @@ static void edit_buffers(mparm_T *parmp, char_u *cwd) arg_idx = 1; for (i = 1; i < parmp->window_count; i++) { if (cwd != NULL) { - os_chdir((char *)cwd); + os_chdir(cwd); } // When w_arg_idx is -1 remove the window (see create_windows()). if (curwin->w_arg_idx == -1) { @@ -1768,17 +1833,19 @@ static void exe_pre_commands(mparm_T *parmp) int cnt = parmp->n_pre_commands; int i; - if (cnt > 0) { - curwin->w_cursor.lnum = 0; // just in case.. - estack_push(ETYPE_ARGS, _("pre-vimrc command line"), 0); - current_sctx.sc_sid = SID_CMDARG; - for (i = 0; i < cnt; i++) { - do_cmdline_cmd(cmds[i]); - } - estack_pop(); - current_sctx.sc_sid = 0; - TIME_MSG("--cmd commands"); + if (cnt <= 0) { + return; + } + + curwin->w_cursor.lnum = 0; // just in case.. + estack_push(ETYPE_ARGS, _("pre-vimrc command line"), 0); + current_sctx.sc_sid = SID_CMDARG; + for (i = 0; i < cnt; i++) { + do_cmdline_cmd(cmds[i]); } + estack_pop(); + current_sctx.sc_sid = 0; + TIME_MSG("--cmd commands"); } // Execute "+", "-c" and "-S" arguments. @@ -1884,28 +1951,29 @@ static bool do_user_initialization(void) return do_exrc; } - char_u *init_lua_path = (char_u *)stdpaths_user_conf_subpath("init.lua"); - char_u *user_vimrc = (char_u *)stdpaths_user_conf_subpath("init.vim"); + char *init_lua_path = stdpaths_user_conf_subpath("init.lua"); + char *user_vimrc = stdpaths_user_conf_subpath("init.vim"); // init.lua - if (os_path_exists((char *)init_lua_path) - && do_source((char *)init_lua_path, true, DOSO_VIMRC)) { - if (os_path_exists((char *)user_vimrc)) { + if (os_path_exists(init_lua_path) + && do_source(init_lua_path, true, DOSO_VIMRC)) { + if (os_path_exists(user_vimrc)) { semsg(_("E5422: Conflicting configs: \"%s\" \"%s\""), init_lua_path, user_vimrc); } xfree(user_vimrc); xfree(init_lua_path); - return false; + do_exrc = p_exrc; + return do_exrc; } xfree(init_lua_path); // init.vim - if (do_source((char *)user_vimrc, true, DOSO_VIMRC) != FAIL) { + if (do_source(user_vimrc, true, DOSO_VIMRC) != FAIL) { do_exrc = p_exrc; if (do_exrc) { - do_exrc = (path_full_compare(VIMRC_FILE, (char *)user_vimrc, false, true) != kEqualFiles); + do_exrc = (path_full_compare(VIMRC_FILE, user_vimrc, false, true) != kEqualFiles); } xfree(user_vimrc); return do_exrc; @@ -1949,6 +2017,41 @@ static bool do_user_initialization(void) return do_exrc; } +// Read initialization commands from ".nvim.lua", ".nvimrc", or ".exrc" in +// current directory. This is only done if the 'exrc' option is set. +// Only do this if VIMRC_FILE is not the same as vimrc file sourced in +// do_user_initialization. +static void do_exrc_initialization(void) +{ + char *str; + + if (os_path_exists(VIMRC_LUA_FILE)) { + str = nlua_read_secure(VIMRC_LUA_FILE); + if (str != NULL) { + Error err = ERROR_INIT; + nlua_exec(cstr_as_string(str), (Array)ARRAY_DICT_INIT, &err); + xfree(str); + if (ERROR_SET(&err)) { + semsg("Error detected while processing %s:", VIMRC_LUA_FILE); + semsg_multiline(err.msg); + api_clear_error(&err); + } + } + } else if (os_path_exists(VIMRC_FILE)) { + str = nlua_read_secure(VIMRC_FILE); + if (str != NULL) { + do_source_str(str, VIMRC_FILE); + xfree(str); + } + } else if (os_path_exists(EXRC_FILE)) { + str = nlua_read_secure(EXRC_FILE); + if (str != NULL) { + do_source_str(str, EXRC_FILE); + xfree(str); + } + } +} + /// Source startup scripts static void source_startup_scripts(const mparm_T *const parmp) FUNC_ATTR_NONNULL_ALL @@ -1967,35 +2070,8 @@ static void source_startup_scripts(const mparm_T *const parmp) do_system_initialization(); if (do_user_initialization()) { - // Read initialization commands from ".vimrc" or ".exrc" in current - // directory. This is only done if the 'exrc' option is set. - // Because of security reasons we disallow shell and write commands - // now, except for unix if the file is owned by the user or 'secure' - // option has been reset in environment of global "exrc" or "vimrc". - // Only do this if VIMRC_FILE is not the same as vimrc file sourced in - // do_user_initialization. -#if defined(UNIX) - // If vimrc file is not owned by user, set 'secure' mode. - if (!os_file_owned(VIMRC_FILE)) // NOLINT(readability/braces) -#endif - secure = p_secure; - - if (do_source(VIMRC_FILE, true, DOSO_VIMRC) == FAIL) { -#if defined(UNIX) - // if ".exrc" is not owned by user set 'secure' mode - if (!os_file_owned(EXRC_FILE)) { - secure = p_secure; - } else { - secure = 0; - } -#endif - (void)do_source(EXRC_FILE, false, DOSO_NONE); - } + do_exrc_initialization(); } - if (secure == 2) { - need_wait_return = true; - } - secure = 0; } TIME_MSG("sourcing vimrc file(s)"); } @@ -2010,19 +2086,20 @@ static int execute_env(char *env) FUNC_ATTR_NONNULL_ALL { const char *initstr = os_getenv(env); - if (initstr != NULL) { - estack_push(ETYPE_ENV, env, 0); - const sctx_T save_current_sctx = current_sctx; - current_sctx.sc_sid = SID_ENV; - current_sctx.sc_seq = 0; - current_sctx.sc_lnum = 0; - do_cmdline_cmd((char *)initstr); - - estack_pop(); - current_sctx = save_current_sctx; - return OK; - } - return FAIL; + if (initstr == NULL) { + return FAIL; + } + + estack_push(ETYPE_ENV, env, 0); + const sctx_T save_current_sctx = current_sctx; + current_sctx.sc_sid = SID_ENV; + current_sctx.sc_seq = 0; + current_sctx.sc_lnum = 0; + do_cmdline_cmd((char *)initstr); + + estack_pop(); + current_sctx = save_current_sctx; + return OK; } /// Prints the following then exits: @@ -2038,17 +2115,17 @@ static void mainerr(const char *errstr, const char *str) signal_stop(); // kill us with CTRL-C here, if you like - mch_errmsg(prgname); - mch_errmsg(": "); - mch_errmsg(_(errstr)); + os_errmsg(prgname); + os_errmsg(": "); + os_errmsg(_(errstr)); if (str != NULL) { - mch_errmsg(": \""); - mch_errmsg(str); - mch_errmsg("\""); + os_errmsg(": \""); + os_errmsg((char *)str); + os_errmsg("\""); } - mch_errmsg(_("\nMore info with \"")); - mch_errmsg(prgname); - mch_errmsg(" -h\"\n"); + os_errmsg(_("\nMore info with \"")); + os_errmsg(prgname); + os_errmsg(" -h\"\n"); os_exit(1); } @@ -2057,8 +2134,8 @@ static void mainerr(const char *errstr, const char *str) static void version(void) { // TODO(bfred): not like this? - nlua_init(); - info_message = true; // use mch_msg(), not mch_errmsg() + nlua_init(NULL, 0, -1); + info_message = true; // use os_msg(), not os_errmsg() list_version(); msg_putchar('\n'); msg_didout = false; @@ -2069,47 +2146,48 @@ static void usage(void) { signal_stop(); // kill us with CTRL-C here, if you like - mch_msg(_("Usage:\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")); - 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(_(" -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(_(" -n No swap file, use memory only\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(_(" -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("\n"); - mch_msg(_(" --api-info Write msgpack-encoded API metadata to stdout\n")); - mch_msg(_(" --clean \"Factory defaults\" (skip user config and plugins, shada)\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(_(" --listen <address> Serve RPC API from this address\n")); - mch_msg(_(" --noplugin Don't load plugins\n")); - mch_msg(_(" --remote[-subcommand] Execute commands remotely on a server\n")); - mch_msg(_(" --server <address> Specify RPC server to send commands to\n")); - mch_msg(_(" --startuptime <file> Write startup timing messages to <file>\n")); - mch_msg(_("\nSee \":help startup-options\" for all options.\n")); + os_msg(_("Usage:\n")); + os_msg(_(" nvim [options] [file ...] Edit file(s)\n")); + os_msg(_(" nvim [options] -t <tag> Edit file where tag is defined\n")); + os_msg(_(" nvim [options] -q [errorfile] Edit file with first error\n")); + os_msg(_("\nOptions:\n")); + os_msg(_(" -- Only file names after this\n")); + os_msg(_(" + Start at end of file\n")); + os_msg(_(" --cmd <cmd> Execute <cmd> before any config\n")); + os_msg(_(" +<cmd>, -c <cmd> Execute <cmd> after config and first file\n")); + os_msg(_(" -l <script> [args...] Execute Lua <script> (with optional args)\n")); + os_msg("\n"); + os_msg(_(" -b Binary mode\n")); + os_msg(_(" -d Diff mode\n")); + os_msg(_(" -e, -E Ex mode\n")); + os_msg(_(" -es, -Es Silent (batch) mode\n")); + os_msg(_(" -h, --help Print this help message\n")); + os_msg(_(" -i <shada> Use this shada file\n")); + os_msg(_(" -m Modifications (writing files) not allowed\n")); + os_msg(_(" -M Modifications in text not allowed\n")); + os_msg(_(" -n No swap file, use memory only\n")); + os_msg(_(" -o[N] Open N windows (default: one per file)\n")); + os_msg(_(" -O[N] Open N vertical windows (default: one per file)\n")); + os_msg(_(" -p[N] Open N tab pages (default: one per file)\n")); + os_msg(_(" -r, -L List swap files\n")); + os_msg(_(" -r <file> Recover edit state for this file\n")); + os_msg(_(" -R Read-only mode\n")); + os_msg(_(" -S <session> Source <session> after loading the first file\n")); + os_msg(_(" -s <scriptin> Read Normal mode commands from <scriptin>\n")); + os_msg(_(" -u <config> Use this config file\n")); + os_msg(_(" -v, --version Print version information\n")); + os_msg(_(" -V[N][file] Verbose [level][file]\n")); + os_msg("\n"); + os_msg(_(" --api-info Write msgpack-encoded API metadata to stdout\n")); + os_msg(_(" --clean \"Factory defaults\" (skip user config and plugins, shada)\n")); + os_msg(_(" --embed Use stdin/stdout as a msgpack-rpc channel\n")); + os_msg(_(" --headless Don't start a user interface\n")); + os_msg(_(" --listen <address> Serve RPC API from this address\n")); + os_msg(_(" --noplugin Don't load plugins\n")); + os_msg(_(" --remote[-subcommand] Execute commands remotely on a server\n")); + os_msg(_(" --server <address> Specify RPC server to send commands to\n")); + os_msg(_(" --startuptime <file> Write startup timing messages to <file>\n")); + os_msg(_("\nSee \":help startup-options\" for all options.\n")); } // Check the result of the ATTENTION dialog: |