1 /* vi:set ts=8 sts=4 sw=4 noet:
2  *
3  * VIM - Vi IMproved	by Bram Moolenaar
4  *
5  * Do ":help uganda"  in Vim to read copying and usage conditions.
6  * Do ":help credits" in Vim to see a list of people who contributed.
7  * See README.txt for an overview of the Vim source code.
8  */
9 
10 /*
11  * ex_docmd.c: functions for executing an Ex command line.
12  */
13 
14 #include "vim.h"
15 
16 static int	quitmore = 0;
17 static int	ex_pressedreturn = FALSE;
18 #ifndef FEAT_PRINTER
19 # define ex_hardcopy	ex_ni
20 #endif
21 
22 #ifdef FEAT_EVAL
23 static char_u	*do_one_cmd(char_u **, int, cstack_T *, char_u *(*fgetline)(int, void *, int, getline_opt_T), void *cookie);
24 #else
25 static char_u	*do_one_cmd(char_u **, int, char_u *(*fgetline)(int, void *, int, getline_opt_T), void *cookie);
26 static int	if_level = 0;		// depth in :if
27 #endif
28 static void	append_command(char_u *cmd);
29 
30 #ifndef FEAT_MENU
31 # define ex_emenu		ex_ni
32 # define ex_menu		ex_ni
33 # define ex_menutranslate	ex_ni
34 #endif
35 static void	ex_autocmd(exarg_T *eap);
36 static void	ex_doautocmd(exarg_T *eap);
37 static void	ex_bunload(exarg_T *eap);
38 static void	ex_buffer(exarg_T *eap);
39 static void	ex_bmodified(exarg_T *eap);
40 static void	ex_bnext(exarg_T *eap);
41 static void	ex_bprevious(exarg_T *eap);
42 static void	ex_brewind(exarg_T *eap);
43 static void	ex_blast(exarg_T *eap);
44 static char_u	*getargcmd(char_u **);
45 static int	getargopt(exarg_T *eap);
46 #ifndef FEAT_QUICKFIX
47 # define ex_make		ex_ni
48 # define ex_cbuffer		ex_ni
49 # define ex_cc			ex_ni
50 # define ex_cnext		ex_ni
51 # define ex_cbelow		ex_ni
52 # define ex_cfile		ex_ni
53 # define qf_list		ex_ni
54 # define qf_age			ex_ni
55 # define qf_history		ex_ni
56 # define ex_helpgrep		ex_ni
57 # define ex_vimgrep		ex_ni
58 #endif
59 #if !defined(FEAT_QUICKFIX)
60 # define ex_cclose		ex_ni
61 # define ex_copen		ex_ni
62 # define ex_cwindow		ex_ni
63 # define ex_cbottom		ex_ni
64 #endif
65 #if !defined(FEAT_QUICKFIX) || !defined(FEAT_EVAL)
66 # define ex_cexpr		ex_ni
67 #endif
68 
69 static linenr_T default_address(exarg_T *eap);
70 static linenr_T get_address(exarg_T *, char_u **, cmd_addr_T addr_type, int skip, int silent, int to_other_file, int address_count);
71 static void address_default_all(exarg_T *eap);
72 static void	get_flags(exarg_T *eap);
73 #if !defined(FEAT_PERL) \
74 	|| !defined(FEAT_PYTHON) || !defined(FEAT_PYTHON3) \
75 	|| !defined(FEAT_TCL) \
76 	|| !defined(FEAT_RUBY) \
77 	|| !defined(FEAT_LUA) \
78 	|| !defined(FEAT_MZSCHEME)
79 # define HAVE_EX_SCRIPT_NI
80 static void	ex_script_ni(exarg_T *eap);
81 #endif
82 static char	*invalid_range(exarg_T *eap);
83 static void	correct_range(exarg_T *eap);
84 #ifdef FEAT_QUICKFIX
85 static char_u	*replace_makeprg(exarg_T *eap, char_u *p, char_u **cmdlinep);
86 #endif
87 static char_u	*repl_cmdline(exarg_T *eap, char_u *src, int srclen, char_u *repl, char_u **cmdlinep);
88 static void	ex_highlight(exarg_T *eap);
89 static void	ex_colorscheme(exarg_T *eap);
90 static void	ex_cquit(exarg_T *eap);
91 static void	ex_quit_all(exarg_T *eap);
92 static void	ex_close(exarg_T *eap);
93 static void	ex_win_close(int forceit, win_T *win, tabpage_T *tp);
94 static void	ex_only(exarg_T *eap);
95 static void	ex_resize(exarg_T *eap);
96 static void	ex_stag(exarg_T *eap);
97 static void	ex_tabclose(exarg_T *eap);
98 static void	ex_tabonly(exarg_T *eap);
99 static void	ex_tabnext(exarg_T *eap);
100 static void	ex_tabmove(exarg_T *eap);
101 static void	ex_tabs(exarg_T *eap);
102 #if defined(FEAT_QUICKFIX)
103 static void	ex_pclose(exarg_T *eap);
104 static void	ex_ptag(exarg_T *eap);
105 static void	ex_pedit(exarg_T *eap);
106 #else
107 # define ex_pclose		ex_ni
108 # define ex_ptag		ex_ni
109 # define ex_pedit		ex_ni
110 #endif
111 static void	ex_hide(exarg_T *eap);
112 static void	ex_stop(exarg_T *eap);
113 static void	ex_exit(exarg_T *eap);
114 static void	ex_print(exarg_T *eap);
115 #ifdef FEAT_BYTEOFF
116 static void	ex_goto(exarg_T *eap);
117 #else
118 # define ex_goto		ex_ni
119 #endif
120 static void	ex_shell(exarg_T *eap);
121 static void	ex_preserve(exarg_T *eap);
122 static void	ex_recover(exarg_T *eap);
123 static void	ex_mode(exarg_T *eap);
124 static void	ex_wrongmodifier(exarg_T *eap);
125 static void	ex_find(exarg_T *eap);
126 static void	ex_open(exarg_T *eap);
127 static void	ex_edit(exarg_T *eap);
128 #ifndef FEAT_GUI
129 # define ex_gui			ex_nogui
130 static void	ex_nogui(exarg_T *eap);
131 #endif
132 #if defined(FEAT_GUI_MSWIN) && defined(FEAT_MENU) && defined(FEAT_TEAROFF)
133 static void	ex_tearoff(exarg_T *eap);
134 #else
135 # define ex_tearoff		ex_ni
136 #endif
137 #if (defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_GTK) \
138 	|| defined(FEAT_TERM_POPUP_MENU)) && defined(FEAT_MENU)
139 static void	ex_popup(exarg_T *eap);
140 #else
141 # define ex_popup		ex_ni
142 #endif
143 #ifndef FEAT_GUI_MSWIN
144 # define ex_simalt		ex_ni
145 #endif
146 #if !defined(FEAT_GUI_MSWIN) && !defined(FEAT_GUI_GTK) && !defined(FEAT_GUI_MOTIF)
147 # define gui_mch_find_dialog	ex_ni
148 # define gui_mch_replace_dialog ex_ni
149 #endif
150 #if !defined(FEAT_GUI_GTK)
151 # define ex_helpfind		ex_ni
152 #endif
153 #ifndef FEAT_CSCOPE
154 # define ex_cscope		ex_ni
155 # define ex_scscope		ex_ni
156 # define ex_cstag		ex_ni
157 #endif
158 #ifndef FEAT_SYN_HL
159 # define ex_syntax		ex_ni
160 # define ex_ownsyntax		ex_ni
161 #endif
162 #if !defined(FEAT_SYN_HL) || !defined(FEAT_PROFILE)
163 # define ex_syntime		ex_ni
164 #endif
165 #ifndef FEAT_SPELL
166 # define ex_spell		ex_ni
167 # define ex_mkspell		ex_ni
168 # define ex_spelldump		ex_ni
169 # define ex_spellinfo		ex_ni
170 # define ex_spellrepall		ex_ni
171 #endif
172 #ifndef FEAT_PERSISTENT_UNDO
173 # define ex_rundo		ex_ni
174 # define ex_wundo		ex_ni
175 #endif
176 #ifndef FEAT_LUA
177 # define ex_lua			ex_script_ni
178 # define ex_luado		ex_ni
179 # define ex_luafile		ex_ni
180 #endif
181 #ifndef FEAT_MZSCHEME
182 # define ex_mzscheme		ex_script_ni
183 # define ex_mzfile		ex_ni
184 #endif
185 #ifndef FEAT_PERL
186 # define ex_perl		ex_script_ni
187 # define ex_perldo		ex_ni
188 #endif
189 #ifndef FEAT_PYTHON
190 # define ex_python		ex_script_ni
191 # define ex_pydo		ex_ni
192 # define ex_pyfile		ex_ni
193 #endif
194 #ifndef FEAT_PYTHON3
195 # define ex_py3			ex_script_ni
196 # define ex_py3do		ex_ni
197 # define ex_py3file		ex_ni
198 #endif
199 #if !defined(FEAT_PYTHON) && !defined(FEAT_PYTHON3)
200 # define ex_pyx			ex_script_ni
201 # define ex_pyxdo		ex_ni
202 # define ex_pyxfile		ex_ni
203 #endif
204 #ifndef FEAT_TCL
205 # define ex_tcl			ex_script_ni
206 # define ex_tcldo		ex_ni
207 # define ex_tclfile		ex_ni
208 #endif
209 #ifndef FEAT_RUBY
210 # define ex_ruby		ex_script_ni
211 # define ex_rubydo		ex_ni
212 # define ex_rubyfile		ex_ni
213 #endif
214 #ifndef FEAT_KEYMAP
215 # define ex_loadkeymap		ex_ni
216 #endif
217 static void	ex_swapname(exarg_T *eap);
218 static void	ex_syncbind(exarg_T *eap);
219 static void	ex_read(exarg_T *eap);
220 static void	ex_pwd(exarg_T *eap);
221 static void	ex_equal(exarg_T *eap);
222 static void	ex_sleep(exarg_T *eap);
223 static void	ex_winsize(exarg_T *eap);
224 static void	ex_wincmd(exarg_T *eap);
225 #if defined(FEAT_GUI) || defined(UNIX) || defined(VMS) || defined(MSWIN)
226 static void	ex_winpos(exarg_T *eap);
227 #else
228 # define ex_winpos	    ex_ni
229 #endif
230 static void	ex_operators(exarg_T *eap);
231 static void	ex_put(exarg_T *eap);
232 static void	ex_copymove(exarg_T *eap);
233 static void	ex_submagic(exarg_T *eap);
234 static void	ex_join(exarg_T *eap);
235 static void	ex_at(exarg_T *eap);
236 static void	ex_bang(exarg_T *eap);
237 static void	ex_undo(exarg_T *eap);
238 #ifdef FEAT_PERSISTENT_UNDO
239 static void	ex_wundo(exarg_T *eap);
240 static void	ex_rundo(exarg_T *eap);
241 #endif
242 static void	ex_redo(exarg_T *eap);
243 static void	ex_later(exarg_T *eap);
244 static void	ex_redir(exarg_T *eap);
245 static void	ex_redrawstatus(exarg_T *eap);
246 static void	ex_redrawtabline(exarg_T *eap);
247 static void	close_redir(void);
248 static void	ex_mark(exarg_T *eap);
249 static void	ex_startinsert(exarg_T *eap);
250 static void	ex_stopinsert(exarg_T *eap);
251 #ifdef FEAT_FIND_ID
252 static void	ex_checkpath(exarg_T *eap);
253 static void	ex_findpat(exarg_T *eap);
254 #else
255 # define ex_findpat		ex_ni
256 # define ex_checkpath		ex_ni
257 #endif
258 #if defined(FEAT_FIND_ID) && defined(FEAT_QUICKFIX)
259 static void	ex_psearch(exarg_T *eap);
260 #else
261 # define ex_psearch		ex_ni
262 #endif
263 static void	ex_tag(exarg_T *eap);
264 static void	ex_tag_cmd(exarg_T *eap, char_u *name);
265 #ifndef FEAT_EVAL
266 # define ex_block		ex_ni
267 # define ex_break		ex_ni
268 # define ex_breakadd		ex_ni
269 # define ex_breakdel		ex_ni
270 # define ex_breaklist		ex_ni
271 # define ex_call		ex_ni
272 # define ex_catch		ex_ni
273 # define ex_compiler		ex_ni
274 # define ex_continue		ex_ni
275 # define ex_debug		ex_ni
276 # define ex_debuggreedy		ex_ni
277 # define ex_defcompile		ex_ni
278 # define ex_delfunction		ex_ni
279 # define ex_disassemble		ex_ni
280 # define ex_echo		ex_ni
281 # define ex_echohl		ex_ni
282 # define ex_else		ex_ni
283 # define ex_endblock		ex_ni
284 # define ex_endfunction		ex_ni
285 # define ex_endif		ex_ni
286 # define ex_endtry		ex_ni
287 # define ex_endwhile		ex_ni
288 # define ex_eval		ex_ni
289 # define ex_execute		ex_ni
290 # define ex_incdec		ex_ni
291 # define ex_finally		ex_ni
292 # define ex_finish		ex_ni
293 # define ex_function		ex_ni
294 # define ex_if			ex_ni
295 # define ex_let			ex_ni
296 # define ex_var			ex_ni
297 # define ex_lockvar		ex_ni
298 # define ex_oldfiles		ex_ni
299 # define ex_options		ex_ni
300 # define ex_packadd		ex_ni
301 # define ex_packloadall		ex_ni
302 # define ex_return		ex_ni
303 # define ex_scriptnames		ex_ni
304 # define ex_throw		ex_ni
305 # define ex_try			ex_ni
306 # define ex_unlet		ex_ni
307 # define ex_unlockvar		ex_ni
308 # define ex_while		ex_ni
309 # define ex_import		ex_ni
310 # define ex_export		ex_ni
311 #endif
312 #ifndef FEAT_SESSION
313 # define ex_loadview		ex_ni
314 #endif
315 #ifndef FEAT_VIMINFO
316 # define ex_viminfo		ex_ni
317 #endif
318 static void	ex_behave(exarg_T *eap);
319 static void	ex_filetype(exarg_T *eap);
320 static void	ex_setfiletype(exarg_T *eap);
321 #ifndef FEAT_DIFF
322 # define ex_diffoff		ex_ni
323 # define ex_diffpatch		ex_ni
324 # define ex_diffgetput		ex_ni
325 # define ex_diffsplit		ex_ni
326 # define ex_diffthis		ex_ni
327 # define ex_diffupdate		ex_ni
328 #endif
329 static void	ex_digraphs(exarg_T *eap);
330 #ifdef FEAT_SEARCH_EXTRA
331 static void	ex_nohlsearch(exarg_T *eap);
332 #else
333 # define ex_nohlsearch		ex_ni
334 # define ex_match		ex_ni
335 #endif
336 #ifdef FEAT_CRYPT
337 static void	ex_X(exarg_T *eap);
338 #else
339 # define ex_X			ex_ni
340 #endif
341 #ifdef FEAT_FOLDING
342 static void	ex_fold(exarg_T *eap);
343 static void	ex_foldopen(exarg_T *eap);
344 static void	ex_folddo(exarg_T *eap);
345 #else
346 # define ex_fold		ex_ni
347 # define ex_foldopen		ex_ni
348 # define ex_folddo		ex_ni
349 #endif
350 #if !(defined(HAVE_LOCALE_H) || defined(X_LOCALE))
351 # define ex_language		ex_ni
352 #endif
353 #ifndef FEAT_SIGNS
354 # define ex_sign		ex_ni
355 #endif
356 #ifndef FEAT_NETBEANS_INTG
357 # define ex_nbclose		ex_ni
358 # define ex_nbkey		ex_ni
359 # define ex_nbstart		ex_ni
360 #endif
361 
362 #ifndef FEAT_JUMPLIST
363 # define ex_jumps		ex_ni
364 # define ex_clearjumps		ex_ni
365 # define ex_changes		ex_ni
366 #endif
367 
368 #ifndef FEAT_PROFILE
369 # define ex_profile		ex_ni
370 #endif
371 #ifndef FEAT_TERMINAL
372 # define ex_terminal		ex_ni
373 #endif
374 #if !defined(FEAT_X11) || !defined(FEAT_XCLIPBOARD)
375 # define ex_xrestore		ex_ni
376 #endif
377 #if !defined(FEAT_PROP_POPUP)
378 # define ex_popupclear		ex_ni
379 #endif
380 
381 /*
382  * Declare cmdnames[].
383  */
384 #define DO_DECLARE_EXCMD
385 #include "ex_cmds.h"
386 #include "ex_cmdidxs.h"
387 
388 static char_u dollar_command[2] = {'$', 0};
389 
390 
391 #ifdef FEAT_EVAL
392 // Struct for storing a line inside a while/for loop
393 typedef struct
394 {
395     char_u	*line;		// command line
396     linenr_T	lnum;		// sourcing_lnum of the line
397 } wcmd_T;
398 
399 /*
400  * Structure used to store info for line position in a while or for loop.
401  * This is required, because do_one_cmd() may invoke ex_function(), which
402  * reads more lines that may come from the while/for loop.
403  */
404 struct loop_cookie
405 {
406     garray_T	*lines_gap;		// growarray with line info
407     int		current_line;		// last read line from growarray
408     int		repeating;		// TRUE when looping a second time
409     // When "repeating" is FALSE use "getline" and "cookie" to get lines
410     char_u	*(*getline)(int, void *, int, getline_opt_T);
411     void	*cookie;
412 };
413 
414 static char_u	*get_loop_line(int c, void *cookie, int indent, getline_opt_T options);
415 static int	store_loop_line(garray_T *gap, char_u *line);
416 static void	free_cmdlines(garray_T *gap);
417 
418 // Struct to save a few things while debugging.  Used in do_cmdline() only.
419 struct dbg_stuff
420 {
421     int		trylevel;
422     int		force_abort;
423     except_T	*caught_stack;
424     char_u	*vv_exception;
425     char_u	*vv_throwpoint;
426     int		did_emsg;
427     int		got_int;
428     int		did_throw;
429     int		need_rethrow;
430     int		check_cstack;
431     except_T	*current_exception;
432 };
433 
434     static void
save_dbg_stuff(struct dbg_stuff * dsp)435 save_dbg_stuff(struct dbg_stuff *dsp)
436 {
437     dsp->trylevel	= trylevel;		trylevel = 0;
438     dsp->force_abort	= force_abort;		force_abort = FALSE;
439     dsp->caught_stack	= caught_stack;		caught_stack = NULL;
440     dsp->vv_exception	= v_exception(NULL);
441     dsp->vv_throwpoint	= v_throwpoint(NULL);
442 
443     // Necessary for debugging an inactive ":catch", ":finally", ":endtry"
444     dsp->did_emsg	= did_emsg;		did_emsg     = FALSE;
445     dsp->got_int	= got_int;		got_int	     = FALSE;
446     dsp->did_throw	= did_throw;		did_throw    = FALSE;
447     dsp->need_rethrow	= need_rethrow;		need_rethrow = FALSE;
448     dsp->check_cstack	= check_cstack;		check_cstack = FALSE;
449     dsp->current_exception = current_exception;	current_exception = NULL;
450 }
451 
452     static void
restore_dbg_stuff(struct dbg_stuff * dsp)453 restore_dbg_stuff(struct dbg_stuff *dsp)
454 {
455     suppress_errthrow = FALSE;
456     trylevel = dsp->trylevel;
457     force_abort = dsp->force_abort;
458     caught_stack = dsp->caught_stack;
459     (void)v_exception(dsp->vv_exception);
460     (void)v_throwpoint(dsp->vv_throwpoint);
461     did_emsg = dsp->did_emsg;
462     got_int = dsp->got_int;
463     did_throw = dsp->did_throw;
464     need_rethrow = dsp->need_rethrow;
465     check_cstack = dsp->check_cstack;
466     current_exception = dsp->current_exception;
467 }
468 #endif
469 
470 /*
471  * do_exmode(): Repeatedly get commands for the "Ex" mode, until the ":vi"
472  * command is given.
473  */
474     void
do_exmode(int improved)475 do_exmode(
476     int		improved)	    // TRUE for "improved Ex" mode
477 {
478     int		save_msg_scroll;
479     int		prev_msg_row;
480     linenr_T	prev_line;
481     varnumber_T	changedtick;
482 
483     if (improved)
484 	exmode_active = EXMODE_VIM;
485     else
486 	exmode_active = EXMODE_NORMAL;
487     State = NORMAL;
488     trigger_modechanged();
489 
490     // When using ":global /pat/ visual" and then "Q" we return to continue
491     // the :global command.
492     if (global_busy)
493 	return;
494 
495     save_msg_scroll = msg_scroll;
496     ++RedrawingDisabled;	    // don't redisplay the window
497     ++no_wait_return;		    // don't wait for return
498 #ifdef FEAT_GUI
499     // Ignore scrollbar and mouse events in Ex mode
500     ++hold_gui_events;
501 #endif
502 
503     msg(_("Entering Ex mode.  Type \"visual\" to go to Normal mode."));
504     while (exmode_active)
505     {
506 	// Check for a ":normal" command and no more characters left.
507 	if (ex_normal_busy > 0 && typebuf.tb_len == 0)
508 	{
509 	    exmode_active = FALSE;
510 	    break;
511 	}
512 	msg_scroll = TRUE;
513 	need_wait_return = FALSE;
514 	ex_pressedreturn = FALSE;
515 	ex_no_reprint = FALSE;
516 	changedtick = CHANGEDTICK(curbuf);
517 	prev_msg_row = msg_row;
518 	prev_line = curwin->w_cursor.lnum;
519 	if (improved)
520 	{
521 	    cmdline_row = msg_row;
522 	    do_cmdline(NULL, getexline, NULL, 0);
523 	}
524 	else
525 	    do_cmdline(NULL, getexmodeline, NULL, DOCMD_NOWAIT);
526 	lines_left = Rows - 1;
527 
528 	if ((prev_line != curwin->w_cursor.lnum
529 		   || changedtick != CHANGEDTICK(curbuf)) && !ex_no_reprint)
530 	{
531 	    if (curbuf->b_ml.ml_flags & ML_EMPTY)
532 		emsg(_(e_emptybuf));
533 	    else
534 	    {
535 		if (ex_pressedreturn)
536 		{
537 		    // go up one line, to overwrite the ":<CR>" line, so the
538 		    // output doesn't contain empty lines.
539 		    msg_row = prev_msg_row;
540 		    if (prev_msg_row == Rows - 1)
541 			msg_row--;
542 		}
543 		msg_col = 0;
544 		print_line_no_prefix(curwin->w_cursor.lnum, FALSE, FALSE);
545 		msg_clr_eos();
546 	    }
547 	}
548 	else if (ex_pressedreturn && !ex_no_reprint)	// must be at EOF
549 	{
550 	    if (curbuf->b_ml.ml_flags & ML_EMPTY)
551 		emsg(_(e_emptybuf));
552 	    else
553 		emsg(_("E501: At end-of-file"));
554 	}
555     }
556 
557 #ifdef FEAT_GUI
558     --hold_gui_events;
559 #endif
560     --RedrawingDisabled;
561     --no_wait_return;
562     update_screen(CLEAR);
563     need_wait_return = FALSE;
564     msg_scroll = save_msg_scroll;
565 }
566 
567 /*
568  * Print the executed command for when 'verbose' is set.
569  * When "lnum" is 0 only print the command.
570  */
571     static void
msg_verbose_cmd(linenr_T lnum,char_u * cmd)572 msg_verbose_cmd(linenr_T lnum, char_u *cmd)
573 {
574     ++no_wait_return;
575     verbose_enter_scroll();
576 
577     if (lnum == 0)
578 	smsg(_("Executing: %s"), cmd);
579     else
580 	smsg(_("line %ld: %s"), (long)lnum, cmd);
581     if (msg_silent == 0)
582 	msg_puts("\n");   // don't overwrite this
583 
584     verbose_leave_scroll();
585     --no_wait_return;
586 }
587 
588 /*
589  * Execute a simple command line.  Used for translated commands like "*".
590  */
591     int
do_cmdline_cmd(char_u * cmd)592 do_cmdline_cmd(char_u *cmd)
593 {
594     return do_cmdline(cmd, NULL, NULL,
595 				   DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_KEYTYPED);
596 }
597 
598 /*
599  * Execute the "+cmd" argument of "edit +cmd fname" and the like.
600  * This allows for using a range without ":" in Vim9 script.
601  */
602     static int
do_cmd_argument(char_u * cmd)603 do_cmd_argument(char_u *cmd)
604 {
605     return do_cmdline(cmd, NULL, NULL,
606 		      DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_KEYTYPED|DOCMD_RANGEOK);
607 }
608 
609 /*
610  * do_cmdline(): execute one Ex command line
611  *
612  * 1. Execute "cmdline" when it is not NULL.
613  *    If "cmdline" is NULL, or more lines are needed, fgetline() is used.
614  * 2. Split up in parts separated with '|'.
615  *
616  * This function can be called recursively!
617  *
618  * flags:
619  * DOCMD_VERBOSE  - The command will be included in the error message.
620  * DOCMD_NOWAIT   - Don't call wait_return() and friends.
621  * DOCMD_REPEAT   - Repeat execution until fgetline() returns NULL.
622  * DOCMD_KEYTYPED - Don't reset KeyTyped.
623  * DOCMD_EXCRESET - Reset the exception environment (used for debugging).
624  * DOCMD_KEEPLINE - Store first typed line (for repeating with ".").
625  *
626  * return FAIL if cmdline could not be executed, OK otherwise
627  */
628     int
do_cmdline(char_u * cmdline,char_u * (* fgetline)(int,void *,int,getline_opt_T),void * cookie,int flags)629 do_cmdline(
630     char_u	*cmdline,
631     char_u	*(*fgetline)(int, void *, int, getline_opt_T),
632     void	*cookie,		// argument for fgetline()
633     int		flags)
634 {
635     char_u	*next_cmdline;		// next cmd to execute
636     char_u	*cmdline_copy = NULL;	// copy of cmd line
637     int		used_getline = FALSE;	// used "fgetline" to obtain command
638     static int	recursive = 0;		// recursive depth
639     int		msg_didout_before_start = 0;
640     int		count = 0;		// line number count
641     int		did_inc = FALSE;	// incremented RedrawingDisabled
642     int		retval = OK;
643 #ifdef FEAT_EVAL
644     cstack_T	cstack;			// conditional stack
645     garray_T	lines_ga;		// keep lines for ":while"/":for"
646     int		current_line = 0;	// active line in lines_ga
647     int		current_line_before = 0;
648     char_u	*fname = NULL;		// function or script name
649     linenr_T	*breakpoint = NULL;	// ptr to breakpoint field in cookie
650     int		*dbg_tick = NULL;	// ptr to dbg_tick field in cookie
651     struct dbg_stuff debug_saved;	// saved things for debug mode
652     int		initial_trylevel;
653     msglist_T	**saved_msg_list = NULL;
654     msglist_T	*private_msg_list = NULL;
655 
656     // "fgetline" and "cookie" passed to do_one_cmd()
657     char_u	*(*cmd_getline)(int, void *, int, getline_opt_T);
658     void	*cmd_cookie;
659     struct loop_cookie cmd_loop_cookie;
660     void	*real_cookie;
661     int		getline_is_func;
662 #else
663 # define cmd_getline fgetline
664 # define cmd_cookie cookie
665 #endif
666     static int	call_depth = 0;		// recursiveness
667 #ifdef FEAT_EVAL
668     // For every pair of do_cmdline()/do_one_cmd() calls, use an extra memory
669     // location for storing error messages to be converted to an exception.
670     // This ensures that the do_errthrow() call in do_one_cmd() does not
671     // combine the messages stored by an earlier invocation of do_one_cmd()
672     // with the command name of the later one.  This would happen when
673     // BufWritePost autocommands are executed after a write error.
674     saved_msg_list = msg_list;
675     msg_list = &private_msg_list;
676 #endif
677 
678     // It's possible to create an endless loop with ":execute", catch that
679     // here.  The value of 200 allows nested function calls, ":source", etc.
680     // Allow 200 or 'maxfuncdepth', whatever is larger.
681     if (call_depth >= 200
682 #ifdef FEAT_EVAL
683 	    && call_depth >= p_mfd
684 #endif
685 	    )
686     {
687 	emsg(_("E169: Command too recursive"));
688 #ifdef FEAT_EVAL
689 	// When converting to an exception, we do not include the command name
690 	// since this is not an error of the specific command.
691 	do_errthrow((cstack_T *)NULL, (char_u *)NULL);
692 	msg_list = saved_msg_list;
693 #endif
694 	return FAIL;
695     }
696     ++call_depth;
697 
698 #ifdef FEAT_EVAL
699     CLEAR_FIELD(cstack);
700     cstack.cs_idx = -1;
701     ga_init2(&lines_ga, (int)sizeof(wcmd_T), 10);
702 
703     real_cookie = getline_cookie(fgetline, cookie);
704 
705     // Inside a function use a higher nesting level.
706     getline_is_func = getline_equal(fgetline, cookie, get_func_line);
707     if (getline_is_func && ex_nesting_level == func_level(real_cookie))
708 	++ex_nesting_level;
709 
710     // Get the function or script name and the address where the next breakpoint
711     // line and the debug tick for a function or script are stored.
712     if (getline_is_func)
713     {
714 	fname = func_name(real_cookie);
715 	breakpoint = func_breakpoint(real_cookie);
716 	dbg_tick = func_dbg_tick(real_cookie);
717     }
718     else if (getline_equal(fgetline, cookie, getsourceline))
719     {
720 	fname = SOURCING_NAME;
721 	breakpoint = source_breakpoint(real_cookie);
722 	dbg_tick = source_dbg_tick(real_cookie);
723     }
724 
725     /*
726      * Initialize "force_abort"  and "suppress_errthrow" at the top level.
727      */
728     if (!recursive)
729     {
730 	force_abort = FALSE;
731 	suppress_errthrow = FALSE;
732     }
733 
734     /*
735      * If requested, store and reset the global values controlling the
736      * exception handling (used when debugging).  Otherwise clear it to avoid
737      * a bogus compiler warning when the optimizer uses inline functions...
738      */
739     if (flags & DOCMD_EXCRESET)
740 	save_dbg_stuff(&debug_saved);
741     else
742 	CLEAR_FIELD(debug_saved);
743 
744     initial_trylevel = trylevel;
745 
746     /*
747      * "did_throw" will be set to TRUE when an exception is being thrown.
748      */
749     did_throw = FALSE;
750 #endif
751     /*
752      * "did_emsg" will be set to TRUE when emsg() is used, in which case we
753      * cancel the whole command line, and any if/endif or loop.
754      * If force_abort is set, we cancel everything.
755      */
756 #ifdef FEAT_EVAL
757     did_emsg_cumul += did_emsg;
758 #endif
759     did_emsg = FALSE;
760 
761     /*
762      * KeyTyped is only set when calling vgetc().  Reset it here when not
763      * calling vgetc() (sourced command lines).
764      */
765     if (!(flags & DOCMD_KEYTYPED)
766 			       && !getline_equal(fgetline, cookie, getexline))
767 	KeyTyped = FALSE;
768 
769     /*
770      * Continue executing command lines:
771      * - when inside an ":if", ":while" or ":for"
772      * - for multiple commands on one line, separated with '|'
773      * - when repeating until there are no more lines (for ":source")
774      */
775     next_cmdline = cmdline;
776     do
777     {
778 #ifdef FEAT_EVAL
779 	getline_is_func = getline_equal(fgetline, cookie, get_func_line);
780 #endif
781 
782 	// stop skipping cmds for an error msg after all endif/while/for
783 	if (next_cmdline == NULL
784 #ifdef FEAT_EVAL
785 		&& !force_abort
786 		&& cstack.cs_idx < 0
787 		&& !(getline_is_func && func_has_abort(real_cookie))
788 #endif
789 							)
790 	{
791 #ifdef FEAT_EVAL
792 	    did_emsg_cumul += did_emsg;
793 #endif
794 	    did_emsg = FALSE;
795 	}
796 
797 	/*
798 	 * 1. If repeating a line in a loop, get a line from lines_ga.
799 	 * 2. If no line given: Get an allocated line with fgetline().
800 	 * 3. If a line is given: Make a copy, so we can mess with it.
801 	 */
802 
803 #ifdef FEAT_EVAL
804 	// 1. If repeating, get a previous line from lines_ga.
805 	if (cstack.cs_looplevel > 0 && current_line < lines_ga.ga_len)
806 	{
807 	    // Each '|' separated command is stored separately in lines_ga, to
808 	    // be able to jump to it.  Don't use next_cmdline now.
809 	    VIM_CLEAR(cmdline_copy);
810 
811 	    // Check if a function has returned or, unless it has an unclosed
812 	    // try conditional, aborted.
813 	    if (getline_is_func)
814 	    {
815 # ifdef FEAT_PROFILE
816 		if (do_profiling == PROF_YES)
817 		    func_line_end(real_cookie);
818 # endif
819 		if (func_has_ended(real_cookie))
820 		{
821 		    retval = FAIL;
822 		    break;
823 		}
824 	    }
825 #ifdef FEAT_PROFILE
826 	    else if (do_profiling == PROF_YES
827 			    && getline_equal(fgetline, cookie, getsourceline))
828 		script_line_end();
829 #endif
830 
831 	    // Check if a sourced file hit a ":finish" command.
832 	    if (source_finished(fgetline, cookie))
833 	    {
834 		retval = FAIL;
835 		break;
836 	    }
837 
838 	    // If breakpoints have been added/deleted need to check for it.
839 	    if (breakpoint != NULL && dbg_tick != NULL
840 						   && *dbg_tick != debug_tick)
841 	    {
842 		*breakpoint = dbg_find_breakpoint(
843 				getline_equal(fgetline, cookie, getsourceline),
844 							fname, SOURCING_LNUM);
845 		*dbg_tick = debug_tick;
846 	    }
847 
848 	    next_cmdline = ((wcmd_T *)(lines_ga.ga_data))[current_line].line;
849 	    SOURCING_LNUM = ((wcmd_T *)(lines_ga.ga_data))[current_line].lnum;
850 
851 	    // Did we encounter a breakpoint?
852 	    if (breakpoint != NULL && *breakpoint != 0
853 					      && *breakpoint <= SOURCING_LNUM)
854 	    {
855 		dbg_breakpoint(fname, SOURCING_LNUM);
856 		// Find next breakpoint.
857 		*breakpoint = dbg_find_breakpoint(
858 			       getline_equal(fgetline, cookie, getsourceline),
859 							fname, SOURCING_LNUM);
860 		*dbg_tick = debug_tick;
861 	    }
862 # ifdef FEAT_PROFILE
863 	    if (do_profiling == PROF_YES)
864 	    {
865 		if (getline_is_func)
866 		    func_line_start(real_cookie, SOURCING_LNUM);
867 		else if (getline_equal(fgetline, cookie, getsourceline))
868 		    script_line_start();
869 	    }
870 # endif
871 	}
872 #endif
873 
874 	// 2. If no line given, get an allocated line with fgetline().
875 	if (next_cmdline == NULL)
876 	{
877 	    /*
878 	     * Need to set msg_didout for the first line after an ":if",
879 	     * otherwise the ":if" will be overwritten.
880 	     */
881 	    if (count == 1 && getline_equal(fgetline, cookie, getexline))
882 		msg_didout = TRUE;
883 	    if (fgetline == NULL || (next_cmdline = fgetline(':', cookie,
884 #ifdef FEAT_EVAL
885 		    cstack.cs_idx < 0 ? 0 : (cstack.cs_idx + 1) * 2
886 #else
887 		    0
888 #endif
889 		    , in_vim9script() ? GETLINE_CONCAT_CONTBAR
890 					       : GETLINE_CONCAT_CONT)) == NULL)
891 	    {
892 		// Don't call wait_return for aborted command line.  The NULL
893 		// returned for the end of a sourced file or executed function
894 		// doesn't do this.
895 		if (KeyTyped && !(flags & DOCMD_REPEAT))
896 		    need_wait_return = FALSE;
897 		retval = FAIL;
898 		break;
899 	    }
900 	    used_getline = TRUE;
901 
902 	    /*
903 	     * Keep the first typed line.  Clear it when more lines are typed.
904 	     */
905 	    if (flags & DOCMD_KEEPLINE)
906 	    {
907 		vim_free(repeat_cmdline);
908 		if (count == 0)
909 		    repeat_cmdline = vim_strsave(next_cmdline);
910 		else
911 		    repeat_cmdline = NULL;
912 	    }
913 	}
914 
915 	// 3. Make a copy of the command so we can mess with it.
916 	else if (cmdline_copy == NULL)
917 	{
918 	    next_cmdline = vim_strsave(next_cmdline);
919 	    if (next_cmdline == NULL)
920 	    {
921 		emsg(_(e_out_of_memory));
922 		retval = FAIL;
923 		break;
924 	    }
925 	}
926 	cmdline_copy = next_cmdline;
927 
928 #ifdef FEAT_EVAL
929 	/*
930 	 * Inside a while/for loop, and when the command looks like a ":while"
931 	 * or ":for", the line is stored, because we may need it later when
932 	 * looping.
933 	 *
934 	 * When there is a '|' and another command, it is stored separately,
935 	 * because we need to be able to jump back to it from an
936 	 * :endwhile/:endfor.
937 	 *
938 	 * Pass a different "fgetline" function to do_one_cmd() below,
939 	 * that it stores lines in or reads them from "lines_ga".  Makes it
940 	 * possible to define a function inside a while/for loop and handles
941 	 * line continuation.
942 	 */
943 	if ((cstack.cs_looplevel > 0 || has_loop_cmd(next_cmdline)))
944 	{
945 	    cmd_getline = get_loop_line;
946 	    cmd_cookie = (void *)&cmd_loop_cookie;
947 	    cmd_loop_cookie.lines_gap = &lines_ga;
948 	    cmd_loop_cookie.current_line = current_line;
949 	    cmd_loop_cookie.getline = fgetline;
950 	    cmd_loop_cookie.cookie = cookie;
951 	    cmd_loop_cookie.repeating = (current_line < lines_ga.ga_len);
952 
953 	    // Save the current line when encountering it the first time.
954 	    if (current_line == lines_ga.ga_len
955 		    && store_loop_line(&lines_ga, next_cmdline) == FAIL)
956 	    {
957 		retval = FAIL;
958 		break;
959 	    }
960 	    current_line_before = current_line;
961 	}
962 	else
963 	{
964 	    cmd_getline = fgetline;
965 	    cmd_cookie = cookie;
966 	}
967 
968 	did_endif = FALSE;
969 #endif
970 
971 	if (count++ == 0)
972 	{
973 	    /*
974 	     * All output from the commands is put below each other, without
975 	     * waiting for a return. Don't do this when executing commands
976 	     * from a script or when being called recursive (e.g. for ":e
977 	     * +command file").
978 	     */
979 	    if (!(flags & DOCMD_NOWAIT) && !recursive)
980 	    {
981 		msg_didout_before_start = msg_didout;
982 		msg_didany = FALSE; // no output yet
983 		msg_start();
984 		msg_scroll = TRUE;  // put messages below each other
985 		++no_wait_return;   // don't wait for return until finished
986 		++RedrawingDisabled;
987 		did_inc = TRUE;
988 	    }
989 	}
990 
991 	if ((p_verbose >= 15 && SOURCING_NAME != NULL) || p_verbose >= 16)
992 	    msg_verbose_cmd(SOURCING_LNUM, cmdline_copy);
993 
994 	/*
995 	 * 2. Execute one '|' separated command.
996 	 *    do_one_cmd() will return NULL if there is no trailing '|'.
997 	 *    "cmdline_copy" can change, e.g. for '%' and '#' expansion.
998 	 */
999 	++recursive;
1000 	next_cmdline = do_one_cmd(&cmdline_copy, flags,
1001 #ifdef FEAT_EVAL
1002 				&cstack,
1003 #endif
1004 				cmd_getline, cmd_cookie);
1005 	--recursive;
1006 
1007 #ifdef FEAT_EVAL
1008 	if (cmd_cookie == (void *)&cmd_loop_cookie)
1009 	    // Use "current_line" from "cmd_loop_cookie", it may have been
1010 	    // incremented when defining a function.
1011 	    current_line = cmd_loop_cookie.current_line;
1012 #endif
1013 
1014 	if (next_cmdline == NULL)
1015 	{
1016 	    VIM_CLEAR(cmdline_copy);
1017 
1018 	    /*
1019 	     * If the command was typed, remember it for the ':' register.
1020 	     * Do this AFTER executing the command to make :@: work.
1021 	     */
1022 	    if (getline_equal(fgetline, cookie, getexline)
1023 						  && new_last_cmdline != NULL)
1024 	    {
1025 		vim_free(last_cmdline);
1026 		last_cmdline = new_last_cmdline;
1027 		new_last_cmdline = NULL;
1028 	    }
1029 	}
1030 	else
1031 	{
1032 	    // need to copy the command after the '|' to cmdline_copy, for the
1033 	    // next do_one_cmd()
1034 	    STRMOVE(cmdline_copy, next_cmdline);
1035 	    next_cmdline = cmdline_copy;
1036 	}
1037 
1038 
1039 #ifdef FEAT_EVAL
1040 	// reset did_emsg for a function that is not aborted by an error
1041 	if (did_emsg && !force_abort
1042 		&& getline_equal(fgetline, cookie, get_func_line)
1043 					      && !func_has_abort(real_cookie))
1044 	{
1045 	    // did_emsg_cumul is not set here
1046 	    did_emsg = FALSE;
1047 	}
1048 
1049 	if (cstack.cs_looplevel > 0)
1050 	{
1051 	    ++current_line;
1052 
1053 	    /*
1054 	     * An ":endwhile", ":endfor" and ":continue" is handled here.
1055 	     * If we were executing commands, jump back to the ":while" or
1056 	     * ":for".
1057 	     * If we were not executing commands, decrement cs_looplevel.
1058 	     */
1059 	    if (cstack.cs_lflags & (CSL_HAD_CONT | CSL_HAD_ENDLOOP))
1060 	    {
1061 		cstack.cs_lflags &= ~(CSL_HAD_CONT | CSL_HAD_ENDLOOP);
1062 
1063 		// Jump back to the matching ":while" or ":for".  Be careful
1064 		// not to use a cs_line[] from an entry that isn't a ":while"
1065 		// or ":for": It would make "current_line" invalid and can
1066 		// cause a crash.
1067 		if (!did_emsg && !got_int && !did_throw
1068 			&& cstack.cs_idx >= 0
1069 			&& (cstack.cs_flags[cstack.cs_idx]
1070 						      & (CSF_WHILE | CSF_FOR))
1071 			&& cstack.cs_line[cstack.cs_idx] >= 0
1072 			&& (cstack.cs_flags[cstack.cs_idx] & CSF_ACTIVE))
1073 		{
1074 		    current_line = cstack.cs_line[cstack.cs_idx];
1075 						// remember we jumped there
1076 		    cstack.cs_lflags |= CSL_HAD_LOOP;
1077 		    line_breakcheck();		// check if CTRL-C typed
1078 
1079 		    // Check for the next breakpoint at or after the ":while"
1080 		    // or ":for".
1081 		    if (breakpoint != NULL)
1082 		    {
1083 			*breakpoint = dbg_find_breakpoint(
1084 			       getline_equal(fgetline, cookie, getsourceline),
1085 									fname,
1086 			   ((wcmd_T *)lines_ga.ga_data)[current_line].lnum-1);
1087 			*dbg_tick = debug_tick;
1088 		    }
1089 		}
1090 		else
1091 		{
1092 		    // can only get here with ":endwhile" or ":endfor"
1093 		    if (cstack.cs_idx >= 0)
1094 			rewind_conditionals(&cstack, cstack.cs_idx - 1,
1095 				   CSF_WHILE | CSF_FOR, &cstack.cs_looplevel);
1096 		}
1097 	    }
1098 
1099 	    /*
1100 	     * For a ":while" or ":for" we need to remember the line number.
1101 	     */
1102 	    else if (cstack.cs_lflags & CSL_HAD_LOOP)
1103 	    {
1104 		cstack.cs_lflags &= ~CSL_HAD_LOOP;
1105 		cstack.cs_line[cstack.cs_idx] = current_line_before;
1106 	    }
1107 	}
1108 
1109 	// Check for the next breakpoint after a watchexpression
1110 	if (breakpoint != NULL && has_watchexpr())
1111 	{
1112 	    *breakpoint = dbg_find_breakpoint(FALSE, fname, SOURCING_LNUM);
1113 	    *dbg_tick = debug_tick;
1114 	}
1115 
1116 	/*
1117 	 * When not inside any ":while" loop, clear remembered lines.
1118 	 */
1119 	if (cstack.cs_looplevel == 0)
1120 	{
1121 	    if (lines_ga.ga_len > 0)
1122 	    {
1123 		SOURCING_LNUM =
1124 		       ((wcmd_T *)lines_ga.ga_data)[lines_ga.ga_len - 1].lnum;
1125 		free_cmdlines(&lines_ga);
1126 	    }
1127 	    current_line = 0;
1128 	}
1129 
1130 	/*
1131 	 * A ":finally" makes did_emsg, got_int, and did_throw pending for
1132 	 * being restored at the ":endtry".  Reset them here and set the
1133 	 * ACTIVE and FINALLY flags, so that the finally clause gets executed.
1134 	 * This includes the case where a missing ":endif", ":endwhile" or
1135 	 * ":endfor" was detected by the ":finally" itself.
1136 	 */
1137 	if (cstack.cs_lflags & CSL_HAD_FINA)
1138 	{
1139 	    cstack.cs_lflags &= ~CSL_HAD_FINA;
1140 	    report_make_pending(cstack.cs_pending[cstack.cs_idx]
1141 		    & (CSTP_ERROR | CSTP_INTERRUPT | CSTP_THROW),
1142 		    did_throw ? (void *)current_exception : NULL);
1143 	    did_emsg = got_int = did_throw = FALSE;
1144 	    cstack.cs_flags[cstack.cs_idx] |= CSF_ACTIVE | CSF_FINALLY;
1145 	}
1146 
1147 	// Update global "trylevel" for recursive calls to do_cmdline() from
1148 	// within this loop.
1149 	trylevel = initial_trylevel + cstack.cs_trylevel;
1150 
1151 	/*
1152 	 * If the outermost try conditional (across function calls and sourced
1153 	 * files) is aborted because of an error, an interrupt, or an uncaught
1154 	 * exception, cancel everything.  If it is left normally, reset
1155 	 * force_abort to get the non-EH compatible abortion behavior for
1156 	 * the rest of the script.
1157 	 */
1158 	if (trylevel == 0 && !did_emsg && !got_int && !did_throw)
1159 	    force_abort = FALSE;
1160 
1161 	// Convert an interrupt to an exception if appropriate.
1162 	(void)do_intthrow(&cstack);
1163 #endif // FEAT_EVAL
1164 
1165     }
1166     /*
1167      * Continue executing command lines when:
1168      * - no CTRL-C typed, no aborting error, no exception thrown or try
1169      *   conditionals need to be checked for executing finally clauses or
1170      *   catching an interrupt exception
1171      * - didn't get an error message or lines are not typed
1172      * - there is a command after '|', inside a :if, :while, :for or :try, or
1173      *   looping for ":source" command or function call.
1174      */
1175     while (!((got_int
1176 #ifdef FEAT_EVAL
1177 		    || (did_emsg && (force_abort || in_vim9script()))
1178 		    || did_throw
1179 #endif
1180 	     )
1181 #ifdef FEAT_EVAL
1182 		&& cstack.cs_trylevel == 0
1183 #endif
1184 	    )
1185 	    && !(did_emsg
1186 #ifdef FEAT_EVAL
1187 		// Keep going when inside try/catch, so that the error can be
1188 		// deal with, except when it is a syntax error, it may cause
1189 		// the :endtry to be missed.
1190 		&& (cstack.cs_trylevel == 0 || did_emsg_syntax)
1191 #endif
1192 		&& used_getline
1193 			    && (getline_equal(fgetline, cookie, getexmodeline)
1194 			       || getline_equal(fgetline, cookie, getexline)))
1195 	    && (next_cmdline != NULL
1196 #ifdef FEAT_EVAL
1197 			|| cstack.cs_idx >= 0
1198 #endif
1199 			|| (flags & DOCMD_REPEAT)));
1200 
1201     vim_free(cmdline_copy);
1202     did_emsg_syntax = FALSE;
1203 #ifdef FEAT_EVAL
1204     free_cmdlines(&lines_ga);
1205     ga_clear(&lines_ga);
1206 
1207     if (cstack.cs_idx >= 0)
1208     {
1209 	/*
1210 	 * If a sourced file or executed function ran to its end, report the
1211 	 * unclosed conditional.
1212 	 * In Vim9 script do not give a second error, executing aborts after
1213 	 * the first one.
1214 	 */
1215 	if (!got_int && !did_throw && !(did_emsg && in_vim9script())
1216 		&& ((getline_equal(fgetline, cookie, getsourceline)
1217 			&& !source_finished(fgetline, cookie))
1218 		    || (getline_equal(fgetline, cookie, get_func_line)
1219 					    && !func_has_ended(real_cookie))))
1220 	{
1221 	    if (cstack.cs_flags[cstack.cs_idx] & CSF_TRY)
1222 		emsg(_(e_endtry));
1223 	    else if (cstack.cs_flags[cstack.cs_idx] & CSF_WHILE)
1224 		emsg(_(e_endwhile));
1225 	    else if (cstack.cs_flags[cstack.cs_idx] & CSF_FOR)
1226 		emsg(_(e_endfor));
1227 	    else
1228 		emsg(_(e_endif));
1229 	}
1230 
1231 	/*
1232 	 * Reset "trylevel" in case of a ":finish" or ":return" or a missing
1233 	 * ":endtry" in a sourced file or executed function.  If the try
1234 	 * conditional is in its finally clause, ignore anything pending.
1235 	 * If it is in a catch clause, finish the caught exception.
1236 	 * Also cleanup any "cs_forinfo" structures.
1237 	 */
1238 	do
1239 	{
1240 	    int idx = cleanup_conditionals(&cstack, 0, TRUE);
1241 
1242 	    if (idx >= 0)
1243 		--idx;	    // remove try block not in its finally clause
1244 	    rewind_conditionals(&cstack, idx, CSF_WHILE | CSF_FOR,
1245 							&cstack.cs_looplevel);
1246 	}
1247 	while (cstack.cs_idx >= 0);
1248 	trylevel = initial_trylevel;
1249     }
1250 
1251     // If a missing ":endtry", ":endwhile", ":endfor", or ":endif" or a memory
1252     // lack was reported above and the error message is to be converted to an
1253     // exception, do this now after rewinding the cstack.
1254     do_errthrow(&cstack, getline_equal(fgetline, cookie, get_func_line)
1255 				  ? (char_u *)"endfunction" : (char_u *)NULL);
1256 
1257     if (trylevel == 0)
1258     {
1259 	// Just in case did_throw got set but current_exception wasn't.
1260 	if (current_exception == NULL)
1261 	    did_throw = FALSE;
1262 
1263 	/*
1264 	 * When an exception is being thrown out of the outermost try
1265 	 * conditional, discard the uncaught exception, disable the conversion
1266 	 * of interrupts or errors to exceptions, and ensure that no more
1267 	 * commands are executed.
1268 	 */
1269 	if (did_throw)
1270 	    handle_did_throw();
1271 
1272 	/*
1273 	 * On an interrupt or an aborting error not converted to an exception,
1274 	 * disable the conversion of errors to exceptions.  (Interrupts are not
1275 	 * converted anymore, here.) This enables also the interrupt message
1276 	 * when force_abort is set and did_emsg unset in case of an interrupt
1277 	 * from a finally clause after an error.
1278 	 */
1279 	else if (got_int || (did_emsg && force_abort))
1280 	    suppress_errthrow = TRUE;
1281     }
1282 
1283     /*
1284      * The current cstack will be freed when do_cmdline() returns.  An uncaught
1285      * exception will have to be rethrown in the previous cstack.  If a function
1286      * has just returned or a script file was just finished and the previous
1287      * cstack belongs to the same function or, respectively, script file, it
1288      * will have to be checked for finally clauses to be executed due to the
1289      * ":return" or ":finish".  This is done in do_one_cmd().
1290      */
1291     if (did_throw)
1292 	need_rethrow = TRUE;
1293     if ((getline_equal(fgetline, cookie, getsourceline)
1294 		&& ex_nesting_level > source_level(real_cookie))
1295 	    || (getline_equal(fgetline, cookie, get_func_line)
1296 		&& ex_nesting_level > func_level(real_cookie) + 1))
1297     {
1298 	if (!did_throw)
1299 	    check_cstack = TRUE;
1300     }
1301     else
1302     {
1303 	// When leaving a function, reduce nesting level.
1304 	if (getline_equal(fgetline, cookie, get_func_line))
1305 	    --ex_nesting_level;
1306 	/*
1307 	 * Go to debug mode when returning from a function in which we are
1308 	 * single-stepping.
1309 	 */
1310 	if ((getline_equal(fgetline, cookie, getsourceline)
1311 		    || getline_equal(fgetline, cookie, get_func_line))
1312 		&& ex_nesting_level + 1 <= debug_break_level)
1313 	    do_debug(getline_equal(fgetline, cookie, getsourceline)
1314 		    ? (char_u *)_("End of sourced file")
1315 		    : (char_u *)_("End of function"));
1316     }
1317 
1318     /*
1319      * Restore the exception environment (done after returning from the
1320      * debugger).
1321      */
1322     if (flags & DOCMD_EXCRESET)
1323 	restore_dbg_stuff(&debug_saved);
1324 
1325     msg_list = saved_msg_list;
1326 
1327     // Cleanup if "cs_emsg_silent_list" remains.
1328     if (cstack.cs_emsg_silent_list != NULL)
1329     {
1330 	eslist_T *elem, *temp;
1331 
1332 	for (elem = cstack.cs_emsg_silent_list; elem != NULL; elem = temp)
1333 	{
1334 	    temp = elem->next;
1335 	    vim_free(elem);
1336 	}
1337     }
1338 #endif // FEAT_EVAL
1339 
1340     /*
1341      * If there was too much output to fit on the command line, ask the user to
1342      * hit return before redrawing the screen. With the ":global" command we do
1343      * this only once after the command is finished.
1344      */
1345     if (did_inc)
1346     {
1347 	--RedrawingDisabled;
1348 	--no_wait_return;
1349 	msg_scroll = FALSE;
1350 
1351 	/*
1352 	 * When just finished an ":if"-":else" which was typed, no need to
1353 	 * wait for hit-return.  Also for an error situation.
1354 	 */
1355 	if (retval == FAIL
1356 #ifdef FEAT_EVAL
1357 		|| (did_endif && KeyTyped && !did_emsg)
1358 #endif
1359 					    )
1360 	{
1361 	    need_wait_return = FALSE;
1362 	    msg_didany = FALSE;		// don't wait when restarting edit
1363 	}
1364 	else if (need_wait_return)
1365 	{
1366 	    /*
1367 	     * The msg_start() above clears msg_didout. The wait_return we do
1368 	     * here should not overwrite the command that may be shown before
1369 	     * doing that.
1370 	     */
1371 	    msg_didout |= msg_didout_before_start;
1372 	    wait_return(FALSE);
1373 	}
1374     }
1375 
1376 #ifdef FEAT_EVAL
1377     did_endif = FALSE;  // in case do_cmdline used recursively
1378 #else
1379     /*
1380      * Reset if_level, in case a sourced script file contains more ":if" than
1381      * ":endif" (could be ":if x | foo | endif").
1382      */
1383     if_level = 0;
1384 #endif
1385 
1386     --call_depth;
1387     return retval;
1388 }
1389 
1390 #if defined(FEAT_EVAL) || defined(PROTO)
1391 /*
1392  * Handle when "did_throw" is set after executing commands.
1393  */
1394     void
handle_did_throw()1395 handle_did_throw()
1396 {
1397     char	*p = NULL;
1398     msglist_T	*messages = NULL;
1399     ESTACK_CHECK_DECLARATION
1400 
1401     /*
1402      * If the uncaught exception is a user exception, report it as an
1403      * error.  If it is an error exception, display the saved error
1404      * message now.  For an interrupt exception, do nothing; the
1405      * interrupt message is given elsewhere.
1406      */
1407     switch (current_exception->type)
1408     {
1409 	case ET_USER:
1410 	    vim_snprintf((char *)IObuff, IOSIZE,
1411 		    _("E605: Exception not caught: %s"),
1412 		    current_exception->value);
1413 	    p = (char *)vim_strsave(IObuff);
1414 	    break;
1415 	case ET_ERROR:
1416 	    messages = current_exception->messages;
1417 	    current_exception->messages = NULL;
1418 	    break;
1419 	case ET_INTERRUPT:
1420 	    break;
1421     }
1422 
1423     estack_push(ETYPE_EXCEPT, current_exception->throw_name,
1424 					current_exception->throw_lnum);
1425     ESTACK_CHECK_SETUP
1426     current_exception->throw_name = NULL;
1427 
1428     discard_current_exception();	// uses IObuff if 'verbose'
1429     suppress_errthrow = TRUE;
1430     force_abort = TRUE;
1431 
1432     if (messages != NULL)
1433     {
1434 	do
1435 	{
1436 	    msglist_T	*next = messages->next;
1437 	    int		save_compiling = estack_compiling;
1438 
1439 	    estack_compiling = messages->msg_compiling;
1440 	    emsg(messages->msg);
1441 	    vim_free(messages->msg);
1442 	    vim_free(messages->sfile);
1443 	    vim_free(messages);
1444 	    messages = next;
1445 	    estack_compiling = save_compiling;
1446 	}
1447 	while (messages != NULL);
1448     }
1449     else if (p != NULL)
1450     {
1451 	emsg(p);
1452 	vim_free(p);
1453     }
1454     vim_free(SOURCING_NAME);
1455     ESTACK_CHECK_NOW
1456     estack_pop();
1457 }
1458 
1459 /*
1460  * Obtain a line when inside a ":while" or ":for" loop.
1461  */
1462     static char_u *
get_loop_line(int c,void * cookie,int indent,getline_opt_T options)1463 get_loop_line(int c, void *cookie, int indent, getline_opt_T options)
1464 {
1465     struct loop_cookie	*cp = (struct loop_cookie *)cookie;
1466     wcmd_T		*wp;
1467     char_u		*line;
1468 
1469     if (cp->current_line + 1 >= cp->lines_gap->ga_len)
1470     {
1471 	if (cp->repeating)
1472 	    return NULL;	// trying to read past ":endwhile"/":endfor"
1473 
1474 	// First time inside the ":while"/":for": get line normally.
1475 	if (cp->getline == NULL)
1476 	    line = getcmdline(c, 0L, indent, options);
1477 	else
1478 	    line = cp->getline(c, cp->cookie, indent, options);
1479 	if (line != NULL && store_loop_line(cp->lines_gap, line) == OK)
1480 	    ++cp->current_line;
1481 
1482 	return line;
1483     }
1484 
1485     KeyTyped = FALSE;
1486     ++cp->current_line;
1487     wp = (wcmd_T *)(cp->lines_gap->ga_data) + cp->current_line;
1488     SOURCING_LNUM = wp->lnum;
1489     return vim_strsave(wp->line);
1490 }
1491 
1492 /*
1493  * Store a line in "gap" so that a ":while" loop can execute it again.
1494  */
1495     static int
store_loop_line(garray_T * gap,char_u * line)1496 store_loop_line(garray_T *gap, char_u *line)
1497 {
1498     if (ga_grow(gap, 1) == FAIL)
1499 	return FAIL;
1500     ((wcmd_T *)(gap->ga_data))[gap->ga_len].line = vim_strsave(line);
1501     ((wcmd_T *)(gap->ga_data))[gap->ga_len].lnum = SOURCING_LNUM;
1502     ++gap->ga_len;
1503     return OK;
1504 }
1505 
1506 /*
1507  * Free the lines stored for a ":while" or ":for" loop.
1508  */
1509     static void
free_cmdlines(garray_T * gap)1510 free_cmdlines(garray_T *gap)
1511 {
1512     while (gap->ga_len > 0)
1513     {
1514 	vim_free(((wcmd_T *)(gap->ga_data))[gap->ga_len - 1].line);
1515 	--gap->ga_len;
1516     }
1517 }
1518 #endif
1519 
1520 /*
1521  * If "fgetline" is get_loop_line(), return TRUE if the getline it uses equals
1522  * "func".  * Otherwise return TRUE when "fgetline" equals "func".
1523  */
1524     int
getline_equal(char_u * (* fgetline)(int,void *,int,getline_opt_T),void * cookie UNUSED,char_u * (* func)(int,void *,int,getline_opt_T))1525 getline_equal(
1526     char_u	*(*fgetline)(int, void *, int, getline_opt_T),
1527     void	*cookie UNUSED,		// argument for fgetline()
1528     char_u	*(*func)(int, void *, int, getline_opt_T))
1529 {
1530 #ifdef FEAT_EVAL
1531     char_u		*(*gp)(int, void *, int, getline_opt_T);
1532     struct loop_cookie *cp;
1533 
1534     // When "fgetline" is "get_loop_line()" use the "cookie" to find the
1535     // function that's originally used to obtain the lines.  This may be
1536     // nested several levels.
1537     gp = fgetline;
1538     cp = (struct loop_cookie *)cookie;
1539     while (gp == get_loop_line)
1540     {
1541 	gp = cp->getline;
1542 	cp = cp->cookie;
1543     }
1544     return gp == func;
1545 #else
1546     return fgetline == func;
1547 #endif
1548 }
1549 
1550 /*
1551  * If "fgetline" is get_loop_line(), return the cookie used by the original
1552  * getline function.  Otherwise return "cookie".
1553  */
1554     void *
getline_cookie(char_u * (* fgetline)(int,void *,int,getline_opt_T)UNUSED,void * cookie)1555 getline_cookie(
1556     char_u	*(*fgetline)(int, void *, int, getline_opt_T) UNUSED,
1557     void	*cookie)		// argument for fgetline()
1558 {
1559 #ifdef FEAT_EVAL
1560     char_u		*(*gp)(int, void *, int, getline_opt_T);
1561     struct loop_cookie  *cp;
1562 
1563     // When "fgetline" is "get_loop_line()" use the "cookie" to find the
1564     // cookie that's originally used to obtain the lines.  This may be nested
1565     // several levels.
1566     gp = fgetline;
1567     cp = (struct loop_cookie *)cookie;
1568     while (gp == get_loop_line)
1569     {
1570 	gp = cp->getline;
1571 	cp = cp->cookie;
1572     }
1573     return cp;
1574 #else
1575     return cookie;
1576 #endif
1577 }
1578 
1579 #if defined(FEAT_EVAL) || defined(PROT)
1580 /*
1581  * Get the next line source line without advancing.
1582  */
1583     char_u *
getline_peek(char_u * (* fgetline)(int,void *,int,getline_opt_T)UNUSED,void * cookie)1584 getline_peek(
1585     char_u	*(*fgetline)(int, void *, int, getline_opt_T) UNUSED,
1586     void	*cookie)		// argument for fgetline()
1587 {
1588     char_u		*(*gp)(int, void *, int, getline_opt_T);
1589     struct loop_cookie  *cp;
1590     wcmd_T		*wp;
1591 
1592     // When "fgetline" is "get_loop_line()" use the "cookie" to find the
1593     // cookie that's originally used to obtain the lines.  This may be nested
1594     // several levels.
1595     gp = fgetline;
1596     cp = (struct loop_cookie *)cookie;
1597     while (gp == get_loop_line)
1598     {
1599 	if (cp->current_line + 1 < cp->lines_gap->ga_len)
1600 	{
1601 	    // executing lines a second time, use the stored copy
1602 	    wp = (wcmd_T *)(cp->lines_gap->ga_data) + cp->current_line + 1;
1603 	    return wp->line;
1604 	}
1605 	gp = cp->getline;
1606 	cp = cp->cookie;
1607     }
1608     if (gp == getsourceline)
1609 	return source_nextline(cp);
1610     return NULL;
1611 }
1612 #endif
1613 
1614 
1615 /*
1616  * Helper function to apply an offset for buffer commands, i.e. ":bdelete",
1617  * ":bwipeout", etc.
1618  * Returns the buffer number.
1619  */
1620     static int
compute_buffer_local_count(int addr_type,int lnum,int offset)1621 compute_buffer_local_count(int addr_type, int lnum, int offset)
1622 {
1623     buf_T   *buf;
1624     buf_T   *nextbuf;
1625     int     count = offset;
1626 
1627     buf = firstbuf;
1628     while (buf->b_next != NULL && buf->b_fnum < lnum)
1629 	buf = buf->b_next;
1630     while (count != 0)
1631     {
1632 	count += (offset < 0) ? 1 : -1;
1633 	nextbuf = (offset < 0) ? buf->b_prev : buf->b_next;
1634 	if (nextbuf == NULL)
1635 	    break;
1636 	buf = nextbuf;
1637 	if (addr_type == ADDR_LOADED_BUFFERS)
1638 	    // skip over unloaded buffers
1639 	    while (buf->b_ml.ml_mfp == NULL)
1640 	    {
1641 		nextbuf = (offset < 0) ? buf->b_prev : buf->b_next;
1642 		if (nextbuf == NULL)
1643 		    break;
1644 		buf = nextbuf;
1645 	    }
1646     }
1647     // we might have gone too far, last buffer is not loadedd
1648     if (addr_type == ADDR_LOADED_BUFFERS)
1649 	while (buf->b_ml.ml_mfp == NULL)
1650 	{
1651 	    nextbuf = (offset >= 0) ? buf->b_prev : buf->b_next;
1652 	    if (nextbuf == NULL)
1653 		break;
1654 	    buf = nextbuf;
1655 	}
1656     return buf->b_fnum;
1657 }
1658 
1659 /*
1660  * Return the window number of "win".
1661  * When "win" is NULL return the number of windows.
1662  */
1663     static int
current_win_nr(win_T * win)1664 current_win_nr(win_T *win)
1665 {
1666     win_T	*wp;
1667     int		nr = 0;
1668 
1669     FOR_ALL_WINDOWS(wp)
1670     {
1671 	++nr;
1672 	if (wp == win)
1673 	    break;
1674     }
1675     return nr;
1676 }
1677 
1678     static int
current_tab_nr(tabpage_T * tab)1679 current_tab_nr(tabpage_T *tab)
1680 {
1681     tabpage_T	*tp;
1682     int		nr = 0;
1683 
1684     FOR_ALL_TABPAGES(tp)
1685     {
1686 	++nr;
1687 	if (tp == tab)
1688 	    break;
1689     }
1690     return nr;
1691 }
1692 
1693     static int
comment_start(char_u * p,int starts_with_colon UNUSED)1694 comment_start(char_u *p, int starts_with_colon UNUSED)
1695 {
1696 #ifdef FEAT_EVAL
1697     if (in_vim9script())
1698 	return p[0] == '#' && !starts_with_colon;
1699 #endif
1700     return *p == '"';
1701 }
1702 
1703 # define CURRENT_WIN_NR current_win_nr(curwin)
1704 # define LAST_WIN_NR current_win_nr(NULL)
1705 # define CURRENT_TAB_NR current_tab_nr(curtab)
1706 # define LAST_TAB_NR current_tab_nr(NULL)
1707 
1708 /*
1709  * Execute one Ex command.
1710  *
1711  * If "flags" has DOCMD_VERBOSE, the command will be included in the error
1712  * message.
1713  *
1714  * 1. skip comment lines and leading space
1715  * 2. handle command modifiers
1716  * 3. find the command
1717  * 4. parse range
1718  * 5. Parse the command.
1719  * 6. parse arguments
1720  * 7. switch on command name
1721  *
1722  * Note: "fgetline" can be NULL.
1723  *
1724  * This function may be called recursively!
1725  */
1726 #if (_MSC_VER == 1200)
1727 /*
1728  * Avoid optimisation bug in VC++ version 6.0
1729  */
1730  #pragma optimize( "g", off )
1731 #endif
1732     static char_u *
do_one_cmd(char_u ** cmdlinep,int flags,cstack_T * cstack,char_u * (* fgetline)(int,void *,int,getline_opt_T),void * cookie)1733 do_one_cmd(
1734     char_u	**cmdlinep,
1735     int		flags,
1736 #ifdef FEAT_EVAL
1737     cstack_T	*cstack,
1738 #endif
1739     char_u	*(*fgetline)(int, void *, int, getline_opt_T),
1740     void	*cookie)		// argument for fgetline()
1741 {
1742     char_u	*p;
1743     linenr_T	lnum;
1744     long	n;
1745     char	*errormsg = NULL;	// error message
1746     char_u	*after_modifier = NULL;
1747     exarg_T	ea;			// Ex command arguments
1748     cmdmod_T	save_cmdmod;
1749     int		save_reg_executing = reg_executing;
1750     int		ni;			// set when Not Implemented
1751     char_u	*cmd;
1752     int		starts_with_colon = FALSE;
1753 #ifdef FEAT_EVAL
1754     int		may_have_range;
1755     int		vim9script;
1756     int		did_set_expr_line = FALSE;
1757 #endif
1758     int		sourcing = flags & DOCMD_VERBOSE;
1759 
1760     CLEAR_FIELD(ea);
1761     ea.line1 = 1;
1762     ea.line2 = 1;
1763 #ifdef FEAT_EVAL
1764     ++ex_nesting_level;
1765 #endif
1766 
1767     // When the last file has not been edited :q has to be typed twice.
1768     if (quitmore
1769 #ifdef FEAT_EVAL
1770 	    // avoid that a function call in 'statusline' does this
1771 	    && !getline_equal(fgetline, cookie, get_func_line)
1772 #endif
1773 	    // avoid that an autocommand, e.g. QuitPre, does this
1774 	    && !getline_equal(fgetline, cookie, getnextac))
1775 	--quitmore;
1776 
1777     /*
1778      * Reset browse, confirm, etc..  They are restored when returning, for
1779      * recursive calls.
1780      */
1781     save_cmdmod = cmdmod;
1782 
1783     // "#!anything" is handled like a comment.
1784     if ((*cmdlinep)[0] == '#' && (*cmdlinep)[1] == '!')
1785 	goto doend;
1786 
1787 /*
1788  * 1. Skip comment lines and leading white space and colons.
1789  * 2. Handle command modifiers.
1790  */
1791     // The "ea" structure holds the arguments that can be used.
1792     ea.cmd = *cmdlinep;
1793     ea.cmdlinep = cmdlinep;
1794     ea.getline = fgetline;
1795     ea.cookie = cookie;
1796 #ifdef FEAT_EVAL
1797     ea.cstack = cstack;
1798     starts_with_colon = *skipwhite(ea.cmd) == ':';
1799 #endif
1800     if (parse_command_modifiers(&ea, &errormsg, &cmdmod, FALSE) == FAIL)
1801 	goto doend;
1802     apply_cmdmod(&cmdmod);
1803 #ifdef FEAT_EVAL
1804     vim9script = in_vim9script();
1805 #endif
1806     after_modifier = ea.cmd;
1807 
1808 #ifdef FEAT_EVAL
1809     ea.skip = did_emsg || got_int || did_throw || (cstack->cs_idx >= 0
1810 			 && !(cstack->cs_flags[cstack->cs_idx] & CSF_ACTIVE));
1811 #else
1812     ea.skip = (if_level > 0);
1813 #endif
1814 
1815 /*
1816  * 3. Skip over the range to find the command.  Let "p" point to after it.
1817  *
1818  * We need the command to know what kind of range it uses.
1819  */
1820     cmd = ea.cmd;
1821 #ifdef FEAT_EVAL
1822     // In Vim9 script a colon is required before the range.  This may also be
1823     // after command modifiers.
1824     if (vim9script && (flags & DOCMD_RANGEOK) == 0)
1825     {
1826 	may_have_range = FALSE;
1827 	for (p = ea.cmd; p >= *cmdlinep; --p)
1828 	{
1829 	    if (*p == ':')
1830 		may_have_range = TRUE;
1831 	    if (p < ea.cmd && !VIM_ISWHITE(*p))
1832 		break;
1833 	}
1834     }
1835     else
1836 	may_have_range = TRUE;
1837     if (may_have_range)
1838 #endif
1839 	ea.cmd = skip_range(ea.cmd, TRUE, NULL);
1840 
1841 #ifdef FEAT_EVAL
1842     if (vim9script && !may_have_range)
1843     {
1844 	if (ea.cmd == cmd + 1 && *cmd == '$')
1845 	    // should be "$VAR = val"
1846 	    --ea.cmd;
1847 	p = find_ex_command(&ea, NULL, lookup_scriptitem, NULL);
1848 	if (ea.cmdidx == CMD_SIZE)
1849 	{
1850 	    char_u *ar = skip_range(ea.cmd, TRUE, NULL);
1851 
1852 	    // If a ':' before the range is missing, give a clearer error
1853 	    // message.
1854 	    if (ar > ea.cmd && !ea.skip)
1855 	    {
1856 		semsg(_(e_colon_required_before_range_str), ea.cmd);
1857 		goto doend;
1858 	    }
1859 	}
1860     }
1861     else
1862 #endif
1863 	p = find_ex_command(&ea, NULL, NULL, NULL);
1864 
1865 #ifdef FEAT_EVAL
1866 # ifdef FEAT_PROFILE
1867     // Count this line for profiling if skip is TRUE.
1868     if (do_profiling == PROF_YES
1869 	    && (!ea.skip || cstack->cs_idx == 0 || (cstack->cs_idx > 0
1870 		     && (cstack->cs_flags[cstack->cs_idx - 1] & CSF_ACTIVE))))
1871     {
1872 	int skip = did_emsg || got_int || did_throw;
1873 
1874 	if (ea.cmdidx == CMD_catch)
1875 	    skip = !skip && !(cstack->cs_idx >= 0
1876 			  && (cstack->cs_flags[cstack->cs_idx] & CSF_THROWN)
1877 			  && !(cstack->cs_flags[cstack->cs_idx] & CSF_CAUGHT));
1878 	else if (ea.cmdidx == CMD_else || ea.cmdidx == CMD_elseif)
1879 	    skip = skip || !(cstack->cs_idx >= 0
1880 			  && !(cstack->cs_flags[cstack->cs_idx]
1881 						  & (CSF_ACTIVE | CSF_TRUE)));
1882 	else if (ea.cmdidx == CMD_finally)
1883 	    skip = FALSE;
1884 	else if (ea.cmdidx != CMD_endif
1885 		&& ea.cmdidx != CMD_endfor
1886 		&& ea.cmdidx != CMD_endtry
1887 		&& ea.cmdidx != CMD_endwhile)
1888 	    skip = ea.skip;
1889 
1890 	if (!skip)
1891 	{
1892 	    if (getline_equal(fgetline, cookie, get_func_line))
1893 		func_line_exec(getline_cookie(fgetline, cookie));
1894 	    else if (getline_equal(fgetline, cookie, getsourceline))
1895 		script_line_exec();
1896 	}
1897     }
1898 # endif
1899 
1900     // May go to debug mode.  If this happens and the ">quit" debug command is
1901     // used, throw an interrupt exception and skip the next command.
1902     dbg_check_breakpoint(&ea);
1903     if (!ea.skip && got_int)
1904     {
1905 	ea.skip = TRUE;
1906 	(void)do_intthrow(cstack);
1907     }
1908 #endif
1909 
1910 /*
1911  * 4. parse a range specifier of the form: addr [,addr] [;addr] ..
1912  *
1913  * where 'addr' is:
1914  *
1915  * %	      (entire file)
1916  * $  [+-NUM]
1917  * 'x [+-NUM] (where x denotes a currently defined mark)
1918  * .  [+-NUM]
1919  * [+-NUM]..
1920  * NUM
1921  *
1922  * The ea.cmd pointer is updated to point to the first character following the
1923  * range spec. If an initial address is found, but no second, the upper bound
1924  * is equal to the lower.
1925  */
1926 
1927     // ea.addr_type for user commands is set by find_ucmd
1928     if (!IS_USER_CMDIDX(ea.cmdidx))
1929     {
1930 	if (ea.cmdidx != CMD_SIZE)
1931 	    ea.addr_type = cmdnames[(int)ea.cmdidx].cmd_addr_type;
1932 	else
1933 	    ea.addr_type = ADDR_LINES;
1934 
1935 	// :wincmd range depends on the argument.
1936 	if (ea.cmdidx == CMD_wincmd && p != NULL)
1937 	    get_wincmd_addr_type(skipwhite(p), &ea);
1938 #ifdef FEAT_QUICKFIX
1939 	// :.cc in quickfix window uses line number
1940 	if ((ea.cmdidx == CMD_cc || ea.cmdidx == CMD_ll) && bt_quickfix(curbuf))
1941 	    ea.addr_type = ADDR_OTHER;
1942 #endif
1943     }
1944 
1945     ea.cmd = cmd;
1946 #ifdef FEAT_EVAL
1947     if (!may_have_range)
1948 	ea.line1 = ea.line2 = default_address(&ea);
1949     else
1950 #endif
1951 	if (parse_cmd_address(&ea, &errormsg, FALSE) == FAIL)
1952 	    goto doend;
1953 
1954 /*
1955  * 5. Parse the command.
1956  */
1957 
1958     /*
1959      * Skip ':' and any white space
1960      */
1961     ea.cmd = skipwhite(ea.cmd);
1962     while (*ea.cmd == ':')
1963 	ea.cmd = skipwhite(ea.cmd + 1);
1964 
1965     /*
1966      * If we got a line, but no command, then go to the line.
1967      * If we find a '|' or '\n' we set ea.nextcmd.
1968      */
1969     if (*ea.cmd == NUL || comment_start(ea.cmd, starts_with_colon)
1970 			       || (ea.nextcmd = check_nextcmd(ea.cmd)) != NULL)
1971     {
1972 	/*
1973 	 * strange vi behaviour:
1974 	 * ":3"		jumps to line 3
1975 	 * ":3|..."	prints line 3  (not in Vim9 script)
1976 	 * ":|"		prints current line  (not in Vim9 script)
1977 	 */
1978 	if (ea.skip)	    // skip this if inside :if
1979 	    goto doend;
1980 	errormsg = ex_range_without_command(&ea);
1981 	goto doend;
1982     }
1983 
1984     // If this looks like an undefined user command and there are CmdUndefined
1985     // autocommands defined, trigger the matching autocommands.
1986     if (p != NULL && ea.cmdidx == CMD_SIZE && !ea.skip
1987 	    && ASCII_ISUPPER(*ea.cmd)
1988 	    && has_cmdundefined())
1989     {
1990 	int ret;
1991 
1992 	p = ea.cmd;
1993 	while (ASCII_ISALNUM(*p))
1994 	    ++p;
1995 	p = vim_strnsave(ea.cmd, p - ea.cmd);
1996 	ret = apply_autocmds(EVENT_CMDUNDEFINED, p, p, TRUE, NULL);
1997 	vim_free(p);
1998 	// If the autocommands did something and didn't cause an error, try
1999 	// finding the command again.
2000 	p = (ret
2001 #ifdef FEAT_EVAL
2002 		&& !aborting()
2003 #endif
2004 		) ? find_ex_command(&ea, NULL, NULL, NULL) : ea.cmd;
2005     }
2006 
2007     if (p == NULL)
2008     {
2009 	if (!ea.skip)
2010 	    errormsg = _(e_ambiguous_use_of_user_defined_command);
2011 	goto doend;
2012     }
2013     // Check for wrong commands.
2014     if (*p == '!' && ea.cmd[1] == 0151 && ea.cmd[0] == 78
2015 	    && !IS_USER_CMDIDX(ea.cmdidx))
2016     {
2017 	errormsg = uc_fun_cmd();
2018 	goto doend;
2019     }
2020 
2021     if (ea.cmdidx == CMD_SIZE)
2022     {
2023 	if (!ea.skip)
2024 	{
2025 	    STRCPY(IObuff, _("E492: Not an editor command"));
2026 	    if (!sourcing)
2027 	    {
2028 		// If the modifier was parsed OK the error must be in the
2029 		// following command
2030 		if (after_modifier != NULL)
2031 		    append_command(after_modifier);
2032 		else
2033 		    append_command(*cmdlinep);
2034 	    }
2035 	    errormsg = (char *)IObuff;
2036 	    did_emsg_syntax = TRUE;
2037 	}
2038 	goto doend;
2039     }
2040 
2041     ni = (!IS_USER_CMDIDX(ea.cmdidx)
2042 	    && (cmdnames[ea.cmdidx].cmd_func == ex_ni
2043 #ifdef HAVE_EX_SCRIPT_NI
2044 	     || cmdnames[ea.cmdidx].cmd_func == ex_script_ni
2045 #endif
2046 	     ));
2047 
2048 #ifndef FEAT_EVAL
2049     /*
2050      * When the expression evaluation is disabled, recognize the ":if" and
2051      * ":endif" commands and ignore everything in between it.
2052      */
2053     if (ea.cmdidx == CMD_if)
2054 	++if_level;
2055     if (if_level)
2056     {
2057 	if (ea.cmdidx == CMD_endif)
2058 	    --if_level;
2059 	goto doend;
2060     }
2061 
2062 #endif
2063 
2064     // forced commands
2065     if (*p == '!' && ea.cmdidx != CMD_substitute
2066 	    && ea.cmdidx != CMD_smagic && ea.cmdidx != CMD_snomagic)
2067     {
2068 	++p;
2069 	ea.forceit = TRUE;
2070     }
2071     else
2072 	ea.forceit = FALSE;
2073 
2074 /*
2075  * 6. Parse arguments.  Then check for errors.
2076  */
2077     if (!IS_USER_CMDIDX(ea.cmdidx))
2078 	ea.argt = (long)cmdnames[(int)ea.cmdidx].cmd_argt;
2079 
2080     if (!ea.skip)
2081     {
2082 #ifdef HAVE_SANDBOX
2083 	if (sandbox != 0 && !(ea.argt & EX_SBOXOK))
2084 	{
2085 	    // Command not allowed in sandbox.
2086 	    errormsg = _(e_not_allowed_in_sandbox);
2087 	    goto doend;
2088 	}
2089 #endif
2090 	if (restricted != 0 && (ea.argt & EX_RESTRICT))
2091 	{
2092 	    errormsg = _("E981: Command not allowed in rvim");
2093 	    goto doend;
2094 	}
2095 	if (!curbuf->b_p_ma && (ea.argt & EX_MODIFY))
2096 	{
2097 	    // Command not allowed in non-'modifiable' buffer
2098 	    errormsg = _(e_cannot_make_changes_modifiable_is_off);
2099 	    goto doend;
2100 	}
2101 
2102 	if (!IS_USER_CMDIDX(ea.cmdidx))
2103 	{
2104 #ifdef FEAT_CMDWIN
2105 	    if (cmdwin_type != 0 && !(ea.argt & EX_CMDWIN))
2106 	    {
2107 		// Command not allowed in the command line window
2108 		errormsg = _(e_invalid_in_cmdline_window);
2109 		goto doend;
2110 	    }
2111 #endif
2112 	    if (text_locked() && !(ea.argt & EX_LOCK_OK))
2113 	    {
2114 		// Command not allowed when text is locked
2115 		errormsg = _(get_text_locked_msg());
2116 		goto doend;
2117 	    }
2118 	}
2119 
2120 	// Disallow editing another buffer when "curbuf_lock" is set.
2121 	// Do allow ":checktime" (it is postponed).
2122 	// Do allow ":edit" (check for an argument later).
2123 	// Do allow ":file" with no arguments (check for an argument later).
2124 	if (!(ea.argt & (EX_CMDWIN | EX_LOCK_OK))
2125 		&& ea.cmdidx != CMD_checktime
2126 		&& ea.cmdidx != CMD_edit
2127 		&& ea.cmdidx != CMD_file
2128 		&& !IS_USER_CMDIDX(ea.cmdidx)
2129 		&& curbuf_locked())
2130 	    goto doend;
2131 
2132 	if (!ni && !(ea.argt & EX_RANGE) && ea.addr_count > 0)
2133 	{
2134 	    // no range allowed
2135 	    errormsg = _(e_norange);
2136 	    goto doend;
2137 	}
2138     }
2139 
2140     if (!ni && !(ea.argt & EX_BANG) && ea.forceit)	// no <!> allowed
2141     {
2142 	errormsg = _(e_nobang);
2143 	goto doend;
2144     }
2145 
2146     /*
2147      * Don't complain about the range if it is not used
2148      * (could happen if line_count is accidentally set to 0).
2149      */
2150     if (!ea.skip && !ni && (ea.argt & EX_RANGE))
2151     {
2152 	/*
2153 	 * If the range is backwards, ask for confirmation and, if given, swap
2154 	 * ea.line1 & ea.line2 so it's forwards again.
2155 	 * When global command is busy, don't ask, will fail below.
2156 	 */
2157 	if (!global_busy && ea.line1 > ea.line2)
2158 	{
2159 	    if (msg_silent == 0)
2160 	    {
2161 		if (sourcing || exmode_active)
2162 		{
2163 		    errormsg = _("E493: Backwards range given");
2164 		    goto doend;
2165 		}
2166 		if (ask_yesno((char_u *)
2167 			_("Backwards range given, OK to swap"), FALSE) != 'y')
2168 		    goto doend;
2169 	    }
2170 	    lnum = ea.line1;
2171 	    ea.line1 = ea.line2;
2172 	    ea.line2 = lnum;
2173 	}
2174 	if ((errormsg = invalid_range(&ea)) != NULL)
2175 	    goto doend;
2176     }
2177 
2178     if ((ea.addr_type == ADDR_OTHER) && ea.addr_count == 0)
2179 	// default is 1, not cursor
2180 	ea.line2 = 1;
2181 
2182     correct_range(&ea);
2183 
2184 #ifdef FEAT_FOLDING
2185     if (((ea.argt & EX_WHOLEFOLD) || ea.addr_count >= 2) && !global_busy
2186 	    && ea.addr_type == ADDR_LINES)
2187     {
2188 	// Put the first line at the start of a closed fold, put the last line
2189 	// at the end of a closed fold.
2190 	(void)hasFolding(ea.line1, &ea.line1, NULL);
2191 	(void)hasFolding(ea.line2, NULL, &ea.line2);
2192     }
2193 #endif
2194 
2195 #ifdef FEAT_QUICKFIX
2196     /*
2197      * For the ":make" and ":grep" commands we insert the 'makeprg'/'grepprg'
2198      * option here, so things like % get expanded.
2199      */
2200     p = replace_makeprg(&ea, p, cmdlinep);
2201     if (p == NULL)
2202 	goto doend;
2203 #endif
2204 
2205     /*
2206      * Skip to start of argument.
2207      * Don't do this for the ":!" command, because ":!! -l" needs the space.
2208      */
2209     if (ea.cmdidx == CMD_bang)
2210 	ea.arg = p;
2211     else
2212 	ea.arg = skipwhite(p);
2213 
2214     // ":file" cannot be run with an argument when "curbuf_lock" is set
2215     if (ea.cmdidx == CMD_file && *ea.arg != NUL && curbuf_locked())
2216 	goto doend;
2217 
2218     /*
2219      * Check for "++opt=val" argument.
2220      * Must be first, allow ":w ++enc=utf8 !cmd"
2221      */
2222     if (ea.argt & EX_ARGOPT)
2223 	while (ea.arg[0] == '+' && ea.arg[1] == '+')
2224 	    if (getargopt(&ea) == FAIL && !ni)
2225 	    {
2226 		errormsg = _(e_invarg);
2227 		goto doend;
2228 	    }
2229 
2230     if (ea.cmdidx == CMD_write || ea.cmdidx == CMD_update)
2231     {
2232 	if (*ea.arg == '>')			// append
2233 	{
2234 	    if (*++ea.arg != '>')		// typed wrong
2235 	    {
2236 		errormsg = _("E494: Use w or w>>");
2237 		goto doend;
2238 	    }
2239 	    ea.arg = skipwhite(ea.arg + 1);
2240 	    ea.append = TRUE;
2241 	}
2242 	else if (*ea.arg == '!' && ea.cmdidx == CMD_write)  // :w !filter
2243 	{
2244 	    ++ea.arg;
2245 	    ea.usefilter = TRUE;
2246 	}
2247     }
2248 
2249     if (ea.cmdidx == CMD_read)
2250     {
2251 	if (ea.forceit)
2252 	{
2253 	    ea.usefilter = TRUE;		// :r! filter if ea.forceit
2254 	    ea.forceit = FALSE;
2255 	}
2256 	else if (*ea.arg == '!')		// :r !filter
2257 	{
2258 	    ++ea.arg;
2259 	    ea.usefilter = TRUE;
2260 	}
2261     }
2262 
2263     if (ea.cmdidx == CMD_lshift || ea.cmdidx == CMD_rshift)
2264     {
2265 	ea.amount = 1;
2266 	while (*ea.arg == *ea.cmd)		// count number of '>' or '<'
2267 	{
2268 	    ++ea.arg;
2269 	    ++ea.amount;
2270 	}
2271 	ea.arg = skipwhite(ea.arg);
2272     }
2273 
2274     /*
2275      * Check for "+command" argument, before checking for next command.
2276      * Don't do this for ":read !cmd" and ":write !cmd".
2277      */
2278     if ((ea.argt & EX_CMDARG) && !ea.usefilter)
2279 	ea.do_ecmd_cmd = getargcmd(&ea.arg);
2280 
2281     /*
2282      * For commands that do not use '|' inside their argument: Check for '|' to
2283      * separate commands and '"' or '#' to start comments.
2284      *
2285      * Otherwise: Check for <newline> to end a shell command.
2286      * Also do this for ":read !cmd", ":write !cmd" and ":global".
2287      * Also do this inside a { - } block after :command and :autocmd.
2288      * Any others?
2289      */
2290     if ((ea.argt & EX_TRLBAR) && !ea.usefilter)
2291     {
2292 	separate_nextcmd(&ea);
2293     }
2294     else if (ea.cmdidx == CMD_bang
2295 	    || ea.cmdidx == CMD_terminal
2296 	    || ea.cmdidx == CMD_global
2297 	    || ea.cmdidx == CMD_vglobal
2298 	    || ea.usefilter
2299 #ifdef FEAT_EVAL
2300 	    || inside_block(&ea)
2301 #endif
2302 	    )
2303     {
2304 	for (p = ea.arg; *p; ++p)
2305 	{
2306 	    // Remove one backslash before a newline, so that it's possible to
2307 	    // pass a newline to the shell and also a newline that is preceded
2308 	    // with a backslash.  This makes it impossible to end a shell
2309 	    // command in a backslash, but that doesn't appear useful.
2310 	    // Halving the number of backslashes is incompatible with previous
2311 	    // versions.
2312 	    if (*p == '\\' && p[1] == '\n')
2313 		STRMOVE(p, p + 1);
2314 	    else if (*p == '\n')
2315 	    {
2316 		ea.nextcmd = p + 1;
2317 		*p = NUL;
2318 		break;
2319 	    }
2320 	}
2321     }
2322 
2323     if ((ea.argt & EX_DFLALL) && ea.addr_count == 0)
2324 	address_default_all(&ea);
2325 
2326     // accept numbered register only when no count allowed (:put)
2327     if (       (ea.argt & EX_REGSTR)
2328 	    && *ea.arg != NUL
2329 	       // Do not allow register = for user commands
2330 	    && (!IS_USER_CMDIDX(ea.cmdidx) || *ea.arg != '=')
2331 	    && !((ea.argt & EX_COUNT) && VIM_ISDIGIT(*ea.arg)))
2332     {
2333 #ifndef FEAT_CLIPBOARD
2334 	// check these explicitly for a more specific error message
2335 	if (*ea.arg == '*' || *ea.arg == '+')
2336 	{
2337 	    errormsg = _(e_invalidreg);
2338 	    goto doend;
2339 	}
2340 #endif
2341 	if (valid_yank_reg(*ea.arg, (ea.cmdidx != CMD_put
2342 					      && !IS_USER_CMDIDX(ea.cmdidx))))
2343 	{
2344 	    ea.regname = *ea.arg++;
2345 #ifdef FEAT_EVAL
2346 	    // for '=' register: accept the rest of the line as an expression
2347 	    if (ea.arg[-1] == '=' && ea.arg[0] != NUL)
2348 	    {
2349 		if (!ea.skip)
2350 		{
2351 		    set_expr_line(vim_strsave(ea.arg), &ea);
2352 		    did_set_expr_line = TRUE;
2353 		}
2354 		ea.arg += STRLEN(ea.arg);
2355 	    }
2356 #endif
2357 	    ea.arg = skipwhite(ea.arg);
2358 	}
2359     }
2360 
2361     /*
2362      * Check for a count.  When accepting a EX_BUFNAME, don't use "123foo" as a
2363      * count, it's a buffer name.
2364      */
2365     if ((ea.argt & EX_COUNT) && VIM_ISDIGIT(*ea.arg)
2366 	    && (!(ea.argt & EX_BUFNAME) || *(p = skipdigits(ea.arg + 1)) == NUL
2367 							  || VIM_ISWHITE(*p)))
2368     {
2369 	n = getdigits_quoted(&ea.arg);
2370 	ea.arg = skipwhite(ea.arg);
2371 	if (n <= 0 && !ni && (ea.argt & EX_ZEROR) == 0)
2372 	{
2373 	    errormsg = _(e_zerocount);
2374 	    goto doend;
2375 	}
2376 	if (ea.addr_type != ADDR_LINES)	// e.g. :buffer 2, :sleep 3
2377 	{
2378 	    ea.line2 = n;
2379 	    if (ea.addr_count == 0)
2380 		ea.addr_count = 1;
2381 	}
2382 	else
2383 	{
2384 	    ea.line1 = ea.line2;
2385 	    ea.line2 += n - 1;
2386 	    ++ea.addr_count;
2387 	    /*
2388 	     * Be vi compatible: no error message for out of range.
2389 	     */
2390 	    if (ea.line2 > curbuf->b_ml.ml_line_count)
2391 		ea.line2 = curbuf->b_ml.ml_line_count;
2392 	}
2393     }
2394 
2395     /*
2396      * Check for flags: 'l', 'p' and '#'.
2397      */
2398     if (ea.argt & EX_FLAGS)
2399 	get_flags(&ea);
2400     if (!ni && !(ea.argt & EX_EXTRA) && *ea.arg != NUL
2401 	    && *ea.arg != '"' && (*ea.arg != '|' || (ea.argt & EX_TRLBAR) == 0))
2402     {
2403 	// no arguments allowed but there is something
2404 	errormsg = ex_errmsg(e_trailing_arg, ea.arg);
2405 	goto doend;
2406     }
2407 
2408     if (!ni && (ea.argt & EX_NEEDARG) && *ea.arg == NUL)
2409     {
2410 	errormsg = _(e_argreq);
2411 	goto doend;
2412     }
2413 
2414 #ifdef FEAT_EVAL
2415     /*
2416      * Skip the command when it's not going to be executed.
2417      * The commands like :if, :endif, etc. always need to be executed.
2418      * Also make an exception for commands that handle a trailing command
2419      * themselves.
2420      */
2421     if (ea.skip)
2422     {
2423 	switch (ea.cmdidx)
2424 	{
2425 	    // commands that need evaluation
2426 	    case CMD_while:
2427 	    case CMD_endwhile:
2428 	    case CMD_for:
2429 	    case CMD_endfor:
2430 	    case CMD_if:
2431 	    case CMD_elseif:
2432 	    case CMD_else:
2433 	    case CMD_endif:
2434 	    case CMD_try:
2435 	    case CMD_catch:
2436 	    case CMD_finally:
2437 	    case CMD_endtry:
2438 	    case CMD_function:
2439 	    case CMD_def:
2440 				break;
2441 
2442 	    // Commands that handle '|' themselves.  Check: A command should
2443 	    // either have the EX_TRLBAR flag, appear in this list or appear in
2444 	    // the list at ":help :bar".
2445 	    case CMD_aboveleft:
2446 	    case CMD_and:
2447 	    case CMD_belowright:
2448 	    case CMD_botright:
2449 	    case CMD_browse:
2450 	    case CMD_call:
2451 	    case CMD_confirm:
2452 	    case CMD_const:
2453 	    case CMD_delfunction:
2454 	    case CMD_djump:
2455 	    case CMD_dlist:
2456 	    case CMD_dsearch:
2457 	    case CMD_dsplit:
2458 	    case CMD_echo:
2459 	    case CMD_echoerr:
2460 	    case CMD_echomsg:
2461 	    case CMD_echon:
2462 	    case CMD_eval:
2463 	    case CMD_execute:
2464 	    case CMD_filter:
2465 	    case CMD_final:
2466 	    case CMD_help:
2467 	    case CMD_hide:
2468 	    case CMD_ijump:
2469 	    case CMD_ilist:
2470 	    case CMD_isearch:
2471 	    case CMD_isplit:
2472 	    case CMD_keepalt:
2473 	    case CMD_keepjumps:
2474 	    case CMD_keepmarks:
2475 	    case CMD_keeppatterns:
2476 	    case CMD_leftabove:
2477 	    case CMD_let:
2478 	    case CMD_lockmarks:
2479 	    case CMD_lockvar:
2480 	    case CMD_lua:
2481 	    case CMD_match:
2482 	    case CMD_mzscheme:
2483 	    case CMD_noautocmd:
2484 	    case CMD_noswapfile:
2485 	    case CMD_perl:
2486 	    case CMD_psearch:
2487 	    case CMD_py3:
2488 	    case CMD_python3:
2489 	    case CMD_python:
2490 	    case CMD_return:
2491 	    case CMD_rightbelow:
2492 	    case CMD_ruby:
2493 	    case CMD_silent:
2494 	    case CMD_smagic:
2495 	    case CMD_snomagic:
2496 	    case CMD_substitute:
2497 	    case CMD_syntax:
2498 	    case CMD_tab:
2499 	    case CMD_tcl:
2500 	    case CMD_throw:
2501 	    case CMD_tilde:
2502 	    case CMD_topleft:
2503 	    case CMD_unlet:
2504 	    case CMD_unlockvar:
2505 	    case CMD_var:
2506 	    case CMD_verbose:
2507 	    case CMD_vertical:
2508 	    case CMD_wincmd:
2509 				break;
2510 
2511 	    default:		goto doend;
2512 	}
2513     }
2514 #endif
2515 
2516     if (ea.argt & EX_XFILE)
2517     {
2518 	if (expand_filename(&ea, cmdlinep, &errormsg) == FAIL)
2519 	    goto doend;
2520     }
2521 
2522     /*
2523      * Accept buffer name.  Cannot be used at the same time with a buffer
2524      * number.  Don't do this for a user command.
2525      */
2526     if ((ea.argt & EX_BUFNAME) && *ea.arg != NUL && ea.addr_count == 0
2527 	    && !IS_USER_CMDIDX(ea.cmdidx))
2528     {
2529 	/*
2530 	 * :bdelete, :bwipeout and :bunload take several arguments, separated
2531 	 * by spaces: find next space (skipping over escaped characters).
2532 	 * The others take one argument: ignore trailing spaces.
2533 	 */
2534 	if (ea.cmdidx == CMD_bdelete || ea.cmdidx == CMD_bwipeout
2535 						  || ea.cmdidx == CMD_bunload)
2536 	    p = skiptowhite_esc(ea.arg);
2537 	else
2538 	{
2539 	    p = ea.arg + STRLEN(ea.arg);
2540 	    while (p > ea.arg && VIM_ISWHITE(p[-1]))
2541 		--p;
2542 	}
2543 	ea.line2 = buflist_findpat(ea.arg, p, (ea.argt & EX_BUFUNL) != 0,
2544 								FALSE, FALSE);
2545 	if (ea.line2 < 0)	    // failed
2546 	    goto doend;
2547 	ea.addr_count = 1;
2548 	ea.arg = skipwhite(p);
2549     }
2550 
2551     // The :try command saves the emsg_silent flag, reset it here when
2552     // ":silent! try" was used, it should only apply to :try itself.
2553     if (ea.cmdidx == CMD_try && cmdmod.cmod_did_esilent > 0)
2554     {
2555 	emsg_silent -= cmdmod.cmod_did_esilent;
2556 	if (emsg_silent < 0)
2557 	    emsg_silent = 0;
2558 	cmdmod.cmod_did_esilent = 0;
2559     }
2560 
2561 /*
2562  * 7. Execute the command.
2563  */
2564 
2565     if (IS_USER_CMDIDX(ea.cmdidx))
2566     {
2567 	/*
2568 	 * Execute a user-defined command.
2569 	 */
2570 	do_ucmd(&ea);
2571     }
2572     else
2573     {
2574 	/*
2575 	 * Call the function to execute the builtin command.
2576 	 */
2577 	ea.errmsg = NULL;
2578 	(cmdnames[ea.cmdidx].cmd_func)(&ea);
2579 	if (ea.errmsg != NULL)
2580 	    errormsg = ea.errmsg;
2581     }
2582 
2583 #ifdef FEAT_EVAL
2584     // Set flag that any command was executed, used by ex_vim9script().
2585     // Not if this was a command that wasn't executed or :endif.
2586     if (getline_equal(ea.getline, ea.cookie, getsourceline)
2587 	    && current_sctx.sc_sid > 0
2588 	    && ea.cmdidx != CMD_endif
2589 	    && (cstack->cs_idx < 0
2590 		    || (cstack->cs_flags[cstack->cs_idx] & CSF_ACTIVE)))
2591 	SCRIPT_ITEM(current_sctx.sc_sid)->sn_state = SN_STATE_HAD_COMMAND;
2592 
2593     /*
2594      * If the command just executed called do_cmdline(), any throw or ":return"
2595      * or ":finish" encountered there must also check the cstack of the still
2596      * active do_cmdline() that called this do_one_cmd().  Rethrow an uncaught
2597      * exception, or reanimate a returned function or finished script file and
2598      * return or finish it again.
2599      */
2600     if (need_rethrow)
2601 	do_throw(cstack);
2602     else if (check_cstack)
2603     {
2604 	if (source_finished(fgetline, cookie))
2605 	    do_finish(&ea, TRUE);
2606 	else if (getline_equal(fgetline, cookie, get_func_line)
2607 						   && current_func_returned())
2608 	    do_return(&ea, TRUE, FALSE, NULL);
2609     }
2610     need_rethrow = check_cstack = FALSE;
2611 #endif
2612 
2613 doend:
2614     if (curwin->w_cursor.lnum == 0)	// can happen with zero line number
2615     {
2616 	curwin->w_cursor.lnum = 1;
2617 	curwin->w_cursor.col = 0;
2618     }
2619 
2620     if (errormsg != NULL && *errormsg != NUL && !did_emsg)
2621     {
2622 	if (sourcing)
2623 	{
2624 	    if (errormsg != (char *)IObuff)
2625 	    {
2626 		STRCPY(IObuff, errormsg);
2627 		errormsg = (char *)IObuff;
2628 	    }
2629 	    append_command(*cmdlinep);
2630 	}
2631 	emsg(errormsg);
2632     }
2633 #ifdef FEAT_EVAL
2634     do_errthrow(cstack,
2635 	    (ea.cmdidx != CMD_SIZE && !IS_USER_CMDIDX(ea.cmdidx))
2636 			? cmdnames[(int)ea.cmdidx].cmd_name : (char_u *)NULL);
2637 
2638     if (did_set_expr_line)
2639 	set_expr_line(NULL, NULL);
2640 #endif
2641 
2642     undo_cmdmod(&cmdmod);
2643     cmdmod = save_cmdmod;
2644     reg_executing = save_reg_executing;
2645 
2646     if (ea.nextcmd && *ea.nextcmd == NUL)	// not really a next command
2647 	ea.nextcmd = NULL;
2648 
2649 #ifdef FEAT_EVAL
2650     --ex_nesting_level;
2651     vim_free(ea.cmdline_tofree);
2652 #endif
2653 
2654     return ea.nextcmd;
2655 }
2656 #if (_MSC_VER == 1200)
2657  #pragma optimize( "", on )
2658 #endif
2659 
2660 static char ex_error_buf[MSG_BUF_LEN];
2661 
2662 /*
2663  * Return an error message with argument included.
2664  * Uses a static buffer, only the last error will be kept.
2665  * "msg" will be translated, caller should use N_().
2666  */
2667      char *
ex_errmsg(char * msg,char_u * arg)2668 ex_errmsg(char *msg, char_u *arg)
2669 {
2670     vim_snprintf(ex_error_buf, MSG_BUF_LEN, _(msg), arg);
2671     return ex_error_buf;
2672 }
2673 
2674 /*
2675  * Handle a range without a command.
2676  * Returns an error message on failure.
2677  */
2678     char *
ex_range_without_command(exarg_T * eap)2679 ex_range_without_command(exarg_T *eap)
2680 {
2681     char *errormsg = NULL;
2682 
2683     if ((*eap->cmd == '|' || (exmode_active && eap->line1 != eap->line2))
2684 #ifdef FEAT_EVAL
2685 	    && !in_vim9script()
2686 #endif
2687        )
2688     {
2689 	eap->cmdidx = CMD_print;
2690 	eap->argt = EX_RANGE+EX_COUNT+EX_TRLBAR;
2691 	if ((errormsg = invalid_range(eap)) == NULL)
2692 	{
2693 	    correct_range(eap);
2694 	    ex_print(eap);
2695 	}
2696     }
2697     else if (eap->addr_count != 0)
2698     {
2699 	if (eap->line2 > curbuf->b_ml.ml_line_count)
2700 	{
2701 	    // With '-' in 'cpoptions' a line number past the file is an
2702 	    // error, otherwise put it at the end of the file.
2703 	    if (vim_strchr(p_cpo, CPO_MINUS) != NULL)
2704 		eap->line2 = -1;
2705 	    else
2706 		eap->line2 = curbuf->b_ml.ml_line_count;
2707 	}
2708 
2709 	if (eap->line2 < 0)
2710 	    errormsg = _(e_invalid_range);
2711 	else
2712 	{
2713 	    if (eap->line2 == 0)
2714 		curwin->w_cursor.lnum = 1;
2715 	    else
2716 		curwin->w_cursor.lnum = eap->line2;
2717 	    beginline(BL_SOL | BL_FIX);
2718 	}
2719     }
2720     return errormsg;
2721 }
2722 
2723 /*
2724  * Check for an Ex command with optional tail.
2725  * If there is a match advance "pp" to the argument and return TRUE.
2726  * If "noparen" is TRUE do not recognize the command followed by "(".
2727  */
2728     static int
checkforcmd_opt(char_u ** pp,char * cmd,int len,int noparen)2729 checkforcmd_opt(
2730     char_u	**pp,		// start of command
2731     char	*cmd,		// name of command
2732     int		len,		// required length
2733     int		noparen)
2734 {
2735     int		i;
2736 
2737     for (i = 0; cmd[i] != NUL; ++i)
2738 	if (((char_u *)cmd)[i] != (*pp)[i])
2739 	    break;
2740     if (i >= len && !isalpha((*pp)[i])
2741 			   && (*pp)[i] != '_' && (!noparen || (*pp)[i] != '('))
2742     {
2743 	*pp = skipwhite(*pp + i);
2744 	return TRUE;
2745     }
2746     return FALSE;
2747 }
2748 
2749 /*
2750  * Check for an Ex command with optional tail.
2751  * If there is a match advance "pp" to the argument and return TRUE.
2752  */
2753     int
checkforcmd(char_u ** pp,char * cmd,int len)2754 checkforcmd(
2755     char_u	**pp,		// start of command
2756     char	*cmd,		// name of command
2757     int		len)		// required length
2758 {
2759     return checkforcmd_opt(pp, cmd, len, FALSE);
2760 }
2761 
2762 /*
2763  * Check for an Ex command with optional tail, not followed by "(".
2764  * If there is a match advance "pp" to the argument and return TRUE.
2765  */
2766     int
checkforcmd_noparen(char_u ** pp,char * cmd,int len)2767 checkforcmd_noparen(
2768     char_u	**pp,		// start of command
2769     char	*cmd,		// name of command
2770     int		len)		// required length
2771 {
2772     return checkforcmd_opt(pp, cmd, len, TRUE);
2773 }
2774 
2775 /*
2776  * Parse and skip over command modifiers:
2777  * - update eap->cmd
2778  * - store flags in "cmod".
2779  * - Set ex_pressedreturn for an empty command line.
2780  * When "skip_only" is TRUE the global variables are not changed, except for
2781  * "cmdmod".
2782  * When "skip_only" is FALSE then undo_cmdmod() must be called later to free
2783  * any cmod_filter_regmatch.regprog.
2784  * Call apply_cmdmod() to get the side effects of the modifiers:
2785  * - Increment "sandbox" for ":sandbox"
2786  * - set p_verbose for ":verbose"
2787  * - set msg_silent for ":silent"
2788  * - set 'eventignore' to "all" for ":noautocmd"
2789  * Return FAIL when the command is not to be executed.
2790  * May set "errormsg" to an error message.
2791  */
2792     int
parse_command_modifiers(exarg_T * eap,char ** errormsg,cmdmod_T * cmod,int skip_only)2793 parse_command_modifiers(
2794 	exarg_T	    *eap,
2795 	char	    **errormsg,
2796 	cmdmod_T    *cmod,
2797 	int	    skip_only)
2798 {
2799     char_u  *p;
2800     int	    starts_with_colon = FALSE;
2801 
2802     CLEAR_POINTER(cmod);
2803 
2804     // Repeat until no more command modifiers are found.
2805     for (;;)
2806     {
2807 	while (*eap->cmd == ' ' || *eap->cmd == '\t' || *eap->cmd == ':')
2808 	{
2809 	    if (*eap->cmd == ':')
2810 		starts_with_colon = TRUE;
2811 	    ++eap->cmd;
2812 	}
2813 
2814 	// in ex mode, an empty line works like :+
2815 	if (*eap->cmd == NUL && exmode_active
2816 		   && (getline_equal(eap->getline, eap->cookie, getexmodeline)
2817 		       || getline_equal(eap->getline, eap->cookie, getexline))
2818 			&& curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count)
2819 	{
2820 	    eap->cmd = (char_u *)"+";
2821 	    if (!skip_only)
2822 		ex_pressedreturn = TRUE;
2823 	}
2824 
2825 	// ignore comment and empty lines
2826 	if (comment_start(eap->cmd, starts_with_colon))
2827 	{
2828 	    // a comment ends at a NL
2829 	    if (eap->nextcmd == NULL)
2830 	    {
2831 		eap->nextcmd = vim_strchr(eap->cmd, '\n');
2832 		if (eap->nextcmd != NULL)
2833 		    ++eap->nextcmd;
2834 	    }
2835 	    return FAIL;
2836 	}
2837 	if (*eap->cmd == NUL)
2838 	{
2839 	    if (!skip_only)
2840 		ex_pressedreturn = TRUE;
2841 	    return FAIL;
2842 	}
2843 
2844 	p = skip_range(eap->cmd, TRUE, NULL);
2845 
2846 	// In Vim9 script a variable can shadow a command modifier:
2847 	//   verbose = 123
2848 	//   verbose += 123
2849 	//   silent! verbose = func()
2850 	//   verbose.member = 2
2851 	//   verbose[expr] = 2
2852 	// But not:
2853 	//   verbose [a, b] = list
2854 	if (in_vim9script())
2855 	{
2856 	    char_u *s, *n;
2857 
2858 	    for (s = p; ASCII_ISALPHA(*s); ++s)
2859 		;
2860 	    n = skipwhite(s);
2861 	    if (vim_strchr((char_u *)".=", *n) != NULL
2862 		    || *s == '['
2863 		    || (*n != NUL && n[1] == '='))
2864 		break;
2865 	}
2866 
2867 	switch (*p)
2868 	{
2869 	    // When adding an entry, also modify cmd_exists().
2870 	    case 'a':	if (!checkforcmd_noparen(&eap->cmd, "aboveleft", 3))
2871 			    break;
2872 			cmod->cmod_split |= WSP_ABOVE;
2873 			continue;
2874 
2875 	    case 'b':	if (checkforcmd_noparen(&eap->cmd, "belowright", 3))
2876 			{
2877 			    cmod->cmod_split |= WSP_BELOW;
2878 			    continue;
2879 			}
2880 			if (checkforcmd_opt(&eap->cmd, "browse", 3, TRUE))
2881 			{
2882 #ifdef FEAT_BROWSE_CMD
2883 			    cmod->cmod_flags |= CMOD_BROWSE;
2884 #endif
2885 			    continue;
2886 			}
2887 			if (!checkforcmd_noparen(&eap->cmd, "botright", 2))
2888 			    break;
2889 			cmod->cmod_split |= WSP_BOT;
2890 			continue;
2891 
2892 	    case 'c':	if (!checkforcmd_opt(&eap->cmd, "confirm", 4, TRUE))
2893 			    break;
2894 #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
2895 			cmod->cmod_flags |= CMOD_CONFIRM;
2896 #endif
2897 			continue;
2898 
2899 	    case 'k':	if (checkforcmd_noparen(&eap->cmd, "keepmarks", 3))
2900 			{
2901 			    cmod->cmod_flags |= CMOD_KEEPMARKS;
2902 			    continue;
2903 			}
2904 			if (checkforcmd_noparen(&eap->cmd, "keepalt", 5))
2905 			{
2906 			    cmod->cmod_flags |= CMOD_KEEPALT;
2907 			    continue;
2908 			}
2909 			if (checkforcmd_noparen(&eap->cmd, "keeppatterns", 5))
2910 			{
2911 			    cmod->cmod_flags |= CMOD_KEEPPATTERNS;
2912 			    continue;
2913 			}
2914 			if (!checkforcmd_noparen(&eap->cmd, "keepjumps", 5))
2915 			    break;
2916 			cmod->cmod_flags |= CMOD_KEEPJUMPS;
2917 			continue;
2918 
2919 	    case 'f':	// only accept ":filter {pat} cmd"
2920 			{
2921 			    char_u  *reg_pat;
2922 			    char_u  *nulp = NULL;
2923 			    int	    c = 0;
2924 
2925 			    if (!checkforcmd_noparen(&p, "filter", 4)
2926 				    || *p == NUL
2927 				    || (ends_excmd(*p)
2928 #ifdef FEAT_EVAL
2929 					// in ":filter #pat# cmd" # does not
2930 					// start a comment
2931 				     && (!in_vim9script() || VIM_ISWHITE(p[1]))
2932 #endif
2933 				     ))
2934 				break;
2935 			    if (*p == '!')
2936 			    {
2937 				cmod->cmod_filter_force = TRUE;
2938 				p = skipwhite(p + 1);
2939 				if (*p == NUL || ends_excmd(*p))
2940 				    break;
2941 			    }
2942 #ifdef FEAT_EVAL
2943 			    // Avoid that "filter(arg)" is recognized.
2944 			    if (in_vim9script() && !VIM_ISWHITE(p[-1]))
2945 				break;
2946 #endif
2947 			    if (skip_only)
2948 				p = skip_vimgrep_pat(p, NULL, NULL);
2949 			    else
2950 				// NOTE: This puts a NUL after the pattern.
2951 				p = skip_vimgrep_pat_ext(p, &reg_pat, NULL,
2952 								    &nulp, &c);
2953 			    if (p == NULL || *p == NUL)
2954 				break;
2955 			    if (!skip_only)
2956 			    {
2957 				cmod->cmod_filter_regmatch.regprog =
2958 						vim_regcomp(reg_pat, RE_MAGIC);
2959 				if (cmod->cmod_filter_regmatch.regprog == NULL)
2960 				    break;
2961 				// restore the character overwritten by NUL
2962 				if (nulp != NULL)
2963 				    *nulp = c;
2964 			    }
2965 			    eap->cmd = p;
2966 			    continue;
2967 			}
2968 
2969 			// ":hide" and ":hide | cmd" are not modifiers
2970 	    case 'h':	if (p != eap->cmd || !checkforcmd_noparen(&p, "hide", 3)
2971 					       || *p == NUL || ends_excmd(*p))
2972 			    break;
2973 			eap->cmd = p;
2974 			cmod->cmod_flags |= CMOD_HIDE;
2975 			continue;
2976 
2977 	    case 'l':	if (checkforcmd_noparen(&eap->cmd, "lockmarks", 3))
2978 			{
2979 			    cmod->cmod_flags |= CMOD_LOCKMARKS;
2980 			    continue;
2981 			}
2982 			if (checkforcmd_noparen(&eap->cmd, "legacy", 3))
2983 			{
2984 			    if (ends_excmd2(p, eap->cmd))
2985 			    {
2986 				*errormsg =
2987 				      _(e_legacy_must_be_followed_by_command);
2988 				return FAIL;
2989 			    }
2990 			    cmod->cmod_flags |= CMOD_LEGACY;
2991 			    continue;
2992 			}
2993 
2994 			if (!checkforcmd_noparen(&eap->cmd, "leftabove", 5))
2995 			    break;
2996 			cmod->cmod_split |= WSP_ABOVE;
2997 			continue;
2998 
2999 	    case 'n':	if (checkforcmd_noparen(&eap->cmd, "noautocmd", 3))
3000 			{
3001 			    cmod->cmod_flags |= CMOD_NOAUTOCMD;
3002 			    continue;
3003 			}
3004 			if (!checkforcmd_noparen(&eap->cmd, "noswapfile", 3))
3005 			    break;
3006 			cmod->cmod_flags |= CMOD_NOSWAPFILE;
3007 			continue;
3008 
3009 	    case 'r':	if (!checkforcmd_noparen(&eap->cmd, "rightbelow", 6))
3010 			    break;
3011 			cmod->cmod_split |= WSP_BELOW;
3012 			continue;
3013 
3014 	    case 's':	if (checkforcmd_noparen(&eap->cmd, "sandbox", 3))
3015 			{
3016 			    cmod->cmod_flags |= CMOD_SANDBOX;
3017 			    continue;
3018 			}
3019 			if (!checkforcmd_noparen(&eap->cmd, "silent", 3))
3020 			    break;
3021 			cmod->cmod_flags |= CMOD_SILENT;
3022 			if (*eap->cmd == '!' && !VIM_ISWHITE(eap->cmd[-1]))
3023 			{
3024 			    // ":silent!", but not "silent !cmd"
3025 			    eap->cmd = skipwhite(eap->cmd + 1);
3026 			    cmod->cmod_flags |= CMOD_ERRSILENT;
3027 			}
3028 			continue;
3029 
3030 	    case 't':	if (checkforcmd_noparen(&p, "tab", 3))
3031 			{
3032 			    if (!skip_only)
3033 			    {
3034 				long tabnr = get_address(eap, &eap->cmd,
3035 						    ADDR_TABS, eap->skip,
3036 						    skip_only, FALSE, 1);
3037 				if (tabnr == MAXLNUM)
3038 				    cmod->cmod_tab = tabpage_index(curtab) + 1;
3039 				else
3040 				{
3041 				    if (tabnr < 0 || tabnr > LAST_TAB_NR)
3042 				    {
3043 					*errormsg = _(e_invalid_range);
3044 					return FAIL;
3045 				    }
3046 				    cmod->cmod_tab = tabnr + 1;
3047 				}
3048 			    }
3049 			    eap->cmd = p;
3050 			    continue;
3051 			}
3052 			if (!checkforcmd_noparen(&eap->cmd, "topleft", 2))
3053 			    break;
3054 			cmod->cmod_split |= WSP_TOP;
3055 			continue;
3056 
3057 	    case 'u':	if (!checkforcmd_noparen(&eap->cmd, "unsilent", 3))
3058 			    break;
3059 			cmod->cmod_flags |= CMOD_UNSILENT;
3060 			continue;
3061 
3062 	    case 'v':	if (checkforcmd_noparen(&eap->cmd, "vertical", 4))
3063 			{
3064 			    cmod->cmod_split |= WSP_VERT;
3065 			    continue;
3066 			}
3067 			if (checkforcmd_noparen(&eap->cmd, "vim9cmd", 4))
3068 			{
3069 			    if (ends_excmd2(p, eap->cmd))
3070 			    {
3071 				*errormsg =
3072 				      _(e_vim9cmd_must_be_followed_by_command);
3073 				return FAIL;
3074 			    }
3075 			    cmod->cmod_flags |= CMOD_VIM9CMD;
3076 			    continue;
3077 			}
3078 			if (!checkforcmd_noparen(&p, "verbose", 4))
3079 			    break;
3080 			if (vim_isdigit(*eap->cmd))
3081 			    cmod->cmod_verbose = atoi((char *)eap->cmd);
3082 			else
3083 			    cmod->cmod_verbose = 1;
3084 			eap->cmd = p;
3085 			continue;
3086 	}
3087 	break;
3088     }
3089 
3090     return OK;
3091 }
3092 
3093 /*
3094  * Return TRUE if "cmod" has anything set.
3095  */
3096     int
has_cmdmod(cmdmod_T * cmod,int ignore_silent)3097 has_cmdmod(cmdmod_T *cmod, int ignore_silent)
3098 {
3099     return (cmod->cmod_flags != 0 && (!ignore_silent
3100 		|| (cmod->cmod_flags
3101 		      & ~(CMOD_SILENT | CMOD_ERRSILENT | CMOD_UNSILENT)) != 0))
3102 	    || cmod->cmod_split != 0
3103 	    || cmod->cmod_verbose != 0
3104 	    || cmod->cmod_tab != 0
3105 	    || cmod->cmod_filter_regmatch.regprog != NULL;
3106 }
3107 
3108 /*
3109  * If Vim9 script and "cmdmod" has anything set give an error and return TRUE.
3110  */
3111     int
cmdmod_error(int ignore_silent)3112 cmdmod_error(int ignore_silent)
3113 {
3114     if (in_vim9script() && has_cmdmod(&cmdmod, ignore_silent))
3115     {
3116 	emsg(_(e_misplaced_command_modifier));
3117 	return TRUE;
3118     }
3119     return FALSE;
3120 }
3121 
3122 /*
3123  * Apply the command modifiers.  Saves current state in "cmdmod", call
3124  * undo_cmdmod() later.
3125  */
3126     void
apply_cmdmod(cmdmod_T * cmod)3127 apply_cmdmod(cmdmod_T *cmod)
3128 {
3129 #ifdef HAVE_SANDBOX
3130     if ((cmod->cmod_flags & CMOD_SANDBOX) && !cmod->cmod_did_sandbox)
3131     {
3132 	++sandbox;
3133 	cmod->cmod_did_sandbox = TRUE;
3134     }
3135 #endif
3136     if (cmod->cmod_verbose > 0)
3137     {
3138 	if (cmod->cmod_verbose_save == 0)
3139 	    cmod->cmod_verbose_save = p_verbose + 1;
3140 	p_verbose = cmod->cmod_verbose;
3141     }
3142 
3143     if ((cmod->cmod_flags & (CMOD_SILENT | CMOD_UNSILENT))
3144 	    && cmod->cmod_save_msg_silent == 0)
3145     {
3146 	cmod->cmod_save_msg_silent = msg_silent + 1;
3147 	cmod->cmod_save_msg_scroll = msg_scroll;
3148     }
3149     if (cmod->cmod_flags & CMOD_SILENT)
3150 	++msg_silent;
3151     if (cmod->cmod_flags & CMOD_UNSILENT)
3152 	msg_silent = 0;
3153 
3154     if (cmod->cmod_flags & CMOD_ERRSILENT)
3155     {
3156 	++emsg_silent;
3157 	++cmod->cmod_did_esilent;
3158     }
3159 
3160     if ((cmod->cmod_flags & CMOD_NOAUTOCMD) && cmod->cmod_save_ei == NULL)
3161     {
3162 	// Set 'eventignore' to "all".
3163 	// First save the existing option value for restoring it later.
3164 	cmod->cmod_save_ei = vim_strsave(p_ei);
3165 	set_string_option_direct((char_u *)"ei", -1,
3166 					  (char_u *)"all", OPT_FREE, SID_NONE);
3167     }
3168 }
3169 
3170 /*
3171  * Undo and free contents of "cmod".
3172  */
3173     void
undo_cmdmod(cmdmod_T * cmod)3174 undo_cmdmod(cmdmod_T *cmod)
3175 {
3176     if (cmod->cmod_verbose_save > 0)
3177     {
3178 	p_verbose = cmod->cmod_verbose_save - 1;
3179 	cmod->cmod_verbose_save = 0;
3180     }
3181 
3182 #ifdef HAVE_SANDBOX
3183     if (cmod->cmod_did_sandbox)
3184     {
3185 	--sandbox;
3186 	cmod->cmod_did_sandbox = FALSE;
3187     }
3188 #endif
3189 
3190     if (cmod->cmod_save_ei != NULL)
3191     {
3192 	// Restore 'eventignore' to the value before ":noautocmd".
3193 	set_string_option_direct((char_u *)"ei", -1, cmod->cmod_save_ei,
3194 							   OPT_FREE, SID_NONE);
3195 	free_string_option(cmod->cmod_save_ei);
3196 	cmod->cmod_save_ei = NULL;
3197     }
3198 
3199     vim_regfree(cmod->cmod_filter_regmatch.regprog);
3200 
3201     if (cmod->cmod_save_msg_silent > 0)
3202     {
3203 	// messages could be enabled for a serious error, need to check if the
3204 	// counters don't become negative
3205 	if (!did_emsg || msg_silent > cmod->cmod_save_msg_silent - 1)
3206 	    msg_silent = cmod->cmod_save_msg_silent - 1;
3207 	emsg_silent -= cmod->cmod_did_esilent;
3208 	if (emsg_silent < 0)
3209 	    emsg_silent = 0;
3210 	// Restore msg_scroll, it's set by file I/O commands, even when no
3211 	// message is actually displayed.
3212 	msg_scroll = cmod->cmod_save_msg_scroll;
3213 
3214 	// "silent reg" or "silent echo x" inside "redir" leaves msg_col
3215 	// somewhere in the line.  Put it back in the first column.
3216 	if (redirecting())
3217 	    msg_col = 0;
3218 
3219 	cmod->cmod_save_msg_silent = 0;
3220 	cmod->cmod_did_esilent = 0;
3221     }
3222 }
3223 
3224 /*
3225  * Parse the address range, if any, in "eap".
3226  * May set the last search pattern, unless "silent" is TRUE.
3227  * Return FAIL and set "errormsg" or return OK.
3228  */
3229     int
parse_cmd_address(exarg_T * eap,char ** errormsg,int silent)3230 parse_cmd_address(exarg_T *eap, char **errormsg, int silent)
3231 {
3232     int		address_count = 1;
3233     linenr_T	lnum;
3234 
3235     // Repeat for all ',' or ';' separated addresses.
3236     for (;;)
3237     {
3238 	eap->line1 = eap->line2;
3239 	eap->line2 = default_address(eap);
3240 	eap->cmd = skipwhite(eap->cmd);
3241 	lnum = get_address(eap, &eap->cmd, eap->addr_type, eap->skip, silent,
3242 					eap->addr_count == 0, address_count++);
3243 	if (eap->cmd == NULL)	// error detected
3244 	    return FAIL;
3245 	if (lnum == MAXLNUM)
3246 	{
3247 	    if (*eap->cmd == '%')   // '%' - all lines
3248 	    {
3249 		++eap->cmd;
3250 		switch (eap->addr_type)
3251 		{
3252 		    case ADDR_LINES:
3253 		    case ADDR_OTHER:
3254 			eap->line1 = 1;
3255 			eap->line2 = curbuf->b_ml.ml_line_count;
3256 			break;
3257 		    case ADDR_LOADED_BUFFERS:
3258 			{
3259 			    buf_T	*buf = firstbuf;
3260 
3261 			    while (buf->b_next != NULL
3262 						  && buf->b_ml.ml_mfp == NULL)
3263 				buf = buf->b_next;
3264 			    eap->line1 = buf->b_fnum;
3265 			    buf = lastbuf;
3266 			    while (buf->b_prev != NULL
3267 						  && buf->b_ml.ml_mfp == NULL)
3268 				buf = buf->b_prev;
3269 			    eap->line2 = buf->b_fnum;
3270 			    break;
3271 			}
3272 		    case ADDR_BUFFERS:
3273 			eap->line1 = firstbuf->b_fnum;
3274 			eap->line2 = lastbuf->b_fnum;
3275 			break;
3276 		    case ADDR_WINDOWS:
3277 		    case ADDR_TABS:
3278 			if (IS_USER_CMDIDX(eap->cmdidx))
3279 			{
3280 			    eap->line1 = 1;
3281 			    eap->line2 = eap->addr_type == ADDR_WINDOWS
3282 						  ? LAST_WIN_NR : LAST_TAB_NR;
3283 			}
3284 			else
3285 			{
3286 			    // there is no Vim command which uses '%' and
3287 			    // ADDR_WINDOWS or ADDR_TABS
3288 			    *errormsg = _(e_invalid_range);
3289 			    return FAIL;
3290 			}
3291 			break;
3292 		    case ADDR_TABS_RELATIVE:
3293 		    case ADDR_UNSIGNED:
3294 		    case ADDR_QUICKFIX:
3295 			*errormsg = _(e_invalid_range);
3296 			return FAIL;
3297 		    case ADDR_ARGUMENTS:
3298 			if (ARGCOUNT == 0)
3299 			    eap->line1 = eap->line2 = 0;
3300 			else
3301 			{
3302 			    eap->line1 = 1;
3303 			    eap->line2 = ARGCOUNT;
3304 			}
3305 			break;
3306 		    case ADDR_QUICKFIX_VALID:
3307 #ifdef FEAT_QUICKFIX
3308 			eap->line1 = 1;
3309 			eap->line2 = qf_get_valid_size(eap);
3310 			if (eap->line2 == 0)
3311 			    eap->line2 = 1;
3312 #endif
3313 			break;
3314 		    case ADDR_NONE:
3315 			// Will give an error later if a range is found.
3316 			break;
3317 		}
3318 		++eap->addr_count;
3319 	    }
3320 	    else if (*eap->cmd == '*' && vim_strchr(p_cpo, CPO_STAR) == NULL)
3321 	    {
3322 		pos_T	    *fp;
3323 
3324 		// '*' - visual area
3325 		if (eap->addr_type != ADDR_LINES)
3326 		{
3327 		    *errormsg = _(e_invalid_range);
3328 		    return FAIL;
3329 		}
3330 
3331 		++eap->cmd;
3332 		if (!eap->skip)
3333 		{
3334 		    fp = getmark('<', FALSE);
3335 		    if (check_mark(fp) == FAIL)
3336 			return FAIL;
3337 		    eap->line1 = fp->lnum;
3338 		    fp = getmark('>', FALSE);
3339 		    if (check_mark(fp) == FAIL)
3340 			return FAIL;
3341 		    eap->line2 = fp->lnum;
3342 		    ++eap->addr_count;
3343 		}
3344 	    }
3345 	}
3346 	else
3347 	    eap->line2 = lnum;
3348 	eap->addr_count++;
3349 
3350 	if (*eap->cmd == ';')
3351 	{
3352 	    if (!eap->skip)
3353 	    {
3354 		curwin->w_cursor.lnum = eap->line2;
3355 		// Don't leave the cursor on an illegal line or column, but do
3356 		// accept zero as address, so 0;/PATTERN/ works correctly.
3357 		if (eap->line2 > 0)
3358 		    check_cursor();
3359 	    }
3360 	}
3361 	else if (*eap->cmd != ',')
3362 	    break;
3363 	++eap->cmd;
3364     }
3365 
3366     // One address given: set start and end lines.
3367     if (eap->addr_count == 1)
3368     {
3369 	eap->line1 = eap->line2;
3370 	// ... but only implicit: really no address given
3371 	if (lnum == MAXLNUM)
3372 	    eap->addr_count = 0;
3373     }
3374     return OK;
3375 }
3376 
3377 /*
3378  * Append "cmd" to the error message in IObuff.
3379  * Takes care of limiting the length and handling 0xa0, which would be
3380  * invisible otherwise.
3381  */
3382     static void
append_command(char_u * cmd)3383 append_command(char_u *cmd)
3384 {
3385     char_u *s = cmd;
3386     char_u *d;
3387 
3388     STRCAT(IObuff, ": ");
3389     d = IObuff + STRLEN(IObuff);
3390     while (*s != NUL && d - IObuff < IOSIZE - 7)
3391     {
3392 	if (enc_utf8 ? (s[0] == 0xc2 && s[1] == 0xa0) : *s == 0xa0)
3393 	{
3394 	    s += enc_utf8 ? 2 : 1;
3395 	    STRCPY(d, "<a0>");
3396 	    d += 4;
3397 	}
3398 	else
3399 	    MB_COPY_CHAR(s, d);
3400     }
3401     *d = NUL;
3402 }
3403 
3404 /*
3405  * If "start" points "&opt", "&l:opt", "&g:opt" or "$ENV" return a pointer to
3406  * the name.  Otherwise just return "start".
3407  */
3408     char_u *
skip_option_env_lead(char_u * start)3409 skip_option_env_lead(char_u *start)
3410 {
3411     char_u *name = start;
3412 
3413     if (*start == '&')
3414     {
3415 	if ((start[1] == 'l' || start[1] == 'g') && start[2] == ':')
3416 	    name += 3;
3417 	else
3418 	    name += 1;
3419     }
3420     else if (*start == '$')
3421 	name += 1;
3422     return name;
3423 }
3424 
3425 /*
3426  * Find an Ex command by its name, either built-in or user.
3427  * Start of the name can be found at eap->cmd.
3428  * Sets eap->cmdidx and returns a pointer to char after the command name.
3429  * "full" is set to TRUE if the whole command name matched.
3430  *
3431  * If "lookup" is not NULL recognize expression without "eval" or "call" and
3432  * assignment without "let".  Sets eap->cmdidx to the command while returning
3433  * "eap->cmd".
3434  *
3435  * Returns NULL for an ambiguous user command.
3436  */
3437     char_u *
find_ex_command(exarg_T * eap,int * full UNUSED,int (* lookup)(char_u *,size_t,int cmd,cctx_T *)UNUSED,cctx_T * cctx UNUSED)3438 find_ex_command(
3439 	exarg_T *eap,
3440 	int	*full UNUSED,
3441 	int	(*lookup)(char_u *, size_t, int cmd, cctx_T *) UNUSED,
3442 	cctx_T	*cctx UNUSED)
3443 {
3444     int		len;
3445     char_u	*p;
3446     int		i;
3447 #ifndef FEAT_EVAL
3448     int		vim9 = FALSE;
3449 #else
3450     int		vim9 = in_vim9script();
3451 
3452     /*
3453      * Recognize a Vim9 script function/method call and assignment:
3454      * "lvar = value", "lvar(arg)", "[1, 2 3]->Func()"
3455      */
3456     p = eap->cmd;
3457     if (lookup != NULL)
3458     {
3459 	char_u *pskip = skip_option_env_lead(eap->cmd);
3460 
3461 	if (vim_strchr((char_u *)"{('[\"@&$", *p) != NULL
3462 	       || ((p = to_name_const_end(pskip)) > eap->cmd && *p != NUL))
3463 	{
3464 	    int	    oplen;
3465 	    int	    heredoc;
3466 	    char_u  *swp;
3467 
3468 	    if (*eap->cmd == '&'
3469 		    || *eap->cmd == '$'
3470 		    || (eap->cmd[0] == '@'
3471 					&& (valid_yank_reg(eap->cmd[1], FALSE)
3472 						       || eap->cmd[1] == '@')))
3473 	    {
3474 		if (*eap->cmd == '&')
3475 		{
3476 		    p = eap->cmd + 1;
3477 		    if (STRNCMP("l:", p, 2) == 0 || STRNCMP("g:", p, 2) == 0)
3478 			p += 2;
3479 		    p = to_name_end(p, FALSE);
3480 		}
3481 		else if (*eap->cmd == '$')
3482 		    p = to_name_end(eap->cmd + 1, FALSE);
3483 		else
3484 		    p = eap->cmd + 2;
3485 		if (ends_excmd(*skipwhite(p)))
3486 		{
3487 		    // "&option <NL>", "$ENV <NL>" and "@r <NL>" are the start
3488 		    // of an expression.
3489 		    eap->cmdidx = CMD_eval;
3490 		    return eap->cmd;
3491 		}
3492 		// "&option" can be followed by "->" or "=", check below
3493 	    }
3494 
3495 	    swp = skipwhite(p);
3496 
3497 	    if (
3498 		// "(..." is an expression.
3499 		// "funcname(" is always a function call.
3500 		*p == '('
3501 		    || (p == eap->cmd
3502 			? (
3503 			    // "{..." is a dict expression or block start.
3504 			    *eap->cmd == '{'
3505 			    // "'string'->func()" is an expression.
3506 			 || *eap->cmd == '\''
3507 			    // '"string"->func()' is an expression.
3508 			 || *eap->cmd == '"'
3509 			    // "g:varname" is an expression.
3510 			 || eap->cmd[1] == ':'
3511 			    )
3512 			    // "varname->func()" is an expression.
3513 			: (*swp == '-' && swp[1] == '>')))
3514 	    {
3515 		if (*eap->cmd == '{' && ends_excmd(*skipwhite(eap->cmd + 1)))
3516 		{
3517 		    // "{" by itself is the start of a block.
3518 		    eap->cmdidx = CMD_block;
3519 		    return eap->cmd + 1;
3520 		}
3521 		eap->cmdidx = CMD_eval;
3522 		return eap->cmd;
3523 	    }
3524 
3525 	    if (p != eap->cmd && (
3526 			    // "varname[]" is an expression.
3527 			    *p == '['
3528 			    // "varname.key" is an expression.
3529 			 || (*p == '.' && (ASCII_ISALPHA(p[1])
3530 							     || p[1] == '_'))))
3531 	    {
3532 		char_u	*after = eap->cmd;
3533 
3534 		// When followed by "=" or "+=" then it is an assignment.
3535 		// Skip over the whole thing, it can be:
3536 		//	name.member = val
3537 		//	name[a : b] = val
3538 		//	name[idx] = val
3539 		//	name[idx].member = val
3540 		//	etc.
3541 		eap->cmdidx = CMD_eval;
3542 		++emsg_silent;
3543 		if (skip_expr(&after, NULL) == OK)
3544 		{
3545 		    after = skipwhite(after);
3546 		    if (*after == '=' || (*after != NUL && after[1] == '=')
3547 					 || (after[0] == '.' && after[1] == '.'
3548 							   && after[2] == '='))
3549 			eap->cmdidx = CMD_var;
3550 		}
3551 		--emsg_silent;
3552 		return eap->cmd;
3553 	    }
3554 
3555 	    // "[...]->Method()" is a list expression, but "[a, b] = Func()" is
3556 	    // an assignment.
3557 	    // If there is no line break inside the "[...]" then "p" is
3558 	    // advanced to after the "]" by to_name_const_end(): check if a "="
3559 	    // follows.
3560 	    // If "[...]" has a line break "p" still points at the "[" and it
3561 	    // can't be an assignment.
3562 	    if (*eap->cmd == '[')
3563 	    {
3564 		char_u	    *eq;
3565 
3566 		p = to_name_const_end(eap->cmd);
3567 		if (p == eap->cmd && *p == '[')
3568 		{
3569 		    int count = 0;
3570 		    int	semicolon = FALSE;
3571 
3572 		    p = skip_var_list(eap->cmd, TRUE, &count, &semicolon, TRUE);
3573 		}
3574 		eq = p;
3575 		if (eq != NULL)
3576 		{
3577 		    eq = skipwhite(eq);
3578 		    if (vim_strchr((char_u *)"+-*/%", *eq) != NULL)
3579 			++eq;
3580 		}
3581 		if (p == NULL || p == eap->cmd || *eq != '=')
3582 		{
3583 		    eap->cmdidx = CMD_eval;
3584 		    return eap->cmd;
3585 		}
3586 		if (p > eap->cmd && *eq == '=')
3587 		{
3588 		    eap->cmdidx = CMD_var;
3589 		    return eap->cmd;
3590 		}
3591 	    }
3592 
3593 	    // Recognize an assignment if we recognize the variable name:
3594 	    // "g:var = expr"
3595 	    // "@r = expr"
3596 	    // "&opt = expr"
3597 	    // "var = expr"  where "var" is a variable name or we are skipping
3598 	    // (variable declaration might have been skipped).
3599 	    oplen = assignment_len(skipwhite(p), &heredoc);
3600 	    if (oplen > 0)
3601 	    {
3602 		if (((p - eap->cmd) > 2 && eap->cmd[1] == ':')
3603 			|| *eap->cmd == '&'
3604 			|| *eap->cmd == '$'
3605 			|| *eap->cmd == '@'
3606 			|| eap->skip
3607 			|| lookup(eap->cmd, p - eap->cmd, TRUE, cctx) == OK)
3608 		{
3609 		    eap->cmdidx = CMD_var;
3610 		    return eap->cmd;
3611 		}
3612 	    }
3613 
3614 	    // Recognize using a type for a w:, b:, t: or g: variable:
3615 	    // "w:varname: number = 123".
3616 	    if (eap->cmd[1] == ':' && *p == ':')
3617 	    {
3618 		eap->cmdidx = CMD_eval;
3619 		return eap->cmd;
3620 	    }
3621 	}
3622 
3623 	// "g:", "s:" and "l:" are always assumed to be a variable, thus start
3624 	// an expression.  A global/substitute/list command needs to use a
3625 	// longer name.
3626 	if (vim_strchr((char_u *)"gsl", *p) != NULL && p[1] == ':')
3627 	{
3628 	    eap->cmdidx = CMD_eval;
3629 	    return eap->cmd;
3630 	}
3631 
3632 	// If it is an ID it might be a variable with an operator on the next
3633 	// line, if the variable exists it can't be an Ex command.
3634 	if (p > eap->cmd && ends_excmd(*skipwhite(p))
3635 		&& (lookup(eap->cmd, p - eap->cmd, TRUE, cctx) == OK
3636 		    || (ASCII_ISALPHA(eap->cmd[0]) && eap->cmd[1] == ':')))
3637 	{
3638 	    eap->cmdidx = CMD_eval;
3639 	    return eap->cmd;
3640 	}
3641 
3642 	// Check for "++nr" and "--nr".
3643 	if (p == eap->cmd && p[0] == p[1] && (*p == '+' || *p == '-'))
3644 	{
3645 	    eap->cmdidx = *p == '+' ? CMD_increment : CMD_decrement;
3646 	    return eap->cmd + 2;
3647 	}
3648     }
3649 #endif
3650 
3651     /*
3652      * Isolate the command and search for it in the command table.
3653      * Exceptions:
3654      * - The 'k' command can directly be followed by any character.
3655      *   But it is not used in Vim9 script.
3656      * - the 's' command can be followed directly by 'c', 'g', 'i', 'I' or 'r'
3657      *	    but :sre[wind] is another command, as are :scr[iptnames],
3658      *	    :scs[cope], :sim[alt], :sig[ns] and :sil[ent].
3659      * - the "d" command can directly be followed by 'l' or 'p' flag.
3660      */
3661     p = eap->cmd;
3662     if (!vim9 && *p == 'k')
3663     {
3664 	eap->cmdidx = CMD_k;
3665 	++p;
3666     }
3667     else if (!vim9
3668 	    && p[0] == 's'
3669 	    && ((p[1] == 'c' && (p[2] == NUL || (p[2] != 's' && p[2] != 'r'
3670 			&& (p[3] == NUL || (p[3] != 'i' && p[4] != 'p')))))
3671 		|| p[1] == 'g'
3672 		|| (p[1] == 'i' && p[2] != 'm' && p[2] != 'l' && p[2] != 'g')
3673 		|| p[1] == 'I'
3674 		|| (p[1] == 'r' && p[2] != 'e')))
3675     {
3676 	eap->cmdidx = CMD_substitute;
3677 	++p;
3678     }
3679     else
3680     {
3681 	while (ASCII_ISALPHA(*p))
3682 	    ++p;
3683 	// for python 3.x support ":py3", ":python3", ":py3file", etc.
3684 	if (eap->cmd[0] == 'p' && eap->cmd[1] == 'y')
3685 	{
3686 	    while (ASCII_ISALNUM(*p))
3687 		++p;
3688 	}
3689 	else if (*p == '9' && STRNCMP("vim9", eap->cmd, 4) == 0)
3690 	{
3691 	    // include "9" for "vim9*" commands; "vim9cmd" and "vim9script".
3692 	    ++p;
3693 	    while (ASCII_ISALPHA(*p))
3694 		++p;
3695 	}
3696 
3697 	// check for non-alpha command
3698 	if (p == eap->cmd && vim_strchr((char_u *)"@*!=><&~#}", *p) != NULL)
3699 	    ++p;
3700 	len = (int)(p - eap->cmd);
3701 	if (!vim9 && *eap->cmd == 'd' && (p[-1] == 'l' || p[-1] == 'p'))
3702 	{
3703 	    // Check for ":dl", ":dell", etc. to ":deletel": that's
3704 	    // :delete with the 'l' flag.  Same for 'p'.
3705 	    for (i = 0; i < len; ++i)
3706 		if (eap->cmd[i] != ((char_u *)"delete")[i])
3707 		    break;
3708 	    if (i == len - 1)
3709 	    {
3710 		--len;
3711 		if (p[-1] == 'l')
3712 		    eap->flags |= EXFLAG_LIST;
3713 		else
3714 		    eap->flags |= EXFLAG_PRINT;
3715 	    }
3716 	}
3717 
3718 	if (ASCII_ISLOWER(eap->cmd[0]))
3719 	{
3720 	    int c1 = eap->cmd[0];
3721 	    int c2 = len == 1 ? NUL : eap->cmd[1];
3722 
3723 	    if (command_count != (int)CMD_SIZE)
3724 	    {
3725 		iemsg(_("E943: Command table needs to be updated, run 'make cmdidxs'"));
3726 		getout(1);
3727 	    }
3728 
3729 	    // Use a precomputed index for fast look-up in cmdnames[]
3730 	    // taking into account the first 2 letters of eap->cmd.
3731 	    eap->cmdidx = cmdidxs1[CharOrdLow(c1)];
3732 	    if (ASCII_ISLOWER(c2))
3733 		eap->cmdidx += cmdidxs2[CharOrdLow(c1)][CharOrdLow(c2)];
3734 	}
3735 	else if (ASCII_ISUPPER(eap->cmd[0]))
3736 	    eap->cmdidx = CMD_Next;
3737 	else
3738 	    eap->cmdidx = CMD_bang;
3739 
3740 	for ( ; (int)eap->cmdidx < (int)CMD_SIZE;
3741 			       eap->cmdidx = (cmdidx_T)((int)eap->cmdidx + 1))
3742 	    if (STRNCMP(cmdnames[(int)eap->cmdidx].cmd_name, (char *)eap->cmd,
3743 							    (size_t)len) == 0)
3744 	    {
3745 #ifdef FEAT_EVAL
3746 		if (full != NULL
3747 			   && cmdnames[(int)eap->cmdidx].cmd_name[len] == NUL)
3748 		    *full = TRUE;
3749 #endif
3750 		break;
3751 	    }
3752 
3753 	// Not not recognize ":*" as the star command unless '*' is in
3754 	// 'cpoptions'.
3755 	if (eap->cmdidx == CMD_star && vim_strchr(p_cpo, CPO_STAR) == NULL)
3756 	    p = eap->cmd;
3757 
3758 	// Look for a user defined command as a last resort.  Let ":Print" be
3759 	// overruled by a user defined command.
3760 	if ((eap->cmdidx == CMD_SIZE || eap->cmdidx == CMD_Print)
3761 		&& *eap->cmd >= 'A' && *eap->cmd <= 'Z')
3762 	{
3763 	    // User defined commands may contain digits.
3764 	    while (ASCII_ISALNUM(*p))
3765 		++p;
3766 	    p = find_ucmd(eap, p, full, NULL, NULL);
3767 	}
3768 	if (p == NULL || p == eap->cmd)
3769 	    eap->cmdidx = CMD_SIZE;
3770     }
3771 
3772     // ":fina" means ":finally" for backwards compatibility.
3773     if (eap->cmdidx == CMD_final && p - eap->cmd == 4)
3774 	eap->cmdidx = CMD_finally;
3775 
3776 #ifdef FEAT_EVAL
3777     if (eap->cmdidx < CMD_SIZE
3778 	    && vim9
3779 	    && !IS_WHITE_OR_NUL(*p) && *p != '\n' && *p != '!' && *p != '|'
3780 	    && (eap->cmdidx < 0 ||
3781 		(cmdnames[eap->cmdidx].cmd_argt & EX_NONWHITE_OK) == 0))
3782     {
3783 	char_u *cmd = vim_strnsave(eap->cmd, p - eap->cmd);
3784 
3785 	semsg(_(e_command_str_not_followed_by_white_space_str), cmd, eap->cmd);
3786 	eap->cmdidx = CMD_SIZE;
3787 	vim_free(cmd);
3788     }
3789 #endif
3790 
3791     return p;
3792 }
3793 
3794 #if defined(FEAT_EVAL) || defined(PROTO)
3795 static struct cmdmod
3796 {
3797     char	*name;
3798     int		minlen;
3799     int		has_count;  // :123verbose  :3tab
3800 } cmdmods[] = {
3801     {"aboveleft", 3, FALSE},
3802     {"belowright", 3, FALSE},
3803     {"botright", 2, FALSE},
3804     {"browse", 3, FALSE},
3805     {"confirm", 4, FALSE},
3806     {"filter", 4, FALSE},
3807     {"hide", 3, FALSE},
3808     {"keepalt", 5, FALSE},
3809     {"keepjumps", 5, FALSE},
3810     {"keepmarks", 3, FALSE},
3811     {"keeppatterns", 5, FALSE},
3812     {"leftabove", 5, FALSE},
3813     {"lockmarks", 3, FALSE},
3814     {"noautocmd", 3, FALSE},
3815     {"noswapfile", 3, FALSE},
3816     {"rightbelow", 6, FALSE},
3817     {"sandbox", 3, FALSE},
3818     {"silent", 3, FALSE},
3819     {"tab", 3, TRUE},
3820     {"topleft", 2, FALSE},
3821     {"unsilent", 3, FALSE},
3822     {"verbose", 4, TRUE},
3823     {"vertical", 4, FALSE},
3824 };
3825 
3826 /*
3827  * Return length of a command modifier (including optional count).
3828  * Return zero when it's not a modifier.
3829  */
3830     int
modifier_len(char_u * cmd)3831 modifier_len(char_u *cmd)
3832 {
3833     int		i, j;
3834     char_u	*p = cmd;
3835 
3836     if (VIM_ISDIGIT(*cmd))
3837 	p = skipwhite(skipdigits(cmd + 1));
3838     for (i = 0; i < (int)ARRAY_LENGTH(cmdmods); ++i)
3839     {
3840 	for (j = 0; p[j] != NUL; ++j)
3841 	    if (p[j] != cmdmods[i].name[j])
3842 		break;
3843 	if (!ASCII_ISALPHA(p[j]) && j >= cmdmods[i].minlen
3844 					&& (p == cmd || cmdmods[i].has_count))
3845 	    return j + (int)(p - cmd);
3846     }
3847     return 0;
3848 }
3849 
3850 /*
3851  * Return > 0 if an Ex command "name" exists.
3852  * Return 2 if there is an exact match.
3853  * Return 3 if there is an ambiguous match.
3854  */
3855     int
cmd_exists(char_u * name)3856 cmd_exists(char_u *name)
3857 {
3858     exarg_T	ea;
3859     int		full = FALSE;
3860     int		i;
3861     int		j;
3862     char_u	*p;
3863 
3864     // Check command modifiers.
3865     for (i = 0; i < (int)ARRAY_LENGTH(cmdmods); ++i)
3866     {
3867 	for (j = 0; name[j] != NUL; ++j)
3868 	    if (name[j] != cmdmods[i].name[j])
3869 		break;
3870 	if (name[j] == NUL && j >= cmdmods[i].minlen)
3871 	    return (cmdmods[i].name[j] == NUL ? 2 : 1);
3872     }
3873 
3874     // Check built-in commands and user defined commands.
3875     // For ":2match" and ":3match" we need to skip the number.
3876     ea.cmd = (*name == '2' || *name == '3') ? name + 1 : name;
3877     ea.cmdidx = (cmdidx_T)0;
3878     p = find_ex_command(&ea, &full, NULL, NULL);
3879     if (p == NULL)
3880 	return 3;
3881     if (vim_isdigit(*name) && ea.cmdidx != CMD_match)
3882 	return 0;
3883     if (*skipwhite(p) != NUL)
3884 	return 0;	// trailing garbage
3885     return (ea.cmdidx == CMD_SIZE ? 0 : (full ? 2 : 1));
3886 }
3887 
3888 /*
3889  * "fullcommand" function
3890  */
3891     void
f_fullcommand(typval_T * argvars,typval_T * rettv)3892 f_fullcommand(typval_T *argvars, typval_T *rettv)
3893 {
3894     exarg_T  ea;
3895     char_u   *name;
3896     char_u   *p;
3897 
3898     rettv->v_type = VAR_STRING;
3899     rettv->vval.v_string = NULL;
3900 
3901     if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
3902 	return;
3903 
3904     name = argvars[0].vval.v_string;
3905     if (name == NULL)
3906 	return;
3907 
3908     while (*name != NUL && *name == ':')
3909 	name++;
3910     name = skip_range(name, TRUE, NULL);
3911 
3912     ea.cmd = (*name == '2' || *name == '3') ? name + 1 : name;
3913     ea.cmdidx = (cmdidx_T)0;
3914     ea.addr_count = 0;
3915     p = find_ex_command(&ea, NULL, NULL, NULL);
3916     if (p == NULL || ea.cmdidx == CMD_SIZE)
3917 	return;
3918     if (in_vim9script())
3919     {
3920 	int	     res;
3921 
3922 	++emsg_silent;
3923 	res = not_in_vim9(&ea);
3924 	--emsg_silent;
3925 
3926 	if (res == FAIL)
3927 	    return;
3928     }
3929 
3930     rettv->vval.v_string = vim_strsave(IS_USER_CMDIDX(ea.cmdidx)
3931 				 ? get_user_command_name(ea.useridx, ea.cmdidx)
3932 				 : cmdnames[ea.cmdidx].cmd_name);
3933 }
3934 #endif
3935 
3936     cmdidx_T
excmd_get_cmdidx(char_u * cmd,int len)3937 excmd_get_cmdidx(char_u *cmd, int len)
3938 {
3939     cmdidx_T idx;
3940 
3941     for (idx = (cmdidx_T)0; (int)idx < (int)CMD_SIZE;
3942 	    idx = (cmdidx_T)((int)idx + 1))
3943 	if (STRNCMP(cmdnames[(int)idx].cmd_name, cmd, (size_t)len) == 0)
3944 	    break;
3945 
3946     return idx;
3947 }
3948 
3949     long
excmd_get_argt(cmdidx_T idx)3950 excmd_get_argt(cmdidx_T idx)
3951 {
3952     return (long)cmdnames[(int)idx].cmd_argt;
3953 }
3954 
3955 /*
3956  * Skip a range specifier of the form: addr [,addr] [;addr] ..
3957  *
3958  * Backslashed delimiters after / or ? will be skipped, and commands will
3959  * not be expanded between /'s and ?'s or after "'".
3960  *
3961  * Also skip white space and ":" characters after the range.
3962  * Returns the "cmd" pointer advanced to beyond the range.
3963  */
3964     char_u *
skip_range(char_u * cmd_start,int skip_star,int * ctx)3965 skip_range(
3966     char_u	*cmd_start,
3967     int		skip_star,	// skip "*" used for Visual range
3968     int		*ctx)		// pointer to xp_context or NULL
3969 {
3970     char_u	*cmd = cmd_start;
3971     unsigned	delim;
3972 
3973     while (vim_strchr((char_u *)" \t0123456789.$%'/?-+,;\\", *cmd) != NULL)
3974     {
3975 	if (*cmd == '\\')
3976 	{
3977 	    if (cmd[1] == '?' || cmd[1] == '/' || cmd[1] == '&')
3978 		++cmd;
3979 	    else
3980 		break;
3981 	}
3982 	else if (*cmd == '\'')
3983 	{
3984 	    char_u *p = cmd;
3985 
3986 	    // a quote is only valid at the start or after a separator
3987 	    while (p > cmd_start)
3988 	    {
3989 		--p;
3990 		if (!VIM_ISWHITE(*p))
3991 		    break;
3992 	    }
3993 	    if (cmd > cmd_start && !VIM_ISWHITE(*p) && *p != ',' && *p != ';')
3994 		break;
3995 	    if (*++cmd == NUL && ctx != NULL)
3996 		*ctx = EXPAND_NOTHING;
3997 	}
3998 	else if (*cmd == '/' || *cmd == '?')
3999 	{
4000 	    delim = *cmd++;
4001 	    while (*cmd != NUL && *cmd != delim)
4002 		if (*cmd++ == '\\' && *cmd != NUL)
4003 		    ++cmd;
4004 	    if (*cmd == NUL && ctx != NULL)
4005 		*ctx = EXPAND_NOTHING;
4006 	}
4007 	if (*cmd != NUL)
4008 	    ++cmd;
4009     }
4010 
4011     // Skip ":" and white space.
4012     while (*cmd == ':')
4013 	cmd = skipwhite(cmd + 1);
4014 
4015     // Skip "*" used for Visual range.
4016     if (skip_star && *cmd == '*' && vim_strchr(p_cpo, CPO_STAR) == NULL)
4017 	cmd = skipwhite(cmd + 1);
4018 
4019     return cmd;
4020 }
4021 
4022     static void
addr_error(cmd_addr_T addr_type)4023 addr_error(cmd_addr_T addr_type)
4024 {
4025     if (addr_type == ADDR_NONE)
4026 	emsg(_(e_norange));
4027     else
4028 	emsg(_(e_invalid_range));
4029 }
4030 
4031 /*
4032  * Return the default address for an address type.
4033  */
4034     static linenr_T
default_address(exarg_T * eap)4035 default_address(exarg_T *eap)
4036 {
4037     linenr_T lnum = 0;
4038 
4039     switch (eap->addr_type)
4040     {
4041 	case ADDR_LINES:
4042 	case ADDR_OTHER:
4043 	    // Default is the cursor line number.  Avoid using an invalid
4044 	    // line number though.
4045 	    if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
4046 		lnum = curbuf->b_ml.ml_line_count;
4047 	    else
4048 		lnum = curwin->w_cursor.lnum;
4049 	    break;
4050 	case ADDR_WINDOWS:
4051 	    lnum = CURRENT_WIN_NR;
4052 	    break;
4053 	case ADDR_ARGUMENTS:
4054 	    lnum = curwin->w_arg_idx + 1;
4055 	    if (lnum > ARGCOUNT)
4056 		lnum = ARGCOUNT;
4057 	    break;
4058 	case ADDR_LOADED_BUFFERS:
4059 	case ADDR_BUFFERS:
4060 	    lnum = curbuf->b_fnum;
4061 	    break;
4062 	case ADDR_TABS:
4063 	    lnum = CURRENT_TAB_NR;
4064 	    break;
4065 	case ADDR_TABS_RELATIVE:
4066 	case ADDR_UNSIGNED:
4067 	    lnum = 1;
4068 	    break;
4069 	case ADDR_QUICKFIX:
4070 #ifdef FEAT_QUICKFIX
4071 	    lnum = qf_get_cur_idx(eap);
4072 #endif
4073 	    break;
4074 	case ADDR_QUICKFIX_VALID:
4075 #ifdef FEAT_QUICKFIX
4076 	    lnum = qf_get_cur_valid_idx(eap);
4077 #endif
4078 	    break;
4079 	case ADDR_NONE:
4080 	    // Will give an error later if a range is found.
4081 	    break;
4082     }
4083     return lnum;
4084 }
4085 
4086 /*
4087  * Get a single EX address.
4088  *
4089  * Set ptr to the next character after the part that was interpreted.
4090  * Set ptr to NULL when an error is encountered.
4091  * This may set the last used search pattern.
4092  *
4093  * Return MAXLNUM when no Ex address was found.
4094  */
4095     static linenr_T
get_address(exarg_T * eap UNUSED,char_u ** ptr,cmd_addr_T addr_type,int skip,int silent,int to_other_file,int address_count UNUSED)4096 get_address(
4097     exarg_T	*eap UNUSED,
4098     char_u	**ptr,
4099     cmd_addr_T	addr_type,
4100     int		skip,		// only skip the address, don't use it
4101     int		silent,		// no errors or side effects
4102     int		to_other_file,  // flag: may jump to other file
4103     int		address_count UNUSED) // 1 for first address, >1 after comma
4104 {
4105     int		c;
4106     int		i;
4107     long	n;
4108     char_u	*cmd;
4109     pos_T	pos;
4110     pos_T	*fp;
4111     linenr_T	lnum;
4112     buf_T	*buf;
4113 
4114     cmd = skipwhite(*ptr);
4115     lnum = MAXLNUM;
4116     do
4117     {
4118 	switch (*cmd)
4119 	{
4120 	    case '.':			    // '.' - Cursor position
4121 		++cmd;
4122 		switch (addr_type)
4123 		{
4124 		    case ADDR_LINES:
4125 		    case ADDR_OTHER:
4126 			lnum = curwin->w_cursor.lnum;
4127 			break;
4128 		    case ADDR_WINDOWS:
4129 			lnum = CURRENT_WIN_NR;
4130 			break;
4131 		    case ADDR_ARGUMENTS:
4132 			lnum = curwin->w_arg_idx + 1;
4133 			break;
4134 		    case ADDR_LOADED_BUFFERS:
4135 		    case ADDR_BUFFERS:
4136 			lnum = curbuf->b_fnum;
4137 			break;
4138 		    case ADDR_TABS:
4139 			lnum = CURRENT_TAB_NR;
4140 			break;
4141 		    case ADDR_NONE:
4142 		    case ADDR_TABS_RELATIVE:
4143 		    case ADDR_UNSIGNED:
4144 			addr_error(addr_type);
4145 			cmd = NULL;
4146 			goto error;
4147 			break;
4148 		    case ADDR_QUICKFIX:
4149 #ifdef FEAT_QUICKFIX
4150 			lnum = qf_get_cur_idx(eap);
4151 #endif
4152 			break;
4153 		    case ADDR_QUICKFIX_VALID:
4154 #ifdef FEAT_QUICKFIX
4155 			lnum = qf_get_cur_valid_idx(eap);
4156 #endif
4157 			break;
4158 		}
4159 		break;
4160 
4161 	    case '$':			    // '$' - last line
4162 		++cmd;
4163 		switch (addr_type)
4164 		{
4165 		    case ADDR_LINES:
4166 		    case ADDR_OTHER:
4167 			lnum = curbuf->b_ml.ml_line_count;
4168 			break;
4169 		    case ADDR_WINDOWS:
4170 			lnum = LAST_WIN_NR;
4171 			break;
4172 		    case ADDR_ARGUMENTS:
4173 			lnum = ARGCOUNT;
4174 			break;
4175 		    case ADDR_LOADED_BUFFERS:
4176 			buf = lastbuf;
4177 			while (buf->b_ml.ml_mfp == NULL)
4178 			{
4179 			    if (buf->b_prev == NULL)
4180 				break;
4181 			    buf = buf->b_prev;
4182 			}
4183 			lnum = buf->b_fnum;
4184 			break;
4185 		    case ADDR_BUFFERS:
4186 			lnum = lastbuf->b_fnum;
4187 			break;
4188 		    case ADDR_TABS:
4189 			lnum = LAST_TAB_NR;
4190 			break;
4191 		    case ADDR_NONE:
4192 		    case ADDR_TABS_RELATIVE:
4193 		    case ADDR_UNSIGNED:
4194 			addr_error(addr_type);
4195 			cmd = NULL;
4196 			goto error;
4197 			break;
4198 		    case ADDR_QUICKFIX:
4199 #ifdef FEAT_QUICKFIX
4200 			lnum = qf_get_size(eap);
4201 			if (lnum == 0)
4202 			    lnum = 1;
4203 #endif
4204 			break;
4205 		    case ADDR_QUICKFIX_VALID:
4206 #ifdef FEAT_QUICKFIX
4207 			lnum = qf_get_valid_size(eap);
4208 			if (lnum == 0)
4209 			    lnum = 1;
4210 #endif
4211 			break;
4212 		}
4213 		break;
4214 
4215 	    case '\'':			    // ''' - mark
4216 		if (*++cmd == NUL)
4217 		{
4218 		    cmd = NULL;
4219 		    goto error;
4220 		}
4221 		if (addr_type != ADDR_LINES)
4222 		{
4223 		    addr_error(addr_type);
4224 		    cmd = NULL;
4225 		    goto error;
4226 		}
4227 		if (skip)
4228 		    ++cmd;
4229 		else
4230 		{
4231 		    // Only accept a mark in another file when it is
4232 		    // used by itself: ":'M".
4233 		    fp = getmark(*cmd, to_other_file && cmd[1] == NUL);
4234 		    ++cmd;
4235 		    if (fp == (pos_T *)-1)
4236 			// Jumped to another file.
4237 			lnum = curwin->w_cursor.lnum;
4238 		    else
4239 		    {
4240 			if (check_mark(fp) == FAIL)
4241 			{
4242 			    cmd = NULL;
4243 			    goto error;
4244 			}
4245 			lnum = fp->lnum;
4246 		    }
4247 		}
4248 		break;
4249 
4250 	    case '/':
4251 	    case '?':			// '/' or '?' - search
4252 		c = *cmd++;
4253 		if (addr_type != ADDR_LINES)
4254 		{
4255 		    addr_error(addr_type);
4256 		    cmd = NULL;
4257 		    goto error;
4258 		}
4259 		if (skip)	// skip "/pat/"
4260 		{
4261 		    cmd = skip_regexp(cmd, c, magic_isset());
4262 		    if (*cmd == c)
4263 			++cmd;
4264 		}
4265 		else
4266 		{
4267 		    int flags;
4268 
4269 		    pos = curwin->w_cursor; // save curwin->w_cursor
4270 
4271 		    // When '/' or '?' follows another address, start from
4272 		    // there.
4273 		    if (lnum > 0 && lnum != MAXLNUM)
4274 			curwin->w_cursor.lnum =
4275 				lnum > curbuf->b_ml.ml_line_count
4276 					   ? curbuf->b_ml.ml_line_count : lnum;
4277 
4278 		    // Start a forward search at the end of the line (unless
4279 		    // before the first line).
4280 		    // Start a backward search at the start of the line.
4281 		    // This makes sure we never match in the current
4282 		    // line, and can match anywhere in the
4283 		    // next/previous line.
4284 		    if (c == '/' && curwin->w_cursor.lnum > 0)
4285 			curwin->w_cursor.col = MAXCOL;
4286 		    else
4287 			curwin->w_cursor.col = 0;
4288 		    searchcmdlen = 0;
4289 		    flags = silent ? 0 : SEARCH_HIS | SEARCH_MSG;
4290 		    if (!do_search(NULL, c, c, cmd, 1L, flags, NULL))
4291 		    {
4292 			curwin->w_cursor = pos;
4293 			cmd = NULL;
4294 			goto error;
4295 		    }
4296 		    lnum = curwin->w_cursor.lnum;
4297 		    curwin->w_cursor = pos;
4298 		    // adjust command string pointer
4299 		    cmd += searchcmdlen;
4300 		}
4301 		break;
4302 
4303 	    case '\\':		    // "\?", "\/" or "\&", repeat search
4304 		++cmd;
4305 		if (addr_type != ADDR_LINES)
4306 		{
4307 		    addr_error(addr_type);
4308 		    cmd = NULL;
4309 		    goto error;
4310 		}
4311 		if (*cmd == '&')
4312 		    i = RE_SUBST;
4313 		else if (*cmd == '?' || *cmd == '/')
4314 		    i = RE_SEARCH;
4315 		else
4316 		{
4317 		    emsg(_(e_backslash_should_be_followed_by));
4318 		    cmd = NULL;
4319 		    goto error;
4320 		}
4321 
4322 		if (!skip)
4323 		{
4324 		    /*
4325 		     * When search follows another address, start from
4326 		     * there.
4327 		     */
4328 		    if (lnum != MAXLNUM)
4329 			pos.lnum = lnum;
4330 		    else
4331 			pos.lnum = curwin->w_cursor.lnum;
4332 
4333 		    /*
4334 		     * Start the search just like for the above
4335 		     * do_search().
4336 		     */
4337 		    if (*cmd != '?')
4338 			pos.col = MAXCOL;
4339 		    else
4340 			pos.col = 0;
4341 		    pos.coladd = 0;
4342 		    if (searchit(curwin, curbuf, &pos, NULL,
4343 				*cmd == '?' ? BACKWARD : FORWARD,
4344 				(char_u *)"", 1L, SEARCH_MSG, i, NULL) != FAIL)
4345 			lnum = pos.lnum;
4346 		    else
4347 		    {
4348 			cmd = NULL;
4349 			goto error;
4350 		    }
4351 		}
4352 		++cmd;
4353 		break;
4354 
4355 	    default:
4356 		if (VIM_ISDIGIT(*cmd))	// absolute line number
4357 		    lnum = getdigits(&cmd);
4358 	}
4359 
4360 	for (;;)
4361 	{
4362 	    cmd = skipwhite(cmd);
4363 	    if (*cmd != '-' && *cmd != '+' && !VIM_ISDIGIT(*cmd))
4364 		break;
4365 
4366 	    if (lnum == MAXLNUM)
4367 	    {
4368 		switch (addr_type)
4369 		{
4370 		    case ADDR_LINES:
4371 		    case ADDR_OTHER:
4372 			// "+1" is same as ".+1"
4373 			lnum = curwin->w_cursor.lnum;
4374 			break;
4375 		    case ADDR_WINDOWS:
4376 			lnum = CURRENT_WIN_NR;
4377 			break;
4378 		    case ADDR_ARGUMENTS:
4379 			lnum = curwin->w_arg_idx + 1;
4380 			break;
4381 		    case ADDR_LOADED_BUFFERS:
4382 		    case ADDR_BUFFERS:
4383 			lnum = curbuf->b_fnum;
4384 			break;
4385 		    case ADDR_TABS:
4386 			lnum = CURRENT_TAB_NR;
4387 			break;
4388 		    case ADDR_TABS_RELATIVE:
4389 			lnum = 1;
4390 			break;
4391 		    case ADDR_QUICKFIX:
4392 #ifdef FEAT_QUICKFIX
4393 			lnum = qf_get_cur_idx(eap);
4394 #endif
4395 			break;
4396 		    case ADDR_QUICKFIX_VALID:
4397 #ifdef FEAT_QUICKFIX
4398 			lnum = qf_get_cur_valid_idx(eap);
4399 #endif
4400 			break;
4401 		    case ADDR_NONE:
4402 		    case ADDR_UNSIGNED:
4403 			lnum = 0;
4404 			break;
4405 		}
4406 	    }
4407 
4408 	    if (VIM_ISDIGIT(*cmd))
4409 		i = '+';		// "number" is same as "+number"
4410 	    else
4411 		i = *cmd++;
4412 	    if (!VIM_ISDIGIT(*cmd))	// '+' is '+1', but '+0' is not '+1'
4413 		n = 1;
4414 	    else
4415 	    {
4416 		n = getdigits(&cmd);
4417 		if (n == MAXLNUM)
4418 		{
4419 		    emsg(_(e_line_number_out_of_range));
4420 		    goto error;
4421 		}
4422 	    }
4423 
4424 	    if (addr_type == ADDR_TABS_RELATIVE)
4425 	    {
4426 		emsg(_(e_invalid_range));
4427 		cmd = NULL;
4428 		goto error;
4429 	    }
4430 	    else if (addr_type == ADDR_LOADED_BUFFERS
4431 		    || addr_type == ADDR_BUFFERS)
4432 		lnum = compute_buffer_local_count(
4433 				    addr_type, lnum, (i == '-') ? -1 * n : n);
4434 	    else
4435 	    {
4436 #ifdef FEAT_FOLDING
4437 		// Relative line addressing, need to adjust for folded lines
4438 		// now, but only do it after the first address.
4439 		if (addr_type == ADDR_LINES && (i == '-' || i == '+')
4440 							 && address_count >= 2)
4441 		    (void)hasFolding(lnum, NULL, &lnum);
4442 #endif
4443 		if (i == '-')
4444 		    lnum -= n;
4445 		else
4446 		{
4447 		    if (n >= LONG_MAX - lnum)
4448 		    {
4449 			emsg(_(e_line_number_out_of_range));
4450 			goto error;
4451 		    }
4452 		    lnum += n;
4453 		}
4454 	    }
4455 	}
4456     } while (*cmd == '/' || *cmd == '?');
4457 
4458 error:
4459     *ptr = cmd;
4460     return lnum;
4461 }
4462 
4463 /*
4464  * Set eap->line1 and eap->line2 to the whole range.
4465  * Used for commands with the EX_DFLALL flag and no range given.
4466  */
4467     static void
address_default_all(exarg_T * eap)4468 address_default_all(exarg_T *eap)
4469 {
4470     eap->line1 = 1;
4471     switch (eap->addr_type)
4472     {
4473 	case ADDR_LINES:
4474 	case ADDR_OTHER:
4475 	    eap->line2 = curbuf->b_ml.ml_line_count;
4476 	    break;
4477 	case ADDR_LOADED_BUFFERS:
4478 	    {
4479 		buf_T *buf = firstbuf;
4480 
4481 		while (buf->b_next != NULL && buf->b_ml.ml_mfp == NULL)
4482 		    buf = buf->b_next;
4483 		eap->line1 = buf->b_fnum;
4484 		buf = lastbuf;
4485 		while (buf->b_prev != NULL && buf->b_ml.ml_mfp == NULL)
4486 		    buf = buf->b_prev;
4487 		eap->line2 = buf->b_fnum;
4488 	    }
4489 	    break;
4490 	case ADDR_BUFFERS:
4491 	    eap->line1 = firstbuf->b_fnum;
4492 	    eap->line2 = lastbuf->b_fnum;
4493 	    break;
4494 	case ADDR_WINDOWS:
4495 	    eap->line2 = LAST_WIN_NR;
4496 	    break;
4497 	case ADDR_TABS:
4498 	    eap->line2 = LAST_TAB_NR;
4499 	    break;
4500 	case ADDR_TABS_RELATIVE:
4501 	    eap->line2 = 1;
4502 	    break;
4503 	case ADDR_ARGUMENTS:
4504 	    if (ARGCOUNT == 0)
4505 		eap->line1 = eap->line2 = 0;
4506 	    else
4507 		eap->line2 = ARGCOUNT;
4508 	    break;
4509 	case ADDR_QUICKFIX_VALID:
4510 #ifdef FEAT_QUICKFIX
4511 	    eap->line2 = qf_get_valid_size(eap);
4512 	    if (eap->line2 == 0)
4513 		eap->line2 = 1;
4514 #endif
4515 	    break;
4516 	case ADDR_NONE:
4517 	case ADDR_UNSIGNED:
4518 	case ADDR_QUICKFIX:
4519 	    iemsg(_("INTERNAL: Cannot use EX_DFLALL with ADDR_NONE, ADDR_UNSIGNED or ADDR_QUICKFIX"));
4520 	    break;
4521     }
4522 }
4523 
4524 
4525 /*
4526  * Get flags from an Ex command argument.
4527  */
4528     static void
get_flags(exarg_T * eap)4529 get_flags(exarg_T *eap)
4530 {
4531     while (vim_strchr((char_u *)"lp#", *eap->arg) != NULL)
4532     {
4533 	if (*eap->arg == 'l')
4534 	    eap->flags |= EXFLAG_LIST;
4535 	else if (*eap->arg == 'p')
4536 	    eap->flags |= EXFLAG_PRINT;
4537 	else
4538 	    eap->flags |= EXFLAG_NR;
4539 	eap->arg = skipwhite(eap->arg + 1);
4540     }
4541 }
4542 
4543 /*
4544  * Function called for command which is Not Implemented.  NI!
4545  */
4546     void
ex_ni(exarg_T * eap)4547 ex_ni(exarg_T *eap)
4548 {
4549     if (!eap->skip)
4550 	eap->errmsg =
4551 		_("E319: Sorry, the command is not available in this version");
4552 }
4553 
4554 #ifdef HAVE_EX_SCRIPT_NI
4555 /*
4556  * Function called for script command which is Not Implemented.  NI!
4557  * Skips over ":perl <<EOF" constructs.
4558  */
4559     static void
ex_script_ni(exarg_T * eap)4560 ex_script_ni(exarg_T *eap)
4561 {
4562     if (!eap->skip)
4563 	ex_ni(eap);
4564     else
4565 	vim_free(script_get(eap, eap->arg));
4566 }
4567 #endif
4568 
4569 /*
4570  * Check range in Ex command for validity.
4571  * Return NULL when valid, error message when invalid.
4572  */
4573     static char *
invalid_range(exarg_T * eap)4574 invalid_range(exarg_T *eap)
4575 {
4576     buf_T	*buf;
4577 
4578     if (       eap->line1 < 0
4579 	    || eap->line2 < 0
4580 	    || eap->line1 > eap->line2)
4581 	return _(e_invalid_range);
4582 
4583     if (eap->argt & EX_RANGE)
4584     {
4585 	switch (eap->addr_type)
4586 	{
4587 	    case ADDR_LINES:
4588 		if (eap->line2 > curbuf->b_ml.ml_line_count
4589 #ifdef FEAT_DIFF
4590 			    + (eap->cmdidx == CMD_diffget)
4591 #endif
4592 		   )
4593 		    return _(e_invalid_range);
4594 		break;
4595 	    case ADDR_ARGUMENTS:
4596 		// add 1 if ARGCOUNT is 0
4597 		if (eap->line2 > ARGCOUNT + (!ARGCOUNT))
4598 		    return _(e_invalid_range);
4599 		break;
4600 	    case ADDR_BUFFERS:
4601 		// Only a boundary check, not whether the buffers actually
4602 		// exist.
4603 		if (eap->line1 < 1 || eap->line2 > get_highest_fnum())
4604 		    return _(e_invalid_range);
4605 		break;
4606 	    case ADDR_LOADED_BUFFERS:
4607 		buf = firstbuf;
4608 		while (buf->b_ml.ml_mfp == NULL)
4609 		{
4610 		    if (buf->b_next == NULL)
4611 			return _(e_invalid_range);
4612 		    buf = buf->b_next;
4613 		}
4614 		if (eap->line1 < buf->b_fnum)
4615 		    return _(e_invalid_range);
4616 		buf = lastbuf;
4617 		while (buf->b_ml.ml_mfp == NULL)
4618 		{
4619 		    if (buf->b_prev == NULL)
4620 			return _(e_invalid_range);
4621 		    buf = buf->b_prev;
4622 		}
4623 		if (eap->line2 > buf->b_fnum)
4624 		    return _(e_invalid_range);
4625 		break;
4626 	    case ADDR_WINDOWS:
4627 		if (eap->line2 > LAST_WIN_NR)
4628 		    return _(e_invalid_range);
4629 		break;
4630 	    case ADDR_TABS:
4631 		if (eap->line2 > LAST_TAB_NR)
4632 		    return _(e_invalid_range);
4633 		break;
4634 	    case ADDR_TABS_RELATIVE:
4635 	    case ADDR_OTHER:
4636 		// Any range is OK.
4637 		break;
4638 	    case ADDR_QUICKFIX:
4639 #ifdef FEAT_QUICKFIX
4640 		// No error for value that is too big, will use the last entry.
4641 		if (eap->line2 <= 0)
4642 		    return _(e_invalid_range);
4643 #endif
4644 		break;
4645 	    case ADDR_QUICKFIX_VALID:
4646 #ifdef FEAT_QUICKFIX
4647 		if ((eap->line2 != 1 && eap->line2 > qf_get_valid_size(eap))
4648 			|| eap->line2 < 0)
4649 		    return _(e_invalid_range);
4650 #endif
4651 		break;
4652 	    case ADDR_UNSIGNED:
4653 	    case ADDR_NONE:
4654 		// Will give an error elsewhere.
4655 		break;
4656 	}
4657     }
4658     return NULL;
4659 }
4660 
4661 /*
4662  * Correct the range for zero line number, if required.
4663  */
4664     static void
correct_range(exarg_T * eap)4665 correct_range(exarg_T *eap)
4666 {
4667     if (!(eap->argt & EX_ZEROR))	    // zero in range not allowed
4668     {
4669 	if (eap->line1 == 0)
4670 	    eap->line1 = 1;
4671 	if (eap->line2 == 0)
4672 	    eap->line2 = 1;
4673     }
4674 }
4675 
4676 #ifdef FEAT_QUICKFIX
4677 /*
4678  * For a ":vimgrep" or ":vimgrepadd" command return a pointer past the
4679  * pattern.  Otherwise return eap->arg.
4680  */
4681     static char_u *
skip_grep_pat(exarg_T * eap)4682 skip_grep_pat(exarg_T *eap)
4683 {
4684     char_u	*p = eap->arg;
4685 
4686     if (*p != NUL && (eap->cmdidx == CMD_vimgrep || eap->cmdidx == CMD_lvimgrep
4687 		|| eap->cmdidx == CMD_vimgrepadd
4688 		|| eap->cmdidx == CMD_lvimgrepadd
4689 		|| grep_internal(eap->cmdidx)))
4690     {
4691 	p = skip_vimgrep_pat(p, NULL, NULL);
4692 	if (p == NULL)
4693 	    p = eap->arg;
4694     }
4695     return p;
4696 }
4697 
4698 /*
4699  * For the ":make" and ":grep" commands insert the 'makeprg'/'grepprg' option
4700  * in the command line, so that things like % get expanded.
4701  */
4702     static char_u *
replace_makeprg(exarg_T * eap,char_u * p,char_u ** cmdlinep)4703 replace_makeprg(exarg_T *eap, char_u *p, char_u **cmdlinep)
4704 {
4705     char_u	*new_cmdline;
4706     char_u	*program;
4707     char_u	*pos;
4708     char_u	*ptr;
4709     int		len;
4710     int		i;
4711 
4712     /*
4713      * Don't do it when ":vimgrep" is used for ":grep".
4714      */
4715     if ((eap->cmdidx == CMD_make || eap->cmdidx == CMD_lmake
4716 		     || eap->cmdidx == CMD_grep || eap->cmdidx == CMD_lgrep
4717 		     || eap->cmdidx == CMD_grepadd
4718 		     || eap->cmdidx == CMD_lgrepadd)
4719 	    && !grep_internal(eap->cmdidx))
4720     {
4721 	if (eap->cmdidx == CMD_grep || eap->cmdidx == CMD_lgrep
4722 	    || eap->cmdidx == CMD_grepadd || eap->cmdidx == CMD_lgrepadd)
4723 	{
4724 	    if (*curbuf->b_p_gp == NUL)
4725 		program = p_gp;
4726 	    else
4727 		program = curbuf->b_p_gp;
4728 	}
4729 	else
4730 	{
4731 	    if (*curbuf->b_p_mp == NUL)
4732 		program = p_mp;
4733 	    else
4734 		program = curbuf->b_p_mp;
4735 	}
4736 
4737 	p = skipwhite(p);
4738 
4739 	if ((pos = (char_u *)strstr((char *)program, "$*")) != NULL)
4740 	{
4741 	    // replace $* by given arguments
4742 	    i = 1;
4743 	    while ((pos = (char_u *)strstr((char *)pos + 2, "$*")) != NULL)
4744 		++i;
4745 	    len = (int)STRLEN(p);
4746 	    new_cmdline = alloc(STRLEN(program) + i * (len - 2) + 1);
4747 	    if (new_cmdline == NULL)
4748 		return NULL;			// out of memory
4749 	    ptr = new_cmdline;
4750 	    while ((pos = (char_u *)strstr((char *)program, "$*")) != NULL)
4751 	    {
4752 		i = (int)(pos - program);
4753 		STRNCPY(ptr, program, i);
4754 		STRCPY(ptr += i, p);
4755 		ptr += len;
4756 		program = pos + 2;
4757 	    }
4758 	    STRCPY(ptr, program);
4759 	}
4760 	else
4761 	{
4762 	    new_cmdline = alloc(STRLEN(program) + STRLEN(p) + 2);
4763 	    if (new_cmdline == NULL)
4764 		return NULL;			// out of memory
4765 	    STRCPY(new_cmdline, program);
4766 	    STRCAT(new_cmdline, " ");
4767 	    STRCAT(new_cmdline, p);
4768 	}
4769 	msg_make(p);
4770 
4771 	// 'eap->cmd' is not set here, because it is not used at CMD_make
4772 	vim_free(*cmdlinep);
4773 	*cmdlinep = new_cmdline;
4774 	p = new_cmdline;
4775     }
4776     return p;
4777 }
4778 #endif
4779 
4780 /*
4781  * Expand file name in Ex command argument.
4782  * When an error is detected, "errormsgp" is set to a non-NULL pointer.
4783  * Return FAIL for failure, OK otherwise.
4784  */
4785     int
expand_filename(exarg_T * eap,char_u ** cmdlinep,char ** errormsgp)4786 expand_filename(
4787     exarg_T	*eap,
4788     char_u	**cmdlinep,
4789     char	**errormsgp)
4790 {
4791     int		has_wildcards;	// need to expand wildcards
4792     char_u	*repl;
4793     int		srclen;
4794     char_u	*p;
4795     int		n;
4796     int		escaped;
4797 
4798 #ifdef FEAT_QUICKFIX
4799     // Skip a regexp pattern for ":vimgrep[add] pat file..."
4800     p = skip_grep_pat(eap);
4801 #else
4802     p = eap->arg;
4803 #endif
4804 
4805     /*
4806      * Decide to expand wildcards *before* replacing '%', '#', etc.  If
4807      * the file name contains a wildcard it should not cause expanding.
4808      * (it will be expanded anyway if there is a wildcard before replacing).
4809      */
4810     has_wildcards = mch_has_wildcard(p);
4811     while (*p != NUL)
4812     {
4813 #ifdef FEAT_EVAL
4814 	// Skip over `=expr`, wildcards in it are not expanded.
4815 	if (p[0] == '`' && p[1] == '=')
4816 	{
4817 	    p += 2;
4818 	    (void)skip_expr(&p, NULL);
4819 	    if (*p == '`')
4820 		++p;
4821 	    continue;
4822 	}
4823 #endif
4824 	/*
4825 	 * Quick check if this cannot be the start of a special string.
4826 	 * Also removes backslash before '%', '#' and '<'.
4827 	 */
4828 	if (vim_strchr((char_u *)"%#<", *p) == NULL)
4829 	{
4830 	    ++p;
4831 	    continue;
4832 	}
4833 
4834 	/*
4835 	 * Try to find a match at this position.
4836 	 */
4837 	repl = eval_vars(p, eap->arg, &srclen, &(eap->do_ecmd_lnum),
4838 							 errormsgp, &escaped);
4839 	if (*errormsgp != NULL)		// error detected
4840 	    return FAIL;
4841 	if (repl == NULL)		// no match found
4842 	{
4843 	    p += srclen;
4844 	    continue;
4845 	}
4846 
4847 	// Wildcards won't be expanded below, the replacement is taken
4848 	// literally.  But do expand "~/file", "~user/file" and "$HOME/file".
4849 	if (vim_strchr(repl, '$') != NULL || vim_strchr(repl, '~') != NULL)
4850 	{
4851 	    char_u *l = repl;
4852 
4853 	    repl = expand_env_save(repl);
4854 	    vim_free(l);
4855 	}
4856 
4857 	// Need to escape white space et al. with a backslash.
4858 	// Don't do this for:
4859 	// - replacement that already has been escaped: "##"
4860 	// - shell commands (may have to use quotes instead).
4861 	// - non-unix systems when there is a single argument (spaces don't
4862 	//   separate arguments then).
4863 	if (!eap->usefilter
4864 		&& !escaped
4865 		&& eap->cmdidx != CMD_bang
4866 		&& eap->cmdidx != CMD_grep
4867 		&& eap->cmdidx != CMD_grepadd
4868 		&& eap->cmdidx != CMD_hardcopy
4869 		&& eap->cmdidx != CMD_lgrep
4870 		&& eap->cmdidx != CMD_lgrepadd
4871 		&& eap->cmdidx != CMD_lmake
4872 		&& eap->cmdidx != CMD_make
4873 		&& eap->cmdidx != CMD_terminal
4874 #ifndef UNIX
4875 		&& !(eap->argt & EX_NOSPC)
4876 #endif
4877 		)
4878 	{
4879 	    char_u	*l;
4880 #ifdef BACKSLASH_IN_FILENAME
4881 	    // Don't escape a backslash here, because rem_backslash() doesn't
4882 	    // remove it later.
4883 	    static char_u *nobslash = (char_u *)" \t\"|";
4884 # define ESCAPE_CHARS nobslash
4885 #else
4886 # define ESCAPE_CHARS escape_chars
4887 #endif
4888 
4889 	    for (l = repl; *l; ++l)
4890 		if (vim_strchr(ESCAPE_CHARS, *l) != NULL)
4891 		{
4892 		    l = vim_strsave_escaped(repl, ESCAPE_CHARS);
4893 		    if (l != NULL)
4894 		    {
4895 			vim_free(repl);
4896 			repl = l;
4897 		    }
4898 		    break;
4899 		}
4900 	}
4901 
4902 	// For a shell command a '!' must be escaped.
4903 	if ((eap->usefilter || eap->cmdidx == CMD_bang
4904 						|| eap->cmdidx == CMD_terminal)
4905 			    && vim_strpbrk(repl, (char_u *)"!") != NULL)
4906 	{
4907 	    char_u	*l;
4908 
4909 	    l = vim_strsave_escaped(repl, (char_u *)"!");
4910 	    if (l != NULL)
4911 	    {
4912 		vim_free(repl);
4913 		repl = l;
4914 	    }
4915 	}
4916 
4917 	p = repl_cmdline(eap, p, srclen, repl, cmdlinep);
4918 	vim_free(repl);
4919 	if (p == NULL)
4920 	    return FAIL;
4921     }
4922 
4923     /*
4924      * One file argument: Expand wildcards.
4925      * Don't do this with ":r !command" or ":w !command".
4926      */
4927     if ((eap->argt & EX_NOSPC) && !eap->usefilter)
4928     {
4929 	/*
4930 	 * May do this twice:
4931 	 * 1. Replace environment variables.
4932 	 * 2. Replace any other wildcards, remove backslashes.
4933 	 */
4934 	for (n = 1; n <= 2; ++n)
4935 	{
4936 	    if (n == 2)
4937 	    {
4938 		/*
4939 		 * Halve the number of backslashes (this is Vi compatible).
4940 		 * For Unix and OS/2, when wildcards are expanded, this is
4941 		 * done by ExpandOne() below.
4942 		 */
4943 #if defined(UNIX)
4944 		if (!has_wildcards)
4945 #endif
4946 		    backslash_halve(eap->arg);
4947 	    }
4948 
4949 	    if (has_wildcards)
4950 	    {
4951 		if (n == 1)
4952 		{
4953 		    /*
4954 		     * First loop: May expand environment variables.  This
4955 		     * can be done much faster with expand_env() than with
4956 		     * something else (e.g., calling a shell).
4957 		     * After expanding environment variables, check again
4958 		     * if there are still wildcards present.
4959 		     */
4960 		    if (vim_strchr(eap->arg, '$') != NULL
4961 			    || vim_strchr(eap->arg, '~') != NULL)
4962 		    {
4963 			expand_env_esc(eap->arg, NameBuff, MAXPATHL,
4964 							    TRUE, TRUE, NULL);
4965 			has_wildcards = mch_has_wildcard(NameBuff);
4966 			p = NameBuff;
4967 		    }
4968 		    else
4969 			p = NULL;
4970 		}
4971 		else // n == 2
4972 		{
4973 		    expand_T	xpc;
4974 		    int		options = WILD_LIST_NOTFOUND
4975 					       | WILD_NOERROR | WILD_ADD_SLASH;
4976 
4977 		    ExpandInit(&xpc);
4978 		    xpc.xp_context = EXPAND_FILES;
4979 		    if (p_wic)
4980 			options += WILD_ICASE;
4981 		    p = ExpandOne(&xpc, eap->arg, NULL,
4982 						   options, WILD_EXPAND_FREE);
4983 		    if (p == NULL)
4984 			return FAIL;
4985 		}
4986 		if (p != NULL)
4987 		{
4988 		    (void)repl_cmdline(eap, eap->arg, (int)STRLEN(eap->arg),
4989 								 p, cmdlinep);
4990 		    if (n == 2)	// p came from ExpandOne()
4991 			vim_free(p);
4992 		}
4993 	    }
4994 	}
4995     }
4996     return OK;
4997 }
4998 
4999 /*
5000  * Replace part of the command line, keeping eap->cmd, eap->arg and
5001  * eap->nextcmd correct.
5002  * "src" points to the part that is to be replaced, of length "srclen".
5003  * "repl" is the replacement string.
5004  * Returns a pointer to the character after the replaced string.
5005  * Returns NULL for failure.
5006  */
5007     static char_u *
repl_cmdline(exarg_T * eap,char_u * src,int srclen,char_u * repl,char_u ** cmdlinep)5008 repl_cmdline(
5009     exarg_T	*eap,
5010     char_u	*src,
5011     int		srclen,
5012     char_u	*repl,
5013     char_u	**cmdlinep)
5014 {
5015     int		len;
5016     int		i;
5017     char_u	*new_cmdline;
5018 
5019     /*
5020      * The new command line is build in new_cmdline[].
5021      * First allocate it.
5022      * Careful: a "+cmd" argument may have been NUL terminated.
5023      */
5024     len = (int)STRLEN(repl);
5025     i = (int)(src - *cmdlinep) + (int)STRLEN(src + srclen) + len + 3;
5026     if (eap->nextcmd != NULL)
5027 	i += (int)STRLEN(eap->nextcmd);// add space for next command
5028     if ((new_cmdline = alloc(i)) == NULL)
5029 	return NULL;			// out of memory!
5030 
5031     /*
5032      * Copy the stuff before the expanded part.
5033      * Copy the expanded stuff.
5034      * Copy what came after the expanded part.
5035      * Copy the next commands, if there are any.
5036      */
5037     i = (int)(src - *cmdlinep);	// length of part before match
5038     mch_memmove(new_cmdline, *cmdlinep, (size_t)i);
5039 
5040     mch_memmove(new_cmdline + i, repl, (size_t)len);
5041     i += len;				// remember the end of the string
5042     STRCPY(new_cmdline + i, src + srclen);
5043     src = new_cmdline + i;		// remember where to continue
5044 
5045     if (eap->nextcmd != NULL)		// append next command
5046     {
5047 	i = (int)STRLEN(new_cmdline) + 1;
5048 	STRCPY(new_cmdline + i, eap->nextcmd);
5049 	eap->nextcmd = new_cmdline + i;
5050     }
5051     eap->cmd = new_cmdline + (eap->cmd - *cmdlinep);
5052     eap->arg = new_cmdline + (eap->arg - *cmdlinep);
5053     if (eap->do_ecmd_cmd != NULL && eap->do_ecmd_cmd != dollar_command)
5054 	eap->do_ecmd_cmd = new_cmdline + (eap->do_ecmd_cmd - *cmdlinep);
5055     vim_free(*cmdlinep);
5056     *cmdlinep = new_cmdline;
5057 
5058     return src;
5059 }
5060 
5061 /*
5062  * Check for '|' to separate commands and '"' to start comments.
5063  */
5064     void
separate_nextcmd(exarg_T * eap)5065 separate_nextcmd(exarg_T *eap)
5066 {
5067     char_u	*p;
5068 
5069 #ifdef FEAT_QUICKFIX
5070     p = skip_grep_pat(eap);
5071 #else
5072     p = eap->arg;
5073 #endif
5074 
5075     for ( ; *p; MB_PTR_ADV(p))
5076     {
5077 	if (*p == Ctrl_V)
5078 	{
5079 	    if (eap->argt & (EX_CTRLV | EX_XFILE))
5080 		++p;		// skip CTRL-V and next char
5081 	    else
5082 				// remove CTRL-V and skip next char
5083 		STRMOVE(p, p + 1);
5084 	    if (*p == NUL)		// stop at NUL after CTRL-V
5085 		break;
5086 	}
5087 
5088 #ifdef FEAT_EVAL
5089 	// Skip over `=expr` when wildcards are expanded.
5090 	else if (p[0] == '`' && p[1] == '=' && (eap->argt & EX_XFILE))
5091 	{
5092 	    p += 2;
5093 	    (void)skip_expr(&p, NULL);
5094 	    if (*p == NUL)		// stop at NUL after CTRL-V
5095 		break;
5096 	}
5097 #endif
5098 
5099 	// Check for '"': start of comment or '|': next command
5100 	// :@" and :*" do not start a comment!
5101 	// :redir @" doesn't either.
5102 	else if ((*p == '"'
5103 #ifdef FEAT_EVAL
5104 		    && !in_vim9script()
5105 #endif
5106 		    && !(eap->argt & EX_NOTRLCOM)
5107 		    && ((eap->cmdidx != CMD_at && eap->cmdidx != CMD_star)
5108 							      || p != eap->arg)
5109 		    && (eap->cmdidx != CMD_redir
5110 					 || p != eap->arg + 1 || p[-1] != '@'))
5111 #ifdef FEAT_EVAL
5112 		|| (*p == '#'
5113 		    && in_vim9script()
5114 		    && !(eap->argt & EX_NOTRLCOM)
5115 		    && p > eap->cmd && VIM_ISWHITE(p[-1]))
5116 #endif
5117 		|| *p == '|' || *p == '\n')
5118 	{
5119 	    /*
5120 	     * We remove the '\' before the '|', unless EX_CTRLV is used
5121 	     * AND 'b' is present in 'cpoptions'.
5122 	     */
5123 	    if ((vim_strchr(p_cpo, CPO_BAR) == NULL
5124 			      || !(eap->argt & EX_CTRLV)) && *(p - 1) == '\\')
5125 	    {
5126 		STRMOVE(p - 1, p);	// remove the '\'
5127 		--p;
5128 	    }
5129 	    else
5130 	    {
5131 		eap->nextcmd = check_nextcmd(p);
5132 		*p = NUL;
5133 		break;
5134 	    }
5135 	}
5136     }
5137 
5138     if (!(eap->argt & EX_NOTRLCOM))	// remove trailing spaces
5139 	del_trailing_spaces(eap->arg);
5140 }
5141 
5142 /*
5143  * get + command from ex argument
5144  */
5145     static char_u *
getargcmd(char_u ** argp)5146 getargcmd(char_u **argp)
5147 {
5148     char_u *arg = *argp;
5149     char_u *command = NULL;
5150 
5151     if (*arg == '+')	    // +[command]
5152     {
5153 	++arg;
5154 	if (vim_isspace(*arg) || *arg == NUL)
5155 	    command = dollar_command;
5156 	else
5157 	{
5158 	    command = arg;
5159 	    arg = skip_cmd_arg(command, TRUE);
5160 	    if (*arg != NUL)
5161 		*arg++ = NUL;		// terminate command with NUL
5162 	}
5163 
5164 	arg = skipwhite(arg);	// skip over spaces
5165 	*argp = arg;
5166     }
5167     return command;
5168 }
5169 
5170 /*
5171  * Find end of "+command" argument.  Skip over "\ " and "\\".
5172  */
5173     char_u *
skip_cmd_arg(char_u * p,int rembs)5174 skip_cmd_arg(
5175     char_u *p,
5176     int	   rembs)	// TRUE to halve the number of backslashes
5177 {
5178     while (*p && !vim_isspace(*p))
5179     {
5180 	if (*p == '\\' && p[1] != NUL)
5181 	{
5182 	    if (rembs)
5183 		STRMOVE(p, p + 1);
5184 	    else
5185 		++p;
5186 	}
5187 	MB_PTR_ADV(p);
5188     }
5189     return p;
5190 }
5191 
5192     int
get_bad_opt(char_u * p,exarg_T * eap)5193 get_bad_opt(char_u *p, exarg_T *eap)
5194 {
5195     if (STRICMP(p, "keep") == 0)
5196 	eap->bad_char = BAD_KEEP;
5197     else if (STRICMP(p, "drop") == 0)
5198 	eap->bad_char = BAD_DROP;
5199     else if (MB_BYTE2LEN(*p) == 1 && p[1] == NUL)
5200 	eap->bad_char = *p;
5201     else
5202 	return FAIL;
5203     return OK;
5204 }
5205 
5206 /*
5207  * Get "++opt=arg" argument.
5208  * Return FAIL or OK.
5209  */
5210     static int
getargopt(exarg_T * eap)5211 getargopt(exarg_T *eap)
5212 {
5213     char_u	*arg = eap->arg + 2;
5214     int		*pp = NULL;
5215     int		bad_char_idx;
5216     char_u	*p;
5217 
5218     // ":edit ++[no]bin[ary] file"
5219     if (STRNCMP(arg, "bin", 3) == 0 || STRNCMP(arg, "nobin", 5) == 0)
5220     {
5221 	if (*arg == 'n')
5222 	{
5223 	    arg += 2;
5224 	    eap->force_bin = FORCE_NOBIN;
5225 	}
5226 	else
5227 	    eap->force_bin = FORCE_BIN;
5228 	if (!checkforcmd(&arg, "binary", 3))
5229 	    return FAIL;
5230 	eap->arg = skipwhite(arg);
5231 	return OK;
5232     }
5233 
5234     // ":read ++edit file"
5235     if (STRNCMP(arg, "edit", 4) == 0)
5236     {
5237 	eap->read_edit = TRUE;
5238 	eap->arg = skipwhite(arg + 4);
5239 	return OK;
5240     }
5241 
5242     if (STRNCMP(arg, "ff", 2) == 0)
5243     {
5244 	arg += 2;
5245 	pp = &eap->force_ff;
5246     }
5247     else if (STRNCMP(arg, "fileformat", 10) == 0)
5248     {
5249 	arg += 10;
5250 	pp = &eap->force_ff;
5251     }
5252     else if (STRNCMP(arg, "enc", 3) == 0)
5253     {
5254 	if (STRNCMP(arg, "encoding", 8) == 0)
5255 	    arg += 8;
5256 	else
5257 	    arg += 3;
5258 	pp = &eap->force_enc;
5259     }
5260     else if (STRNCMP(arg, "bad", 3) == 0)
5261     {
5262 	arg += 3;
5263 	pp = &bad_char_idx;
5264     }
5265 
5266     if (pp == NULL || *arg != '=')
5267 	return FAIL;
5268 
5269     ++arg;
5270     *pp = (int)(arg - eap->cmd);
5271     arg = skip_cmd_arg(arg, FALSE);
5272     eap->arg = skipwhite(arg);
5273     *arg = NUL;
5274 
5275     if (pp == &eap->force_ff)
5276     {
5277 	if (check_ff_value(eap->cmd + eap->force_ff) == FAIL)
5278 	    return FAIL;
5279 	eap->force_ff = eap->cmd[eap->force_ff];
5280     }
5281     else if (pp == &eap->force_enc)
5282     {
5283 	// Make 'fileencoding' lower case.
5284 	for (p = eap->cmd + eap->force_enc; *p != NUL; ++p)
5285 	    *p = TOLOWER_ASC(*p);
5286     }
5287     else
5288     {
5289 	// Check ++bad= argument.  Must be a single-byte character, "keep" or
5290 	// "drop".
5291 	if (get_bad_opt(eap->cmd + bad_char_idx, eap) == FAIL)
5292 	    return FAIL;
5293     }
5294 
5295     return OK;
5296 }
5297 
5298     static void
ex_autocmd(exarg_T * eap)5299 ex_autocmd(exarg_T *eap)
5300 {
5301     /*
5302      * Disallow autocommands from .exrc and .vimrc in current
5303      * directory for security reasons.
5304      */
5305     if (secure)
5306     {
5307 	secure = 2;
5308 	eap->errmsg =
5309 	      _(e_command_not_allowed_from_vimrc_in_current_dir_or_tag_search);
5310     }
5311     else if (eap->cmdidx == CMD_autocmd)
5312 	do_autocmd(eap, eap->arg, eap->forceit);
5313     else
5314 	do_augroup(eap->arg, eap->forceit);
5315 }
5316 
5317 /*
5318  * ":doautocmd": Apply the automatic commands to the current buffer.
5319  */
5320     static void
ex_doautocmd(exarg_T * eap)5321 ex_doautocmd(exarg_T *eap)
5322 {
5323     char_u	*arg = eap->arg;
5324     int		call_do_modelines = check_nomodeline(&arg);
5325     int		did_aucmd;
5326 
5327     (void)do_doautocmd(arg, TRUE, &did_aucmd);
5328     // Only when there is no <nomodeline>.
5329     if (call_do_modelines && did_aucmd)
5330 	do_modelines(0);
5331 }
5332 
5333 /*
5334  * :[N]bunload[!] [N] [bufname] unload buffer
5335  * :[N]bdelete[!] [N] [bufname] delete buffer from buffer list
5336  * :[N]bwipeout[!] [N] [bufname] delete buffer really
5337  */
5338     static void
ex_bunload(exarg_T * eap)5339 ex_bunload(exarg_T *eap)
5340 {
5341     if (ERROR_IF_ANY_POPUP_WINDOW)
5342 	return;
5343     eap->errmsg = do_bufdel(
5344 	    eap->cmdidx == CMD_bdelete ? DOBUF_DEL
5345 		: eap->cmdidx == CMD_bwipeout ? DOBUF_WIPE
5346 		: DOBUF_UNLOAD, eap->arg,
5347 	    eap->addr_count, (int)eap->line1, (int)eap->line2, eap->forceit);
5348 }
5349 
5350 /*
5351  * :[N]buffer [N]	to buffer N
5352  * :[N]sbuffer [N]	to buffer N
5353  */
5354     static void
ex_buffer(exarg_T * eap)5355 ex_buffer(exarg_T *eap)
5356 {
5357     if (ERROR_IF_ANY_POPUP_WINDOW)
5358 	return;
5359     if (*eap->arg)
5360 	eap->errmsg = ex_errmsg(e_trailing_arg, eap->arg);
5361     else
5362     {
5363 	if (eap->addr_count == 0)	// default is current buffer
5364 	    goto_buffer(eap, DOBUF_CURRENT, FORWARD, 0);
5365 	else
5366 	    goto_buffer(eap, DOBUF_FIRST, FORWARD, (int)eap->line2);
5367 	if (eap->do_ecmd_cmd != NULL)
5368 	    do_cmd_argument(eap->do_ecmd_cmd);
5369     }
5370 }
5371 
5372 /*
5373  * :[N]bmodified [N]	to next mod. buffer
5374  * :[N]sbmodified [N]	to next mod. buffer
5375  */
5376     static void
ex_bmodified(exarg_T * eap)5377 ex_bmodified(exarg_T *eap)
5378 {
5379     goto_buffer(eap, DOBUF_MOD, FORWARD, (int)eap->line2);
5380     if (eap->do_ecmd_cmd != NULL)
5381 	do_cmd_argument(eap->do_ecmd_cmd);
5382 }
5383 
5384 /*
5385  * :[N]bnext [N]	to next buffer
5386  * :[N]sbnext [N]	split and to next buffer
5387  */
5388     static void
ex_bnext(exarg_T * eap)5389 ex_bnext(exarg_T *eap)
5390 {
5391     if (ERROR_IF_ANY_POPUP_WINDOW)
5392 	return;
5393 
5394     goto_buffer(eap, DOBUF_CURRENT, FORWARD, (int)eap->line2);
5395     if (eap->do_ecmd_cmd != NULL)
5396 	do_cmd_argument(eap->do_ecmd_cmd);
5397 }
5398 
5399 /*
5400  * :[N]bNext [N]	to previous buffer
5401  * :[N]bprevious [N]	to previous buffer
5402  * :[N]sbNext [N]	split and to previous buffer
5403  * :[N]sbprevious [N]	split and to previous buffer
5404  */
5405     static void
ex_bprevious(exarg_T * eap)5406 ex_bprevious(exarg_T *eap)
5407 {
5408     if (ERROR_IF_ANY_POPUP_WINDOW)
5409 	return;
5410 
5411     goto_buffer(eap, DOBUF_CURRENT, BACKWARD, (int)eap->line2);
5412     if (eap->do_ecmd_cmd != NULL)
5413 	do_cmd_argument(eap->do_ecmd_cmd);
5414 }
5415 
5416 /*
5417  * :brewind		to first buffer
5418  * :bfirst		to first buffer
5419  * :sbrewind		split and to first buffer
5420  * :sbfirst		split and to first buffer
5421  */
5422     static void
ex_brewind(exarg_T * eap)5423 ex_brewind(exarg_T *eap)
5424 {
5425     if (ERROR_IF_ANY_POPUP_WINDOW)
5426 	return;
5427 
5428     goto_buffer(eap, DOBUF_FIRST, FORWARD, 0);
5429     if (eap->do_ecmd_cmd != NULL)
5430 	do_cmd_argument(eap->do_ecmd_cmd);
5431 }
5432 
5433 /*
5434  * :blast		to last buffer
5435  * :sblast		split and to last buffer
5436  */
5437     static void
ex_blast(exarg_T * eap)5438 ex_blast(exarg_T *eap)
5439 {
5440     if (ERROR_IF_ANY_POPUP_WINDOW)
5441 	return;
5442 
5443     goto_buffer(eap, DOBUF_LAST, BACKWARD, 0);
5444     if (eap->do_ecmd_cmd != NULL)
5445 	do_cmd_argument(eap->do_ecmd_cmd);
5446 }
5447 
5448 /*
5449  * Check if "c" ends an Ex command.
5450  * In Vim9 script does not check for white space before #.
5451  */
5452     int
ends_excmd(int c)5453 ends_excmd(int c)
5454 {
5455     int comment_char = '"';
5456 
5457 #ifdef FEAT_EVAL
5458     if (in_vim9script())
5459 	comment_char = '#';
5460 #endif
5461     return (c == NUL || c == '|' || c == comment_char || c == '\n');
5462 }
5463 
5464 /*
5465  * Like ends_excmd() but checks that a # in Vim9 script either has "cmd" equal
5466  * to "cmd_start" or has a white space character before it.
5467  */
5468     int
ends_excmd2(char_u * cmd_start UNUSED,char_u * cmd)5469 ends_excmd2(char_u *cmd_start UNUSED, char_u *cmd)
5470 {
5471     int c = *cmd;
5472 
5473     if (c == NUL || c == '|' || c == '\n')
5474 	return TRUE;
5475 #ifdef FEAT_EVAL
5476     if (in_vim9script())
5477 	//  # starts a comment, #{ might be a mistake, #{{ can start a fold
5478 	return c == '#' && (cmd[1] != '{' || cmd[2] == '{')
5479 				 && (cmd == cmd_start || VIM_ISWHITE(cmd[-1]));
5480 #endif
5481     return c == '"';
5482 }
5483 
5484 #if defined(FEAT_SYN_HL) || defined(FEAT_SEARCH_EXTRA) || defined(FEAT_EVAL) \
5485 	|| defined(PROTO)
5486 /*
5487  * Return the next command, after the first '|' or '\n'.
5488  * Return NULL if not found.
5489  */
5490     char_u *
find_nextcmd(char_u * p)5491 find_nextcmd(char_u *p)
5492 {
5493     while (*p != '|' && *p != '\n')
5494     {
5495 	if (*p == NUL)
5496 	    return NULL;
5497 	++p;
5498     }
5499     return (p + 1);
5500 }
5501 #endif
5502 
5503 /*
5504  * Check if *p is a separator between Ex commands, skipping over white space.
5505  * Return NULL if it isn't, the following character if it is.
5506  */
5507     char_u *
check_nextcmd(char_u * p)5508 check_nextcmd(char_u *p)
5509 {
5510     char_u *s = skipwhite(p);
5511 
5512     if (*s == '|' || *s == '\n')
5513 	return (s + 1);
5514     else
5515 	return NULL;
5516 }
5517 
5518 /*
5519  * If "eap->nextcmd" is not set, check for a next command at "p".
5520  */
5521     void
set_nextcmd(exarg_T * eap,char_u * arg)5522 set_nextcmd(exarg_T *eap, char_u *arg)
5523 {
5524     char_u *p = check_nextcmd(arg);
5525 
5526     if (eap->nextcmd == NULL)
5527 	eap->nextcmd = p;
5528     else if (p != NULL)
5529 	// cannot use "| command" inside a  {} block
5530 	semsg(_(e_cannot_use_bar_to_separate_commands_here_str), arg);
5531 }
5532 
5533 /*
5534  * - if there are more files to edit
5535  * - and this is the last window
5536  * - and forceit not used
5537  * - and not repeated twice on a row
5538  *    return FAIL and give error message if 'message' TRUE
5539  * return OK otherwise
5540  */
5541     static int
check_more(int message,int forceit)5542 check_more(
5543     int message,	    // when FALSE check only, no messages
5544     int forceit)
5545 {
5546     int	    n = ARGCOUNT - curwin->w_arg_idx - 1;
5547 
5548     if (!forceit && only_one_window()
5549 	    && ARGCOUNT > 1 && !arg_had_last && n > 0 && quitmore == 0)
5550     {
5551 	if (message)
5552 	{
5553 #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
5554 	    if ((p_confirm || (cmdmod.cmod_flags & CMOD_CONFIRM))
5555 						    && curbuf->b_fname != NULL)
5556 	    {
5557 		char_u	buff[DIALOG_MSG_SIZE];
5558 
5559 		vim_snprintf((char *)buff, DIALOG_MSG_SIZE,
5560 			NGETTEXT("%d more file to edit.  Quit anyway?",
5561 			    "%d more files to edit.  Quit anyway?", n), n);
5562 		if (vim_dialog_yesno(VIM_QUESTION, NULL, buff, 1) == VIM_YES)
5563 		    return OK;
5564 		return FAIL;
5565 	    }
5566 #endif
5567 	    semsg(NGETTEXT("E173: %d more file to edit",
5568 			"E173: %d more files to edit", n), n);
5569 	    quitmore = 2;	    // next try to quit is allowed
5570 	}
5571 	return FAIL;
5572     }
5573     return OK;
5574 }
5575 
5576 /*
5577  * Function given to ExpandGeneric() to obtain the list of command names.
5578  */
5579     char_u *
get_command_name(expand_T * xp UNUSED,int idx)5580 get_command_name(expand_T *xp UNUSED, int idx)
5581 {
5582     if (idx >= (int)CMD_SIZE)
5583 	return expand_user_command_name(idx);
5584     return cmdnames[idx].cmd_name;
5585 }
5586 
5587     static void
ex_colorscheme(exarg_T * eap)5588 ex_colorscheme(exarg_T *eap)
5589 {
5590     if (*eap->arg == NUL)
5591     {
5592 #ifdef FEAT_EVAL
5593 	char_u *expr = vim_strsave((char_u *)"g:colors_name");
5594 	char_u *p = NULL;
5595 
5596 	if (expr != NULL)
5597 	{
5598 	    ++emsg_off;
5599 	    p = eval_to_string(expr, FALSE);
5600 	    --emsg_off;
5601 	    vim_free(expr);
5602 	}
5603 	if (p != NULL)
5604 	{
5605 	    msg((char *)p);
5606 	    vim_free(p);
5607 	}
5608 	else
5609 	    msg("default");
5610 #else
5611 	msg(_("unknown"));
5612 #endif
5613     }
5614     else if (load_colors(eap->arg) == FAIL)
5615 	semsg(_("E185: Cannot find color scheme '%s'"), eap->arg);
5616 
5617 #ifdef FEAT_VTP
5618     else if (has_vtp_working())
5619     {
5620 	// background color change requires clear + redraw
5621 	update_screen(CLEAR);
5622 	redrawcmd();
5623     }
5624 #endif
5625 }
5626 
5627     static void
ex_highlight(exarg_T * eap)5628 ex_highlight(exarg_T *eap)
5629 {
5630     if (*eap->arg == NUL && eap->cmd[2] == '!')
5631 	msg(_("Greetings, Vim user!"));
5632     do_highlight(eap->arg, eap->forceit, FALSE);
5633 }
5634 
5635 
5636 /*
5637  * Call this function if we thought we were going to exit, but we won't
5638  * (because of an error).  May need to restore the terminal mode.
5639  */
5640     void
not_exiting(void)5641 not_exiting(void)
5642 {
5643     exiting = FALSE;
5644     settmode(TMODE_RAW);
5645 }
5646 
5647     int
before_quit_autocmds(win_T * wp,int quit_all,int forceit)5648 before_quit_autocmds(win_T *wp, int quit_all, int forceit)
5649 {
5650     apply_autocmds(EVENT_QUITPRE, NULL, NULL, FALSE, wp->w_buffer);
5651 
5652     // Bail out when autocommands closed the window.
5653     // Refuse to quit when the buffer in the last window is being closed (can
5654     // only happen in autocommands).
5655     if (!win_valid(wp)
5656 	    || curbuf_locked()
5657 	    || (wp->w_buffer->b_nwindows == 1 && wp->w_buffer->b_locked > 0))
5658 	return TRUE;
5659 
5660     if (quit_all || (check_more(FALSE, forceit) == OK && only_one_window()))
5661     {
5662 	apply_autocmds(EVENT_EXITPRE, NULL, NULL, FALSE, curbuf);
5663 	// Refuse to quit when locked or when the window was closed or the
5664 	// buffer in the last window is being closed (can only happen in
5665 	// autocommands).
5666 	if (!win_valid(wp) || curbuf_locked()
5667 			  || (curbuf->b_nwindows == 1 && curbuf->b_locked > 0))
5668 	    return TRUE;
5669     }
5670 
5671     return FALSE;
5672 }
5673 
5674 /*
5675  * ":quit": quit current window, quit Vim if the last window is closed.
5676  * ":{nr}quit": quit window {nr}
5677  * Also used when closing a terminal window that's the last one.
5678  */
5679     void
ex_quit(exarg_T * eap)5680 ex_quit(exarg_T *eap)
5681 {
5682     win_T	*wp;
5683 
5684 #ifdef FEAT_CMDWIN
5685     if (cmdwin_type != 0)
5686     {
5687 	cmdwin_result = Ctrl_C;
5688 	return;
5689     }
5690 #endif
5691     // Don't quit while editing the command line.
5692     if (text_locked())
5693     {
5694 	text_locked_msg();
5695 	return;
5696     }
5697     if (eap->addr_count > 0)
5698     {
5699 	int	wnr = eap->line2;
5700 
5701 	for (wp = firstwin; wp->w_next != NULL; wp = wp->w_next)
5702 	    if (--wnr <= 0)
5703 		break;
5704     }
5705     else
5706 	wp = curwin;
5707 
5708     // Refuse to quit when locked.
5709     if (curbuf_locked())
5710 	return;
5711 
5712     // Trigger QuitPre and maybe ExitPre
5713     if (before_quit_autocmds(wp, FALSE, eap->forceit))
5714 	return;
5715 
5716 #ifdef FEAT_NETBEANS_INTG
5717     netbeansForcedQuit = eap->forceit;
5718 #endif
5719 
5720     /*
5721      * If there is only one relevant window we will exit.
5722      */
5723     if (check_more(FALSE, eap->forceit) == OK && only_one_window())
5724 	exiting = TRUE;
5725     if ((!buf_hide(wp->w_buffer)
5726 		&& check_changed(wp->w_buffer, (p_awa ? CCGD_AW : 0)
5727 				       | (eap->forceit ? CCGD_FORCEIT : 0)
5728 				       | CCGD_EXCMD))
5729 	    || check_more(TRUE, eap->forceit) == FAIL
5730 	    || (only_one_window() && check_changed_any(eap->forceit, TRUE)))
5731     {
5732 	not_exiting();
5733     }
5734     else
5735     {
5736 	// quit last window
5737 	// Note: only_one_window() returns true, even so a help window is
5738 	// still open. In that case only quit, if no address has been
5739 	// specified. Example:
5740 	// :h|wincmd w|1q     - don't quit
5741 	// :h|wincmd w|q      - quit
5742 	if (only_one_window() && (ONE_WINDOW || eap->addr_count == 0))
5743 	    getout(0);
5744 	not_exiting();
5745 #ifdef FEAT_GUI
5746 	need_mouse_correct = TRUE;
5747 #endif
5748 	// close window; may free buffer
5749 	win_close(wp, !buf_hide(wp->w_buffer) || eap->forceit);
5750     }
5751 }
5752 
5753 /*
5754  * ":cquit".
5755  */
5756     static void
ex_cquit(exarg_T * eap UNUSED)5757 ex_cquit(exarg_T *eap UNUSED)
5758 {
5759     // this does not always pass on the exit code to the Manx compiler. why?
5760     getout(eap->addr_count > 0 ? (int)eap->line2 : EXIT_FAILURE);
5761 }
5762 
5763 /*
5764  * ":qall": try to quit all windows
5765  */
5766     static void
ex_quit_all(exarg_T * eap)5767 ex_quit_all(exarg_T *eap)
5768 {
5769 # ifdef FEAT_CMDWIN
5770     if (cmdwin_type != 0)
5771     {
5772 	if (eap->forceit)
5773 	    cmdwin_result = K_XF1;	// ex_window() takes care of this
5774 	else
5775 	    cmdwin_result = K_XF2;
5776 	return;
5777     }
5778 # endif
5779 
5780     // Don't quit while editing the command line.
5781     if (text_locked())
5782     {
5783 	text_locked_msg();
5784 	return;
5785     }
5786 
5787     if (before_quit_autocmds(curwin, TRUE, eap->forceit))
5788 	return;
5789 
5790     exiting = TRUE;
5791     if (eap->forceit || !check_changed_any(FALSE, FALSE))
5792 	getout(0);
5793     not_exiting();
5794 }
5795 
5796 /*
5797  * ":close": close current window, unless it is the last one
5798  */
5799     static void
ex_close(exarg_T * eap)5800 ex_close(exarg_T *eap)
5801 {
5802     win_T	*win;
5803     int		winnr = 0;
5804 #ifdef FEAT_CMDWIN
5805     if (cmdwin_type != 0)
5806 	cmdwin_result = Ctrl_C;
5807     else
5808 #endif
5809 	if (!text_locked() && !curbuf_locked())
5810 	{
5811 	    if (eap->addr_count == 0)
5812 		ex_win_close(eap->forceit, curwin, NULL);
5813 	    else
5814 	    {
5815 		FOR_ALL_WINDOWS(win)
5816 		{
5817 		    winnr++;
5818 		    if (winnr == eap->line2)
5819 			break;
5820 		}
5821 		if (win == NULL)
5822 		    win = lastwin;
5823 		ex_win_close(eap->forceit, win, NULL);
5824 	    }
5825 	}
5826 }
5827 
5828 #ifdef FEAT_QUICKFIX
5829 /*
5830  * ":pclose": Close any preview window.
5831  */
5832     static void
ex_pclose(exarg_T * eap)5833 ex_pclose(exarg_T *eap)
5834 {
5835     win_T	*win;
5836 
5837     // First close any normal window.
5838     FOR_ALL_WINDOWS(win)
5839 	if (win->w_p_pvw)
5840 	{
5841 	    ex_win_close(eap->forceit, win, NULL);
5842 	    return;
5843 	}
5844 # ifdef FEAT_PROP_POPUP
5845     // Also when 'previewpopup' is empty, it might have been cleared.
5846     popup_close_preview();
5847 # endif
5848 }
5849 #endif
5850 
5851 /*
5852  * Close window "win" and take care of handling closing the last window for a
5853  * modified buffer.
5854  */
5855     static void
ex_win_close(int forceit,win_T * win,tabpage_T * tp)5856 ex_win_close(
5857     int		forceit,
5858     win_T	*win,
5859     tabpage_T	*tp)		// NULL or the tab page "win" is in
5860 {
5861     int		need_hide;
5862     buf_T	*buf = win->w_buffer;
5863 
5864     // Never close the autocommand window.
5865     if (win == aucmd_win)
5866     {
5867 	emsg(_(e_autocmd_close));
5868 	return;
5869     }
5870 
5871     need_hide = (bufIsChanged(buf) && buf->b_nwindows <= 1);
5872     if (need_hide && !buf_hide(buf) && !forceit)
5873     {
5874 #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
5875 	if ((p_confirm || (cmdmod.cmod_flags & CMOD_CONFIRM)) && p_write)
5876 	{
5877 	    bufref_T bufref;
5878 
5879 	    set_bufref(&bufref, buf);
5880 	    dialog_changed(buf, FALSE);
5881 	    if (bufref_valid(&bufref) && bufIsChanged(buf))
5882 		return;
5883 	    need_hide = FALSE;
5884 	}
5885 	else
5886 #endif
5887 	{
5888 	    no_write_message();
5889 	    return;
5890 	}
5891     }
5892 
5893 #ifdef FEAT_GUI
5894     need_mouse_correct = TRUE;
5895 #endif
5896 
5897     // free buffer when not hiding it or when it's a scratch buffer
5898     if (tp == NULL)
5899 	win_close(win, !need_hide && !buf_hide(buf));
5900     else
5901 	win_close_othertab(win, !need_hide && !buf_hide(buf), tp);
5902 }
5903 
5904 /*
5905  * Handle the argument for a tabpage related ex command.
5906  * Returns a tabpage number.
5907  * When an error is encountered then eap->errmsg is set.
5908  */
5909     static int
get_tabpage_arg(exarg_T * eap)5910 get_tabpage_arg(exarg_T *eap)
5911 {
5912     int tab_number;
5913     int unaccept_arg0 = (eap->cmdidx == CMD_tabmove) ? 0 : 1;
5914 
5915     if (eap->arg && *eap->arg != NUL)
5916     {
5917 	char_u *p = eap->arg;
5918 	char_u *p_save;
5919 	int    relative = 0; // argument +N/-N means: go to N places to the
5920 			     // right/left relative to the current position.
5921 
5922 	if (*p == '-')
5923 	{
5924 	    relative = -1;
5925 	    p++;
5926 	}
5927 	else if (*p == '+')
5928 	{
5929 	    relative = 1;
5930 	    p++;
5931 	}
5932 
5933 	p_save = p;
5934 	tab_number = getdigits(&p);
5935 
5936 	if (relative == 0)
5937 	{
5938 	    if (STRCMP(p, "$") == 0)
5939 		tab_number = LAST_TAB_NR;
5940 	    else if (STRCMP(p, "#") == 0)
5941 		if (valid_tabpage(lastused_tabpage))
5942 		    tab_number = tabpage_index(lastused_tabpage);
5943 		else
5944 		{
5945 		    eap->errmsg = ex_errmsg(e_invargval, eap->arg);
5946 		    tab_number = 0;
5947 		    goto theend;
5948 		}
5949 	    else if (p == p_save || *p_save == '-' || *p != NUL
5950 		    || tab_number > LAST_TAB_NR)
5951 	    {
5952 		// No numbers as argument.
5953 		eap->errmsg = ex_errmsg(e_invarg2, eap->arg);
5954 		goto theend;
5955 	    }
5956 	}
5957 	else
5958 	{
5959 	    if (*p_save == NUL)
5960 		tab_number = 1;
5961 	    else if (p == p_save || *p_save == '-' || *p != NUL
5962 		    || tab_number == 0)
5963 	    {
5964 		// No numbers as argument.
5965 		eap->errmsg = ex_errmsg(e_invarg2, eap->arg);
5966 		goto theend;
5967 	    }
5968 	    tab_number = tab_number * relative + tabpage_index(curtab);
5969 	    if (!unaccept_arg0 && relative == -1)
5970 		--tab_number;
5971 	}
5972 	if (tab_number < unaccept_arg0 || tab_number > LAST_TAB_NR)
5973 	    eap->errmsg = ex_errmsg(e_invarg2, eap->arg);
5974     }
5975     else if (eap->addr_count > 0)
5976     {
5977 	if (unaccept_arg0 && eap->line2 == 0)
5978 	{
5979 	    eap->errmsg = _(e_invalid_range);
5980 	    tab_number = 0;
5981 	}
5982 	else
5983 	{
5984 	    tab_number = eap->line2;
5985 	    if (!unaccept_arg0 && *skipwhite(*eap->cmdlinep) == '-')
5986 	    {
5987 		--tab_number;
5988 		if (tab_number < unaccept_arg0)
5989 		    eap->errmsg = _(e_invalid_range);
5990 	    }
5991 	}
5992     }
5993     else
5994     {
5995 	switch (eap->cmdidx)
5996 	{
5997 	case CMD_tabnext:
5998 	    tab_number = tabpage_index(curtab) + 1;
5999 	    if (tab_number > LAST_TAB_NR)
6000 		tab_number = 1;
6001 	    break;
6002 	case CMD_tabmove:
6003 	    tab_number = LAST_TAB_NR;
6004 	    break;
6005 	default:
6006 	    tab_number = tabpage_index(curtab);
6007 	}
6008     }
6009 
6010 theend:
6011     return tab_number;
6012 }
6013 
6014 /*
6015  * ":tabclose": close current tab page, unless it is the last one.
6016  * ":tabclose N": close tab page N.
6017  */
6018     static void
ex_tabclose(exarg_T * eap)6019 ex_tabclose(exarg_T *eap)
6020 {
6021     tabpage_T	*tp;
6022     int		tab_number;
6023 
6024 # ifdef FEAT_CMDWIN
6025     if (cmdwin_type != 0)
6026 	cmdwin_result = K_IGNORE;
6027     else
6028 # endif
6029 	if (first_tabpage->tp_next == NULL)
6030 	    emsg(_("E784: Cannot close last tab page"));
6031 	else
6032 	{
6033 	    tab_number = get_tabpage_arg(eap);
6034 	    if (eap->errmsg == NULL)
6035 	    {
6036 		tp = find_tabpage(tab_number);
6037 		if (tp == NULL)
6038 		{
6039 		    beep_flush();
6040 		    return;
6041 		}
6042 		if (tp != curtab)
6043 		{
6044 		    tabpage_close_other(tp, eap->forceit);
6045 		    return;
6046 		}
6047 		else if (!text_locked() && !curbuf_locked())
6048 		    tabpage_close(eap->forceit);
6049 	    }
6050 	}
6051 }
6052 
6053 /*
6054  * ":tabonly": close all tab pages except the current one
6055  */
6056     static void
ex_tabonly(exarg_T * eap)6057 ex_tabonly(exarg_T *eap)
6058 {
6059     tabpage_T	*tp;
6060     int		done;
6061     int		tab_number;
6062 
6063 # ifdef FEAT_CMDWIN
6064     if (cmdwin_type != 0)
6065 	cmdwin_result = K_IGNORE;
6066     else
6067 # endif
6068 	if (first_tabpage->tp_next == NULL)
6069 	    msg(_("Already only one tab page"));
6070 	else
6071 	{
6072 	    tab_number = get_tabpage_arg(eap);
6073 	    if (eap->errmsg == NULL)
6074 	    {
6075 		goto_tabpage(tab_number);
6076 		// Repeat this up to a 1000 times, because autocommands may
6077 		// mess up the lists.
6078 		for (done = 0; done < 1000; ++done)
6079 		{
6080 		    FOR_ALL_TABPAGES(tp)
6081 			if (tp->tp_topframe != topframe)
6082 			{
6083 			    tabpage_close_other(tp, eap->forceit);
6084 			    // if we failed to close it quit
6085 			    if (valid_tabpage(tp))
6086 				done = 1000;
6087 			    // start over, "tp" is now invalid
6088 			    break;
6089 			}
6090 		    if (first_tabpage->tp_next == NULL)
6091 			break;
6092 		}
6093 	    }
6094 	}
6095 }
6096 
6097 /*
6098  * Close the current tab page.
6099  */
6100     void
tabpage_close(int forceit)6101 tabpage_close(int forceit)
6102 {
6103     // First close all the windows but the current one.  If that worked then
6104     // close the last window in this tab, that will close it.
6105     if (!ONE_WINDOW)
6106 	close_others(TRUE, forceit);
6107     if (ONE_WINDOW)
6108 	ex_win_close(forceit, curwin, NULL);
6109 # ifdef FEAT_GUI
6110     need_mouse_correct = TRUE;
6111 # endif
6112 }
6113 
6114 /*
6115  * Close tab page "tp", which is not the current tab page.
6116  * Note that autocommands may make "tp" invalid.
6117  * Also takes care of the tab pages line disappearing when closing the
6118  * last-but-one tab page.
6119  */
6120     void
tabpage_close_other(tabpage_T * tp,int forceit)6121 tabpage_close_other(tabpage_T *tp, int forceit)
6122 {
6123     int		done = 0;
6124     win_T	*wp;
6125     int		h = tabline_height();
6126 
6127     // Limit to 1000 windows, autocommands may add a window while we close
6128     // one.  OK, so I'm paranoid...
6129     while (++done < 1000)
6130     {
6131 	wp = tp->tp_firstwin;
6132 	ex_win_close(forceit, wp, tp);
6133 
6134 	// Autocommands may delete the tab page under our fingers and we may
6135 	// fail to close a window with a modified buffer.
6136 	if (!valid_tabpage(tp) || tp->tp_firstwin == wp)
6137 	    break;
6138     }
6139 
6140     apply_autocmds(EVENT_TABCLOSED, NULL, NULL, FALSE, curbuf);
6141 
6142     redraw_tabline = TRUE;
6143     if (h != tabline_height())
6144 	shell_new_rows();
6145 }
6146 
6147 /*
6148  * ":only".
6149  */
6150     static void
ex_only(exarg_T * eap)6151 ex_only(exarg_T *eap)
6152 {
6153     win_T   *wp;
6154     int	    wnr;
6155 # ifdef FEAT_GUI
6156     need_mouse_correct = TRUE;
6157 # endif
6158     if (eap->addr_count > 0)
6159     {
6160 	wnr = eap->line2;
6161 	for (wp = firstwin; --wnr > 0; )
6162 	{
6163 	    if (wp->w_next == NULL)
6164 		break;
6165 	    else
6166 		wp = wp->w_next;
6167 	}
6168 	win_goto(wp);
6169     }
6170     close_others(TRUE, eap->forceit);
6171 }
6172 
6173     static void
ex_hide(exarg_T * eap UNUSED)6174 ex_hide(exarg_T *eap UNUSED)
6175 {
6176     // ":hide" or ":hide | cmd": hide current window
6177     if (!eap->skip)
6178     {
6179 #ifdef FEAT_GUI
6180 	need_mouse_correct = TRUE;
6181 #endif
6182 	if (eap->addr_count == 0)
6183 	    win_close(curwin, FALSE);	// don't free buffer
6184 	else
6185 	{
6186 	    int	winnr = 0;
6187 	    win_T	*win;
6188 
6189 	    FOR_ALL_WINDOWS(win)
6190 	    {
6191 		winnr++;
6192 		if (winnr == eap->line2)
6193 		    break;
6194 	    }
6195 	    if (win == NULL)
6196 		win = lastwin;
6197 	    win_close(win, FALSE);
6198 	}
6199     }
6200 }
6201 
6202 /*
6203  * ":stop" and ":suspend": Suspend Vim.
6204  */
6205     static void
ex_stop(exarg_T * eap)6206 ex_stop(exarg_T *eap)
6207 {
6208     /*
6209      * Disallow suspending for "rvim".
6210      */
6211     if (!check_restricted())
6212     {
6213 	if (!eap->forceit)
6214 	    autowrite_all();
6215 	apply_autocmds(EVENT_VIMSUSPEND, NULL, NULL, FALSE, NULL);
6216 	windgoto((int)Rows - 1, 0);
6217 	out_char('\n');
6218 	out_flush();
6219 	stoptermcap();
6220 	out_flush();		// needed for SUN to restore xterm buffer
6221 	mch_restore_title(SAVE_RESTORE_BOTH);	// restore window titles
6222 	ui_suspend();		// call machine specific function
6223 	maketitle();
6224 	resettitle();		// force updating the title
6225 	starttermcap();
6226 	scroll_start();		// scroll screen before redrawing
6227 	redraw_later_clear();
6228 	shell_resized();	// may have resized window
6229 	apply_autocmds(EVENT_VIMRESUME, NULL, NULL, FALSE, NULL);
6230     }
6231 }
6232 
6233 /*
6234  * ":exit", ":xit" and ":wq": Write file and quit the current window.
6235  */
6236     static void
ex_exit(exarg_T * eap)6237 ex_exit(exarg_T *eap)
6238 {
6239 #ifdef FEAT_EVAL
6240     if (not_in_vim9(eap) == FAIL)
6241 	return;
6242 #endif
6243 #ifdef FEAT_CMDWIN
6244     if (cmdwin_type != 0)
6245     {
6246 	cmdwin_result = Ctrl_C;
6247 	return;
6248     }
6249 #endif
6250     // Don't quit while editing the command line.
6251     if (text_locked())
6252     {
6253 	text_locked_msg();
6254 	return;
6255     }
6256 
6257     /*
6258      * we plan to exit if there is only one relevant window
6259      */
6260     if (check_more(FALSE, eap->forceit) == OK && only_one_window())
6261 	exiting = TRUE;
6262 
6263     // Write the buffer for ":wq" or when it was changed.
6264     // Trigger QuitPre and ExitPre.
6265     // Check if we can exit now, after autocommands have changed things.
6266     if (((eap->cmdidx == CMD_wq || curbufIsChanged()) && do_write(eap) == FAIL)
6267 	    || before_quit_autocmds(curwin, FALSE, eap->forceit)
6268 	    || check_more(TRUE, eap->forceit) == FAIL
6269 	    || (only_one_window() && check_changed_any(eap->forceit, FALSE)))
6270     {
6271 	not_exiting();
6272     }
6273     else
6274     {
6275 	if (only_one_window())	    // quit last window, exit Vim
6276 	    getout(0);
6277 	not_exiting();
6278 # ifdef FEAT_GUI
6279 	need_mouse_correct = TRUE;
6280 # endif
6281 	// Quit current window, may free the buffer.
6282 	win_close(curwin, !buf_hide(curwin->w_buffer));
6283     }
6284 }
6285 
6286 /*
6287  * ":print", ":list", ":number".
6288  */
6289     static void
ex_print(exarg_T * eap)6290 ex_print(exarg_T *eap)
6291 {
6292     if (curbuf->b_ml.ml_flags & ML_EMPTY)
6293 	emsg(_(e_emptybuf));
6294     else
6295     {
6296 	for ( ;!got_int; ui_breakcheck())
6297 	{
6298 	    print_line(eap->line1,
6299 		    (eap->cmdidx == CMD_number || eap->cmdidx == CMD_pound
6300 						 || (eap->flags & EXFLAG_NR)),
6301 		    eap->cmdidx == CMD_list || (eap->flags & EXFLAG_LIST));
6302 	    if (++eap->line1 > eap->line2)
6303 		break;
6304 	    out_flush();	    // show one line at a time
6305 	}
6306 	setpcmark();
6307 	// put cursor at last line
6308 	curwin->w_cursor.lnum = eap->line2;
6309 	beginline(BL_SOL | BL_FIX);
6310     }
6311 
6312     ex_no_reprint = TRUE;
6313 }
6314 
6315 #ifdef FEAT_BYTEOFF
6316     static void
ex_goto(exarg_T * eap)6317 ex_goto(exarg_T *eap)
6318 {
6319     goto_byte(eap->line2);
6320 }
6321 #endif
6322 
6323 /*
6324  * ":shell".
6325  */
6326     static void
ex_shell(exarg_T * eap UNUSED)6327 ex_shell(exarg_T *eap UNUSED)
6328 {
6329     do_shell(NULL, 0);
6330 }
6331 
6332 #if defined(HAVE_DROP_FILE) || defined(PROTO)
6333 
6334 static int drop_busy = FALSE;
6335 static int drop_filec;
6336 static char_u **drop_filev = NULL;
6337 static int drop_split;
6338 static void (*drop_callback)(void *);
6339 static void *drop_cookie;
6340 
6341     static void
handle_drop_internal(void)6342 handle_drop_internal(void)
6343 {
6344     exarg_T	ea;
6345     int		save_msg_scroll = msg_scroll;
6346 
6347     // Setting the argument list may cause screen updates and being called
6348     // recursively.  Avoid that by setting drop_busy.
6349     drop_busy = TRUE;
6350 
6351     // Check whether the current buffer is changed. If so, we will need
6352     // to split the current window or data could be lost.
6353     // We don't need to check if the 'hidden' option is set, as in this
6354     // case the buffer won't be lost.
6355     if (!buf_hide(curbuf) && !drop_split)
6356     {
6357 	++emsg_off;
6358 	drop_split = check_changed(curbuf, CCGD_AW);
6359 	--emsg_off;
6360     }
6361     if (drop_split)
6362     {
6363 	if (win_split(0, 0) == FAIL)
6364 	    return;
6365 	RESET_BINDING(curwin);
6366 
6367 	// When splitting the window, create a new alist.  Otherwise the
6368 	// existing one is overwritten.
6369 	alist_unlink(curwin->w_alist);
6370 	alist_new();
6371     }
6372 
6373     /*
6374      * Set up the new argument list.
6375      */
6376     alist_set(ALIST(curwin), drop_filec, drop_filev, FALSE, NULL, 0);
6377 
6378     /*
6379      * Move to the first file.
6380      */
6381     // Fake up a minimal "next" command for do_argfile()
6382     CLEAR_FIELD(ea);
6383     ea.cmd = (char_u *)"next";
6384     do_argfile(&ea, 0);
6385 
6386     // do_ecmd() may set need_start_insertmode, but since we never left Insert
6387     // mode that is not needed here.
6388     need_start_insertmode = FALSE;
6389 
6390     // Restore msg_scroll, otherwise a following command may cause scrolling
6391     // unexpectedly.  The screen will be redrawn by the caller, thus
6392     // msg_scroll being set by displaying a message is irrelevant.
6393     msg_scroll = save_msg_scroll;
6394 
6395     if (drop_callback != NULL)
6396 	drop_callback(drop_cookie);
6397 
6398     drop_filev = NULL;
6399     drop_busy = FALSE;
6400 }
6401 
6402 /*
6403  * Handle a file drop. The code is here because a drop is *nearly* like an
6404  * :args command, but not quite (we have a list of exact filenames, so we
6405  * don't want to (a) parse a command line, or (b) expand wildcards). So the
6406  * code is very similar to :args and hence needs access to a lot of the static
6407  * functions in this file.
6408  *
6409  * The "filev" list must have been allocated using alloc(), as should each item
6410  * in the list. This function takes over responsibility for freeing the "filev"
6411  * list.
6412  */
6413     void
handle_drop(int filec,char_u ** filev,int split,void (* callback)(void *),void * cookie)6414 handle_drop(
6415     int		filec,		// the number of files dropped
6416     char_u	**filev,	// the list of files dropped
6417     int		split,		// force splitting the window
6418     void	(*callback)(void *), // to be called after setting the argument
6419 				     // list
6420     void	*cookie)	// argument for "callback" (allocated)
6421 {
6422     // Cannot handle recursive drops, finish the pending one.
6423     if (drop_busy)
6424     {
6425 	FreeWild(filec, filev);
6426 	vim_free(cookie);
6427 	return;
6428     }
6429 
6430     // When calling handle_drop() more than once in a row we only use the last
6431     // one.
6432     if (drop_filev != NULL)
6433     {
6434 	FreeWild(drop_filec, drop_filev);
6435 	vim_free(drop_cookie);
6436     }
6437 
6438     drop_filec = filec;
6439     drop_filev = filev;
6440     drop_split = split;
6441     drop_callback = callback;
6442     drop_cookie = cookie;
6443 
6444     // Postpone this when:
6445     // - editing the command line
6446     // - not possible to change the current buffer
6447     // - updating the screen
6448     // As it may change buffers and window structures that are in use and cause
6449     // freed memory to be used.
6450     if (text_locked() || curbuf_locked() || updating_screen)
6451 	return;
6452 
6453     handle_drop_internal();
6454 }
6455 
6456 /*
6457  * To be called when text is unlocked, curbuf is unlocked or updating_screen is
6458  * reset: Handle a postponed drop.
6459  */
6460     void
handle_any_postponed_drop(void)6461 handle_any_postponed_drop(void)
6462 {
6463     if (!drop_busy && drop_filev != NULL
6464 	     && !text_locked() && !curbuf_locked() && !updating_screen)
6465 	handle_drop_internal();
6466 }
6467 #endif
6468 
6469 /*
6470  * ":preserve".
6471  */
6472     static void
ex_preserve(exarg_T * eap UNUSED)6473 ex_preserve(exarg_T *eap UNUSED)
6474 {
6475     curbuf->b_flags |= BF_PRESERVED;
6476     ml_preserve(curbuf, TRUE);
6477 }
6478 
6479 /*
6480  * ":recover".
6481  */
6482     static void
ex_recover(exarg_T * eap)6483 ex_recover(exarg_T *eap)
6484 {
6485     // Set recoverymode right away to avoid the ATTENTION prompt.
6486     recoverymode = TRUE;
6487     if (!check_changed(curbuf, (p_awa ? CCGD_AW : 0)
6488 			     | CCGD_MULTWIN
6489 			     | (eap->forceit ? CCGD_FORCEIT : 0)
6490 			     | CCGD_EXCMD)
6491 
6492 	    && (*eap->arg == NUL
6493 			     || setfname(curbuf, eap->arg, NULL, TRUE) == OK))
6494 	ml_recover(TRUE);
6495     recoverymode = FALSE;
6496 }
6497 
6498 /*
6499  * Command modifier used in a wrong way.
6500  */
6501     static void
ex_wrongmodifier(exarg_T * eap)6502 ex_wrongmodifier(exarg_T *eap)
6503 {
6504     eap->errmsg = _(e_invalid_command);
6505 }
6506 
6507 /*
6508  * :sview [+command] file	split window with new file, read-only
6509  * :split [[+command] file]	split window with current or new file
6510  * :vsplit [[+command] file]	split window vertically with current or new file
6511  * :new [[+command] file]	split window with no or new file
6512  * :vnew [[+command] file]	split vertically window with no or new file
6513  * :sfind [+command] file	split window with file in 'path'
6514  *
6515  * :tabedit			open new Tab page with empty window
6516  * :tabedit [+command] file	open new Tab page and edit "file"
6517  * :tabnew [[+command] file]	just like :tabedit
6518  * :tabfind [+command] file	open new Tab page and find "file"
6519  */
6520     void
ex_splitview(exarg_T * eap)6521 ex_splitview(exarg_T *eap)
6522 {
6523     win_T	*old_curwin = curwin;
6524 #if defined(FEAT_SEARCHPATH) || defined(FEAT_BROWSE)
6525     char_u	*fname = NULL;
6526 #endif
6527 #ifdef FEAT_BROWSE
6528     char_u	dot_path[] = ".";
6529     int		save_cmod_flags = cmdmod.cmod_flags;
6530 #endif
6531     int		use_tab = eap->cmdidx == CMD_tabedit
6532 		       || eap->cmdidx == CMD_tabfind
6533 		       || eap->cmdidx == CMD_tabnew;
6534 
6535     if (ERROR_IF_ANY_POPUP_WINDOW)
6536 	return;
6537 
6538 #ifdef FEAT_GUI
6539     need_mouse_correct = TRUE;
6540 #endif
6541 
6542 #ifdef FEAT_QUICKFIX
6543     // A ":split" in the quickfix window works like ":new".  Don't want two
6544     // quickfix windows.  But it's OK when doing ":tab split".
6545     if (bt_quickfix(curbuf) && cmdmod.cmod_tab == 0)
6546     {
6547 	if (eap->cmdidx == CMD_split)
6548 	    eap->cmdidx = CMD_new;
6549 	if (eap->cmdidx == CMD_vsplit)
6550 	    eap->cmdidx = CMD_vnew;
6551     }
6552 #endif
6553 
6554 #ifdef FEAT_SEARCHPATH
6555     if (eap->cmdidx == CMD_sfind || eap->cmdidx == CMD_tabfind)
6556     {
6557 	fname = find_file_in_path(eap->arg, (int)STRLEN(eap->arg),
6558 					  FNAME_MESS, TRUE, curbuf->b_ffname);
6559 	if (fname == NULL)
6560 	    goto theend;
6561 	eap->arg = fname;
6562     }
6563 # ifdef FEAT_BROWSE
6564     else
6565 # endif
6566 #endif
6567 #ifdef FEAT_BROWSE
6568     if ((cmdmod.cmod_flags & CMOD_BROWSE)
6569 	    && eap->cmdidx != CMD_vnew
6570 	    && eap->cmdidx != CMD_new)
6571     {
6572 	if (
6573 # ifdef FEAT_GUI
6574 	    !gui.in_use &&
6575 # endif
6576 		au_has_group((char_u *)"FileExplorer"))
6577 	{
6578 	    // No browsing supported but we do have the file explorer:
6579 	    // Edit the directory.
6580 	    if (*eap->arg == NUL || !mch_isdir(eap->arg))
6581 		eap->arg = dot_path;
6582 	}
6583 	else
6584 	{
6585 	    fname = do_browse(0, (char_u *)(use_tab
6586 			? _("Edit File in new tab page")
6587 			: _("Edit File in new window")),
6588 					  eap->arg, NULL, NULL, NULL, curbuf);
6589 	    if (fname == NULL)
6590 		goto theend;
6591 	    eap->arg = fname;
6592 	}
6593     }
6594     cmdmod.cmod_flags &= ~CMOD_BROWSE;	// Don't browse again in do_ecmd().
6595 #endif
6596 
6597     /*
6598      * Either open new tab page or split the window.
6599      */
6600     if (use_tab)
6601     {
6602 	if (win_new_tabpage(cmdmod.cmod_tab != 0 ? cmdmod.cmod_tab
6603 			 : eap->addr_count == 0 ? 0
6604 					       : (int)eap->line2 + 1) != FAIL)
6605 	{
6606 	    do_exedit(eap, old_curwin);
6607 
6608 	    // set the alternate buffer for the window we came from
6609 	    if (curwin != old_curwin
6610 		    && win_valid(old_curwin)
6611 		    && old_curwin->w_buffer != curbuf
6612 		    && (cmdmod.cmod_flags & CMOD_KEEPALT) == 0)
6613 		old_curwin->w_alt_fnum = curbuf->b_fnum;
6614 	}
6615     }
6616     else if (win_split(eap->addr_count > 0 ? (int)eap->line2 : 0,
6617 				     *eap->cmd == 'v' ? WSP_VERT : 0) != FAIL)
6618     {
6619 	// Reset 'scrollbind' when editing another file, but keep it when
6620 	// doing ":split" without arguments.
6621 	if (*eap->arg != NUL)
6622 	    RESET_BINDING(curwin);
6623 	else
6624 	    do_check_scrollbind(FALSE);
6625 	do_exedit(eap, old_curwin);
6626     }
6627 
6628 # ifdef FEAT_BROWSE
6629     cmdmod.cmod_flags = save_cmod_flags;
6630 # endif
6631 
6632 # if defined(FEAT_SEARCHPATH) || defined(FEAT_BROWSE)
6633 theend:
6634     vim_free(fname);
6635 # endif
6636 }
6637 
6638 /*
6639  * Open a new tab page.
6640  */
6641     void
tabpage_new(void)6642 tabpage_new(void)
6643 {
6644     exarg_T	ea;
6645 
6646     CLEAR_FIELD(ea);
6647     ea.cmdidx = CMD_tabnew;
6648     ea.cmd = (char_u *)"tabn";
6649     ea.arg = (char_u *)"";
6650     ex_splitview(&ea);
6651 }
6652 
6653 /*
6654  * :tabnext command
6655  */
6656     static void
ex_tabnext(exarg_T * eap)6657 ex_tabnext(exarg_T *eap)
6658 {
6659     int tab_number;
6660 
6661     if (ERROR_IF_POPUP_WINDOW)
6662 	return;
6663     switch (eap->cmdidx)
6664     {
6665 	case CMD_tabfirst:
6666 	case CMD_tabrewind:
6667 	    goto_tabpage(1);
6668 	    break;
6669 	case CMD_tablast:
6670 	    goto_tabpage(9999);
6671 	    break;
6672 	case CMD_tabprevious:
6673 	case CMD_tabNext:
6674 	    if (eap->arg && *eap->arg != NUL)
6675 	    {
6676 		char_u *p = eap->arg;
6677 		char_u *p_save = p;
6678 
6679 		tab_number = getdigits(&p);
6680 		if (p == p_save || *p_save == '-' || *p != NUL
6681 			    || tab_number == 0)
6682 		{
6683 		    // No numbers as argument.
6684 		    eap->errmsg = ex_errmsg(e_invarg2, eap->arg);
6685 		    return;
6686 		}
6687 	    }
6688 	    else
6689 	    {
6690 		if (eap->addr_count == 0)
6691 		    tab_number = 1;
6692 		else
6693 		{
6694 		    tab_number = eap->line2;
6695 		    if (tab_number < 1)
6696 		    {
6697 			eap->errmsg = _(e_invalid_range);
6698 			return;
6699 		    }
6700 		}
6701 	    }
6702 	    goto_tabpage(-tab_number);
6703 	    break;
6704 	default: // CMD_tabnext
6705 	    tab_number = get_tabpage_arg(eap);
6706 	    if (eap->errmsg == NULL)
6707 		goto_tabpage(tab_number);
6708 	    break;
6709     }
6710 }
6711 
6712 /*
6713  * :tabmove command
6714  */
6715     static void
ex_tabmove(exarg_T * eap)6716 ex_tabmove(exarg_T *eap)
6717 {
6718     int tab_number;
6719 
6720     tab_number = get_tabpage_arg(eap);
6721     if (eap->errmsg == NULL)
6722 	tabpage_move(tab_number);
6723 }
6724 
6725 /*
6726  * :tabs command: List tabs and their contents.
6727  */
6728     static void
ex_tabs(exarg_T * eap UNUSED)6729 ex_tabs(exarg_T *eap UNUSED)
6730 {
6731     tabpage_T	*tp;
6732     win_T	*wp;
6733     int		tabcount = 1;
6734 
6735     msg_start();
6736     msg_scroll = TRUE;
6737     for (tp = first_tabpage; tp != NULL && !got_int; tp = tp->tp_next)
6738     {
6739 	msg_putchar('\n');
6740 	vim_snprintf((char *)IObuff, IOSIZE, _("Tab page %d"), tabcount++);
6741 	msg_outtrans_attr(IObuff, HL_ATTR(HLF_T));
6742 	out_flush();	    // output one line at a time
6743 	ui_breakcheck();
6744 
6745 	if (tp  == curtab)
6746 	    wp = firstwin;
6747 	else
6748 	    wp = tp->tp_firstwin;
6749 	for ( ; wp != NULL && !got_int; wp = wp->w_next)
6750 	{
6751 	    msg_putchar('\n');
6752 	    msg_putchar(wp == curwin ? '>' : ' ');
6753 	    msg_putchar(' ');
6754 	    msg_putchar(bufIsChanged(wp->w_buffer) ? '+' : ' ');
6755 	    msg_putchar(' ');
6756 	    if (buf_spname(wp->w_buffer) != NULL)
6757 		vim_strncpy(IObuff, buf_spname(wp->w_buffer), IOSIZE - 1);
6758 	    else
6759 		home_replace(wp->w_buffer, wp->w_buffer->b_fname,
6760 							IObuff, IOSIZE, TRUE);
6761 	    msg_outtrans(IObuff);
6762 	    out_flush();	    // output one line at a time
6763 	    ui_breakcheck();
6764 	}
6765     }
6766 }
6767 
6768 /*
6769  * ":mode": Set screen mode.
6770  * If no argument given, just get the screen size and redraw.
6771  */
6772     static void
ex_mode(exarg_T * eap)6773 ex_mode(exarg_T *eap)
6774 {
6775     if (*eap->arg == NUL)
6776 	shell_resized();
6777     else
6778 	emsg(_(e_screenmode));
6779 }
6780 
6781 /*
6782  * ":resize".
6783  * set, increment or decrement current window height
6784  */
6785     static void
ex_resize(exarg_T * eap)6786 ex_resize(exarg_T *eap)
6787 {
6788     int		n;
6789     win_T	*wp = curwin;
6790 
6791     if (eap->addr_count > 0)
6792     {
6793 	n = eap->line2;
6794 	for (wp = firstwin; wp->w_next != NULL && --n > 0; wp = wp->w_next)
6795 	    ;
6796     }
6797 
6798 # ifdef FEAT_GUI
6799     need_mouse_correct = TRUE;
6800 # endif
6801     n = atol((char *)eap->arg);
6802     if (cmdmod.cmod_split & WSP_VERT)
6803     {
6804 	if (*eap->arg == '-' || *eap->arg == '+')
6805 	    n += wp->w_width;
6806 	else if (n == 0 && eap->arg[0] == NUL)	// default is very wide
6807 	    n = 9999;
6808 	win_setwidth_win((int)n, wp);
6809     }
6810     else
6811     {
6812 	if (*eap->arg == '-' || *eap->arg == '+')
6813 	    n += wp->w_height;
6814 	else if (n == 0 && eap->arg[0] == NUL)	// default is very high
6815 	    n = 9999;
6816 	win_setheight_win((int)n, wp);
6817     }
6818 }
6819 
6820 /*
6821  * ":find [+command] <file>" command.
6822  */
6823     static void
ex_find(exarg_T * eap)6824 ex_find(exarg_T *eap)
6825 {
6826 #ifdef FEAT_SEARCHPATH
6827     char_u	*fname;
6828     int		count;
6829 
6830     fname = find_file_in_path(eap->arg, (int)STRLEN(eap->arg), FNAME_MESS,
6831 						      TRUE, curbuf->b_ffname);
6832     if (eap->addr_count > 0)
6833     {
6834 	// Repeat finding the file "count" times.  This matters when it
6835 	// appears several times in the path.
6836 	count = eap->line2;
6837 	while (fname != NULL && --count > 0)
6838 	{
6839 	    vim_free(fname);
6840 	    fname = find_file_in_path(NULL, 0, FNAME_MESS,
6841 						     FALSE, curbuf->b_ffname);
6842 	}
6843     }
6844 
6845     if (fname != NULL)
6846     {
6847 	eap->arg = fname;
6848 #endif
6849 	do_exedit(eap, NULL);
6850 #ifdef FEAT_SEARCHPATH
6851 	vim_free(fname);
6852     }
6853 #endif
6854 }
6855 
6856 /*
6857  * ":open" simulation: for now just work like ":visual".
6858  */
6859     static void
ex_open(exarg_T * eap)6860 ex_open(exarg_T *eap)
6861 {
6862     regmatch_T	regmatch;
6863     char_u	*p;
6864 
6865 #ifdef FEAT_EVAL
6866     if (not_in_vim9(eap) == FAIL)
6867 	return;
6868 #endif
6869     curwin->w_cursor.lnum = eap->line2;
6870     beginline(BL_SOL | BL_FIX);
6871     if (*eap->arg == '/')
6872     {
6873 	// ":open /pattern/": put cursor in column found with pattern
6874 	++eap->arg;
6875 	p = skip_regexp(eap->arg, '/', magic_isset());
6876 	*p = NUL;
6877 	regmatch.regprog = vim_regcomp(eap->arg, magic_isset() ? RE_MAGIC : 0);
6878 	if (regmatch.regprog != NULL)
6879 	{
6880 	    // make a copy of the line, when searching for a mark it might be
6881 	    // flushed
6882 	    char_u *line = vim_strsave(ml_get_curline());
6883 
6884 	    regmatch.rm_ic = p_ic;
6885 	    if (vim_regexec(&regmatch, line, (colnr_T)0))
6886 		curwin->w_cursor.col = (colnr_T)(regmatch.startp[0] - line);
6887 	    else
6888 		emsg(_(e_nomatch));
6889 	    vim_regfree(regmatch.regprog);
6890 	    vim_free(line);
6891 	}
6892 	// Move to the NUL, ignore any other arguments.
6893 	eap->arg += STRLEN(eap->arg);
6894     }
6895     check_cursor();
6896 
6897     eap->cmdidx = CMD_visual;
6898     do_exedit(eap, NULL);
6899 }
6900 
6901 /*
6902  * ":edit", ":badd", ":balt", ":visual".
6903  */
6904     static void
ex_edit(exarg_T * eap)6905 ex_edit(exarg_T *eap)
6906 {
6907     do_exedit(eap, NULL);
6908 }
6909 
6910 /*
6911  * ":edit <file>" command and alike.
6912  */
6913     void
do_exedit(exarg_T * eap,win_T * old_curwin)6914 do_exedit(
6915     exarg_T	*eap,
6916     win_T	*old_curwin)	    // curwin before doing a split or NULL
6917 {
6918     int		n;
6919     int		need_hide;
6920     int		exmode_was = exmode_active;
6921 
6922     if ((eap->cmdidx != CMD_pedit && ERROR_IF_POPUP_WINDOW)
6923 						 || ERROR_IF_TERM_POPUP_WINDOW)
6924 	return;
6925     /*
6926      * ":vi" command ends Ex mode.
6927      */
6928     if (exmode_active && (eap->cmdidx == CMD_visual
6929 						|| eap->cmdidx == CMD_view))
6930     {
6931 	exmode_active = FALSE;
6932 	ex_pressedreturn = FALSE;
6933 	if (*eap->arg == NUL)
6934 	{
6935 	    // Special case:  ":global/pat/visual\NLvi-commands"
6936 	    if (global_busy)
6937 	    {
6938 		int	rd = RedrawingDisabled;
6939 		int	nwr = no_wait_return;
6940 		int	ms = msg_scroll;
6941 #ifdef FEAT_GUI
6942 		int	he = hold_gui_events;
6943 #endif
6944 
6945 		if (eap->nextcmd != NULL)
6946 		{
6947 		    stuffReadbuff(eap->nextcmd);
6948 		    eap->nextcmd = NULL;
6949 		}
6950 
6951 		if (exmode_was != EXMODE_VIM)
6952 		    settmode(TMODE_RAW);
6953 		RedrawingDisabled = 0;
6954 		no_wait_return = 0;
6955 		need_wait_return = FALSE;
6956 		msg_scroll = 0;
6957 #ifdef FEAT_GUI
6958 		hold_gui_events = 0;
6959 #endif
6960 		must_redraw = CLEAR;
6961 		pending_exmode_active = TRUE;
6962 
6963 		main_loop(FALSE, TRUE);
6964 
6965 		pending_exmode_active = FALSE;
6966 		RedrawingDisabled = rd;
6967 		no_wait_return = nwr;
6968 		msg_scroll = ms;
6969 #ifdef FEAT_GUI
6970 		hold_gui_events = he;
6971 #endif
6972 	    }
6973 	    return;
6974 	}
6975     }
6976 
6977     if ((eap->cmdidx == CMD_new
6978 		|| eap->cmdidx == CMD_tabnew
6979 		|| eap->cmdidx == CMD_tabedit
6980 		|| eap->cmdidx == CMD_vnew) && *eap->arg == NUL)
6981     {
6982 	// ":new" or ":tabnew" without argument: edit an new empty buffer
6983 	setpcmark();
6984 	(void)do_ecmd(0, NULL, NULL, eap, ECMD_ONE,
6985 		      ECMD_HIDE + (eap->forceit ? ECMD_FORCEIT : 0),
6986 		      old_curwin == NULL ? curwin : NULL);
6987     }
6988     else if ((eap->cmdidx != CMD_split && eap->cmdidx != CMD_vsplit)
6989 	    || *eap->arg != NUL
6990 #ifdef FEAT_BROWSE
6991 	    || (cmdmod.cmod_flags & CMOD_BROWSE)
6992 #endif
6993 	    )
6994     {
6995 	// Can't edit another file when "curbuf_lock" is set.  Only ":edit"
6996 	// can bring us here, others are stopped earlier.
6997 	if (*eap->arg != NUL && curbuf_locked())
6998 	    return;
6999 
7000 	n = readonlymode;
7001 	if (eap->cmdidx == CMD_view || eap->cmdidx == CMD_sview)
7002 	    readonlymode = TRUE;
7003 	else if (eap->cmdidx == CMD_enew)
7004 	    readonlymode = FALSE;   // 'readonly' doesn't make sense in an
7005 				    // empty buffer
7006 	if (eap->cmdidx != CMD_balt && eap->cmdidx != CMD_badd)
7007 	    setpcmark();
7008 	if (do_ecmd(0, (eap->cmdidx == CMD_enew ? NULL : eap->arg),
7009 		    NULL, eap,
7010 		    // ":edit" goes to first line if Vi compatible
7011 		    (*eap->arg == NUL && eap->do_ecmd_lnum == 0
7012 				      && vim_strchr(p_cpo, CPO_GOTO1) != NULL)
7013 					       ? ECMD_ONE : eap->do_ecmd_lnum,
7014 		    (buf_hide(curbuf) ? ECMD_HIDE : 0)
7015 		    + (eap->forceit ? ECMD_FORCEIT : 0)
7016 		      // after a split we can use an existing buffer
7017 		    + (old_curwin != NULL ? ECMD_OLDBUF : 0)
7018 		    + (eap->cmdidx == CMD_badd ? ECMD_ADDBUF : 0)
7019 		    + (eap->cmdidx == CMD_balt ? ECMD_ALTBUF : 0)
7020 		    , old_curwin == NULL ? curwin : NULL) == FAIL)
7021 	{
7022 	    // Editing the file failed.  If the window was split, close it.
7023 	    if (old_curwin != NULL)
7024 	    {
7025 		need_hide = (curbufIsChanged() && curbuf->b_nwindows <= 1);
7026 		if (!need_hide || buf_hide(curbuf))
7027 		{
7028 #if defined(FEAT_EVAL)
7029 		    cleanup_T   cs;
7030 
7031 		    // Reset the error/interrupt/exception state here so that
7032 		    // aborting() returns FALSE when closing a window.
7033 		    enter_cleanup(&cs);
7034 #endif
7035 #ifdef FEAT_GUI
7036 		    need_mouse_correct = TRUE;
7037 #endif
7038 		    win_close(curwin, !need_hide && !buf_hide(curbuf));
7039 
7040 #if defined(FEAT_EVAL)
7041 		    // Restore the error/interrupt/exception state if not
7042 		    // discarded by a new aborting error, interrupt, or
7043 		    // uncaught exception.
7044 		    leave_cleanup(&cs);
7045 #endif
7046 		}
7047 	    }
7048 	}
7049 	else if (readonlymode && curbuf->b_nwindows == 1)
7050 	{
7051 	    // When editing an already visited buffer, 'readonly' won't be set
7052 	    // but the previous value is kept.  With ":view" and ":sview" we
7053 	    // want the  file to be readonly, except when another window is
7054 	    // editing the same buffer.
7055 	    curbuf->b_p_ro = TRUE;
7056 	}
7057 	readonlymode = n;
7058     }
7059     else
7060     {
7061 	if (eap->do_ecmd_cmd != NULL)
7062 	    do_cmd_argument(eap->do_ecmd_cmd);
7063 	n = curwin->w_arg_idx_invalid;
7064 	check_arg_idx(curwin);
7065 	if (n != curwin->w_arg_idx_invalid)
7066 	    maketitle();
7067     }
7068 
7069     /*
7070      * if ":split file" worked, set alternate file name in old window to new
7071      * file
7072      */
7073     if (old_curwin != NULL
7074 	    && *eap->arg != NUL
7075 	    && curwin != old_curwin
7076 	    && win_valid(old_curwin)
7077 	    && old_curwin->w_buffer != curbuf
7078 	    && (cmdmod.cmod_flags & CMOD_KEEPALT) == 0)
7079 	old_curwin->w_alt_fnum = curbuf->b_fnum;
7080 
7081     ex_no_reprint = TRUE;
7082 }
7083 
7084 #ifndef FEAT_GUI
7085 /*
7086  * ":gui" and ":gvim" when there is no GUI.
7087  */
7088     static void
ex_nogui(exarg_T * eap)7089 ex_nogui(exarg_T *eap)
7090 {
7091     eap->errmsg = _(e_gui_cannot_be_used_not_enabled_at_compile_time);
7092 }
7093 #endif
7094 
7095 #if defined(FEAT_GUI_MSWIN) && defined(FEAT_MENU) && defined(FEAT_TEAROFF)
7096     static void
ex_tearoff(exarg_T * eap)7097 ex_tearoff(exarg_T *eap)
7098 {
7099     gui_make_tearoff(eap->arg);
7100 }
7101 #endif
7102 
7103 #if (defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_GTK) \
7104 	|| defined(FEAT_TERM_POPUP_MENU)) && defined(FEAT_MENU)
7105     static void
ex_popup(exarg_T * eap)7106 ex_popup(exarg_T *eap)
7107 {
7108 # if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_GTK)
7109     if (gui.in_use)
7110 	gui_make_popup(eap->arg, eap->forceit);
7111 #  ifdef FEAT_TERM_POPUP_MENU
7112     else
7113 #  endif
7114 # endif
7115 # ifdef FEAT_TERM_POPUP_MENU
7116 	pum_make_popup(eap->arg, eap->forceit);
7117 # endif
7118 }
7119 #endif
7120 
7121     static void
ex_swapname(exarg_T * eap UNUSED)7122 ex_swapname(exarg_T *eap UNUSED)
7123 {
7124     if (curbuf->b_ml.ml_mfp == NULL || curbuf->b_ml.ml_mfp->mf_fname == NULL)
7125 	msg(_("No swap file"));
7126     else
7127 	msg((char *)curbuf->b_ml.ml_mfp->mf_fname);
7128 }
7129 
7130 /*
7131  * ":syncbind" forces all 'scrollbind' windows to have the same relative
7132  * offset.
7133  * (1998-11-02 16:21:01  R. Edward Ralston <eralston@computer.org>)
7134  */
7135     static void
ex_syncbind(exarg_T * eap UNUSED)7136 ex_syncbind(exarg_T *eap UNUSED)
7137 {
7138     win_T	*wp;
7139     win_T	*save_curwin = curwin;
7140     buf_T	*save_curbuf = curbuf;
7141     long	topline;
7142     long	y;
7143     linenr_T	old_linenr = curwin->w_cursor.lnum;
7144 
7145     setpcmark();
7146 
7147     /*
7148      * determine max topline
7149      */
7150     if (curwin->w_p_scb)
7151     {
7152 	topline = curwin->w_topline;
7153 	FOR_ALL_WINDOWS(wp)
7154 	{
7155 	    if (wp->w_p_scb && wp->w_buffer)
7156 	    {
7157 		y = wp->w_buffer->b_ml.ml_line_count - get_scrolloff_value();
7158 		if (topline > y)
7159 		    topline = y;
7160 	    }
7161 	}
7162 	if (topline < 1)
7163 	    topline = 1;
7164     }
7165     else
7166     {
7167 	topline = 1;
7168     }
7169 
7170 
7171     /*
7172      * Set all scrollbind windows to the same topline.
7173      */
7174     FOR_ALL_WINDOWS(curwin)
7175     {
7176 	if (curwin->w_p_scb)
7177 	{
7178 	    curbuf = curwin->w_buffer;
7179 	    y = topline - curwin->w_topline;
7180 	    if (y > 0)
7181 		scrollup(y, TRUE);
7182 	    else
7183 		scrolldown(-y, TRUE);
7184 	    curwin->w_scbind_pos = topline;
7185 	    redraw_later(VALID);
7186 	    cursor_correct();
7187 	    curwin->w_redr_status = TRUE;
7188 	}
7189     }
7190     curwin = save_curwin;
7191     curbuf = save_curbuf;
7192     if (curwin->w_p_scb)
7193     {
7194 	did_syncbind = TRUE;
7195 	checkpcmark();
7196 	if (old_linenr != curwin->w_cursor.lnum)
7197 	{
7198 	    char_u ctrl_o[2];
7199 
7200 	    ctrl_o[0] = Ctrl_O;
7201 	    ctrl_o[1] = 0;
7202 	    ins_typebuf(ctrl_o, REMAP_NONE, 0, TRUE, FALSE);
7203 	}
7204     }
7205 }
7206 
7207 
7208     static void
ex_read(exarg_T * eap)7209 ex_read(exarg_T *eap)
7210 {
7211     int		i;
7212     int		empty = (curbuf->b_ml.ml_flags & ML_EMPTY);
7213     linenr_T	lnum;
7214 
7215     if (eap->usefilter)			// :r!cmd
7216 	do_bang(1, eap, FALSE, FALSE, TRUE);
7217     else
7218     {
7219 	if (u_save(eap->line2, (linenr_T)(eap->line2 + 1)) == FAIL)
7220 	    return;
7221 
7222 #ifdef FEAT_BROWSE
7223 	if (cmdmod.cmod_flags & CMOD_BROWSE)
7224 	{
7225 	    char_u *browseFile;
7226 
7227 	    browseFile = do_browse(0, (char_u *)_("Append File"), eap->arg,
7228 						    NULL, NULL, NULL, curbuf);
7229 	    if (browseFile != NULL)
7230 	    {
7231 		i = readfile(browseFile, NULL,
7232 			  eap->line2, (linenr_T)0, (linenr_T)MAXLNUM, eap, 0);
7233 		vim_free(browseFile);
7234 	    }
7235 	    else
7236 		i = OK;
7237 	}
7238 	else
7239 #endif
7240 	     if (*eap->arg == NUL)
7241 	{
7242 	    if (check_fname() == FAIL)	// check for no file name
7243 		return;
7244 	    i = readfile(curbuf->b_ffname, curbuf->b_fname,
7245 			  eap->line2, (linenr_T)0, (linenr_T)MAXLNUM, eap, 0);
7246 	}
7247 	else
7248 	{
7249 	    if (vim_strchr(p_cpo, CPO_ALTREAD) != NULL)
7250 		(void)setaltfname(eap->arg, eap->arg, (linenr_T)1);
7251 	    i = readfile(eap->arg, NULL,
7252 			  eap->line2, (linenr_T)0, (linenr_T)MAXLNUM, eap, 0);
7253 
7254 	}
7255 	if (i != OK)
7256 	{
7257 #if defined(FEAT_EVAL)
7258 	    if (!aborting())
7259 #endif
7260 		semsg(_(e_notopen), eap->arg);
7261 	}
7262 	else
7263 	{
7264 	    if (empty && exmode_active)
7265 	    {
7266 		// Delete the empty line that remains.  Historically ex does
7267 		// this but vi doesn't.
7268 		if (eap->line2 == 0)
7269 		    lnum = curbuf->b_ml.ml_line_count;
7270 		else
7271 		    lnum = 1;
7272 		if (*ml_get(lnum) == NUL && u_savedel(lnum, 1L) == OK)
7273 		{
7274 		    ml_delete(lnum);
7275 		    if (curwin->w_cursor.lnum > 1
7276 					     && curwin->w_cursor.lnum >= lnum)
7277 			--curwin->w_cursor.lnum;
7278 		    deleted_lines_mark(lnum, 1L);
7279 		}
7280 	    }
7281 	    redraw_curbuf_later(VALID);
7282 	}
7283     }
7284 }
7285 
7286 static char_u	*prev_dir = NULL;
7287 
7288 #if defined(EXITFREE) || defined(PROTO)
7289     void
free_cd_dir(void)7290 free_cd_dir(void)
7291 {
7292     VIM_CLEAR(prev_dir);
7293     VIM_CLEAR(globaldir);
7294 }
7295 #endif
7296 
7297 /*
7298  * Get the previous directory for the given chdir scope.
7299  */
7300     static char_u *
get_prevdir(cdscope_T scope)7301 get_prevdir(cdscope_T scope)
7302 {
7303     if (scope == CDSCOPE_WINDOW)
7304 	return curwin->w_prevdir;
7305     else if (scope == CDSCOPE_TABPAGE)
7306 	return curtab->tp_prevdir;
7307     return prev_dir;
7308 }
7309 
7310 /*
7311  * Deal with the side effects of changing the current directory.
7312  * When 'scope' is CDSCOPE_TABPAGE then this was after an ":tcd" command.
7313  * When 'scope' is CDSCOPE_WINDOW then this was after an ":lcd" command.
7314  */
7315     void
post_chdir(cdscope_T scope)7316 post_chdir(cdscope_T scope)
7317 {
7318     if (scope != CDSCOPE_WINDOW)
7319 	// Clear tab local directory for both :cd and :tcd
7320 	VIM_CLEAR(curtab->tp_localdir);
7321     VIM_CLEAR(curwin->w_localdir);
7322     if (scope != CDSCOPE_GLOBAL)
7323     {
7324 	char_u	*pdir = get_prevdir(scope);
7325 
7326 	// If still in the global directory, need to remember current
7327 	// directory as the global directory.
7328 	if (globaldir == NULL && pdir != NULL)
7329 	    globaldir = vim_strsave(pdir);
7330 
7331 	// Remember this local directory for the window.
7332 	if (mch_dirname(NameBuff, MAXPATHL) == OK)
7333 	{
7334 	    if (scope == CDSCOPE_TABPAGE)
7335 		curtab->tp_localdir = vim_strsave(NameBuff);
7336 	    else
7337 		curwin->w_localdir = vim_strsave(NameBuff);
7338 	}
7339     }
7340     else
7341     {
7342 	// We are now in the global directory, no need to remember its name.
7343 	VIM_CLEAR(globaldir);
7344     }
7345 
7346     last_chdir_reason = NULL;
7347     shorten_fnames(TRUE);
7348 }
7349 
7350 /*
7351  * Change directory function used by :cd/:tcd/:lcd Ex commands and the
7352  * chdir() function.
7353  * scope == CDSCOPE_WINDOW: changes the window-local directory
7354  * scope == CDSCOPE_TABPAGE: changes the tab-local directory
7355  * Otherwise: changes the global directory
7356  * Returns TRUE if the directory is successfully changed.
7357  */
7358     int
changedir_func(char_u * new_dir,int forceit,cdscope_T scope)7359 changedir_func(
7360 	char_u		*new_dir,
7361 	int		forceit,
7362 	cdscope_T	scope)
7363 {
7364     char_u	*tofree;
7365     char_u	*pdir = NULL;
7366     int		dir_differs;
7367     int		retval = FALSE;
7368 
7369     if (new_dir == NULL || allbuf_locked())
7370 	return FALSE;
7371 
7372     if (vim_strchr(p_cpo, CPO_CHDIR) != NULL && curbufIsChanged() && !forceit)
7373     {
7374 	emsg(_("E747: Cannot change directory, buffer is modified (add ! to override)"));
7375 	return FALSE;
7376     }
7377 
7378     // ":cd -": Change to previous directory
7379     if (STRCMP(new_dir, "-") == 0)
7380     {
7381 	pdir = get_prevdir(scope);
7382 	if (pdir == NULL)
7383 	{
7384 	    emsg(_("E186: No previous directory"));
7385 	    return FALSE;
7386 	}
7387 	new_dir = pdir;
7388     }
7389 
7390     // Free the previous directory
7391     tofree = get_prevdir(scope);
7392 
7393     // Save current directory for next ":cd -"
7394     if (mch_dirname(NameBuff, MAXPATHL) == OK)
7395 	pdir = vim_strsave(NameBuff);
7396     else
7397 	pdir = NULL;
7398     if (scope == CDSCOPE_WINDOW)
7399 	curwin->w_prevdir = pdir;
7400     else if (scope == CDSCOPE_TABPAGE)
7401 	curtab->tp_prevdir = pdir;
7402     else
7403 	prev_dir = pdir;
7404 
7405 #if defined(UNIX) || defined(VMS)
7406     // for UNIX ":cd" means: go to home directory
7407     if (*new_dir == NUL)
7408     {
7409 	// use NameBuff for home directory name
7410 # ifdef VMS
7411 	char_u	*p;
7412 
7413 	p = mch_getenv((char_u *)"SYS$LOGIN");
7414 	if (p == NULL || *p == NUL)	// empty is the same as not set
7415 	    NameBuff[0] = NUL;
7416 	else
7417 	    vim_strncpy(NameBuff, p, MAXPATHL - 1);
7418 # else
7419 	expand_env((char_u *)"$HOME", NameBuff, MAXPATHL);
7420 # endif
7421 	new_dir = NameBuff;
7422     }
7423 #endif
7424     dir_differs = new_dir == NULL || pdir == NULL
7425 	|| pathcmp((char *)pdir, (char *)new_dir, -1) != 0;
7426     if (new_dir == NULL || (dir_differs && vim_chdir(new_dir)))
7427 	emsg(_(e_failed));
7428     else
7429     {
7430 	char_u  *acmd_fname;
7431 
7432 	post_chdir(scope);
7433 
7434 	if (dir_differs)
7435 	{
7436 	    if (scope == CDSCOPE_WINDOW)
7437 		acmd_fname = (char_u *)"window";
7438 	    else if (scope == CDSCOPE_TABPAGE)
7439 		acmd_fname = (char_u *)"tabpage";
7440 	    else
7441 		acmd_fname = (char_u *)"global";
7442 	    apply_autocmds(EVENT_DIRCHANGED, acmd_fname, new_dir, FALSE,
7443 								curbuf);
7444 	}
7445 	retval = TRUE;
7446     }
7447     vim_free(tofree);
7448 
7449     return retval;
7450 }
7451 
7452 /*
7453  * ":cd", ":tcd", ":lcd", ":chdir" ":tchdir" and ":lchdir".
7454  */
7455     void
ex_cd(exarg_T * eap)7456 ex_cd(exarg_T *eap)
7457 {
7458     char_u	*new_dir;
7459 
7460     new_dir = eap->arg;
7461 #if !defined(UNIX) && !defined(VMS)
7462     // for non-UNIX ":cd" means: print current directory
7463     if (*new_dir == NUL)
7464 	ex_pwd(NULL);
7465     else
7466 #endif
7467     {
7468 	cdscope_T	scope = CDSCOPE_GLOBAL;
7469 
7470 	if (eap->cmdidx == CMD_lcd || eap->cmdidx == CMD_lchdir)
7471 	    scope = CDSCOPE_WINDOW;
7472 	else if (eap->cmdidx == CMD_tcd || eap->cmdidx == CMD_tchdir)
7473 	    scope = CDSCOPE_TABPAGE;
7474 
7475 	if (changedir_func(new_dir, eap->forceit, scope))
7476 	{
7477 	    // Echo the new current directory if the command was typed.
7478 	    if (KeyTyped || p_verbose >= 5)
7479 		ex_pwd(eap);
7480 	}
7481     }
7482 }
7483 
7484 /*
7485  * ":pwd".
7486  */
7487     static void
ex_pwd(exarg_T * eap UNUSED)7488 ex_pwd(exarg_T *eap UNUSED)
7489 {
7490     if (mch_dirname(NameBuff, MAXPATHL) == OK)
7491     {
7492 #ifdef BACKSLASH_IN_FILENAME
7493 	slash_adjust(NameBuff);
7494 #endif
7495 	if (p_verbose > 0)
7496 	{
7497 	    char *context = "global";
7498 
7499 	    if (last_chdir_reason != NULL)
7500 		context = last_chdir_reason;
7501 	    else if (curwin->w_localdir != NULL)
7502 		context = "window";
7503 	    else if (curtab->tp_localdir != NULL)
7504 		context = "tabpage";
7505 	    smsg("[%s] %s", context, (char *)NameBuff);
7506 	}
7507 	else
7508 	    msg((char *)NameBuff);
7509     }
7510     else
7511 	emsg(_("E187: Unknown"));
7512 }
7513 
7514 /*
7515  * ":=".
7516  */
7517     static void
ex_equal(exarg_T * eap)7518 ex_equal(exarg_T *eap)
7519 {
7520     smsg("%ld", (long)eap->line2);
7521     ex_may_print(eap);
7522 }
7523 
7524     static void
ex_sleep(exarg_T * eap)7525 ex_sleep(exarg_T *eap)
7526 {
7527     int		n;
7528     long	len;
7529 
7530     if (cursor_valid())
7531     {
7532 	n = W_WINROW(curwin) + curwin->w_wrow - msg_scrolled;
7533 	if (n >= 0)
7534 	    windgoto((int)n, curwin->w_wincol + curwin->w_wcol);
7535     }
7536 
7537     len = eap->line2;
7538     switch (*eap->arg)
7539     {
7540 	case 'm': break;
7541 	case NUL: len *= 1000L; break;
7542 	default: semsg(_(e_invarg2), eap->arg); return;
7543     }
7544 
7545     // Hide the cursor if invoked with !
7546     do_sleep(len, eap->forceit);
7547 }
7548 
7549 /*
7550  * Sleep for "msec" milliseconds, but keep checking for a CTRL-C every second.
7551  * Hide the cursor if "hide_cursor" is TRUE.
7552  */
7553     void
do_sleep(long msec,int hide_cursor)7554 do_sleep(long msec, int hide_cursor)
7555 {
7556     long	done = 0;
7557     long	wait_now;
7558 # ifdef ELAPSED_FUNC
7559     elapsed_T	start_tv;
7560 
7561     // Remember at what time we started, so that we know how much longer we
7562     // should wait after waiting for a bit.
7563     ELAPSED_INIT(start_tv);
7564 # endif
7565 
7566     if (hide_cursor)
7567         cursor_sleep();
7568     else
7569         cursor_on();
7570 
7571     out_flush_cursor(FALSE, FALSE);
7572     while (!got_int && done < msec)
7573     {
7574 	wait_now = msec - done > 1000L ? 1000L : msec - done;
7575 #ifdef FEAT_TIMERS
7576 	{
7577 	    long    due_time = check_due_timer();
7578 
7579 	    if (due_time > 0 && due_time < wait_now)
7580 		wait_now = due_time;
7581 	}
7582 #endif
7583 #ifdef FEAT_JOB_CHANNEL
7584 	if (has_any_channel() && wait_now > 20L)
7585 	    wait_now = 20L;
7586 #endif
7587 #ifdef FEAT_SOUND
7588 	if (has_any_sound_callback() && wait_now > 20L)
7589 	    wait_now = 20L;
7590 #endif
7591 	ui_delay(wait_now, TRUE);
7592 
7593 #ifdef FEAT_JOB_CHANNEL
7594 	if (has_any_channel())
7595 	    ui_breakcheck_force(TRUE);
7596 	else
7597 #endif
7598 	    ui_breakcheck();
7599 #ifdef MESSAGE_QUEUE
7600 	// Process the netbeans and clientserver messages that may have been
7601 	// received in the call to ui_breakcheck() when the GUI is in use. This
7602 	// may occur when running a test case.
7603 	parse_queued_messages();
7604 #endif
7605 
7606 # ifdef ELAPSED_FUNC
7607 	// actual time passed
7608 	done = ELAPSED_FUNC(start_tv);
7609 # else
7610 	// guestimate time passed (will actually be more)
7611 	done += wait_now;
7612 # endif
7613     }
7614 
7615     // If CTRL-C was typed to interrupt the sleep, drop the CTRL-C from the
7616     // input buffer, otherwise a following call to input() fails.
7617     if (got_int)
7618 	(void)vpeekc();
7619 
7620     if (hide_cursor)
7621         cursor_unsleep();
7622 }
7623 
7624 /*
7625  * ":winsize" command (obsolete).
7626  */
7627     static void
ex_winsize(exarg_T * eap)7628 ex_winsize(exarg_T *eap)
7629 {
7630     int		w, h;
7631     char_u	*arg = eap->arg;
7632     char_u	*p;
7633 
7634     if (!isdigit(*arg))
7635     {
7636 	semsg(_(e_invarg2), arg);
7637 	return;
7638     }
7639     w = getdigits(&arg);
7640     arg = skipwhite(arg);
7641     p = arg;
7642     h = getdigits(&arg);
7643     if (*p != NUL && *arg == NUL)
7644 	set_shellsize(w, h, TRUE);
7645     else
7646 	emsg(_("E465: :winsize requires two number arguments"));
7647 }
7648 
7649     static void
ex_wincmd(exarg_T * eap)7650 ex_wincmd(exarg_T *eap)
7651 {
7652     int		xchar = NUL;
7653     char_u	*p;
7654 
7655     if (*eap->arg == 'g' || *eap->arg == Ctrl_G)
7656     {
7657 	// CTRL-W g and CTRL-W CTRL-G  have an extra command character
7658 	if (eap->arg[1] == NUL)
7659 	{
7660 	    emsg(_(e_invarg));
7661 	    return;
7662 	}
7663 	xchar = eap->arg[1];
7664 	p = eap->arg + 2;
7665     }
7666     else
7667 	p = eap->arg + 1;
7668 
7669     set_nextcmd(eap, p);
7670     p = skipwhite(p);
7671     if (*p != NUL && *p != (
7672 #ifdef FEAT_EVAL
7673 	    in_vim9script() ? '#' :
7674 #endif
7675 		'"')
7676 	    && eap->nextcmd == NULL)
7677 	emsg(_(e_invarg));
7678     else if (!eap->skip)
7679     {
7680 	// Pass flags on for ":vertical wincmd ]".
7681 	postponed_split_flags = cmdmod.cmod_split;
7682 	postponed_split_tab = cmdmod.cmod_tab;
7683 	do_window(*eap->arg, eap->addr_count > 0 ? eap->line2 : 0L, xchar);
7684 	postponed_split_flags = 0;
7685 	postponed_split_tab = 0;
7686     }
7687 }
7688 
7689 #if defined(FEAT_GUI) || defined(UNIX) || defined(VMS) || defined(MSWIN)
7690 /*
7691  * ":winpos".
7692  */
7693     static void
ex_winpos(exarg_T * eap)7694 ex_winpos(exarg_T *eap)
7695 {
7696     int		x, y;
7697     char_u	*arg = eap->arg;
7698     char_u	*p;
7699 
7700     if (*arg == NUL)
7701     {
7702 # if defined(FEAT_GUI) || defined(MSWIN)
7703 #  ifdef VIMDLL
7704 	if (gui.in_use ? gui_mch_get_winpos(&x, &y) != FAIL :
7705 		mch_get_winpos(&x, &y) != FAIL)
7706 #  elif defined(FEAT_GUI)
7707 	if (gui.in_use && gui_mch_get_winpos(&x, &y) != FAIL)
7708 #  else
7709 	if (mch_get_winpos(&x, &y) != FAIL)
7710 #  endif
7711 	{
7712 	    sprintf((char *)IObuff, _("Window position: X %d, Y %d"), x, y);
7713 	    msg((char *)IObuff);
7714 	}
7715 	else
7716 # endif
7717 	    emsg(_("E188: Obtaining window position not implemented for this platform"));
7718     }
7719     else
7720     {
7721 	x = getdigits(&arg);
7722 	arg = skipwhite(arg);
7723 	p = arg;
7724 	y = getdigits(&arg);
7725 	if (*p == NUL || *arg != NUL)
7726 	{
7727 	    emsg(_("E466: :winpos requires two number arguments"));
7728 	    return;
7729 	}
7730 # ifdef FEAT_GUI
7731 	if (gui.in_use)
7732 	    gui_mch_set_winpos(x, y);
7733 	else if (gui.starting)
7734 	{
7735 	    // Remember the coordinates for when the window is opened.
7736 	    gui_win_x = x;
7737 	    gui_win_y = y;
7738 	}
7739 #  if defined(HAVE_TGETENT) || defined(VIMDLL)
7740 	else
7741 #  endif
7742 # endif
7743 # if defined(MSWIN) && (!defined(FEAT_GUI) || defined(VIMDLL))
7744 	    mch_set_winpos(x, y);
7745 # endif
7746 # ifdef HAVE_TGETENT
7747 	if (*T_CWP)
7748 	    term_set_winpos(x, y);
7749 # endif
7750     }
7751 }
7752 #endif
7753 
7754 /*
7755  * Handle command that work like operators: ":delete", ":yank", ":>" and ":<".
7756  */
7757     static void
ex_operators(exarg_T * eap)7758 ex_operators(exarg_T *eap)
7759 {
7760     oparg_T	oa;
7761 
7762     clear_oparg(&oa);
7763     oa.regname = eap->regname;
7764     oa.start.lnum = eap->line1;
7765     oa.end.lnum = eap->line2;
7766     oa.line_count = eap->line2 - eap->line1 + 1;
7767     oa.motion_type = MLINE;
7768     virtual_op = FALSE;
7769     if (eap->cmdidx != CMD_yank)	// position cursor for undo
7770     {
7771 	setpcmark();
7772 	curwin->w_cursor.lnum = eap->line1;
7773 	beginline(BL_SOL | BL_FIX);
7774     }
7775 
7776     if (VIsual_active)
7777 	end_visual_mode();
7778 
7779     switch (eap->cmdidx)
7780     {
7781 	case CMD_delete:
7782 	    oa.op_type = OP_DELETE;
7783 	    op_delete(&oa);
7784 	    break;
7785 
7786 	case CMD_yank:
7787 	    oa.op_type = OP_YANK;
7788 	    (void)op_yank(&oa, FALSE, TRUE);
7789 	    break;
7790 
7791 	default:    // CMD_rshift or CMD_lshift
7792 	    if (
7793 #ifdef FEAT_RIGHTLEFT
7794 		(eap->cmdidx == CMD_rshift) ^ curwin->w_p_rl
7795 #else
7796 		eap->cmdidx == CMD_rshift
7797 #endif
7798 						)
7799 		oa.op_type = OP_RSHIFT;
7800 	    else
7801 		oa.op_type = OP_LSHIFT;
7802 	    op_shift(&oa, FALSE, eap->amount);
7803 	    break;
7804     }
7805     virtual_op = MAYBE;
7806     ex_may_print(eap);
7807 }
7808 
7809 /*
7810  * ":put".
7811  */
7812     static void
ex_put(exarg_T * eap)7813 ex_put(exarg_T *eap)
7814 {
7815     // ":0put" works like ":1put!".
7816     if (eap->line2 == 0)
7817     {
7818 	eap->line2 = 1;
7819 	eap->forceit = TRUE;
7820     }
7821     curwin->w_cursor.lnum = eap->line2;
7822     check_cursor_col();
7823     do_put(eap->regname, NULL, eap->forceit ? BACKWARD : FORWARD, 1L,
7824 						       PUT_LINE|PUT_CURSLINE);
7825 }
7826 
7827 /*
7828  * Handle ":copy" and ":move".
7829  */
7830     static void
ex_copymove(exarg_T * eap)7831 ex_copymove(exarg_T *eap)
7832 {
7833     long	n;
7834 
7835 #ifdef FEAT_EVAL
7836     if (not_in_vim9(eap) == FAIL)
7837 	return;
7838 #endif
7839     n = get_address(eap, &eap->arg, eap->addr_type, FALSE, FALSE, FALSE, 1);
7840     if (eap->arg == NULL)	    // error detected
7841     {
7842 	eap->nextcmd = NULL;
7843 	return;
7844     }
7845     get_flags(eap);
7846 
7847     /*
7848      * move or copy lines from 'eap->line1'-'eap->line2' to below line 'n'
7849      */
7850     if (n == MAXLNUM || n < 0 || n > curbuf->b_ml.ml_line_count)
7851     {
7852 	emsg(_(e_invalid_range));
7853 	return;
7854     }
7855 
7856     if (eap->cmdidx == CMD_move)
7857     {
7858 	if (do_move(eap->line1, eap->line2, n) == FAIL)
7859 	    return;
7860     }
7861     else
7862 	ex_copy(eap->line1, eap->line2, n);
7863     u_clearline();
7864     beginline(BL_SOL | BL_FIX);
7865     ex_may_print(eap);
7866 }
7867 
7868 /*
7869  * Print the current line if flags were given to the Ex command.
7870  */
7871     void
ex_may_print(exarg_T * eap)7872 ex_may_print(exarg_T *eap)
7873 {
7874     if (eap->flags != 0)
7875     {
7876 	print_line(curwin->w_cursor.lnum, (eap->flags & EXFLAG_NR),
7877 						  (eap->flags & EXFLAG_LIST));
7878 	ex_no_reprint = TRUE;
7879     }
7880 }
7881 
7882 /*
7883  * ":smagic" and ":snomagic".
7884  */
7885     static void
ex_submagic(exarg_T * eap)7886 ex_submagic(exarg_T *eap)
7887 {
7888     optmagic_T saved = magic_overruled;
7889 
7890     magic_overruled = eap->cmdidx == CMD_smagic
7891 					  ? OPTION_MAGIC_ON : OPTION_MAGIC_OFF;
7892     ex_substitute(eap);
7893     magic_overruled = saved;
7894 }
7895 
7896 /*
7897  * ":join".
7898  */
7899     static void
ex_join(exarg_T * eap)7900 ex_join(exarg_T *eap)
7901 {
7902     curwin->w_cursor.lnum = eap->line1;
7903     if (eap->line1 == eap->line2)
7904     {
7905 	if (eap->addr_count >= 2)   // :2,2join does nothing
7906 	    return;
7907 	if (eap->line2 == curbuf->b_ml.ml_line_count)
7908 	{
7909 	    beep_flush();
7910 	    return;
7911 	}
7912 	++eap->line2;
7913     }
7914     (void)do_join(eap->line2 - eap->line1 + 1, !eap->forceit, TRUE, TRUE, TRUE);
7915     beginline(BL_WHITE | BL_FIX);
7916     ex_may_print(eap);
7917 }
7918 
7919 /*
7920  * ":[addr]@r" or ":[addr]*r": execute register
7921  */
7922     static void
ex_at(exarg_T * eap)7923 ex_at(exarg_T *eap)
7924 {
7925     int		c;
7926     int		prev_len = typebuf.tb_len;
7927 
7928     curwin->w_cursor.lnum = eap->line2;
7929     check_cursor_col();
7930 
7931 #ifdef USE_ON_FLY_SCROLL
7932     dont_scroll = TRUE;		// disallow scrolling here
7933 #endif
7934 
7935     // get the register name.  No name means to use the previous one
7936     c = *eap->arg;
7937     if (c == NUL || (c == '*' && *eap->cmd == '*'))
7938 	c = '@';
7939     // Put the register in the typeahead buffer with the "silent" flag.
7940     if (do_execreg(c, TRUE, vim_strchr(p_cpo, CPO_EXECBUF) != NULL, TRUE)
7941 								      == FAIL)
7942     {
7943 	beep_flush();
7944     }
7945     else
7946     {
7947 	int	save_efr = exec_from_reg;
7948 
7949 	exec_from_reg = TRUE;
7950 
7951 	/*
7952 	 * Execute from the typeahead buffer.
7953 	 * Continue until the stuff buffer is empty and all added characters
7954 	 * have been consumed.
7955 	 */
7956 	while (!stuff_empty() || typebuf.tb_len > prev_len)
7957 	    (void)do_cmdline(NULL, getexline, NULL, DOCMD_NOWAIT|DOCMD_VERBOSE);
7958 
7959 	exec_from_reg = save_efr;
7960     }
7961 }
7962 
7963 /*
7964  * ":!".
7965  */
7966     static void
ex_bang(exarg_T * eap)7967 ex_bang(exarg_T *eap)
7968 {
7969     do_bang(eap->addr_count, eap, eap->forceit, TRUE, TRUE);
7970 }
7971 
7972 /*
7973  * ":undo".
7974  */
7975     static void
ex_undo(exarg_T * eap)7976 ex_undo(exarg_T *eap)
7977 {
7978     if (eap->addr_count == 1)	    // :undo 123
7979 	undo_time(eap->line2, FALSE, FALSE, TRUE);
7980     else
7981 	u_undo(1);
7982 }
7983 
7984 #ifdef FEAT_PERSISTENT_UNDO
7985     static void
ex_wundo(exarg_T * eap)7986 ex_wundo(exarg_T *eap)
7987 {
7988     char_u hash[UNDO_HASH_SIZE];
7989 
7990     u_compute_hash(hash);
7991     u_write_undo(eap->arg, eap->forceit, curbuf, hash);
7992 }
7993 
7994     static void
ex_rundo(exarg_T * eap)7995 ex_rundo(exarg_T *eap)
7996 {
7997     char_u hash[UNDO_HASH_SIZE];
7998 
7999     u_compute_hash(hash);
8000     u_read_undo(eap->arg, hash, NULL);
8001 }
8002 #endif
8003 
8004 /*
8005  * ":redo".
8006  */
8007     static void
ex_redo(exarg_T * eap UNUSED)8008 ex_redo(exarg_T *eap UNUSED)
8009 {
8010     u_redo(1);
8011 }
8012 
8013 /*
8014  * ":earlier" and ":later".
8015  */
8016     static void
ex_later(exarg_T * eap)8017 ex_later(exarg_T *eap)
8018 {
8019     long	count = 0;
8020     int		sec = FALSE;
8021     int		file = FALSE;
8022     char_u	*p = eap->arg;
8023 
8024     if (*p == NUL)
8025 	count = 1;
8026     else if (isdigit(*p))
8027     {
8028 	count = getdigits(&p);
8029 	switch (*p)
8030 	{
8031 	    case 's': ++p; sec = TRUE; break;
8032 	    case 'm': ++p; sec = TRUE; count *= 60; break;
8033 	    case 'h': ++p; sec = TRUE; count *= 60 * 60; break;
8034 	    case 'd': ++p; sec = TRUE; count *= 24 * 60 * 60; break;
8035 	    case 'f': ++p; file = TRUE; break;
8036 	}
8037     }
8038 
8039     if (*p != NUL)
8040 	semsg(_(e_invarg2), eap->arg);
8041     else
8042 	undo_time(eap->cmdidx == CMD_earlier ? -count : count,
8043 							    sec, file, FALSE);
8044 }
8045 
8046 /*
8047  * ":redir": start/stop redirection.
8048  */
8049     static void
ex_redir(exarg_T * eap)8050 ex_redir(exarg_T *eap)
8051 {
8052     char	*mode;
8053     char_u	*fname;
8054     char_u	*arg = eap->arg;
8055 
8056 #ifdef FEAT_EVAL
8057     if (redir_execute)
8058     {
8059 	emsg(_("E930: Cannot use :redir inside execute()"));
8060 	return;
8061     }
8062 #endif
8063 
8064     if (STRICMP(eap->arg, "END") == 0)
8065 	close_redir();
8066     else
8067     {
8068 	if (*arg == '>')
8069 	{
8070 	    ++arg;
8071 	    if (*arg == '>')
8072 	    {
8073 		++arg;
8074 		mode = "a";
8075 	    }
8076 	    else
8077 		mode = "w";
8078 	    arg = skipwhite(arg);
8079 
8080 	    close_redir();
8081 
8082 	    // Expand environment variables and "~/".
8083 	    fname = expand_env_save(arg);
8084 	    if (fname == NULL)
8085 		return;
8086 #ifdef FEAT_BROWSE
8087 	    if (cmdmod.cmod_flags & CMOD_BROWSE)
8088 	    {
8089 		char_u	*browseFile;
8090 
8091 		browseFile = do_browse(BROWSE_SAVE,
8092 			(char_u *)_("Save Redirection"),
8093 			fname, NULL, NULL,
8094 			(char_u *)_(BROWSE_FILTER_ALL_FILES), curbuf);
8095 		if (browseFile == NULL)
8096 		    return;		// operation cancelled
8097 		vim_free(fname);
8098 		fname = browseFile;
8099 		eap->forceit = TRUE;	// since dialog already asked
8100 	    }
8101 #endif
8102 
8103 	    redir_fd = open_exfile(fname, eap->forceit, mode);
8104 	    vim_free(fname);
8105 	}
8106 #ifdef FEAT_EVAL
8107 	else if (*arg == '@')
8108 	{
8109 	    // redirect to a register a-z (resp. A-Z for appending)
8110 	    close_redir();
8111 	    ++arg;
8112 	    if (ASCII_ISALPHA(*arg)
8113 # ifdef FEAT_CLIPBOARD
8114 		    || *arg == '*'
8115 		    || *arg == '+'
8116 # endif
8117 		    || *arg == '"')
8118 	    {
8119 		redir_reg = *arg++;
8120 		if (*arg == '>' && arg[1] == '>')  // append
8121 		    arg += 2;
8122 		else
8123 		{
8124 		    // Can use both "@a" and "@a>".
8125 		    if (*arg == '>')
8126 			arg++;
8127 		    // Make register empty when not using @A-@Z and the
8128 		    // command is valid.
8129 		    if (*arg == NUL && !isupper(redir_reg))
8130 			write_reg_contents(redir_reg, (char_u *)"", -1, FALSE);
8131 		}
8132 	    }
8133 	    if (*arg != NUL)
8134 	    {
8135 		redir_reg = 0;
8136 		semsg(_(e_invarg2), eap->arg);
8137 	    }
8138 	}
8139 	else if (*arg == '=' && arg[1] == '>')
8140 	{
8141 	    int append;
8142 
8143 	    // redirect to a variable
8144 	    close_redir();
8145 	    arg += 2;
8146 
8147 	    if (*arg == '>')
8148 	    {
8149 		++arg;
8150 		append = TRUE;
8151 	    }
8152 	    else
8153 		append = FALSE;
8154 
8155 	    if (var_redir_start(skipwhite(arg), append) == OK)
8156 		redir_vname = 1;
8157 	}
8158 #endif
8159 
8160 	// TODO: redirect to a buffer
8161 
8162 	else
8163 	    semsg(_(e_invarg2), eap->arg);
8164     }
8165 
8166     // Make sure redirection is not off.  Can happen for cmdline completion
8167     // that indirectly invokes a command to catch its output.
8168     if (redir_fd != NULL
8169 #ifdef FEAT_EVAL
8170 			  || redir_reg || redir_vname
8171 #endif
8172 							)
8173 	redir_off = FALSE;
8174 }
8175 
8176 /*
8177  * ":redraw": force redraw
8178  */
8179     void
ex_redraw(exarg_T * eap)8180 ex_redraw(exarg_T *eap)
8181 {
8182     int		r = RedrawingDisabled;
8183     int		p = p_lz;
8184 
8185     RedrawingDisabled = 0;
8186     p_lz = FALSE;
8187     validate_cursor();
8188     update_topline();
8189     update_screen(eap->forceit ? CLEAR : VIsual_active ? INVERTED : 0);
8190     if (need_maketitle)
8191 	maketitle();
8192 #if defined(MSWIN) && (!defined(FEAT_GUI_MSWIN) || defined(VIMDLL))
8193 # ifdef VIMDLL
8194     if (!gui.in_use)
8195 # endif
8196 	resize_console_buf();
8197 #endif
8198     RedrawingDisabled = r;
8199     p_lz = p;
8200 
8201     // Reset msg_didout, so that a message that's there is overwritten.
8202     msg_didout = FALSE;
8203     msg_col = 0;
8204 
8205     // No need to wait after an intentional redraw.
8206     need_wait_return = FALSE;
8207 
8208     out_flush();
8209 }
8210 
8211 /*
8212  * ":redrawstatus": force redraw of status line(s)
8213  */
8214     static void
ex_redrawstatus(exarg_T * eap UNUSED)8215 ex_redrawstatus(exarg_T *eap UNUSED)
8216 {
8217     int		r = RedrawingDisabled;
8218     int		p = p_lz;
8219 
8220     RedrawingDisabled = 0;
8221     p_lz = FALSE;
8222     if (eap->forceit)
8223 	status_redraw_all();
8224     else
8225 	status_redraw_curbuf();
8226     update_screen(VIsual_active ? INVERTED : 0);
8227     RedrawingDisabled = r;
8228     p_lz = p;
8229     out_flush();
8230 }
8231 
8232 /*
8233  * ":redrawtabline": force redraw of the tabline
8234  */
8235     static void
ex_redrawtabline(exarg_T * eap UNUSED)8236 ex_redrawtabline(exarg_T *eap UNUSED)
8237 {
8238     int		r = RedrawingDisabled;
8239     int		p = p_lz;
8240 
8241     RedrawingDisabled = 0;
8242     p_lz = FALSE;
8243 
8244     draw_tabline();
8245 
8246     RedrawingDisabled = r;
8247     p_lz = p;
8248     out_flush();
8249 }
8250 
8251     static void
close_redir(void)8252 close_redir(void)
8253 {
8254     if (redir_fd != NULL)
8255     {
8256 	fclose(redir_fd);
8257 	redir_fd = NULL;
8258     }
8259 #ifdef FEAT_EVAL
8260     redir_reg = 0;
8261     if (redir_vname)
8262     {
8263 	var_redir_stop();
8264 	redir_vname = 0;
8265     }
8266 #endif
8267 }
8268 
8269 #if (defined(FEAT_SESSION) || defined(FEAT_EVAL)) || defined(PROTO)
8270     int
vim_mkdir_emsg(char_u * name,int prot UNUSED)8271 vim_mkdir_emsg(char_u *name, int prot UNUSED)
8272 {
8273     if (vim_mkdir(name, prot) != 0)
8274     {
8275 	semsg(_("E739: Cannot create directory: %s"), name);
8276 	return FAIL;
8277     }
8278     return OK;
8279 }
8280 #endif
8281 
8282 /*
8283  * Open a file for writing for an Ex command, with some checks.
8284  * Return file descriptor, or NULL on failure.
8285  */
8286     FILE *
open_exfile(char_u * fname,int forceit,char * mode)8287 open_exfile(
8288     char_u	*fname,
8289     int		forceit,
8290     char	*mode)	    // "w" for create new file or "a" for append
8291 {
8292     FILE	*fd;
8293 
8294 #ifdef UNIX
8295     // with Unix it is possible to open a directory
8296     if (mch_isdir(fname))
8297     {
8298 	semsg(_(e_src_is_directory), fname);
8299 	return NULL;
8300     }
8301 #endif
8302     if (!forceit && *mode != 'a' && vim_fexists(fname))
8303     {
8304 	semsg(_("E189: \"%s\" exists (add ! to override)"), fname);
8305 	return NULL;
8306     }
8307 
8308     if ((fd = mch_fopen((char *)fname, mode)) == NULL)
8309 	semsg(_("E190: Cannot open \"%s\" for writing"), fname);
8310 
8311     return fd;
8312 }
8313 
8314 /*
8315  * ":mark" and ":k".
8316  */
8317     static void
ex_mark(exarg_T * eap)8318 ex_mark(exarg_T *eap)
8319 {
8320     pos_T	pos;
8321 
8322 #ifdef FEAT_EVAL
8323     if (not_in_vim9(eap) == FAIL)
8324 	return;
8325 #endif
8326     if (*eap->arg == NUL)		// No argument?
8327 	emsg(_(e_argreq));
8328     else if (eap->arg[1] != NUL)	// more than one character?
8329 	semsg(_(e_trailing_arg), eap->arg);
8330     else
8331     {
8332 	pos = curwin->w_cursor;		// save curwin->w_cursor
8333 	curwin->w_cursor.lnum = eap->line2;
8334 	beginline(BL_WHITE | BL_FIX);
8335 	if (setmark(*eap->arg) == FAIL)	// set mark
8336 	    emsg(_("E191: Argument must be a letter or forward/backward quote"));
8337 	curwin->w_cursor = pos;		// restore curwin->w_cursor
8338     }
8339 }
8340 
8341 /*
8342  * Update w_topline, w_leftcol and the cursor position.
8343  */
8344     void
update_topline_cursor(void)8345 update_topline_cursor(void)
8346 {
8347     check_cursor();		// put cursor on valid line
8348     update_topline();
8349     if (!curwin->w_p_wrap)
8350 	validate_cursor();
8351     update_curswant();
8352 }
8353 
8354 /*
8355  * Save the current State and go to Normal mode.
8356  * Return TRUE if the typeahead could be saved.
8357  */
8358     int
save_current_state(save_state_T * sst)8359 save_current_state(save_state_T *sst)
8360 {
8361     sst->save_msg_scroll = msg_scroll;
8362     sst->save_restart_edit = restart_edit;
8363     sst->save_msg_didout = msg_didout;
8364     sst->save_State = State;
8365     sst->save_insertmode = p_im;
8366     sst->save_finish_op = finish_op;
8367     sst->save_opcount = opcount;
8368     sst->save_reg_executing = reg_executing;
8369 
8370     msg_scroll = FALSE;		    // no msg scrolling in Normal mode
8371     restart_edit = 0;		    // don't go to Insert mode
8372     p_im = FALSE;		    // don't use 'insertmode'
8373 
8374     sst->save_script_version = current_sctx.sc_version;
8375     current_sctx.sc_version = 1;    // not in Vim9 script
8376 
8377     /*
8378      * Save the current typeahead.  This is required to allow using ":normal"
8379      * from an event handler and makes sure we don't hang when the argument
8380      * ends with half a command.
8381      */
8382     save_typeahead(&sst->tabuf);
8383     return sst->tabuf.typebuf_valid;
8384 }
8385 
8386     void
restore_current_state(save_state_T * sst)8387 restore_current_state(save_state_T *sst)
8388 {
8389     // Restore the previous typeahead.
8390     restore_typeahead(&sst->tabuf, FALSE);
8391 
8392     msg_scroll = sst->save_msg_scroll;
8393     restart_edit = sst->save_restart_edit;
8394     p_im = sst->save_insertmode;
8395     finish_op = sst->save_finish_op;
8396     opcount = sst->save_opcount;
8397     reg_executing = sst->save_reg_executing;
8398     msg_didout |= sst->save_msg_didout;	// don't reset msg_didout now
8399     current_sctx.sc_version = sst->save_script_version;
8400 
8401     // Restore the state (needed when called from a function executed for
8402     // 'indentexpr'). Update the mouse and cursor, they may have changed.
8403     State = sst->save_State;
8404 #ifdef CURSOR_SHAPE
8405     ui_cursor_shape();		// may show different cursor shape
8406 #endif
8407 }
8408 
8409 /*
8410  * ":normal[!] {commands}": Execute normal mode commands.
8411  */
8412     void
ex_normal(exarg_T * eap)8413 ex_normal(exarg_T *eap)
8414 {
8415     save_state_T save_state;
8416     char_u	*arg = NULL;
8417     int		l;
8418     char_u	*p;
8419 
8420     if (ex_normal_lock > 0)
8421     {
8422 	emsg(_(e_secure));
8423 	return;
8424     }
8425     if (ex_normal_busy >= p_mmd)
8426     {
8427 	emsg(_("E192: Recursive use of :normal too deep"));
8428 	return;
8429     }
8430 
8431     /*
8432      * vgetc() expects a CSI and K_SPECIAL to have been escaped.  Don't do
8433      * this for the K_SPECIAL leading byte, otherwise special keys will not
8434      * work.
8435      */
8436     if (has_mbyte)
8437     {
8438 	int	len = 0;
8439 
8440 	// Count the number of characters to be escaped.
8441 	for (p = eap->arg; *p != NUL; ++p)
8442 	{
8443 #ifdef FEAT_GUI
8444 	    if (*p == CSI)  // leadbyte CSI
8445 		len += 2;
8446 #endif
8447 	    for (l = (*mb_ptr2len)(p) - 1; l > 0; --l)
8448 		if (*++p == K_SPECIAL	  // trailbyte K_SPECIAL or CSI
8449 #ifdef FEAT_GUI
8450 			|| *p == CSI
8451 #endif
8452 			)
8453 		    len += 2;
8454 	}
8455 	if (len > 0)
8456 	{
8457 	    arg = alloc(STRLEN(eap->arg) + len + 1);
8458 	    if (arg != NULL)
8459 	    {
8460 		len = 0;
8461 		for (p = eap->arg; *p != NUL; ++p)
8462 		{
8463 		    arg[len++] = *p;
8464 #ifdef FEAT_GUI
8465 		    if (*p == CSI)
8466 		    {
8467 			arg[len++] = KS_EXTRA;
8468 			arg[len++] = (int)KE_CSI;
8469 		    }
8470 #endif
8471 		    for (l = (*mb_ptr2len)(p) - 1; l > 0; --l)
8472 		    {
8473 			arg[len++] = *++p;
8474 			if (*p == K_SPECIAL)
8475 			{
8476 			    arg[len++] = KS_SPECIAL;
8477 			    arg[len++] = KE_FILLER;
8478 			}
8479 #ifdef FEAT_GUI
8480 			else if (*p == CSI)
8481 			{
8482 			    arg[len++] = KS_EXTRA;
8483 			    arg[len++] = (int)KE_CSI;
8484 			}
8485 #endif
8486 		    }
8487 		    arg[len] = NUL;
8488 		}
8489 	    }
8490 	}
8491     }
8492 
8493     ++ex_normal_busy;
8494     if (save_current_state(&save_state))
8495     {
8496 	/*
8497 	 * Repeat the :normal command for each line in the range.  When no
8498 	 * range given, execute it just once, without positioning the cursor
8499 	 * first.
8500 	 */
8501 	do
8502 	{
8503 	    if (eap->addr_count != 0)
8504 	    {
8505 		curwin->w_cursor.lnum = eap->line1++;
8506 		curwin->w_cursor.col = 0;
8507 		check_cursor_moved(curwin);
8508 	    }
8509 
8510 	    exec_normal_cmd(arg != NULL
8511 		     ? arg
8512 		     : eap->arg, eap->forceit ? REMAP_NONE : REMAP_YES, FALSE);
8513 	}
8514 	while (eap->addr_count > 0 && eap->line1 <= eap->line2 && !got_int);
8515     }
8516 
8517     // Might not return to the main loop when in an event handler.
8518     update_topline_cursor();
8519 
8520     restore_current_state(&save_state);
8521     --ex_normal_busy;
8522     setmouse();
8523 #ifdef CURSOR_SHAPE
8524     ui_cursor_shape();		// may show different cursor shape
8525 #endif
8526 
8527     vim_free(arg);
8528 }
8529 
8530 /*
8531  * ":startinsert", ":startreplace" and ":startgreplace"
8532  */
8533     static void
ex_startinsert(exarg_T * eap)8534 ex_startinsert(exarg_T *eap)
8535 {
8536     if (eap->forceit)
8537     {
8538 	// cursor line can be zero on startup
8539 	if (!curwin->w_cursor.lnum)
8540 	    curwin->w_cursor.lnum = 1;
8541 	set_cursor_for_append_to_line();
8542     }
8543 #ifdef FEAT_TERMINAL
8544     // Ignore this when running in an active terminal.
8545     if (term_job_running(curbuf->b_term))
8546 	return;
8547 #endif
8548 
8549     // Ignore the command when already in Insert mode.  Inserting an
8550     // expression register that invokes a function can do this.
8551     if (State & INSERT)
8552 	return;
8553 
8554     if (eap->cmdidx == CMD_startinsert)
8555 	restart_edit = 'a';
8556     else if (eap->cmdidx == CMD_startreplace)
8557 	restart_edit = 'R';
8558     else
8559 	restart_edit = 'V';
8560 
8561     if (!eap->forceit)
8562     {
8563 	if (eap->cmdidx == CMD_startinsert)
8564 	    restart_edit = 'i';
8565 	curwin->w_curswant = 0;	    // avoid MAXCOL
8566     }
8567 
8568     if (VIsual_active)
8569 	showmode();
8570 }
8571 
8572 /*
8573  * ":stopinsert"
8574  */
8575     static void
ex_stopinsert(exarg_T * eap UNUSED)8576 ex_stopinsert(exarg_T *eap UNUSED)
8577 {
8578     restart_edit = 0;
8579     stop_insert_mode = TRUE;
8580     clearmode();
8581 }
8582 
8583 /*
8584  * Execute normal mode command "cmd".
8585  * "remap" can be REMAP_NONE or REMAP_YES.
8586  */
8587     void
exec_normal_cmd(char_u * cmd,int remap,int silent)8588 exec_normal_cmd(char_u *cmd, int remap, int silent)
8589 {
8590     // Stuff the argument into the typeahead buffer.
8591     ins_typebuf(cmd, remap, 0, TRUE, silent);
8592     exec_normal(FALSE, FALSE, FALSE);
8593 }
8594 
8595 /*
8596  * Execute normal_cmd() until there is no typeahead left.
8597  * When "use_vpeekc" is TRUE use vpeekc() to check for available chars.
8598  */
8599     void
exec_normal(int was_typed,int use_vpeekc,int may_use_terminal_loop UNUSED)8600 exec_normal(int was_typed, int use_vpeekc, int may_use_terminal_loop UNUSED)
8601 {
8602     oparg_T	oa;
8603     int		c;
8604 
8605     // When calling vpeekc() from feedkeys() it will return Ctrl_C when there
8606     // is nothing to get, so also check for Ctrl_C.
8607     clear_oparg(&oa);
8608     finish_op = FALSE;
8609     while ((!stuff_empty()
8610 		|| ((was_typed || !typebuf_typed()) && typebuf.tb_len > 0)
8611 		|| (use_vpeekc && (c = vpeekc()) != NUL && c != Ctrl_C))
8612 	    && !got_int)
8613     {
8614 	update_topline_cursor();
8615 #ifdef FEAT_TERMINAL
8616 	if (may_use_terminal_loop && term_use_loop()
8617 		&& oa.op_type == OP_NOP && oa.regname == NUL
8618 		&& !VIsual_active)
8619 	{
8620 	    // If terminal_loop() returns OK we got a key that is handled
8621 	    // in Normal model.  With FAIL we first need to position the
8622 	    // cursor and the screen needs to be redrawn.
8623 	    if (terminal_loop(TRUE) == OK)
8624 		normal_cmd(&oa, TRUE);
8625 	}
8626 	else
8627 #endif
8628 	    // execute a Normal mode cmd
8629 	    normal_cmd(&oa, TRUE);
8630     }
8631 }
8632 
8633 #ifdef FEAT_FIND_ID
8634     static void
ex_checkpath(exarg_T * eap)8635 ex_checkpath(exarg_T *eap)
8636 {
8637     find_pattern_in_path(NULL, 0, 0, FALSE, FALSE, CHECK_PATH, 1L,
8638 				   eap->forceit ? ACTION_SHOW_ALL : ACTION_SHOW,
8639 					      (linenr_T)1, (linenr_T)MAXLNUM);
8640 }
8641 
8642 #if defined(FEAT_QUICKFIX)
8643 /*
8644  * ":psearch"
8645  */
8646     static void
ex_psearch(exarg_T * eap)8647 ex_psearch(exarg_T *eap)
8648 {
8649     g_do_tagpreview = p_pvh;
8650     ex_findpat(eap);
8651     g_do_tagpreview = 0;
8652 }
8653 #endif
8654 
8655     static void
ex_findpat(exarg_T * eap)8656 ex_findpat(exarg_T *eap)
8657 {
8658     int		whole = TRUE;
8659     long	n;
8660     char_u	*p;
8661     int		action;
8662 
8663     switch (cmdnames[eap->cmdidx].cmd_name[2])
8664     {
8665 	case 'e':	// ":psearch", ":isearch" and ":dsearch"
8666 		if (cmdnames[eap->cmdidx].cmd_name[0] == 'p')
8667 		    action = ACTION_GOTO;
8668 		else
8669 		    action = ACTION_SHOW;
8670 		break;
8671 	case 'i':	// ":ilist" and ":dlist"
8672 		action = ACTION_SHOW_ALL;
8673 		break;
8674 	case 'u':	// ":ijump" and ":djump"
8675 		action = ACTION_GOTO;
8676 		break;
8677 	default:	// ":isplit" and ":dsplit"
8678 		action = ACTION_SPLIT;
8679 		break;
8680     }
8681 
8682     n = 1;
8683     if (vim_isdigit(*eap->arg))	// get count
8684     {
8685 	n = getdigits(&eap->arg);
8686 	eap->arg = skipwhite(eap->arg);
8687     }
8688     if (*eap->arg == '/')   // Match regexp, not just whole words
8689     {
8690 	whole = FALSE;
8691 	++eap->arg;
8692 	p = skip_regexp(eap->arg, '/', magic_isset());
8693 	if (*p)
8694 	{
8695 	    *p++ = NUL;
8696 	    p = skipwhite(p);
8697 
8698 	    // Check for trailing illegal characters
8699 	    if (!ends_excmd2(eap->arg, p))
8700 		eap->errmsg = ex_errmsg(e_trailing_arg, p);
8701 	    else
8702 		set_nextcmd(eap, p);
8703 	}
8704     }
8705     if (!eap->skip)
8706 	find_pattern_in_path(eap->arg, 0, (int)STRLEN(eap->arg),
8707 			    whole, !eap->forceit,
8708 			    *eap->cmd == 'd' ?	FIND_DEFINE : FIND_ANY,
8709 			    n, action, eap->line1, eap->line2);
8710 }
8711 #endif
8712 
8713 
8714 #ifdef FEAT_QUICKFIX
8715 /*
8716  * ":ptag", ":ptselect", ":ptjump", ":ptnext", etc.
8717  */
8718     static void
ex_ptag(exarg_T * eap)8719 ex_ptag(exarg_T *eap)
8720 {
8721     g_do_tagpreview = p_pvh;  // will be reset to 0 in ex_tag_cmd()
8722     ex_tag_cmd(eap, cmdnames[eap->cmdidx].cmd_name + 1);
8723 }
8724 
8725 /*
8726  * ":pedit"
8727  */
8728     static void
ex_pedit(exarg_T * eap)8729 ex_pedit(exarg_T *eap)
8730 {
8731     win_T	*curwin_save = curwin;
8732 
8733     if (ERROR_IF_ANY_POPUP_WINDOW)
8734 	return;
8735 
8736     // Open the preview window or popup and make it the current window.
8737     g_do_tagpreview = p_pvh;
8738     prepare_tagpreview(TRUE, TRUE, FALSE);
8739 
8740     // Edit the file.
8741     do_exedit(eap, NULL);
8742 
8743     if (curwin != curwin_save && win_valid(curwin_save))
8744     {
8745 	// Return cursor to where we were
8746 	validate_cursor();
8747 	redraw_later(VALID);
8748 	win_enter(curwin_save, TRUE);
8749     }
8750 # ifdef FEAT_PROP_POPUP
8751     else if (WIN_IS_POPUP(curwin))
8752     {
8753 	// can't keep focus in popup window
8754 	win_enter(firstwin, TRUE);
8755     }
8756 # endif
8757     g_do_tagpreview = 0;
8758 }
8759 #endif
8760 
8761 /*
8762  * ":stag", ":stselect" and ":stjump".
8763  */
8764     static void
ex_stag(exarg_T * eap)8765 ex_stag(exarg_T *eap)
8766 {
8767     postponed_split = -1;
8768     postponed_split_flags = cmdmod.cmod_split;
8769     postponed_split_tab = cmdmod.cmod_tab;
8770     ex_tag_cmd(eap, cmdnames[eap->cmdidx].cmd_name + 1);
8771     postponed_split_flags = 0;
8772     postponed_split_tab = 0;
8773 }
8774 
8775 /*
8776  * ":tag", ":tselect", ":tjump", ":tnext", etc.
8777  */
8778     static void
ex_tag(exarg_T * eap)8779 ex_tag(exarg_T *eap)
8780 {
8781     ex_tag_cmd(eap, cmdnames[eap->cmdidx].cmd_name);
8782 }
8783 
8784     static void
ex_tag_cmd(exarg_T * eap,char_u * name)8785 ex_tag_cmd(exarg_T *eap, char_u *name)
8786 {
8787     int		cmd;
8788 
8789     switch (name[1])
8790     {
8791 	case 'j': cmd = DT_JUMP;	// ":tjump"
8792 		  break;
8793 	case 's': cmd = DT_SELECT;	// ":tselect"
8794 		  break;
8795 	case 'p': cmd = DT_PREV;	// ":tprevious"
8796 		  break;
8797 	case 'N': cmd = DT_PREV;	// ":tNext"
8798 		  break;
8799 	case 'n': cmd = DT_NEXT;	// ":tnext"
8800 		  break;
8801 	case 'o': cmd = DT_POP;		// ":pop"
8802 		  break;
8803 	case 'f':			// ":tfirst"
8804 	case 'r': cmd = DT_FIRST;	// ":trewind"
8805 		  break;
8806 	case 'l': cmd = DT_LAST;	// ":tlast"
8807 		  break;
8808 	default:			// ":tag"
8809 #ifdef FEAT_CSCOPE
8810 		  if (p_cst && *eap->arg != NUL)
8811 		  {
8812 		      ex_cstag(eap);
8813 		      return;
8814 		  }
8815 #endif
8816 		  cmd = DT_TAG;
8817 		  break;
8818     }
8819 
8820     if (name[0] == 'l')
8821     {
8822 #ifndef FEAT_QUICKFIX
8823 	ex_ni(eap);
8824 	return;
8825 #else
8826 	cmd = DT_LTAG;
8827 #endif
8828     }
8829 
8830     do_tag(eap->arg, cmd, eap->addr_count > 0 ? (int)eap->line2 : 1,
8831 							  eap->forceit, TRUE);
8832 }
8833 
8834 /*
8835  * Check "str" for starting with a special cmdline variable.
8836  * If found return one of the SPEC_ values and set "*usedlen" to the length of
8837  * the variable.  Otherwise return -1 and "*usedlen" is unchanged.
8838  */
8839     int
find_cmdline_var(char_u * src,int * usedlen)8840 find_cmdline_var(char_u *src, int *usedlen)
8841 {
8842     int		len;
8843     int		i;
8844     static char *(spec_str[]) = {
8845 		    "%",
8846 #define SPEC_PERC   0
8847 		    "#",
8848 #define SPEC_HASH   (SPEC_PERC + 1)
8849 		    "<cword>",		// cursor word
8850 #define SPEC_CWORD  (SPEC_HASH + 1)
8851 		    "<cWORD>",		// cursor WORD
8852 #define SPEC_CCWORD (SPEC_CWORD + 1)
8853 		    "<cexpr>",		// expr under cursor
8854 #define SPEC_CEXPR  (SPEC_CCWORD + 1)
8855 		    "<cfile>",		// cursor path name
8856 #define SPEC_CFILE  (SPEC_CEXPR + 1)
8857 		    "<sfile>",		// ":so" file name
8858 #define SPEC_SFILE  (SPEC_CFILE + 1)
8859 		    "<slnum>",		// ":so" file line number
8860 #define SPEC_SLNUM  (SPEC_SFILE + 1)
8861 		    "<stack>",		// call stack
8862 #define SPEC_STACK  (SPEC_SLNUM + 1)
8863 		    "<afile>",		// autocommand file name
8864 #define SPEC_AFILE  (SPEC_STACK + 1)
8865 		    "<abuf>",		// autocommand buffer number
8866 #define SPEC_ABUF   (SPEC_AFILE + 1)
8867 		    "<amatch>",		// autocommand match name
8868 #define SPEC_AMATCH (SPEC_ABUF + 1)
8869 		    "<sflnum>",		// script file line number
8870 #define SPEC_SFLNUM  (SPEC_AMATCH + 1)
8871 		    "<SID>",		// script ID: <SNR>123_
8872 #define SPEC_SID  (SPEC_SFLNUM + 1)
8873 #ifdef FEAT_CLIENTSERVER
8874 		    "<client>"
8875 # define SPEC_CLIENT (SPEC_SID + 1)
8876 #endif
8877     };
8878 
8879     for (i = 0; i < (int)ARRAY_LENGTH(spec_str); ++i)
8880     {
8881 	len = (int)STRLEN(spec_str[i]);
8882 	if (STRNCMP(src, spec_str[i], len) == 0)
8883 	{
8884 	    *usedlen = len;
8885 	    return i;
8886 	}
8887     }
8888     return -1;
8889 }
8890 
8891 /*
8892  * Evaluate cmdline variables.
8893  *
8894  * change "%"	    to curbuf->b_ffname
8895  *	  "#"	    to curwin->w_alt_fnum
8896  *	  "%%"	    to curwin->w_alt_fnum in Vim9 script
8897  *	  "<cword>" to word under the cursor
8898  *	  "<cWORD>" to WORD under the cursor
8899  *	  "<cexpr>" to C-expression under the cursor
8900  *	  "<cfile>" to path name under the cursor
8901  *	  "<sfile>" to sourced file name
8902  *	  "<stack>" to call stack
8903  *	  "<slnum>" to sourced file line number
8904  *	  "<afile>" to file name for autocommand
8905  *	  "<abuf>"  to buffer number for autocommand
8906  *	  "<amatch>" to matching name for autocommand
8907  *
8908  * When an error is detected, "errormsg" is set to a non-NULL pointer (may be
8909  * "" for error without a message) and NULL is returned.
8910  * Returns an allocated string if a valid match was found.
8911  * Returns NULL if no match was found.	"usedlen" then still contains the
8912  * number of characters to skip.
8913  */
8914     char_u *
eval_vars(char_u * src,char_u * srcstart,int * usedlen,linenr_T * lnump,char ** errormsg,int * escaped)8915 eval_vars(
8916     char_u	*src,		// pointer into commandline
8917     char_u	*srcstart,	// beginning of valid memory for src
8918     int		*usedlen,	// characters after src that are used
8919     linenr_T	*lnump,		// line number for :e command, or NULL
8920     char	**errormsg,	// pointer to error message
8921     int		*escaped)	// return value has escaped white space (can
8922 				// be NULL)
8923 {
8924     int		i;
8925     char_u	*s;
8926     char_u	*result;
8927     char_u	*resultbuf = NULL;
8928     int		resultlen;
8929     buf_T	*buf;
8930     int		valid = VALID_HEAD + VALID_PATH;    // assume valid result
8931     int		spec_idx;
8932     int		tilde_file = FALSE;
8933     int		skip_mod = FALSE;
8934     char_u	strbuf[30];
8935 
8936     *errormsg = NULL;
8937     if (escaped != NULL)
8938 	*escaped = FALSE;
8939 
8940     /*
8941      * Check if there is something to do.
8942      */
8943     spec_idx = find_cmdline_var(src, usedlen);
8944     if (spec_idx < 0)	// no match
8945     {
8946 	*usedlen = 1;
8947 	return NULL;
8948     }
8949 
8950     /*
8951      * Skip when preceded with a backslash "\%" and "\#".
8952      * Note: In "\\%" the % is also not recognized!
8953      */
8954     if (src > srcstart && src[-1] == '\\')
8955     {
8956 	*usedlen = 0;
8957 	STRMOVE(src - 1, src);	// remove backslash
8958 	return NULL;
8959     }
8960 
8961     /*
8962      * word or WORD under cursor
8963      */
8964     if (spec_idx == SPEC_CWORD || spec_idx == SPEC_CCWORD
8965 						     || spec_idx == SPEC_CEXPR)
8966     {
8967 	resultlen = find_ident_under_cursor(&result,
8968 		spec_idx == SPEC_CWORD ? (FIND_IDENT | FIND_STRING)
8969 	      : spec_idx == SPEC_CEXPR ? (FIND_IDENT | FIND_STRING | FIND_EVAL)
8970 	      : FIND_STRING);
8971 	if (resultlen == 0)
8972 	{
8973 	    *errormsg = "";
8974 	    return NULL;
8975 	}
8976     }
8977 
8978     /*
8979      * '#': Alternate file name
8980      * '%': Current file name
8981      *	    File name under the cursor
8982      *	    File name for autocommand
8983      *	and following modifiers
8984      */
8985     else
8986     {
8987 	int off = 0;
8988 
8989 	switch (spec_idx)
8990 	{
8991 	case SPEC_PERC:
8992 #ifdef FEAT_EVAL
8993 		if (!in_vim9script() || src[1] != '%')
8994 #endif
8995 		{
8996 		    // '%': current file
8997 		    if (curbuf->b_fname == NULL)
8998 		    {
8999 			result = (char_u *)"";
9000 			valid = 0;	    // Must have ":p:h" to be valid
9001 		    }
9002 		    else
9003 		    {
9004 			result = curbuf->b_fname;
9005 			tilde_file = STRCMP(result, "~") == 0;
9006 		    }
9007 		    break;
9008 		}
9009 #ifdef FEAT_EVAL
9010 		// "%%" alternate file
9011 		off = 1;
9012 #endif
9013 		// FALLTHROUGH
9014 	case SPEC_HASH:		// '#' or "#99": alternate file
9015 		if (off == 0 ? src[1] == '#' : src[2] == '%')
9016 		{
9017 		    // "##" or "%%%": the argument list
9018 		    result = arg_all();
9019 		    resultbuf = result;
9020 		    *usedlen = off + 2;
9021 		    if (escaped != NULL)
9022 			*escaped = TRUE;
9023 		    skip_mod = TRUE;
9024 		    break;
9025 		}
9026 		s = src + off + 1;
9027 		if (*s == '<')		// "#<99" uses v:oldfiles
9028 		    ++s;
9029 		i = (int)getdigits(&s);
9030 		if (s == src + off + 2 && src[off + 1] == '-')
9031 		    // just a minus sign, don't skip over it
9032 		    s--;
9033 		*usedlen = (int)(s - src); // length of what we expand
9034 
9035 		if (src[off + 1] == '<' && i != 0)
9036 		{
9037 		    if (*usedlen < off + 2)
9038 		    {
9039 			// Should we give an error message for #<text?
9040 			*usedlen = off + 1;
9041 			return NULL;
9042 		    }
9043 #ifdef FEAT_EVAL
9044 		    result = list_find_str(get_vim_var_list(VV_OLDFILES),
9045 								     (long)i);
9046 		    if (result == NULL)
9047 		    {
9048 			*errormsg = "";
9049 			return NULL;
9050 		    }
9051 #else
9052 		    *errormsg = _("E809: #< is not available without the +eval feature");
9053 		    return NULL;
9054 #endif
9055 		}
9056 		else
9057 		{
9058 		    if (i == 0 && src[off + 1] == '<' && *usedlen > off + 1)
9059 			*usedlen = off + 1;
9060 		    buf = buflist_findnr(i);
9061 		    if (buf == NULL)
9062 		    {
9063 			*errormsg = _("E194: No alternate file name to substitute for '#'");
9064 			return NULL;
9065 		    }
9066 		    if (lnump != NULL)
9067 			*lnump = ECMD_LAST;
9068 		    if (buf->b_fname == NULL)
9069 		    {
9070 			result = (char_u *)"";
9071 			valid = 0;	    // Must have ":p:h" to be valid
9072 		    }
9073 		    else
9074 		    {
9075 			result = buf->b_fname;
9076 			tilde_file = STRCMP(result, "~") == 0;
9077 		    }
9078 		}
9079 		break;
9080 
9081 #ifdef FEAT_SEARCHPATH
9082 	case SPEC_CFILE:	// file name under cursor
9083 		result = file_name_at_cursor(FNAME_MESS|FNAME_HYP, 1L, NULL);
9084 		if (result == NULL)
9085 		{
9086 		    *errormsg = "";
9087 		    return NULL;
9088 		}
9089 		resultbuf = result;	    // remember allocated string
9090 		break;
9091 #endif
9092 
9093 	case SPEC_AFILE:	// file name for autocommand
9094 		result = autocmd_fname;
9095 		if (result != NULL && !autocmd_fname_full)
9096 		{
9097 		    // Still need to turn the fname into a full path.  It is
9098 		    // postponed to avoid a delay when <afile> is not used.
9099 		    autocmd_fname_full = TRUE;
9100 		    result = FullName_save(autocmd_fname, FALSE);
9101 		    vim_free(autocmd_fname);
9102 		    autocmd_fname = result;
9103 		}
9104 		if (result == NULL)
9105 		{
9106 		    *errormsg = _("E495: no autocommand file name to substitute for \"<afile>\"");
9107 		    return NULL;
9108 		}
9109 		result = shorten_fname1(result);
9110 		break;
9111 
9112 	case SPEC_ABUF:		// buffer number for autocommand
9113 		if (autocmd_bufnr <= 0)
9114 		{
9115 		    *errormsg = _("E496: no autocommand buffer number to substitute for \"<abuf>\"");
9116 		    return NULL;
9117 		}
9118 		sprintf((char *)strbuf, "%d", autocmd_bufnr);
9119 		result = strbuf;
9120 		break;
9121 
9122 	case SPEC_AMATCH:	// match name for autocommand
9123 		result = autocmd_match;
9124 		if (result == NULL)
9125 		{
9126 		    *errormsg = _("E497: no autocommand match name to substitute for \"<amatch>\"");
9127 		    return NULL;
9128 		}
9129 		break;
9130 
9131 	case SPEC_SFILE:	// file name for ":so" command
9132 	case SPEC_STACK:	// call stack
9133 		result = estack_sfile(spec_idx == SPEC_SFILE
9134 						? ESTACK_SFILE : ESTACK_STACK);
9135 		if (result == NULL)
9136 		{
9137 		    *errormsg = spec_idx == SPEC_SFILE
9138 			? _("E498: no :source file name to substitute for \"<sfile>\"")
9139 			: _("E489: no call stack to substitute for \"<stack>\"");
9140 		    return NULL;
9141 		}
9142 		resultbuf = result;	    // remember allocated string
9143 		break;
9144 
9145 	case SPEC_SLNUM:	// line in file for ":so" command
9146 		if (SOURCING_NAME == NULL || SOURCING_LNUM == 0)
9147 		{
9148 		    *errormsg = _("E842: no line number to use for \"<slnum>\"");
9149 		    return NULL;
9150 		}
9151 		sprintf((char *)strbuf, "%ld", SOURCING_LNUM);
9152 		result = strbuf;
9153 		break;
9154 
9155 #ifdef FEAT_EVAL
9156 	case SPEC_SFLNUM:	// line in script file
9157 		if (current_sctx.sc_lnum + SOURCING_LNUM == 0)
9158 		{
9159 		    *errormsg = _("E961: no line number to use for \"<sflnum>\"");
9160 		    return NULL;
9161 		}
9162 		sprintf((char *)strbuf, "%ld",
9163 				 (long)(current_sctx.sc_lnum + SOURCING_LNUM));
9164 		result = strbuf;
9165 		break;
9166 
9167 	case SPEC_SID:
9168 		if (current_sctx.sc_sid <= 0)
9169 		{
9170 		    *errormsg = _(e_usingsid);
9171 		    return NULL;
9172 		}
9173 		sprintf((char *)strbuf, "<SNR>%d_", current_sctx.sc_sid);
9174 		result = strbuf;
9175 		break;
9176 #endif
9177 
9178 #ifdef FEAT_CLIENTSERVER
9179 	case SPEC_CLIENT:	// Source of last submitted input
9180 		sprintf((char *)strbuf, PRINTF_HEX_LONG_U,
9181 							(long_u)clientWindow);
9182 		result = strbuf;
9183 		break;
9184 #endif
9185 
9186 	default:
9187 		result = (char_u *)""; // avoid gcc warning
9188 		break;
9189 	}
9190 
9191 	resultlen = (int)STRLEN(result);	// length of new string
9192 	if (src[*usedlen] == '<')	// remove the file name extension
9193 	{
9194 	    ++*usedlen;
9195 	    if ((s = vim_strrchr(result, '.')) != NULL && s >= gettail(result))
9196 		resultlen = (int)(s - result);
9197 	}
9198 	else if (!skip_mod)
9199 	{
9200 	    valid |= modify_fname(src, tilde_file, usedlen, &result, &resultbuf,
9201 								  &resultlen);
9202 	    if (result == NULL)
9203 	    {
9204 		*errormsg = "";
9205 		return NULL;
9206 	    }
9207 	}
9208     }
9209 
9210     if (resultlen == 0 || valid != VALID_HEAD + VALID_PATH)
9211     {
9212 	if (valid != VALID_HEAD + VALID_PATH)
9213 	    // xgettext:no-c-format
9214 	    *errormsg = _("E499: Empty file name for '%' or '#', only works with \":p:h\"");
9215 	else
9216 	    *errormsg = _("E500: Evaluates to an empty string");
9217 	result = NULL;
9218     }
9219     else
9220 	result = vim_strnsave(result, resultlen);
9221     vim_free(resultbuf);
9222     return result;
9223 }
9224 
9225 /*
9226  * Expand the <sfile> string in "arg".
9227  *
9228  * Returns an allocated string, or NULL for any error.
9229  */
9230     char_u *
expand_sfile(char_u * arg)9231 expand_sfile(char_u *arg)
9232 {
9233     char	*errormsg;
9234     int		len;
9235     char_u	*result;
9236     char_u	*newres;
9237     char_u	*repl;
9238     int		srclen;
9239     char_u	*p;
9240 
9241     result = vim_strsave(arg);
9242     if (result == NULL)
9243 	return NULL;
9244 
9245     for (p = result; *p; )
9246     {
9247 	if (STRNCMP(p, "<sfile>", 7) != 0)
9248 	    ++p;
9249 	else
9250 	{
9251 	    // replace "<sfile>" with the sourced file name, and do ":" stuff
9252 	    repl = eval_vars(p, result, &srclen, NULL, &errormsg, NULL);
9253 	    if (errormsg != NULL)
9254 	    {
9255 		if (*errormsg)
9256 		    emsg(errormsg);
9257 		vim_free(result);
9258 		return NULL;
9259 	    }
9260 	    if (repl == NULL)		// no match (cannot happen)
9261 	    {
9262 		p += srclen;
9263 		continue;
9264 	    }
9265 	    len = (int)STRLEN(result) - srclen + (int)STRLEN(repl) + 1;
9266 	    newres = alloc(len);
9267 	    if (newres == NULL)
9268 	    {
9269 		vim_free(repl);
9270 		vim_free(result);
9271 		return NULL;
9272 	    }
9273 	    mch_memmove(newres, result, (size_t)(p - result));
9274 	    STRCPY(newres + (p - result), repl);
9275 	    len = (int)STRLEN(newres);
9276 	    STRCAT(newres, p + srclen);
9277 	    vim_free(repl);
9278 	    vim_free(result);
9279 	    result = newres;
9280 	    p = newres + len;		// continue after the match
9281 	}
9282     }
9283 
9284     return result;
9285 }
9286 
9287 #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG) || defined(PROTO)
9288 /*
9289  * Make a dialog message in "buff[DIALOG_MSG_SIZE]".
9290  * "format" must contain "%s".
9291  */
9292     void
dialog_msg(char_u * buff,char * format,char_u * fname)9293 dialog_msg(char_u *buff, char *format, char_u *fname)
9294 {
9295     if (fname == NULL)
9296 	fname = (char_u *)_("Untitled");
9297     vim_snprintf((char *)buff, DIALOG_MSG_SIZE, format, fname);
9298 }
9299 #endif
9300 
9301 /*
9302  * ":behave {mswin,xterm}"
9303  */
9304     static void
ex_behave(exarg_T * eap)9305 ex_behave(exarg_T *eap)
9306 {
9307     if (STRCMP(eap->arg, "mswin") == 0)
9308     {
9309 	set_option_value((char_u *)"selection", 0L, (char_u *)"exclusive", 0);
9310 	set_option_value((char_u *)"selectmode", 0L, (char_u *)"mouse,key", 0);
9311 	set_option_value((char_u *)"mousemodel", 0L, (char_u *)"popup", 0);
9312 	set_option_value((char_u *)"keymodel", 0L,
9313 					     (char_u *)"startsel,stopsel", 0);
9314     }
9315     else if (STRCMP(eap->arg, "xterm") == 0)
9316     {
9317 	set_option_value((char_u *)"selection", 0L, (char_u *)"inclusive", 0);
9318 	set_option_value((char_u *)"selectmode", 0L, (char_u *)"", 0);
9319 	set_option_value((char_u *)"mousemodel", 0L, (char_u *)"extend", 0);
9320 	set_option_value((char_u *)"keymodel", 0L, (char_u *)"", 0);
9321     }
9322     else
9323 	semsg(_(e_invarg2), eap->arg);
9324 }
9325 
9326 static int filetype_detect = FALSE;
9327 static int filetype_plugin = FALSE;
9328 static int filetype_indent = FALSE;
9329 
9330 /*
9331  * ":filetype [plugin] [indent] {on,off,detect}"
9332  * on: Load the filetype.vim file to install autocommands for file types.
9333  * off: Load the ftoff.vim file to remove all autocommands for file types.
9334  * plugin on: load filetype.vim and ftplugin.vim
9335  * plugin off: load ftplugof.vim
9336  * indent on: load filetype.vim and indent.vim
9337  * indent off: load indoff.vim
9338  */
9339     static void
ex_filetype(exarg_T * eap)9340 ex_filetype(exarg_T *eap)
9341 {
9342     char_u	*arg = eap->arg;
9343     int		plugin = FALSE;
9344     int		indent = FALSE;
9345 
9346     if (*eap->arg == NUL)
9347     {
9348 	// Print current status.
9349 	smsg("filetype detection:%s  plugin:%s  indent:%s",
9350 		filetype_detect ? "ON" : "OFF",
9351 		filetype_plugin ? (filetype_detect ? "ON" : "(on)") : "OFF",
9352 		filetype_indent ? (filetype_detect ? "ON" : "(on)") : "OFF");
9353 	return;
9354     }
9355 
9356     // Accept "plugin" and "indent" in any order.
9357     for (;;)
9358     {
9359 	if (STRNCMP(arg, "plugin", 6) == 0)
9360 	{
9361 	    plugin = TRUE;
9362 	    arg = skipwhite(arg + 6);
9363 	    continue;
9364 	}
9365 	if (STRNCMP(arg, "indent", 6) == 0)
9366 	{
9367 	    indent = TRUE;
9368 	    arg = skipwhite(arg + 6);
9369 	    continue;
9370 	}
9371 	break;
9372     }
9373     if (STRCMP(arg, "on") == 0 || STRCMP(arg, "detect") == 0)
9374     {
9375 	if (*arg == 'o' || !filetype_detect)
9376 	{
9377 	    source_runtime((char_u *)FILETYPE_FILE, DIP_ALL);
9378 	    filetype_detect = TRUE;
9379 	    if (plugin)
9380 	    {
9381 		source_runtime((char_u *)FTPLUGIN_FILE, DIP_ALL);
9382 		filetype_plugin = TRUE;
9383 	    }
9384 	    if (indent)
9385 	    {
9386 		source_runtime((char_u *)INDENT_FILE, DIP_ALL);
9387 		filetype_indent = TRUE;
9388 	    }
9389 	}
9390 	if (*arg == 'd')
9391 	{
9392 	    (void)do_doautocmd((char_u *)"filetypedetect BufRead", TRUE, NULL);
9393 	    do_modelines(0);
9394 	}
9395     }
9396     else if (STRCMP(arg, "off") == 0)
9397     {
9398 	if (plugin || indent)
9399 	{
9400 	    if (plugin)
9401 	    {
9402 		source_runtime((char_u *)FTPLUGOF_FILE, DIP_ALL);
9403 		filetype_plugin = FALSE;
9404 	    }
9405 	    if (indent)
9406 	    {
9407 		source_runtime((char_u *)INDOFF_FILE, DIP_ALL);
9408 		filetype_indent = FALSE;
9409 	    }
9410 	}
9411 	else
9412 	{
9413 	    source_runtime((char_u *)FTOFF_FILE, DIP_ALL);
9414 	    filetype_detect = FALSE;
9415 	}
9416     }
9417     else
9418 	semsg(_(e_invarg2), arg);
9419 }
9420 
9421 /*
9422  * ":setfiletype [FALLBACK] {name}"
9423  */
9424     static void
ex_setfiletype(exarg_T * eap)9425 ex_setfiletype(exarg_T *eap)
9426 {
9427     if (!did_filetype)
9428     {
9429 	char_u *arg = eap->arg;
9430 
9431 	if (STRNCMP(arg, "FALLBACK ", 9) == 0)
9432 	    arg += 9;
9433 
9434 	set_option_value((char_u *)"filetype", 0L, arg, OPT_LOCAL);
9435 	if (arg != eap->arg)
9436 	    did_filetype = FALSE;
9437     }
9438 }
9439 
9440     static void
ex_digraphs(exarg_T * eap UNUSED)9441 ex_digraphs(exarg_T *eap UNUSED)
9442 {
9443 #ifdef FEAT_DIGRAPHS
9444     if (*eap->arg != NUL)
9445 	putdigraph(eap->arg);
9446     else
9447 	listdigraphs(eap->forceit);
9448 #else
9449     emsg(_(e_no_digraphs_version));
9450 #endif
9451 }
9452 
9453 #if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
9454     void
set_no_hlsearch(int flag)9455 set_no_hlsearch(int flag)
9456 {
9457     no_hlsearch = flag;
9458 # ifdef FEAT_EVAL
9459     set_vim_var_nr(VV_HLSEARCH, !no_hlsearch && p_hls);
9460 # endif
9461 }
9462 
9463 /*
9464  * ":nohlsearch"
9465  */
9466     static void
ex_nohlsearch(exarg_T * eap UNUSED)9467 ex_nohlsearch(exarg_T *eap UNUSED)
9468 {
9469     set_no_hlsearch(TRUE);
9470     redraw_all_later(SOME_VALID);
9471 }
9472 #endif
9473 
9474 #ifdef FEAT_CRYPT
9475 /*
9476  * ":X": Get crypt key
9477  */
9478     static void
ex_X(exarg_T * eap UNUSED)9479 ex_X(exarg_T *eap UNUSED)
9480 {
9481     crypt_check_current_method();
9482     (void)crypt_get_key(TRUE, TRUE);
9483 }
9484 #endif
9485 
9486 #ifdef FEAT_FOLDING
9487     static void
ex_fold(exarg_T * eap)9488 ex_fold(exarg_T *eap)
9489 {
9490     if (foldManualAllowed(TRUE))
9491 	foldCreate(eap->line1, eap->line2);
9492 }
9493 
9494     static void
ex_foldopen(exarg_T * eap)9495 ex_foldopen(exarg_T *eap)
9496 {
9497     opFoldRange(eap->line1, eap->line2, eap->cmdidx == CMD_foldopen,
9498 							 eap->forceit, FALSE);
9499 }
9500 
9501     static void
ex_folddo(exarg_T * eap)9502 ex_folddo(exarg_T *eap)
9503 {
9504     linenr_T	lnum;
9505 
9506 # ifdef FEAT_CLIPBOARD
9507     start_global_changes();
9508 # endif
9509 
9510     // First set the marks for all lines closed/open.
9511     for (lnum = eap->line1; lnum <= eap->line2; ++lnum)
9512 	if (hasFolding(lnum, NULL, NULL) == (eap->cmdidx == CMD_folddoclosed))
9513 	    ml_setmarked(lnum);
9514 
9515     // Execute the command on the marked lines.
9516     global_exe(eap->arg);
9517     ml_clearmarked();	   // clear rest of the marks
9518 # ifdef FEAT_CLIPBOARD
9519     end_global_changes();
9520 # endif
9521 }
9522 #endif
9523 
9524 #if defined(FEAT_QUICKFIX) || defined(PROTO)
9525 /*
9526  * Returns TRUE if the supplied Ex cmdidx is for a location list command
9527  * instead of a quickfix command.
9528  */
9529     int
is_loclist_cmd(int cmdidx)9530 is_loclist_cmd(int cmdidx)
9531 {
9532     if (cmdidx < 0 || cmdidx >= CMD_SIZE)
9533 	return FALSE;
9534     return cmdnames[cmdidx].cmd_name[0] == 'l';
9535 }
9536 #endif
9537 
9538 #if defined(FEAT_TIMERS) || defined(PROTO)
9539     int
get_pressedreturn(void)9540 get_pressedreturn(void)
9541 {
9542     return ex_pressedreturn;
9543 }
9544 
9545     void
set_pressedreturn(int val)9546 set_pressedreturn(int val)
9547 {
9548      ex_pressedreturn = val;
9549 }
9550 #endif
9551