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