1// This is an open source non-commercial project. Dear PVS-Studio, please check
2// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
3
4#define EXTERN
5#include <assert.h>
6#include <stdint.h>
7#include <string.h>
8#include <stdbool.h>
9
10#include <msgpack.h>
11
12#include "nvim/ascii.h"
13#include "nvim/vim.h"
14#include "nvim/main.h"
15#include "nvim/aucmd.h"
16#include "nvim/buffer.h"
17#include "nvim/charset.h"
18#include "nvim/diff.h"
19#include "nvim/eval.h"
20#include "nvim/ex_cmds.h"
21#include "nvim/ex_cmds2.h"
22#include "nvim/ex_docmd.h"
23#include "nvim/fileio.h"
24#include "nvim/fold.h"
25#include "nvim/getchar.h"
26#include "nvim/hashtab.h"
27#include "nvim/highlight.h"
28#include "nvim/iconv.h"
29#include "nvim/if_cscope.h"
30#ifdef HAVE_LOCALE_H
31# include <locale.h>
32#endif
33#include "nvim/mark.h"
34#include "nvim/mbyte.h"
35#include "nvim/memline.h"
36#include "nvim/message.h"
37#include "nvim/misc1.h"
38#include "nvim/garray.h"
39#include "nvim/log.h"
40#include "nvim/memory.h"
41#include "nvim/move.h"
42#include "nvim/mouse.h"
43#include "nvim/normal.h"
44#include "nvim/ops.h"
45#include "nvim/option.h"
46#include "nvim/os_unix.h"
47#include "nvim/os/os_defs.h"
48#include "nvim/path.h"
49#include "nvim/profile.h"
50#include "nvim/popupmnu.h"
51#include "nvim/quickfix.h"
52#include "nvim/screen.h"
53#include "nvim/sign.h"
54#include "nvim/state.h"
55#include "nvim/strings.h"
56#include "nvim/syntax.h"
57#include "nvim/ui.h"
58#include "nvim/ui_compositor.h"
59#include "nvim/version.h"
60#include "nvim/window.h"
61#include "nvim/shada.h"
62#include "nvim/os/input.h"
63#include "nvim/os/os.h"
64#include "nvim/os/time.h"
65#include "nvim/os/fileio.h"
66#include "nvim/event/loop.h"
67#include "nvim/os/signal.h"
68#include "nvim/event/process.h"
69#include "nvim/msgpack_rpc/helpers.h"
70#include "nvim/msgpack_rpc/server.h"
71#include "nvim/msgpack_rpc/channel.h"
72#include "nvim/api/ui.h"
73#include "nvim/api/private/defs.h"
74#include "nvim/api/private/helpers.h"
75#include "nvim/api/private/handle.h"
76#include "nvim/api/private/dispatch.h"
77#ifndef WIN32
78# include "nvim/os/pty_process_unix.h"
79#endif
80#include "nvim/api/vim.h"
81
82// Maximum number of commands from + or -c arguments.
83#define MAX_ARG_CMDS 10
84
85// values for "window_layout"
86#define WIN_HOR 1 // "-o" horizontally split windows
87#define WIN_VER 2 // "-O" vertically split windows
88#define WIN_TABS 3 // "-p" windows on tab pages
89
90// Struct for various parameters passed between main() and other functions.
91typedef struct {
92 int argc;
93 char **argv;
94
95 char *use_vimrc; // vimrc from -u argument
96
97 int n_commands; // no. of commands from + or -c
98 char *commands[MAX_ARG_CMDS]; // commands from + or -c arg
99 char_u cmds_tofree[MAX_ARG_CMDS]; // commands that need free()
100 int n_pre_commands; // no. of commands from --cmd
101 char *pre_commands[MAX_ARG_CMDS]; // commands from --cmd argument
102
103 int edit_type; // type of editing to do
104 char_u *tagname; // tag from -t argument
105 char_u *use_ef; // 'errorfile' from -q argument
106
107 bool input_isatty; // stdin is a terminal
108 bool output_isatty; // stdout is a terminal
109 bool err_isatty; // stderr is a terminal
110 int no_swap_file; // "-n" argument used
111 int use_debug_break_level;
112 int window_count; // number of windows to use
113 int window_layout; // 0, WIN_HOR, WIN_VER or WIN_TABS
114
115 int diff_mode; // start with 'diff' set
116
117 char *listen_addr; // --listen {address}
118} mparm_T;
119
120// Values for edit_type.
121#define EDIT_NONE 0 // no edit type yet
122#define EDIT_FILE 1 // file name argument[s] given, use argument list
123#define EDIT_STDIN 2 // read file from stdin
124#define EDIT_TAG 3 // tag name argument given, use tagname
125#define EDIT_QF 4 // start in quickfix mode
126
127#ifdef INCLUDE_GENERATED_DECLARATIONS
128# include "main.c.generated.h"
129#endif
130
131Loop main_loop;
132
133static char *argv0 = NULL;
134
135// Error messages
136static const char *err_arg_missing = N_("Argument missing after");
137static const char *err_opt_garbage = N_("Garbage after option argument");
138static const char *err_opt_unknown = N_("Unknown option argument");
139static const char *err_too_many_args = N_("Too many edit arguments");
140static const char *err_extra_cmd =
141 N_("Too many \"+command\", \"-c command\" or \"--cmd command\" arguments");
142
143
144void event_init(void)
145{
146 log_init();
147 loop_init(&main_loop, NULL);
148 resize_events = multiqueue_new_child(main_loop.events);
149
150 // early msgpack-rpc initialization
151 msgpack_rpc_init_method_table();
152 msgpack_rpc_helpers_init();
153 input_init();
154 signal_init();
155 // finish mspgack-rpc initialization
156 channel_init();
157 remote_ui_init();
158 api_vim_init();
159 terminal_init();
160 ui_init();
161}
162
163/// @returns false if main_loop could not be closed gracefully
164bool event_teardown(void)
165{
166 if (!main_loop.events) {
167 input_stop();
168 return true;
169 }
170
171 multiqueue_process_events(main_loop.events);
172 loop_poll_events(&main_loop, 0); // Drain thread_events, fast_events.
173 input_stop();
174 channel_teardown();
175 process_teardown(&main_loop);
176 timer_teardown();
177 server_teardown();
178 signal_teardown();
179 terminal_teardown();
180
181 return loop_close(&main_loop, true);
182}
183
184/// Performs early initialization.
185///
186/// Needed for unit tests. Must be called after `time_init()`.
187void early_init(void)
188{
189 env_init();
190 fs_init();
191 handle_init();
192 eval_init(); // init global variables
193 init_path(argv0 ? argv0 : "nvim");
194 init_normal_cmds(); // Init the table of Normal mode commands.
195 highlight_init();
196
197#if defined(HAVE_LOCALE_H)
198 // Setup to use the current locale (for ctype() and many other things).
199 // NOTE: Translated messages with encodings other than latin1 will not
200 // work until set_init_1() has been called!
201 init_locale();
202#endif
203
204 // Allocate the first window and buffer.
205 // Can't do anything without it, exit when it fails.
206 if (!win_alloc_first()) {
207 mch_exit(0);
208 }
209
210 init_yank(); // init yank buffers
211
212 alist_init(&global_alist); // Init the argument list to empty.
213 global_alist.id = 0;
214
215 // Set the default values for the options.
216 // NOTE: Non-latin1 translated messages are working only after this,
217 // because this is where "has_mbyte" will be set, which is used by
218 // msg_outtrans_len_attr().
219 // First find out the home directory, needed to expand "~" in options.
220 init_homedir(); // find real value of $HOME
221 set_init_1();
222 TIME_MSG("inits 1");
223
224 set_lang_var(); // set v:lang and v:ctype
225
226 init_signs();
227 ui_comp_syn_init();
228}
229
230#ifdef MAKE_LIB
231int nvim_main(int argc, char **argv); // silence -Wmissing-prototypes
232int nvim_main(int argc, char **argv)
233#elif defined(WIN32)
234int wmain(int argc, wchar_t **argv_w) // multibyte args on Windows. #7060
235#else
236int main(int argc, char **argv)
237#endif
238{
239#if defined(WIN32) && !defined(MAKE_LIB)
240 char **argv = xmalloc((size_t)argc * sizeof(char *));
241 for (int i = 0; i < argc; i++) {
242 char *buf = NULL;
243 utf16_to_utf8(argv_w[i], -1, &buf);
244 assert(buf);
245 argv[i] = buf;
246 }
247#endif
248
249 argv0 = argv[0];
250
251 char_u *fname = NULL; // file name from command line
252 mparm_T params; // various parameters passed between
253 // main() and other functions.
254 char_u *cwd = NULL; // current working dir on startup
255 time_init();
256
257 // Many variables are in `params` so that we can pass them around easily.
258 // `argc` and `argv` are also copied, so that they can be changed.
259 init_params(&params, argc, argv);
260
261 init_startuptime(&params);
262
263 event_init();
264
265 early_init();
266
267 // Check if we have an interactive window.
268 check_and_set_isatty(&params);
269
270 // Process the command line arguments. File names are put in the global
271 // argument list "global_alist".
272 command_line_scan(&params);
273
274 if (embedded_mode) {
275 const char *err;
276 if (!channel_from_stdio(true, CALLBACK_READER_INIT, &err)) {
277 abort();
278 }
279 }
280
281 server_init(params.listen_addr);
282
283 if (GARGCOUNT > 0) {
284 fname = get_fname(&params, cwd);
285 }
286
287 TIME_MSG("expanding arguments");
288
289 if (params.diff_mode && params.window_count == -1)
290 params.window_count = 0; /* open up to 3 windows */
291
292 /* Don't redraw until much later. */
293 ++RedrawingDisabled;
294
295 setbuf(stdout, NULL);
296
297 full_screen = !silent_mode;
298
299 // Set the default values for the options that use Rows and Columns.
300 win_init_size();
301 // Set the 'diff' option now, so that it can be checked for in a vimrc
302 // file. There is no buffer yet though.
303 if (params.diff_mode) {
304 diff_win_options(firstwin, false);
305 }
306
307 assert(p_ch >= 0 && Rows >= p_ch && Rows - p_ch <= INT_MAX);
308 cmdline_row = (int)(Rows - p_ch);
309 msg_row = cmdline_row;
310 screenalloc(); // allocate screen buffers
311 set_init_2(headless_mode);
312 TIME_MSG("inits 2");
313
314 msg_scroll = true;
315 no_wait_return = true;
316
317 init_highlight(true, false); // Default highlight groups.
318 TIME_MSG("init highlight");
319
320 // Set the break level after the terminal is initialized.
321 debug_break_level = params.use_debug_break_level;
322
323 // Read ex-commands if invoked with "-es".
324 if (!params.input_isatty && silent_mode && exmode_active == EXMODE_NORMAL) {
325 input_start(STDIN_FILENO);
326 }
327
328 // open terminals when opening files that start with term://
329#define PROTO "term://"
330 do_cmdline_cmd("augroup nvim_terminal");
331 do_cmdline_cmd("autocmd!");
332 do_cmdline_cmd("autocmd BufReadCmd " PROTO "* nested "
333 ":if !exists('b:term_title')|call termopen( "
334 // Capture the command string
335 "matchstr(expand(\"<amatch>\"), "
336 "'\\c\\m" PROTO "\\%(.\\{-}//\\%(\\d\\+:\\)\\?\\)\\?\\zs.*'), "
337 // capture the working directory
338 "{'cwd': get(matchlist(expand(\"<amatch>\"), "
339 "'\\c\\m" PROTO "\\(.\\{-}\\)//'), 1, '')})"
340 "|endif");
341 do_cmdline_cmd("augroup END");
342#undef PROTO
343
344 // Reset 'loadplugins' for "-u NONE" before "--cmd" arguments.
345 // Allows for setting 'loadplugins' there.
346 if (params.use_vimrc != NULL && strequal(params.use_vimrc, "NONE")) {
347 p_lpl = false;
348 }
349
350 // Wait for UIs to set up Nvim or show early messages
351 // and prompts (--cmd, swapfile dialog, …).
352 bool use_remote_ui = (embedded_mode && !headless_mode);
353 bool use_builtin_ui = (!headless_mode && !embedded_mode && !silent_mode);
354 if (use_remote_ui || use_builtin_ui) {
355 TIME_MSG("waiting for UI");
356 if (use_remote_ui) {
357 remote_ui_wait_for_attach();
358 } else {
359 ui_builtin_start();
360 }
361 TIME_MSG("done waiting for UI");
362
363 // prepare screen now, so external UIs can display messages
364 starting = NO_BUFFERS;
365 screenclear();
366 TIME_MSG("initialized screen early for UI");
367 }
368
369 // Execute --cmd arguments.
370 exe_pre_commands(&params);
371
372 // Source startup scripts.
373 source_startup_scripts(&params);
374
375 // If using the runtime (-u is not NONE), enable syntax & filetype plugins.
376 if (params.use_vimrc == NULL || !strequal(params.use_vimrc, "NONE")) {
377 // Does ":filetype plugin indent on".
378 filetype_maybe_enable();
379 // Sources syntax/syntax.vim, which calls `:filetype on`.
380 syn_maybe_on();
381 }
382
383 /*
384 * Read all the plugin files.
385 * Only when compiled with +eval, since most plugins need it.
386 */
387 load_plugins();
388
389 // Decide about window layout for diff mode after reading vimrc.
390 set_window_layout(&params);
391
392 /*
393 * Recovery mode without a file name: List swap files.
394 * This uses the 'dir' option, therefore it must be after the
395 * initializations.
396 */
397 if (recoverymode && fname == NULL) {
398 recover_names(NULL, TRUE, 0, NULL);
399 mch_exit(0);
400 }
401
402 // Set some option defaults after reading vimrc files.
403 set_init_3();
404 TIME_MSG("inits 3");
405
406 // "-n" argument: Disable swap file by setting 'updatecount' to 0.
407 // Note that this overrides anything from a vimrc file.
408 if (params.no_swap_file) {
409 p_uc = 0;
410 }
411
412 // XXX: Minimize 'updatetime' for -es/-Es. #7679
413 if (silent_mode) {
414 p_ut = 1;
415 }
416
417 //
418 // Read in registers, history etc, from the ShaDa file.
419 // This is where v:oldfiles gets filled.
420 //
421 if (*p_shada != NUL) {
422 shada_read_everything(NULL, false, true);
423 TIME_MSG("reading ShaDa");
424 }
425 // It's better to make v:oldfiles an empty list than NULL.
426 if (get_vim_var_list(VV_OLDFILES) == NULL) {
427 set_vim_var_list(VV_OLDFILES, tv_list_alloc(0));
428 }
429
430 /*
431 * "-q errorfile": Load the error file now.
432 * If the error file can't be read, exit before doing anything else.
433 */
434 handle_quickfix(&params);
435
436 /*
437 * Start putting things on the screen.
438 * Scroll screen down before drawing over it
439 * Clear screen now, so file message will not be cleared.
440 */
441 starting = NO_BUFFERS;
442 no_wait_return = false;
443 if (!exmode_active) {
444 msg_scroll = false;
445 }
446
447 // Read file (text, not commands) from stdin if:
448 // - stdin is not a tty
449 // - and -e/-es was not given
450 //
451 // Do this before starting Raw mode, because it may change things that the
452 // writing end of the pipe doesn't like, e.g., in case stdin and stderr
453 // are the same terminal: "cat | vim -".
454 // Using autocommands here may cause trouble...
455 if (params.edit_type == EDIT_STDIN && !recoverymode) {
456 read_stdin();
457 }
458
459 setmouse(); // may start using the mouse
460
461 if (exmode_active || use_remote_ui || use_builtin_ui) {
462 // Don't clear the screen when starting in Ex mode, or when a UI might have
463 // displayed messages.
464 redraw_later(VALID);
465 } else {
466 screenclear(); // clear screen
467 TIME_MSG("clearing screen");
468 }
469
470 no_wait_return = true;
471
472 /*
473 * Create the requested number of windows and edit buffers in them.
474 * Also does recovery if "recoverymode" set.
475 */
476 create_windows(&params);
477 TIME_MSG("opening buffers");
478
479 /* clear v:swapcommand */
480 set_vim_var_string(VV_SWAPCOMMAND, NULL, -1);
481
482 /* Ex starts at last line of the file */
483 if (exmode_active)
484 curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
485
486 apply_autocmds(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf);
487 TIME_MSG("BufEnter autocommands");
488 setpcmark();
489
490 /*
491 * When started with "-q errorfile" jump to first error now.
492 */
493 if (params.edit_type == EDIT_QF) {
494 qf_jump(NULL, 0, 0, FALSE);
495 TIME_MSG("jump to first error");
496 }
497
498 // If opened more than one window, start editing files in the other
499 // windows.
500 edit_buffers(&params, cwd);
501 xfree(cwd);
502
503 if (params.diff_mode) {
504 /* set options in each window for "nvim -d". */
505 FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
506 diff_win_options(wp, TRUE);
507 }
508 }
509
510 /*
511 * Shorten any of the filenames, but only when absolute.
512 */
513 shorten_fnames(FALSE);
514
515 /*
516 * Need to jump to the tag before executing the '-c command'.
517 * Makes "vim -c '/return' -t main" work.
518 */
519 handle_tag(params.tagname);
520
521 /* Execute any "+", "-c" and "-S" arguments. */
522 if (params.n_commands > 0)
523 exe_commands(&params);
524
525 starting = 0;
526
527 RedrawingDisabled = 0;
528 redraw_all_later(NOT_VALID);
529 no_wait_return = false;
530
531 // 'autochdir' has been postponed.
532 do_autochdir();
533
534 /* start in insert mode */
535 if (p_im)
536 need_start_insertmode = TRUE;
537
538 set_vim_var_nr(VV_VIM_DID_ENTER, 1L);
539 apply_autocmds(EVENT_VIMENTER, NULL, NULL, false, curbuf);
540 TIME_MSG("VimEnter autocommands");
541 if (use_remote_ui || use_builtin_ui) {
542 do_autocmd_uienter(use_remote_ui ? CHAN_STDIO : 0, true);
543 TIME_MSG("UIEnter autocommands");
544 }
545
546 // Adjust default register name for "unnamed" in 'clipboard'. Can only be
547 // done after the clipboard is available and all initial commands that may
548 // modify the 'clipboard' setting have run; i.e. just before entering the
549 // main loop.
550 set_reg_var(get_default_register_name());
551
552 /* When a startup script or session file setup for diff'ing and
553 * scrollbind, sync the scrollbind now. */
554 if (curwin->w_p_diff && curwin->w_p_scb) {
555 update_topline();
556 check_scrollbind((linenr_T)0, 0L);
557 TIME_MSG("diff scrollbinding");
558 }
559
560 /* If ":startinsert" command used, stuff a dummy command to be able to
561 * call normal_cmd(), which will then start Insert mode. */
562 if (restart_edit != 0)
563 stuffcharReadbuff(K_NOP);
564
565 // WORKAROUND(mhi): #3023
566 if (cb_flags & CB_UNNAMEDMASK) {
567 (void)eval_has_provider("clipboard");
568 }
569
570 TIME_MSG("before starting main loop");
571 ILOG("starting main loop");
572
573 /*
574 * Call the main command loop. This never returns.
575 */
576 normal_enter(false, false);
577
578#if defined(WIN32) && !defined(MAKE_LIB)
579 xfree(argv);
580#endif
581 return 0;
582}
583
584/// Exit properly
585void getout(int exitval)
586 FUNC_ATTR_NORETURN
587{
588 exiting = true;
589
590 /* When running in Ex mode an error causes us to exit with a non-zero exit
591 * code. POSIX requires this, although it's not 100% clear from the
592 * standard. */
593 if (exmode_active)
594 exitval += ex_exitval;
595
596 set_vim_var_nr(VV_EXITING, exitval);
597
598 // Position the cursor on the last screen line, below all the text
599 ui_cursor_goto(Rows - 1, 0);
600
601 /* Optionally print hashtable efficiency. */
602 hash_debug_results();
603
604 if (get_vim_var_nr(VV_DYING) <= 1) {
605 const tabpage_T *next_tp;
606
607 // Trigger BufWinLeave for all windows, but only once per buffer.
608 for (const tabpage_T *tp = first_tabpage; tp != NULL; tp = next_tp) {
609 next_tp = tp->tp_next;
610 FOR_ALL_WINDOWS_IN_TAB(wp, tp) {
611 if (wp->w_buffer == NULL) {
612 /* Autocmd must have close the buffer already, skip. */
613 continue;
614 }
615
616 buf_T *buf = wp->w_buffer;
617 if (buf_get_changedtick(buf) != -1) {
618 bufref_T bufref;
619
620 set_bufref(&bufref, buf);
621 apply_autocmds(EVENT_BUFWINLEAVE, buf->b_fname,
622 buf->b_fname, false, buf);
623 if (bufref_valid(&bufref)) {
624 buf_set_changedtick(buf, -1); // note that we did it already
625 }
626 // start all over, autocommands may mess up the lists
627 next_tp = first_tabpage;
628 break;
629 }
630 }
631 }
632
633 /* Trigger BufUnload for buffers that are loaded */
634 FOR_ALL_BUFFERS(buf) {
635 if (buf->b_ml.ml_mfp != NULL) {
636 bufref_T bufref;
637 set_bufref(&bufref, buf);
638 apply_autocmds(EVENT_BUFUNLOAD, buf->b_fname, buf->b_fname, false, buf);
639 if (!bufref_valid(&bufref)) {
640 // Autocmd deleted the buffer.
641 break;
642 }
643 }
644 }
645 apply_autocmds(EVENT_VIMLEAVEPRE, NULL, NULL, FALSE, curbuf);
646 }
647
648 if (p_shada && *p_shada != NUL) {
649 // Write out the registers, history, marks etc, to the ShaDa file
650 shada_write_file(NULL, false);
651 }
652
653 if (get_vim_var_nr(VV_DYING) <= 1)
654 apply_autocmds(EVENT_VIMLEAVE, NULL, NULL, FALSE, curbuf);
655
656 profile_dump();
657
658 if (did_emsg
659 ) {
660 /* give the user a chance to read the (error) message */
661 no_wait_return = FALSE;
662 wait_return(FALSE);
663 }
664
665 // Position the cursor again, the autocommands may have moved it
666 ui_cursor_goto(Rows - 1, 0);
667
668 // Apply 'titleold'.
669 if (p_title && *p_titleold != NUL) {
670 ui_call_set_title(cstr_as_string((char *)p_titleold));
671 }
672
673 cs_end();
674 if (garbage_collect_at_exit) {
675 garbage_collect(false);
676 }
677
678 mch_exit(exitval);
679}
680
681/// Gets the integer value of a numeric command line argument if given,
682/// such as '-o10'.
683///
684/// @param[in] p pointer to argument
685/// @param[in, out] idx pointer to index in argument, is incremented
686/// @param[in] def default value
687///
688/// @return def unmodified if:
689/// - argument isn't given
690/// - argument is non-numeric
691///
692/// @return argument's numeric value otherwise
693static int get_number_arg(const char *p, int *idx, int def)
694 FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
695{
696 if (ascii_isdigit(p[*idx])) { // -V522
697 def = atoi(&(p[*idx]));
698 while (ascii_isdigit(p[*idx])) {
699 *idx = *idx + 1;
700 }
701 }
702 return def;
703}
704
705#if defined(HAVE_LOCALE_H)
706/// Setup to use the current locale (for ctype() and many other things).
707static void init_locale(void)
708{
709 setlocale(LC_ALL, "");
710
711# ifdef LC_NUMERIC
712 /* Make sure strtod() uses a decimal point, not a comma. */
713 setlocale(LC_NUMERIC, "C");
714# endif
715
716 char localepath[MAXPATHL] = { 0 };
717 snprintf(localepath, sizeof(localepath), "%s", get_vim_var_str(VV_PROGPATH));
718 char *tail = (char *)path_tail_with_sep((char_u *)localepath);
719 *tail = NUL;
720 tail = (char *)path_tail((char_u *)localepath);
721 xstrlcpy(tail, "share/locale",
722 sizeof(localepath) - (size_t)(tail - localepath));
723 bindtextdomain(PROJECT_NAME, localepath);
724 textdomain(PROJECT_NAME);
725 TIME_MSG("locale set");
726}
727#endif
728
729/// Decides whether text (as opposed to commands) will be read from stdin.
730/// @see EDIT_STDIN
731static bool edit_stdin(bool explicit, mparm_T *parmp)
732{
733 bool implicit = !headless_mode
734 && !embedded_mode
735 && exmode_active != EXMODE_NORMAL // -E/-Es but not -e/-es.
736 && !parmp->input_isatty
737 && scriptin[0] == NULL; // `-s -` was not given.
738 return explicit || implicit;
739}
740
741/// Scan the command line arguments.
742static void command_line_scan(mparm_T *parmp)
743{
744 int argc = parmp->argc;
745 char **argv = parmp->argv;
746 int argv_idx; // index in argv[n][]
747 bool had_stdin_file = false; // found explicit "-" argument
748 bool had_minmin = false; // found "--" argument
749 int want_argument; // option argument with argument
750 int c;
751 long n;
752
753 argc--;
754 argv++;
755 argv_idx = 1; // active option letter is argv[0][argv_idx]
756 while (argc > 0) {
757 // "+" or "+{number}" or "+/{pat}" or "+{command}" argument.
758 if (argv[0][0] == '+' && !had_minmin) {
759 if (parmp->n_commands >= MAX_ARG_CMDS) {
760 mainerr(err_extra_cmd, NULL);
761 }
762 argv_idx = -1; // skip to next argument
763 if (argv[0][1] == NUL) {
764 parmp->commands[parmp->n_commands++] = "$";
765 } else {
766 parmp->commands[parmp->n_commands++] = &(argv[0][1]);
767 }
768
769 // Optional argument.
770 } else if (argv[0][0] == '-' && !had_minmin) {
771 want_argument = false;
772 c = argv[0][argv_idx++];
773 switch (c) {
774 case NUL: { // "nvim -" read from stdin
775 if (exmode_active) {
776 // "nvim -e -" silent mode
777 silent_mode = true;
778 parmp->no_swap_file = true;
779 } else {
780 if (parmp->edit_type != EDIT_NONE
781 && parmp->edit_type != EDIT_FILE
782 && parmp->edit_type != EDIT_STDIN) {
783 mainerr(err_too_many_args, argv[0]);
784 }
785 had_stdin_file = true;
786 parmp->edit_type = EDIT_STDIN;
787 }
788 argv_idx = -1; // skip to next argument
789 break;
790 }
791 case '-': { // "--" don't take any more option arguments
792 // "--help" give help message
793 // "--version" give version message
794 // "--noplugin[s]" skip plugins
795 // "--cmd <cmd>" execute cmd before vimrc
796 if (STRICMP(argv[0] + argv_idx, "help") == 0) {
797 usage();
798 mch_exit(0);
799 } else if (STRICMP(argv[0] + argv_idx, "version") == 0) {
800 version();
801 mch_exit(0);
802 } else if (STRICMP(argv[0] + argv_idx, "api-info") == 0) {
803 FileDescriptor fp;
804 const int fof_ret = file_open_fd(&fp, STDOUT_FILENO,
805 kFileWriteOnly);
806 msgpack_packer *p = msgpack_packer_new(&fp, msgpack_file_write);
807
808 if (fof_ret != 0) {
809 emsgf(_("E5421: Failed to open stdin: %s"), os_strerror(fof_ret));
810 }
811
812 if (p == NULL) {
813 EMSG(_(e_outofmem));
814 }
815
816 Object md = DICTIONARY_OBJ(api_metadata());
817 msgpack_rpc_from_object(md, p);
818
819 msgpack_packer_free(p);
820 const int ff_ret = file_flush(&fp);
821 if (ff_ret < 0) {
822 msgpack_file_write_error(ff_ret);
823 }
824 mch_exit(0);
825 } else if (STRICMP(argv[0] + argv_idx, "headless") == 0) {
826 headless_mode = true;
827 } else if (STRICMP(argv[0] + argv_idx, "embed") == 0) {
828 embedded_mode = true;
829 } else if (STRNICMP(argv[0] + argv_idx, "listen", 6) == 0) {
830 want_argument = true;
831 argv_idx += 6;
832 } else if (STRNICMP(argv[0] + argv_idx, "literal", 7) == 0) {
833 // Do nothing: file args are always literal. #7679
834 } else if (STRNICMP(argv[0] + argv_idx, "noplugin", 8) == 0) {
835 p_lpl = false;
836 } else if (STRNICMP(argv[0] + argv_idx, "cmd", 3) == 0) {
837 want_argument = true;
838 argv_idx += 3;
839 } else if (STRNICMP(argv[0] + argv_idx, "startuptime", 11) == 0) {
840 want_argument = true;
841 argv_idx += 11;
842 } else if (STRNICMP(argv[0] + argv_idx, "clean", 5) == 0) {
843 parmp->use_vimrc = "NONE";
844 set_option_value("shadafile", 0L, "NONE", 0);
845 } else {
846 if (argv[0][argv_idx])
847 mainerr(err_opt_unknown, argv[0]);
848 had_minmin = true;
849 }
850 if (!want_argument) {
851 argv_idx = -1; // skip to next argument
852 }
853 break;
854 }
855 case 'A': { // "-A" start in Arabic mode.
856 set_option_value("arabic", 1L, NULL, 0);
857 break;
858 }
859 case 'b': { // "-b" binary mode.
860 // Needs to be effective before expanding file names, because
861 // for Win32 this makes us edit a shortcut file itself,
862 // instead of the file it links to.
863 set_options_bin(curbuf->b_p_bin, 1, 0);
864 curbuf->b_p_bin = 1; // Binary file I/O.
865 break;
866 }
867
868 case 'D': { // "-D" Debugging
869 parmp->use_debug_break_level = 9999;
870 break;
871 }
872 case 'd': { // "-d" 'diff'
873 parmp->diff_mode = true;
874 break;
875 }
876 case 'e': { // "-e" Ex mode
877 exmode_active = EXMODE_NORMAL;
878 break;
879 }
880 case 'E': { // "-E" Ex mode
881 exmode_active = EXMODE_VIM;
882 break;
883 }
884 case 'f': { // "-f" GUI: run in foreground.
885 break;
886 }
887 case '?': // "-?" give help message (for MS-Windows)
888 case 'h': { // "-h" give help message
889 usage();
890 mch_exit(0);
891 }
892 case 'H': { // "-H" start in Hebrew mode: rl + hkmap set.
893 p_hkmap = true;
894 set_option_value("rl", 1L, NULL, 0);
895 break;
896 }
897 case 'l': { // "-l" lisp mode, 'lisp' and 'showmatch' on.
898 set_option_value("lisp", 1L, NULL, 0);
899 p_sm = true;
900 break;
901 }
902 case 'M': { // "-M" no changes or writing of files
903 reset_modifiable();
904 FALLTHROUGH;
905 }
906 case 'm': { // "-m" no writing of files
907 p_write = false;
908 break;
909 }
910
911 case 'N': // "-N" Nocompatible
912 case 'X': // "-X" Do not connect to X server
913 // No-op
914 break;
915
916 case 'n': { // "-n" no swap file
917 parmp->no_swap_file = true;
918 break;
919 }
920 case 'p': { // "-p[N]" open N tab pages
921 // default is 0: open window for each file
922 parmp->window_count = get_number_arg(argv[0], &argv_idx, 0);
923 parmp->window_layout = WIN_TABS;
924 break;
925 }
926 case 'o': { // "-o[N]" open N horizontal split windows
927 // default is 0: open window for each file
928 parmp->window_count = get_number_arg(argv[0], &argv_idx, 0);
929 parmp->window_layout = WIN_HOR;
930 break;
931 }
932 case 'O': { // "-O[N]" open N vertical split windows
933 // default is 0: open window for each file
934 parmp->window_count = get_number_arg(argv[0], &argv_idx, 0);
935 parmp->window_layout = WIN_VER;
936 break;
937 }
938 case 'q': { // "-q" QuickFix mode
939 if (parmp->edit_type != EDIT_NONE) {
940 mainerr(err_too_many_args, argv[0]);
941 }
942 parmp->edit_type = EDIT_QF;
943 if (argv[0][argv_idx]) { // "-q{errorfile}"
944 parmp->use_ef = (char_u *)argv[0] + argv_idx;
945 argv_idx = -1;
946 } else if (argc > 1) { // "-q {errorfile}"
947 want_argument = true;
948 }
949 break;
950 }
951 case 'R': { // "-R" readonly mode
952 readonlymode = true;
953 curbuf->b_p_ro = true;
954 p_uc = 10000; // don't update very often
955 break;
956 }
957 case 'r': // "-r" recovery mode
958 case 'L': { // "-L" recovery mode
959 recoverymode = 1;
960 break;
961 }
962 case 's': {
963 if (exmode_active) { // "-es" silent (batch) Ex-mode
964 silent_mode = true;
965 parmp->no_swap_file = true;
966 } else { // "-s {scriptin}" read from script file
967 want_argument = true;
968 }
969 break;
970 }
971 case 't': { // "-t {tag}" or "-t{tag}" jump to tag
972 if (parmp->edit_type != EDIT_NONE) {
973 mainerr(err_too_many_args, argv[0]);
974 }
975 parmp->edit_type = EDIT_TAG;
976 if (argv[0][argv_idx]) { // "-t{tag}"
977 parmp->tagname = (char_u *)argv[0] + argv_idx;
978 argv_idx = -1;
979 } else { // "-t {tag}"
980 want_argument = true;
981 }
982 break;
983 }
984 case 'v': {
985 version();
986 mch_exit(0);
987 }
988 case 'V': { // "-V{N}" Verbose level
989 // default is 10: a little bit verbose
990 p_verbose = get_number_arg(argv[0], &argv_idx, 10);
991 if (argv[0][argv_idx] != NUL) {
992 set_option_value("verbosefile", 0L, argv[0] + argv_idx, 0);
993 argv_idx = (int)STRLEN(argv[0]);
994 }
995 break;
996 }
997 case 'w': { // "-w{number}" set window height
998 // "-w {scriptout}" write to script
999 if (ascii_isdigit(((char_u *)argv[0])[argv_idx])) {
1000 n = get_number_arg(argv[0], &argv_idx, 10);
1001 set_option_value("window", n, NULL, 0);
1002 break;
1003 }
1004 want_argument = true;
1005 break;
1006 }
1007 case 'Z': { // "-Z" restricted mode
1008 restricted = true;
1009 break;
1010 }
1011
1012 case 'c': { // "-c{command}" or "-c {command}" exec command
1013 if (argv[0][argv_idx] != NUL) {
1014 if (parmp->n_commands >= MAX_ARG_CMDS) {
1015 mainerr(err_extra_cmd, NULL);
1016 }
1017 parmp->commands[parmp->n_commands++] = argv[0] + argv_idx;
1018 argv_idx = -1;
1019 break;
1020 }
1021 FALLTHROUGH;
1022 }
1023 case 'S': // "-S {file}" execute Vim script
1024 case 'i': // "-i {shada}" use for ShaDa file
1025 case 'u': // "-u {vimrc}" vim inits file
1026 case 'U': // "-U {gvimrc}" gvim inits file
1027 case 'W': { // "-W {scriptout}" overwrite
1028 want_argument = true;
1029 break;
1030 }
1031
1032 default: {
1033 mainerr(err_opt_unknown, argv[0]);
1034 }
1035 }
1036
1037 // Handle option arguments with argument.
1038 if (want_argument) {
1039 // Check for garbage immediately after the option letter.
1040 if (argv[0][argv_idx] != NUL) {
1041 mainerr(err_opt_garbage, argv[0]);
1042 }
1043
1044 argc--;
1045 if (argc < 1 && c != 'S') { // -S has an optional argument
1046 mainerr(err_arg_missing, argv[0]);
1047 }
1048 argv++;
1049 argv_idx = -1;
1050
1051 switch (c) {
1052 case 'c': // "-c {command}" execute command
1053 case 'S': { // "-S {file}" execute Vim script
1054 if (parmp->n_commands >= MAX_ARG_CMDS) {
1055 mainerr(err_extra_cmd, NULL);
1056 }
1057 if (c == 'S') {
1058 char *a;
1059
1060 if (argc < 1) {
1061 // "-S" without argument: use default session file name.
1062 a = SESSION_FILE;
1063 } else if (argv[0][0] == '-') {
1064 // "-S" followed by another option: use default session file.
1065 a = SESSION_FILE;
1066 ++argc;
1067 --argv;
1068 } else {
1069 a = argv[0];
1070 }
1071 size_t s_size = STRLEN(a) + 4;
1072 char *s = xmalloc(s_size);
1073 snprintf(s, s_size, "so %s", a);
1074 parmp->cmds_tofree[parmp->n_commands] = true;
1075 parmp->commands[parmp->n_commands++] = s;
1076 } else {
1077 parmp->commands[parmp->n_commands++] = argv[0];
1078 }
1079 break;
1080 }
1081
1082 case '-': {
1083 if (strequal(argv[-1], "--cmd")) {
1084 // "--cmd {command}" execute command
1085 if (parmp->n_pre_commands >= MAX_ARG_CMDS) {
1086 mainerr(err_extra_cmd, NULL);
1087 }
1088 parmp->pre_commands[parmp->n_pre_commands++] = argv[0];
1089 } else if (strequal(argv[-1], "--listen")) {
1090 // "--listen {address}"
1091 parmp->listen_addr = argv[0];
1092 }
1093 // "--startuptime <file>" already handled
1094 break;
1095 }
1096
1097 case 'q': { // "-q {errorfile}" QuickFix mode
1098 parmp->use_ef = (char_u *)argv[0];
1099 break;
1100 }
1101
1102 case 'i': { // "-i {shada}" use for shada
1103 set_option_value("shadafile", 0L, argv[0], 0);
1104 break;
1105 }
1106
1107 case 's': { // "-s {scriptin}" read from script file
1108 if (scriptin[0] != NULL) {
1109scripterror:
1110 vim_snprintf((char *)IObuff, IOSIZE,
1111 _("Attempt to open script file again: \"%s %s\"\n"),
1112 argv[-1], argv[0]);
1113 mch_errmsg((const char *)IObuff);
1114 mch_exit(2);
1115 }
1116 int error;
1117 if (strequal(argv[0], "-")) {
1118 const int stdin_dup_fd = os_dup(STDIN_FILENO);
1119#ifdef WIN32
1120 // Replace the original stdin with the console input handle.
1121 close(STDIN_FILENO);
1122 const HANDLE conin_handle =
1123 CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE,
1124 FILE_SHARE_READ, (LPSECURITY_ATTRIBUTES)NULL,
1125 OPEN_EXISTING, 0, (HANDLE)NULL);
1126 const int conin_fd = _open_osfhandle(conin_handle, _O_RDONLY);
1127 assert(conin_fd == STDIN_FILENO);
1128#endif
1129 FileDescriptor *const stdin_dup = file_open_fd_new(
1130 &error, stdin_dup_fd, kFileReadOnly|kFileNonBlocking);
1131 assert(stdin_dup != NULL);
1132 scriptin[0] = stdin_dup;
1133 } else if ((scriptin[0] = file_open_new(
1134 &error, argv[0], kFileReadOnly|kFileNonBlocking, 0)) == NULL) {
1135 vim_snprintf((char *)IObuff, IOSIZE,
1136 _("Cannot open for reading: \"%s\": %s\n"),
1137 argv[0], os_strerror(error));
1138 mch_errmsg((const char *)IObuff);
1139 mch_exit(2);
1140 }
1141 save_typebuf();
1142 break;
1143 }
1144
1145 case 't': { // "-t {tag}"
1146 parmp->tagname = (char_u *)argv[0];
1147 break;
1148 }
1149 case 'u': { // "-u {vimrc}" vim inits file
1150 parmp->use_vimrc = argv[0];
1151 break;
1152 }
1153 case 'U': { // "-U {gvimrc}" gvim inits file
1154 break;
1155 }
1156
1157 case 'w': { // "-w {nr}" 'window' value
1158 // "-w {scriptout}" append to script file
1159 if (ascii_isdigit(*((char_u *)argv[0]))) {
1160 argv_idx = 0;
1161 n = get_number_arg(argv[0], &argv_idx, 10);
1162 set_option_value("window", n, NULL, 0);
1163 argv_idx = -1;
1164 break;
1165 }
1166 FALLTHROUGH;
1167 }
1168 case 'W': { // "-W {scriptout}" overwrite script file
1169 if (scriptout != NULL) {
1170 goto scripterror;
1171 }
1172 if ((scriptout = os_fopen(argv[0], c == 'w' ? APPENDBIN : WRITEBIN))
1173 == NULL) {
1174 mch_errmsg(_("Cannot open for script output: \""));
1175 mch_errmsg(argv[0]);
1176 mch_errmsg("\"\n");
1177 mch_exit(2);
1178 }
1179 break;
1180 }
1181 }
1182 }
1183 } else { // File name argument.
1184 argv_idx = -1; // skip to next argument
1185
1186 // Check for only one type of editing.
1187 if (parmp->edit_type != EDIT_NONE
1188 && parmp->edit_type != EDIT_FILE
1189 && parmp->edit_type != EDIT_STDIN) {
1190 mainerr(err_too_many_args, argv[0]);
1191 }
1192 parmp->edit_type = EDIT_FILE;
1193
1194 // Add the file to the global argument list.
1195 ga_grow(&global_alist.al_ga, 1);
1196 char_u *p = vim_strsave((char_u *)argv[0]);
1197
1198 if (parmp->diff_mode && os_isdir(p) && GARGCOUNT > 0
1199 && !os_isdir(alist_name(&GARGLIST[0]))) {
1200 char_u *r = (char_u *)concat_fnames((char *)p,
1201 (char *)path_tail(alist_name(&GARGLIST[0])), true);
1202 xfree(p);
1203 p = r;
1204 }
1205
1206#ifdef USE_FNAME_CASE
1207 // Make the case of the file name match the actual file.
1208 path_fix_case(p);
1209#endif
1210
1211 int alist_fnum_flag = edit_stdin(had_stdin_file, parmp)
1212 ? 1 // add buffer nr after exp.
1213 : 2; // add buffer number now and use curbuf
1214 alist_add(&global_alist, p, alist_fnum_flag);
1215 }
1216
1217 // If there are no more letters after the current "-", go to next argument.
1218 // argv_idx is set to -1 when the current argument is to be skipped.
1219 if (argv_idx <= 0 || argv[0][argv_idx] == NUL) {
1220 argc--;
1221 argv++;
1222 argv_idx = 1;
1223 }
1224 }
1225
1226 if (embedded_mode && silent_mode) {
1227 mainerr(_("--embed conflicts with -es/-Es"), NULL);
1228 }
1229
1230 // If there is a "+123" or "-c" command, set v:swapcommand to the first one.
1231 if (parmp->n_commands > 0) {
1232 const size_t swcmd_len = STRLEN(parmp->commands[0]) + 3;
1233 char *const swcmd = xmalloc(swcmd_len);
1234 snprintf(swcmd, swcmd_len, ":%s\r", parmp->commands[0]);
1235 set_vim_var_string(VV_SWAPCOMMAND, swcmd, -1);
1236 xfree(swcmd);
1237 }
1238
1239 // Handle "foo | nvim". EDIT_FILE may be overwritten now. #6299
1240 if (edit_stdin(had_stdin_file, parmp)) {
1241 parmp->edit_type = EDIT_STDIN;
1242 }
1243
1244 TIME_MSG("parsing arguments");
1245}
1246
1247/*
1248 * Many variables are in "params" so that we can pass them to invoked
1249 * functions without a lot of arguments. "argc" and "argv" are also
1250 * copied, so that they can be changed. */
1251static void init_params(mparm_T *paramp, int argc, char **argv)
1252{
1253 memset(paramp, 0, sizeof(*paramp));
1254 paramp->argc = argc;
1255 paramp->argv = argv;
1256 paramp->use_debug_break_level = -1;
1257 paramp->window_count = -1;
1258 paramp->listen_addr = NULL;
1259}
1260
1261/// Initialize global startuptime file if "--startuptime" passed as an argument.
1262static void init_startuptime(mparm_T *paramp)
1263{
1264 for (int i = 1; i < paramp->argc; i++) {
1265 if (STRICMP(paramp->argv[i], "--startuptime") == 0
1266 && i + 1 < paramp->argc) {
1267 time_fd = os_fopen(paramp->argv[i + 1], "a");
1268 time_start("--- NVIM STARTING ---");
1269 break;
1270 }
1271 }
1272
1273 starttime = time(NULL);
1274}
1275
1276static void check_and_set_isatty(mparm_T *paramp)
1277{
1278 stdin_isatty
1279 = paramp->input_isatty = os_isatty(STDIN_FILENO);
1280 stdout_isatty
1281 = paramp->output_isatty = os_isatty(STDOUT_FILENO);
1282 paramp->err_isatty = os_isatty(STDERR_FILENO);
1283#ifndef WIN32
1284 int tty_fd = paramp->input_isatty
1285 ? STDIN_FILENO
1286 : (paramp->output_isatty
1287 ? STDOUT_FILENO
1288 : (paramp->err_isatty ? STDERR_FILENO : -1));
1289 pty_process_save_termios(tty_fd);
1290#endif
1291 TIME_MSG("window checked");
1292}
1293
1294// Sets v:progname and v:progpath. Also modifies $PATH on Windows.
1295static void init_path(const char *exename)
1296 FUNC_ATTR_NONNULL_ALL
1297{
1298 char exepath[MAXPATHL] = { 0 };
1299 size_t exepathlen = MAXPATHL;
1300 // Make v:progpath absolute.
1301 if (os_exepath(exepath, &exepathlen) != 0) {
1302 // Fall back to argv[0]. Missing procfs? #6734
1303 path_guess_exepath(exename, exepath, sizeof(exepath));
1304 }
1305 set_vim_var_string(VV_PROGPATH, exepath, -1);
1306 set_vim_var_string(VV_PROGNAME, (char *)path_tail((char_u *)exename), -1);
1307
1308#ifdef WIN32
1309 // Append the process start directory to $PATH, so that ":!foo" finds tools
1310 // shipped with Windows package. This also mimics SearchPath().
1311 os_setenv_append_path(exepath);
1312#endif
1313}
1314
1315/// Get filename from command line, if any.
1316static char_u *get_fname(mparm_T *parmp, char_u *cwd)
1317{
1318 return alist_name(&GARGLIST[0]);
1319}
1320
1321/*
1322 * Decide about window layout for diff mode after reading vimrc.
1323 */
1324static void set_window_layout(mparm_T *paramp)
1325{
1326 if (paramp->diff_mode && paramp->window_layout == 0) {
1327 if (diffopt_horizontal())
1328 paramp->window_layout = WIN_HOR; /* use horizontal split */
1329 else
1330 paramp->window_layout = WIN_VER; /* use vertical split */
1331 }
1332}
1333
1334/*
1335 * Read all the plugin files.
1336 * Only when compiled with +eval, since most plugins need it.
1337 */
1338static void load_plugins(void)
1339{
1340 if (p_lpl) {
1341 char_u *rtp_copy = NULL;
1342
1343 // First add all package directories to 'runtimepath', so that their
1344 // autoload directories can be found. Only if not done already with a
1345 // :packloadall command.
1346 // Make a copy of 'runtimepath', so that source_runtime does not use the
1347 // pack directories.
1348 if (!did_source_packages) {
1349 rtp_copy = vim_strsave(p_rtp);
1350 add_pack_start_dirs();
1351 }
1352
1353 source_in_path(rtp_copy == NULL ? p_rtp : rtp_copy,
1354 (char_u *)"plugin/**/*.vim", // NOLINT
1355 DIP_ALL | DIP_NOAFTER);
1356 TIME_MSG("loading plugins");
1357 xfree(rtp_copy);
1358
1359 // Only source "start" packages if not done already with a :packloadall
1360 // command.
1361 if (!did_source_packages) {
1362 load_start_packages();
1363 }
1364 TIME_MSG("loading packages");
1365
1366 source_runtime((char_u *)"plugin/**/*.vim", DIP_ALL | DIP_AFTER);
1367 TIME_MSG("loading after plugins");
1368 }
1369}
1370
1371/*
1372 * "-q errorfile": Load the error file now.
1373 * If the error file can't be read, exit before doing anything else.
1374 */
1375static void handle_quickfix(mparm_T *paramp)
1376{
1377 if (paramp->edit_type == EDIT_QF) {
1378 if (paramp->use_ef != NULL)
1379 set_string_option_direct((char_u *)"ef", -1,
1380 paramp->use_ef, OPT_FREE, SID_CARG);
1381 vim_snprintf((char *)IObuff, IOSIZE, "cfile %s", p_ef);
1382 if (qf_init(NULL, p_ef, p_efm, true, IObuff, p_menc) < 0) {
1383 msg_putchar('\n');
1384 mch_exit(3);
1385 }
1386 TIME_MSG("reading errorfile");
1387 }
1388}
1389
1390/*
1391 * Need to jump to the tag before executing the '-c command'.
1392 * Makes "vim -c '/return' -t main" work.
1393 */
1394static void handle_tag(char_u *tagname)
1395{
1396 if (tagname != NULL) {
1397 swap_exists_did_quit = FALSE;
1398
1399 vim_snprintf((char *)IObuff, IOSIZE, "ta %s", tagname);
1400 do_cmdline_cmd((char *)IObuff);
1401 TIME_MSG("jumping to tag");
1402
1403 /* If the user doesn't want to edit the file then we quit here. */
1404 if (swap_exists_did_quit)
1405 getout(1);
1406 }
1407}
1408
1409/// Read text from stdin.
1410static void read_stdin(void)
1411{
1412 // When getting the ATTENTION prompt here, use a dialog.
1413 swap_exists_action = SEA_DIALOG;
1414 no_wait_return = true;
1415 int save_msg_didany = msg_didany;
1416 set_buflisted(true);
1417 (void)open_buffer(true, NULL, 0); // create memfile and read file
1418 if (BUFEMPTY() && curbuf->b_next != NULL) {
1419 // stdin was empty, go to buffer 2 (e.g. "echo file1 | xargs nvim"). #8561
1420 do_cmdline_cmd("silent! bnext");
1421 // Delete the empty stdin buffer.
1422 do_cmdline_cmd("bwipeout 1");
1423 }
1424 no_wait_return = false;
1425 msg_didany = save_msg_didany;
1426 TIME_MSG("reading stdin");
1427 check_swap_exists_action();
1428}
1429
1430/*
1431 * Create the requested number of windows and edit buffers in them.
1432 * Also does recovery if "recoverymode" set.
1433 */
1434static void create_windows(mparm_T *parmp)
1435{
1436 int dorewind;
1437 int done = 0;
1438
1439 /*
1440 * Create the number of windows that was requested.
1441 */
1442 if (parmp->window_count == -1) /* was not set */
1443 parmp->window_count = 1;
1444 if (parmp->window_count == 0)
1445 parmp->window_count = GARGCOUNT;
1446 if (parmp->window_count > 1) {
1447 // Don't change the windows if there was a command in vimrc that
1448 // already split some windows
1449 if (parmp->window_layout == 0)
1450 parmp->window_layout = WIN_HOR;
1451 if (parmp->window_layout == WIN_TABS) {
1452 parmp->window_count = make_tabpages(parmp->window_count);
1453 TIME_MSG("making tab pages");
1454 } else if (firstwin->w_next == NULL) {
1455 parmp->window_count = make_windows(parmp->window_count,
1456 parmp->window_layout == WIN_VER);
1457 TIME_MSG("making windows");
1458 } else
1459 parmp->window_count = win_count();
1460 } else
1461 parmp->window_count = 1;
1462
1463 if (recoverymode) { /* do recover */
1464 msg_scroll = TRUE; /* scroll message up */
1465 ml_recover();
1466 if (curbuf->b_ml.ml_mfp == NULL) /* failed */
1467 getout(1);
1468 do_modelines(0); /* do modelines */
1469 } else {
1470 // Open a buffer for windows that don't have one yet.
1471 // Commands in the vimrc might have loaded a file or split the window.
1472 // Watch out for autocommands that delete a window.
1473 //
1474 // Don't execute Win/Buf Enter/Leave autocommands here
1475 ++autocmd_no_enter;
1476 ++autocmd_no_leave;
1477 dorewind = TRUE;
1478 while (done++ < 1000) {
1479 if (dorewind) {
1480 if (parmp->window_layout == WIN_TABS)
1481 goto_tabpage(1);
1482 else
1483 curwin = firstwin;
1484 } else if (parmp->window_layout == WIN_TABS) {
1485 if (curtab->tp_next == NULL)
1486 break;
1487 goto_tabpage(0);
1488 } else {
1489 if (curwin->w_next == NULL)
1490 break;
1491 curwin = curwin->w_next;
1492 }
1493 dorewind = FALSE;
1494 curbuf = curwin->w_buffer;
1495 if (curbuf->b_ml.ml_mfp == NULL) {
1496 // Set 'foldlevel' to 'foldlevelstart' if it's not negative..
1497 if (p_fdls >= 0) {
1498 curwin->w_p_fdl = p_fdls;
1499 }
1500 // When getting the ATTENTION prompt here, use a dialog.
1501 swap_exists_action = SEA_DIALOG;
1502 set_buflisted(TRUE);
1503
1504 /* create memfile, read file */
1505 (void)open_buffer(FALSE, NULL, 0);
1506
1507 if (swap_exists_action == SEA_QUIT) {
1508 if (got_int || only_one_window()) {
1509 /* abort selected or quit and only one window */
1510 did_emsg = FALSE; /* avoid hit-enter prompt */
1511 getout(1);
1512 }
1513 /* We can't close the window, it would disturb what
1514 * happens next. Clear the file name and set the arg
1515 * index to -1 to delete it later. */
1516 setfname(curbuf, NULL, NULL, FALSE);
1517 curwin->w_arg_idx = -1;
1518 swap_exists_action = SEA_NONE;
1519 } else
1520 handle_swap_exists(NULL);
1521 dorewind = TRUE; /* start again */
1522 }
1523 os_breakcheck();
1524 if (got_int) {
1525 (void)vgetc(); /* only break the file loading, not the rest */
1526 break;
1527 }
1528 }
1529 if (parmp->window_layout == WIN_TABS)
1530 goto_tabpage(1);
1531 else
1532 curwin = firstwin;
1533 curbuf = curwin->w_buffer;
1534 --autocmd_no_enter;
1535 --autocmd_no_leave;
1536 }
1537}
1538
1539/// If opened more than one window, start editing files in the other
1540/// windows. make_windows() has already opened the windows.
1541static void edit_buffers(mparm_T *parmp, char_u *cwd)
1542{
1543 int arg_idx; /* index in argument list */
1544 int i;
1545 bool advance = true;
1546 win_T *win;
1547 char *p_shm_save = NULL;
1548
1549 /*
1550 * Don't execute Win/Buf Enter/Leave autocommands here
1551 */
1552 ++autocmd_no_enter;
1553 ++autocmd_no_leave;
1554
1555 /* When w_arg_idx is -1 remove the window (see create_windows()). */
1556 if (curwin->w_arg_idx == -1) {
1557 win_close(curwin, true);
1558 advance = false;
1559 }
1560
1561 arg_idx = 1;
1562 for (i = 1; i < parmp->window_count; ++i) {
1563 if (cwd != NULL) {
1564 os_chdir((char *)cwd);
1565 }
1566 // When w_arg_idx is -1 remove the window (see create_windows()).
1567 if (curwin->w_arg_idx == -1) {
1568 arg_idx++;
1569 win_close(curwin, true);
1570 advance = false;
1571 continue;
1572 }
1573
1574 if (advance) {
1575 if (parmp->window_layout == WIN_TABS) {
1576 if (curtab->tp_next == NULL) /* just checking */
1577 break;
1578 goto_tabpage(0);
1579 // Temporarily reset 'shm' option to not print fileinfo when
1580 // loading the other buffers. This would overwrite the already
1581 // existing fileinfo for the first tab.
1582 if (i == 1) {
1583 char buf[100];
1584
1585 p_shm_save = xstrdup((char *)p_shm);
1586 snprintf(buf, sizeof(buf), "F%s", p_shm);
1587 set_option_value("shm", 0L, buf, 0);
1588 }
1589 } else {
1590 if (curwin->w_next == NULL) /* just checking */
1591 break;
1592 win_enter(curwin->w_next, false);
1593 }
1594 }
1595 advance = true;
1596
1597 // Only open the file if there is no file in this window yet (that can
1598 // happen when vimrc contains ":sall").
1599 if (curbuf == firstwin->w_buffer || curbuf->b_ffname == NULL) {
1600 curwin->w_arg_idx = arg_idx;
1601 /* Edit file from arg list, if there is one. When "Quit" selected
1602 * at the ATTENTION prompt close the window. */
1603 swap_exists_did_quit = FALSE;
1604 (void)do_ecmd(0, arg_idx < GARGCOUNT
1605 ? alist_name(&GARGLIST[arg_idx]) : NULL,
1606 NULL, NULL, ECMD_LASTL, ECMD_HIDE, curwin);
1607 if (swap_exists_did_quit) {
1608 /* abort or quit selected */
1609 if (got_int || only_one_window()) {
1610 /* abort selected and only one window */
1611 did_emsg = FALSE; /* avoid hit-enter prompt */
1612 getout(1);
1613 }
1614 win_close(curwin, true);
1615 advance = false;
1616 }
1617 if (arg_idx == GARGCOUNT - 1) {
1618 arg_had_last = true;
1619 }
1620 arg_idx++;
1621 }
1622 os_breakcheck();
1623 if (got_int) {
1624 (void)vgetc(); /* only break the file loading, not the rest */
1625 break;
1626 }
1627 }
1628
1629 if (p_shm_save != NULL) {
1630 set_option_value("shm", 0L, p_shm_save, 0);
1631 xfree(p_shm_save);
1632 }
1633
1634 if (parmp->window_layout == WIN_TABS)
1635 goto_tabpage(1);
1636 --autocmd_no_enter;
1637
1638 /* make the first window the current window */
1639 win = firstwin;
1640 /* Avoid making a preview window the current window. */
1641 while (win->w_p_pvw) {
1642 win = win->w_next;
1643 if (win == NULL) {
1644 win = firstwin;
1645 break;
1646 }
1647 }
1648 win_enter(win, false);
1649
1650 --autocmd_no_leave;
1651 TIME_MSG("editing files in windows");
1652 if (parmp->window_count > 1 && parmp->window_layout != WIN_TABS)
1653 win_equal(curwin, false, 'b'); /* adjust heights */
1654}
1655
1656/*
1657 * Execute the commands from --cmd arguments "cmds[cnt]".
1658 */
1659static void exe_pre_commands(mparm_T *parmp)
1660{
1661 char **cmds = parmp->pre_commands;
1662 int cnt = parmp->n_pre_commands;
1663 int i;
1664
1665 if (cnt > 0) {
1666 curwin->w_cursor.lnum = 0; /* just in case.. */
1667 sourcing_name = (char_u *)_("pre-vimrc command line");
1668 current_sctx.sc_sid = SID_CMDARG;
1669 for (i = 0; i < cnt; i++) {
1670 do_cmdline_cmd(cmds[i]);
1671 }
1672 sourcing_name = NULL;
1673 current_sctx.sc_sid = 0;
1674 TIME_MSG("--cmd commands");
1675 }
1676}
1677
1678/*
1679 * Execute "+", "-c" and "-S" arguments.
1680 */
1681static void exe_commands(mparm_T *parmp)
1682{
1683 int i;
1684
1685 /*
1686 * We start commands on line 0, make "vim +/pat file" match a
1687 * pattern on line 1. But don't move the cursor when an autocommand
1688 * with g`" was used.
1689 */
1690 msg_scroll = TRUE;
1691 if (parmp->tagname == NULL && curwin->w_cursor.lnum <= 1)
1692 curwin->w_cursor.lnum = 0;
1693 sourcing_name = (char_u *)"command line";
1694 current_sctx.sc_sid = SID_CARG;
1695 current_sctx.sc_seq = 0;
1696 for (i = 0; i < parmp->n_commands; i++) {
1697 do_cmdline_cmd(parmp->commands[i]);
1698 if (parmp->cmds_tofree[i])
1699 xfree(parmp->commands[i]);
1700 }
1701 sourcing_name = NULL;
1702 current_sctx.sc_sid = 0;
1703 if (curwin->w_cursor.lnum == 0) {
1704 curwin->w_cursor.lnum = 1;
1705 }
1706
1707 if (!exmode_active)
1708 msg_scroll = FALSE;
1709
1710 /* When started with "-q errorfile" jump to first error again. */
1711 if (parmp->edit_type == EDIT_QF)
1712 qf_jump(NULL, 0, 0, FALSE);
1713 TIME_MSG("executing command arguments");
1714}
1715
1716/// Source system-wide vimrc if built with one defined
1717///
1718/// Does one of the following things, stops after whichever succeeds:
1719///
1720/// 1. Source system vimrc file from $XDG_CONFIG_DIRS/nvim/sysinit.vim
1721/// 2. Source system vimrc file from $VIM
1722static void do_system_initialization(void)
1723{
1724 char *const config_dirs = stdpaths_get_xdg_var(kXDGConfigDirs);
1725 if (config_dirs != NULL) {
1726 const void *iter = NULL;
1727 const char path_tail[] = {
1728 'n', 'v', 'i', 'm', PATHSEP,
1729 's', 'y', 's', 'i', 'n', 'i', 't', '.', 'v', 'i', 'm', NUL
1730 };
1731 do {
1732 const char *dir;
1733 size_t dir_len;
1734 iter = vim_env_iter(':', config_dirs, iter, &dir, &dir_len);
1735 if (dir == NULL || dir_len == 0) {
1736 break;
1737 }
1738 char *vimrc = xmalloc(dir_len + sizeof(path_tail) + 1);
1739 memcpy(vimrc, dir, dir_len);
1740 vimrc[dir_len] = PATHSEP;
1741 memcpy(vimrc + dir_len + 1, path_tail, sizeof(path_tail));
1742 if (do_source((char_u *)vimrc, false, DOSO_NONE) != FAIL) {
1743 xfree(vimrc);
1744 xfree(config_dirs);
1745 return;
1746 }
1747 xfree(vimrc);
1748 } while (iter != NULL);
1749 xfree(config_dirs);
1750 }
1751
1752#ifdef SYS_VIMRC_FILE
1753 // Get system wide defaults, if the file name is defined.
1754 (void)do_source((char_u *)SYS_VIMRC_FILE, false, DOSO_NONE);
1755#endif
1756}
1757
1758/// Source vimrc or do other user initialization
1759///
1760/// Does one of the following things, stops after whichever succeeds:
1761///
1762/// 1. Execution of VIMINIT environment variable.
1763/// 2. Sourcing user vimrc file ($XDG_CONFIG_HOME/nvim/init.vim).
1764/// 3. Sourcing other vimrc files ($XDG_CONFIG_DIRS[1]/nvim/init.vim, …).
1765/// 4. Execution of EXINIT environment variable.
1766///
1767/// @return True if it is needed to attempt to source exrc file according to
1768/// 'exrc' option definition.
1769static bool do_user_initialization(void)
1770 FUNC_ATTR_WARN_UNUSED_RESULT
1771{
1772 bool do_exrc = p_exrc;
1773 if (execute_env("VIMINIT") == OK) {
1774 do_exrc = p_exrc;
1775 return do_exrc;
1776 }
1777 char_u *user_vimrc = (char_u *)stdpaths_user_conf_subpath("init.vim");
1778 if (do_source(user_vimrc, true, DOSO_VIMRC) != FAIL) {
1779 do_exrc = p_exrc;
1780 if (do_exrc) {
1781 do_exrc = (path_full_compare((char_u *)VIMRC_FILE, user_vimrc, false)
1782 != kEqualFiles);
1783 }
1784 xfree(user_vimrc);
1785 return do_exrc;
1786 }
1787 xfree(user_vimrc);
1788 char *const config_dirs = stdpaths_get_xdg_var(kXDGConfigDirs);
1789 if (config_dirs != NULL) {
1790 const void *iter = NULL;
1791 do {
1792 const char *dir;
1793 size_t dir_len;
1794 iter = vim_env_iter(':', config_dirs, iter, &dir, &dir_len);
1795 if (dir == NULL || dir_len == 0) {
1796 break;
1797 }
1798 const char path_tail[] = { 'n', 'v', 'i', 'm', PATHSEP,
1799 'i', 'n', 'i', 't', '.', 'v', 'i', 'm', NUL };
1800 char *vimrc = xmalloc(dir_len + sizeof(path_tail) + 1);
1801 memmove(vimrc, dir, dir_len);
1802 vimrc[dir_len] = PATHSEP;
1803 memmove(vimrc + dir_len + 1, path_tail, sizeof(path_tail));
1804 if (do_source((char_u *) vimrc, true, DOSO_VIMRC) != FAIL) {
1805 do_exrc = p_exrc;
1806 if (do_exrc) {
1807 do_exrc = (path_full_compare((char_u *)VIMRC_FILE, (char_u *)vimrc,
1808 false) != kEqualFiles);
1809 }
1810 xfree(vimrc);
1811 xfree(config_dirs);
1812 return do_exrc;
1813 }
1814 xfree(vimrc);
1815 } while (iter != NULL);
1816 xfree(config_dirs);
1817 }
1818 if (execute_env("EXINIT") == OK) {
1819 do_exrc = p_exrc;
1820 return do_exrc;
1821 }
1822 return do_exrc;
1823}
1824
1825/// Source startup scripts
1826static void source_startup_scripts(const mparm_T *const parmp)
1827 FUNC_ATTR_NONNULL_ALL
1828{
1829 // If -u given, use only the initializations from that file and nothing else.
1830 if (parmp->use_vimrc != NULL) {
1831 if (strequal(parmp->use_vimrc, "NONE")
1832 || strequal(parmp->use_vimrc, "NORC")) {
1833 // Do nothing.
1834 } else {
1835 if (do_source((char_u *)parmp->use_vimrc, false, DOSO_NONE) != OK) {
1836 EMSG2(_("E282: Cannot read from \"%s\""), parmp->use_vimrc);
1837 }
1838 }
1839 } else if (!silent_mode) {
1840 do_system_initialization();
1841
1842 if (do_user_initialization()) {
1843 // Read initialization commands from ".vimrc" or ".exrc" in current
1844 // directory. This is only done if the 'exrc' option is set.
1845 // Because of security reasons we disallow shell and write commands
1846 // now, except for unix if the file is owned by the user or 'secure'
1847 // option has been reset in environment of global "exrc" or "vimrc".
1848 // Only do this if VIMRC_FILE is not the same as vimrc file sourced in
1849 // do_user_initialization.
1850#if defined(UNIX)
1851 // If vimrc file is not owned by user, set 'secure' mode.
1852 if (!file_owned(VIMRC_FILE))
1853#endif
1854 secure = p_secure;
1855
1856 if (do_source((char_u *)VIMRC_FILE, true, DOSO_VIMRC) == FAIL) {
1857#if defined(UNIX)
1858 // if ".exrc" is not owned by user set 'secure' mode
1859 if (!file_owned(EXRC_FILE)) {
1860 secure = p_secure;
1861 } else {
1862 secure = 0;
1863 }
1864#endif
1865 (void)do_source((char_u *)EXRC_FILE, false, DOSO_NONE);
1866 }
1867 }
1868 if (secure == 2) {
1869 need_wait_return = true;
1870 }
1871 secure = 0;
1872 }
1873 TIME_MSG("sourcing vimrc file(s)");
1874}
1875
1876/// Get an environment variable, and execute it as Ex commands.
1877///
1878/// @param env environment variable to execute
1879///
1880/// @return FAIL if the environment variable was not executed,
1881/// OK otherwise.
1882static int execute_env(char *env)
1883 FUNC_ATTR_NONNULL_ALL
1884{
1885 const char *initstr = os_getenv(env);
1886 if (initstr != NULL) {
1887 char_u *save_sourcing_name = sourcing_name;
1888 linenr_T save_sourcing_lnum = sourcing_lnum;
1889 sourcing_name = (char_u *)env;
1890 sourcing_lnum = 0;
1891 const sctx_T save_current_sctx = current_sctx;
1892 current_sctx.sc_sid = SID_ENV;
1893 current_sctx.sc_seq = 0;
1894 current_sctx.sc_lnum = 0;
1895 do_cmdline_cmd((char *)initstr);
1896 sourcing_name = save_sourcing_name;
1897 sourcing_lnum = save_sourcing_lnum;
1898 current_sctx = save_current_sctx;
1899 return OK;
1900 }
1901 return FAIL;
1902}
1903
1904#ifdef UNIX
1905/// Checks if user owns file.
1906/// Use both uv_fs_stat() and uv_fs_lstat() through os_fileinfo() and
1907/// os_fileinfo_link() respectively for extra security.
1908static bool file_owned(const char *fname)
1909{
1910 assert(fname != NULL);
1911 uid_t uid = getuid();
1912 FileInfo file_info;
1913 bool file_owned = os_fileinfo(fname, &file_info)
1914 && file_info.stat.st_uid == uid;
1915 bool link_owned = os_fileinfo_link(fname, &file_info)
1916 && file_info.stat.st_uid == uid;
1917 return file_owned && link_owned;
1918}
1919#endif
1920
1921/// Prints the following then exits:
1922/// - An error message `errstr`
1923/// - A string `str` if not null
1924///
1925/// @param errstr string containing an error message
1926/// @param str string to append to the primary error message, or NULL
1927static void mainerr(const char *errstr, const char *str)
1928{
1929 char *prgname = (char *)path_tail((char_u *)argv0);
1930
1931 signal_stop(); // kill us with CTRL-C here, if you like
1932
1933 mch_errmsg(prgname);
1934 mch_errmsg(": ");
1935 mch_errmsg(_(errstr));
1936 if (str != NULL) {
1937 mch_errmsg(": \"");
1938 mch_errmsg(str);
1939 mch_errmsg("\"");
1940 }
1941 mch_errmsg(_("\nMore info with \""));
1942 mch_errmsg(prgname);
1943 mch_errmsg(" -h\"\n");
1944
1945 mch_exit(1);
1946}
1947
1948/// Prints version information for "nvim -v" or "nvim --version".
1949static void version(void)
1950{
1951 info_message = TRUE; // use mch_msg(), not mch_errmsg()
1952 list_version();
1953 msg_putchar('\n');
1954 msg_didout = FALSE;
1955}
1956
1957/// Prints help message for "nvim -h" or "nvim --help".
1958static void usage(void)
1959{
1960 signal_stop(); // kill us with CTRL-C here, if you like
1961
1962 mch_msg(_("Usage:\n"));
1963 mch_msg(_(" nvim [options] [file ...] Edit file(s)\n"));
1964 mch_msg(_(" nvim [options] -t <tag> Edit file where tag is defined\n"));
1965 mch_msg(_(" nvim [options] -q [errorfile] Edit file with first error\n"));
1966 mch_msg(_("\nOptions:\n"));
1967 mch_msg(_(" -- Only file names after this\n"));
1968 mch_msg(_(" + Start at end of file\n"));
1969 mch_msg(_(" --cmd <cmd> Execute <cmd> before any config\n"));
1970 mch_msg(_(" +<cmd>, -c <cmd> Execute <cmd> after config and first file\n"));
1971 mch_msg("\n");
1972 mch_msg(_(" -b Binary mode\n"));
1973 mch_msg(_(" -d Diff mode\n"));
1974 mch_msg(_(" -e, -E Ex mode\n"));
1975 mch_msg(_(" -es, -Es Silent (batch) mode\n"));
1976 mch_msg(_(" -h, --help Print this help message\n"));
1977 mch_msg(_(" -i <shada> Use this shada file\n"));
1978 mch_msg(_(" -m Modifications (writing files) not allowed\n"));
1979 mch_msg(_(" -M Modifications in text not allowed\n"));
1980 mch_msg(_(" -n No swap file, use memory only\n"));
1981 mch_msg(_(" -o[N] Open N windows (default: one per file)\n"));
1982 mch_msg(_(" -O[N] Open N vertical windows (default: one per file)\n"));
1983 mch_msg(_(" -p[N] Open N tab pages (default: one per file)\n"));
1984 mch_msg(_(" -r, -L List swap files\n"));
1985 mch_msg(_(" -r <file> Recover edit state for this file\n"));
1986 mch_msg(_(" -R Read-only mode\n"));
1987 mch_msg(_(" -S <session> Source <session> after loading the first file\n"));
1988 mch_msg(_(" -s <scriptin> Read Normal mode commands from <scriptin>\n"));
1989 mch_msg(_(" -u <config> Use this config file\n"));
1990 mch_msg(_(" -v, --version Print version information\n"));
1991 mch_msg(_(" -V[N][file] Verbose [level][file]\n"));
1992 mch_msg(_(" -Z Restricted mode\n"));
1993 mch_msg("\n");
1994 mch_msg(_(" --api-info Write msgpack-encoded API metadata to stdout\n"));
1995 mch_msg(_(" --embed Use stdin/stdout as a msgpack-rpc channel\n"));
1996 mch_msg(_(" --headless Don't start a user interface\n"));
1997 mch_msg(_(" --listen <address> Serve RPC API from this address\n"));
1998 mch_msg(_(" --noplugin Don't load plugins\n"));
1999 mch_msg(_(" --startuptime <file> Write startup timing messages to <file>\n"));
2000 mch_msg(_("\nSee \":help startup-options\" for all options.\n"));
2001}
2002
2003
2004/*
2005 * Check the result of the ATTENTION dialog:
2006 * When "Quit" selected, exit Vim.
2007 * When "Recover" selected, recover the file.
2008 */
2009static void check_swap_exists_action(void)
2010{
2011 if (swap_exists_action == SEA_QUIT)
2012 getout(1);
2013 handle_swap_exists(NULL);
2014}
2015