xref: /vim-8.2.3635/src/ex_docmd.c (revision 26190b27)
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
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
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
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
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
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
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
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
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 *
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
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
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
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 *
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 *
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
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
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
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
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 *
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 	if ((*ea.cmd == '|' || (exmode_active && ea.line1 != ea.line2))
1981 #ifdef FEAT_EVAL
1982 		&& !vim9script
1983 #endif
1984 	   )
1985 	{
1986 	    ea.cmdidx = CMD_print;
1987 	    ea.argt = EX_RANGE+EX_COUNT+EX_TRLBAR;
1988 	    if ((errormsg = invalid_range(&ea)) == NULL)
1989 	    {
1990 		correct_range(&ea);
1991 		ex_print(&ea);
1992 	    }
1993 	}
1994 	else if (ea.addr_count != 0)
1995 	{
1996 	    if (ea.line2 > curbuf->b_ml.ml_line_count)
1997 	    {
1998 		// With '-' in 'cpoptions' a line number past the file is an
1999 		// error, otherwise put it at the end of the file.
2000 		if (vim_strchr(p_cpo, CPO_MINUS) != NULL)
2001 		    ea.line2 = -1;
2002 		else
2003 		    ea.line2 = curbuf->b_ml.ml_line_count;
2004 	    }
2005 
2006 	    if (ea.line2 < 0)
2007 		errormsg = _(e_invalid_range);
2008 	    else
2009 	    {
2010 		if (ea.line2 == 0)
2011 		    curwin->w_cursor.lnum = 1;
2012 		else
2013 		    curwin->w_cursor.lnum = ea.line2;
2014 		beginline(BL_SOL | BL_FIX);
2015 	    }
2016 	}
2017 	goto doend;
2018     }
2019 
2020     // If this looks like an undefined user command and there are CmdUndefined
2021     // autocommands defined, trigger the matching autocommands.
2022     if (p != NULL && ea.cmdidx == CMD_SIZE && !ea.skip
2023 	    && ASCII_ISUPPER(*ea.cmd)
2024 	    && has_cmdundefined())
2025     {
2026 	int ret;
2027 
2028 	p = ea.cmd;
2029 	while (ASCII_ISALNUM(*p))
2030 	    ++p;
2031 	p = vim_strnsave(ea.cmd, p - ea.cmd);
2032 	ret = apply_autocmds(EVENT_CMDUNDEFINED, p, p, TRUE, NULL);
2033 	vim_free(p);
2034 	// If the autocommands did something and didn't cause an error, try
2035 	// finding the command again.
2036 	p = (ret
2037 #ifdef FEAT_EVAL
2038 		&& !aborting()
2039 #endif
2040 		) ? find_ex_command(&ea, NULL, NULL, NULL) : ea.cmd;
2041     }
2042 
2043     if (p == NULL)
2044     {
2045 	if (!ea.skip)
2046 	    errormsg = _(e_ambiguous_use_of_user_defined_command);
2047 	goto doend;
2048     }
2049     // Check for wrong commands.
2050     if (*p == '!' && ea.cmd[1] == 0151 && ea.cmd[0] == 78
2051 	    && !IS_USER_CMDIDX(ea.cmdidx))
2052     {
2053 	errormsg = uc_fun_cmd();
2054 	goto doend;
2055     }
2056 
2057     if (ea.cmdidx == CMD_SIZE)
2058     {
2059 	if (!ea.skip)
2060 	{
2061 	    STRCPY(IObuff, _("E492: Not an editor command"));
2062 	    if (!sourcing)
2063 	    {
2064 		// If the modifier was parsed OK the error must be in the
2065 		// following command
2066 		if (after_modifier != NULL)
2067 		    append_command(after_modifier);
2068 		else
2069 		    append_command(*cmdlinep);
2070 	    }
2071 	    errormsg = (char *)IObuff;
2072 	    did_emsg_syntax = TRUE;
2073 	}
2074 	goto doend;
2075     }
2076 
2077     ni = (!IS_USER_CMDIDX(ea.cmdidx)
2078 	    && (cmdnames[ea.cmdidx].cmd_func == ex_ni
2079 #ifdef HAVE_EX_SCRIPT_NI
2080 	     || cmdnames[ea.cmdidx].cmd_func == ex_script_ni
2081 #endif
2082 	     ));
2083 
2084 #ifndef FEAT_EVAL
2085     /*
2086      * When the expression evaluation is disabled, recognize the ":if" and
2087      * ":endif" commands and ignore everything in between it.
2088      */
2089     if (ea.cmdidx == CMD_if)
2090 	++if_level;
2091     if (if_level)
2092     {
2093 	if (ea.cmdidx == CMD_endif)
2094 	    --if_level;
2095 	goto doend;
2096     }
2097 
2098 #endif
2099 
2100     // forced commands
2101     if (*p == '!' && ea.cmdidx != CMD_substitute
2102 	    && ea.cmdidx != CMD_smagic && ea.cmdidx != CMD_snomagic)
2103     {
2104 	++p;
2105 	ea.forceit = TRUE;
2106     }
2107     else
2108 	ea.forceit = FALSE;
2109 
2110 /*
2111  * 6. Parse arguments.  Then check for errors.
2112  */
2113     if (!IS_USER_CMDIDX(ea.cmdidx))
2114 	ea.argt = (long)cmdnames[(int)ea.cmdidx].cmd_argt;
2115 
2116     if (!ea.skip)
2117     {
2118 #ifdef HAVE_SANDBOX
2119 	if (sandbox != 0 && !(ea.argt & EX_SBOXOK))
2120 	{
2121 	    // Command not allowed in sandbox.
2122 	    errormsg = _(e_not_allowed_in_sandbox);
2123 	    goto doend;
2124 	}
2125 #endif
2126 	if (restricted != 0 && (ea.argt & EX_RESTRICT))
2127 	{
2128 	    errormsg = _("E981: Command not allowed in rvim");
2129 	    goto doend;
2130 	}
2131 	if (!curbuf->b_p_ma && (ea.argt & EX_MODIFY))
2132 	{
2133 	    // Command not allowed in non-'modifiable' buffer
2134 	    errormsg = _(e_cannot_make_changes_modifiable_is_off);
2135 	    goto doend;
2136 	}
2137 
2138 	if (!IS_USER_CMDIDX(ea.cmdidx))
2139 	{
2140 #ifdef FEAT_CMDWIN
2141 	    if (cmdwin_type != 0 && !(ea.argt & EX_CMDWIN))
2142 	    {
2143 		// Command not allowed in the command line window
2144 		errormsg = _(e_invalid_in_cmdline_window);
2145 		goto doend;
2146 	    }
2147 #endif
2148 	    if (text_locked() && !(ea.argt & EX_LOCK_OK))
2149 	    {
2150 		// Command not allowed when text is locked
2151 		errormsg = _(get_text_locked_msg());
2152 		goto doend;
2153 	    }
2154 	}
2155 
2156 	// Disallow editing another buffer when "curbuf_lock" is set.
2157 	// Do allow ":checktime" (it is postponed).
2158 	// Do allow ":edit" (check for an argument later).
2159 	// Do allow ":file" with no arguments (check for an argument later).
2160 	if (!(ea.argt & (EX_CMDWIN | EX_LOCK_OK))
2161 		&& ea.cmdidx != CMD_checktime
2162 		&& ea.cmdidx != CMD_edit
2163 		&& ea.cmdidx != CMD_file
2164 		&& !IS_USER_CMDIDX(ea.cmdidx)
2165 		&& curbuf_locked())
2166 	    goto doend;
2167 
2168 	if (!ni && !(ea.argt & EX_RANGE) && ea.addr_count > 0)
2169 	{
2170 	    // no range allowed
2171 	    errormsg = _(e_norange);
2172 	    goto doend;
2173 	}
2174     }
2175 
2176     if (!ni && !(ea.argt & EX_BANG) && ea.forceit)	// no <!> allowed
2177     {
2178 	errormsg = _(e_nobang);
2179 	goto doend;
2180     }
2181 
2182     /*
2183      * Don't complain about the range if it is not used
2184      * (could happen if line_count is accidentally set to 0).
2185      */
2186     if (!ea.skip && !ni && (ea.argt & EX_RANGE))
2187     {
2188 	/*
2189 	 * If the range is backwards, ask for confirmation and, if given, swap
2190 	 * ea.line1 & ea.line2 so it's forwards again.
2191 	 * When global command is busy, don't ask, will fail below.
2192 	 */
2193 	if (!global_busy && ea.line1 > ea.line2)
2194 	{
2195 	    if (msg_silent == 0)
2196 	    {
2197 		if (sourcing || exmode_active)
2198 		{
2199 		    errormsg = _("E493: Backwards range given");
2200 		    goto doend;
2201 		}
2202 		if (ask_yesno((char_u *)
2203 			_("Backwards range given, OK to swap"), FALSE) != 'y')
2204 		    goto doend;
2205 	    }
2206 	    lnum = ea.line1;
2207 	    ea.line1 = ea.line2;
2208 	    ea.line2 = lnum;
2209 	}
2210 	if ((errormsg = invalid_range(&ea)) != NULL)
2211 	    goto doend;
2212     }
2213 
2214     if ((ea.addr_type == ADDR_OTHER) && ea.addr_count == 0)
2215 	// default is 1, not cursor
2216 	ea.line2 = 1;
2217 
2218     correct_range(&ea);
2219 
2220 #ifdef FEAT_FOLDING
2221     if (((ea.argt & EX_WHOLEFOLD) || ea.addr_count >= 2) && !global_busy
2222 	    && ea.addr_type == ADDR_LINES)
2223     {
2224 	// Put the first line at the start of a closed fold, put the last line
2225 	// at the end of a closed fold.
2226 	(void)hasFolding(ea.line1, &ea.line1, NULL);
2227 	(void)hasFolding(ea.line2, NULL, &ea.line2);
2228     }
2229 #endif
2230 
2231 #ifdef FEAT_QUICKFIX
2232     /*
2233      * For the ":make" and ":grep" commands we insert the 'makeprg'/'grepprg'
2234      * option here, so things like % get expanded.
2235      */
2236     p = replace_makeprg(&ea, p, cmdlinep);
2237     if (p == NULL)
2238 	goto doend;
2239 #endif
2240 
2241     /*
2242      * Skip to start of argument.
2243      * Don't do this for the ":!" command, because ":!! -l" needs the space.
2244      */
2245     if (ea.cmdidx == CMD_bang)
2246 	ea.arg = p;
2247     else
2248 	ea.arg = skipwhite(p);
2249 
2250     // ":file" cannot be run with an argument when "curbuf_lock" is set
2251     if (ea.cmdidx == CMD_file && *ea.arg != NUL && curbuf_locked())
2252 	goto doend;
2253 
2254     /*
2255      * Check for "++opt=val" argument.
2256      * Must be first, allow ":w ++enc=utf8 !cmd"
2257      */
2258     if (ea.argt & EX_ARGOPT)
2259 	while (ea.arg[0] == '+' && ea.arg[1] == '+')
2260 	    if (getargopt(&ea) == FAIL && !ni)
2261 	    {
2262 		errormsg = _(e_invarg);
2263 		goto doend;
2264 	    }
2265 
2266     if (ea.cmdidx == CMD_write || ea.cmdidx == CMD_update)
2267     {
2268 	if (*ea.arg == '>')			// append
2269 	{
2270 	    if (*++ea.arg != '>')		// typed wrong
2271 	    {
2272 		errormsg = _("E494: Use w or w>>");
2273 		goto doend;
2274 	    }
2275 	    ea.arg = skipwhite(ea.arg + 1);
2276 	    ea.append = TRUE;
2277 	}
2278 	else if (*ea.arg == '!' && ea.cmdidx == CMD_write)  // :w !filter
2279 	{
2280 	    ++ea.arg;
2281 	    ea.usefilter = TRUE;
2282 	}
2283     }
2284 
2285     if (ea.cmdidx == CMD_read)
2286     {
2287 	if (ea.forceit)
2288 	{
2289 	    ea.usefilter = TRUE;		// :r! filter if ea.forceit
2290 	    ea.forceit = FALSE;
2291 	}
2292 	else if (*ea.arg == '!')		// :r !filter
2293 	{
2294 	    ++ea.arg;
2295 	    ea.usefilter = TRUE;
2296 	}
2297     }
2298 
2299     if (ea.cmdidx == CMD_lshift || ea.cmdidx == CMD_rshift)
2300     {
2301 	ea.amount = 1;
2302 	while (*ea.arg == *ea.cmd)		// count number of '>' or '<'
2303 	{
2304 	    ++ea.arg;
2305 	    ++ea.amount;
2306 	}
2307 	ea.arg = skipwhite(ea.arg);
2308     }
2309 
2310     /*
2311      * Check for "+command" argument, before checking for next command.
2312      * Don't do this for ":read !cmd" and ":write !cmd".
2313      */
2314     if ((ea.argt & EX_CMDARG) && !ea.usefilter)
2315 	ea.do_ecmd_cmd = getargcmd(&ea.arg);
2316 
2317     /*
2318      * For commands that do not use '|' inside their argument: Check for '|' to
2319      * separate commands and '"' or '#' to start comments.
2320      *
2321      * Otherwise: Check for <newline> to end a shell command.
2322      * Also do this for ":read !cmd", ":write !cmd" and ":global".
2323      * Also do this inside a { - } block after :command and :autocmd.
2324      * Any others?
2325      */
2326     if ((ea.argt & EX_TRLBAR) && !ea.usefilter)
2327     {
2328 	separate_nextcmd(&ea);
2329     }
2330     else if (ea.cmdidx == CMD_bang
2331 	    || ea.cmdidx == CMD_terminal
2332 	    || ea.cmdidx == CMD_global
2333 	    || ea.cmdidx == CMD_vglobal
2334 	    || ea.usefilter
2335 #ifdef FEAT_EVAL
2336 	    || inside_block(&ea)
2337 #endif
2338 	    )
2339     {
2340 	for (p = ea.arg; *p; ++p)
2341 	{
2342 	    // Remove one backslash before a newline, so that it's possible to
2343 	    // pass a newline to the shell and also a newline that is preceded
2344 	    // with a backslash.  This makes it impossible to end a shell
2345 	    // command in a backslash, but that doesn't appear useful.
2346 	    // Halving the number of backslashes is incompatible with previous
2347 	    // versions.
2348 	    if (*p == '\\' && p[1] == '\n')
2349 		STRMOVE(p, p + 1);
2350 	    else if (*p == '\n')
2351 	    {
2352 		ea.nextcmd = p + 1;
2353 		*p = NUL;
2354 		break;
2355 	    }
2356 	}
2357     }
2358 
2359     if ((ea.argt & EX_DFLALL) && ea.addr_count == 0)
2360 	address_default_all(&ea);
2361 
2362     // accept numbered register only when no count allowed (:put)
2363     if (       (ea.argt & EX_REGSTR)
2364 	    && *ea.arg != NUL
2365 	       // Do not allow register = for user commands
2366 	    && (!IS_USER_CMDIDX(ea.cmdidx) || *ea.arg != '=')
2367 	    && !((ea.argt & EX_COUNT) && VIM_ISDIGIT(*ea.arg)))
2368     {
2369 #ifndef FEAT_CLIPBOARD
2370 	// check these explicitly for a more specific error message
2371 	if (*ea.arg == '*' || *ea.arg == '+')
2372 	{
2373 	    errormsg = _(e_invalidreg);
2374 	    goto doend;
2375 	}
2376 #endif
2377 	if (valid_yank_reg(*ea.arg, (ea.cmdidx != CMD_put
2378 					      && !IS_USER_CMDIDX(ea.cmdidx))))
2379 	{
2380 	    ea.regname = *ea.arg++;
2381 #ifdef FEAT_EVAL
2382 	    // for '=' register: accept the rest of the line as an expression
2383 	    if (ea.arg[-1] == '=' && ea.arg[0] != NUL)
2384 	    {
2385 		if (!ea.skip)
2386 		{
2387 		    set_expr_line(vim_strsave(ea.arg), &ea);
2388 		    did_set_expr_line = TRUE;
2389 		}
2390 		ea.arg += STRLEN(ea.arg);
2391 	    }
2392 #endif
2393 	    ea.arg = skipwhite(ea.arg);
2394 	}
2395     }
2396 
2397     /*
2398      * Check for a count.  When accepting a EX_BUFNAME, don't use "123foo" as a
2399      * count, it's a buffer name.
2400      */
2401     if ((ea.argt & EX_COUNT) && VIM_ISDIGIT(*ea.arg)
2402 	    && (!(ea.argt & EX_BUFNAME) || *(p = skipdigits(ea.arg + 1)) == NUL
2403 							  || VIM_ISWHITE(*p)))
2404     {
2405 	n = getdigits(&ea.arg);
2406 	ea.arg = skipwhite(ea.arg);
2407 	if (n <= 0 && !ni && (ea.argt & EX_ZEROR) == 0)
2408 	{
2409 	    errormsg = _(e_zerocount);
2410 	    goto doend;
2411 	}
2412 	if (ea.addr_type != ADDR_LINES)	// e.g. :buffer 2, :sleep 3
2413 	{
2414 	    ea.line2 = n;
2415 	    if (ea.addr_count == 0)
2416 		ea.addr_count = 1;
2417 	}
2418 	else
2419 	{
2420 	    ea.line1 = ea.line2;
2421 	    ea.line2 += n - 1;
2422 	    ++ea.addr_count;
2423 	    /*
2424 	     * Be vi compatible: no error message for out of range.
2425 	     */
2426 	    if (ea.line2 > curbuf->b_ml.ml_line_count)
2427 		ea.line2 = curbuf->b_ml.ml_line_count;
2428 	}
2429     }
2430 
2431     /*
2432      * Check for flags: 'l', 'p' and '#'.
2433      */
2434     if (ea.argt & EX_FLAGS)
2435 	get_flags(&ea);
2436     if (!ni && !(ea.argt & EX_EXTRA) && *ea.arg != NUL
2437 	    && *ea.arg != '"' && (*ea.arg != '|' || (ea.argt & EX_TRLBAR) == 0))
2438     {
2439 	// no arguments allowed but there is something
2440 	errormsg = ex_errmsg(e_trailing_arg, ea.arg);
2441 	goto doend;
2442     }
2443 
2444     if (!ni && (ea.argt & EX_NEEDARG) && *ea.arg == NUL)
2445     {
2446 	errormsg = _(e_argreq);
2447 	goto doend;
2448     }
2449 
2450 #ifdef FEAT_EVAL
2451     /*
2452      * Skip the command when it's not going to be executed.
2453      * The commands like :if, :endif, etc. always need to be executed.
2454      * Also make an exception for commands that handle a trailing command
2455      * themselves.
2456      */
2457     if (ea.skip)
2458     {
2459 	switch (ea.cmdidx)
2460 	{
2461 	    // commands that need evaluation
2462 	    case CMD_while:
2463 	    case CMD_endwhile:
2464 	    case CMD_for:
2465 	    case CMD_endfor:
2466 	    case CMD_if:
2467 	    case CMD_elseif:
2468 	    case CMD_else:
2469 	    case CMD_endif:
2470 	    case CMD_try:
2471 	    case CMD_catch:
2472 	    case CMD_finally:
2473 	    case CMD_endtry:
2474 	    case CMD_function:
2475 	    case CMD_def:
2476 				break;
2477 
2478 	    // Commands that handle '|' themselves.  Check: A command should
2479 	    // either have the EX_TRLBAR flag, appear in this list or appear in
2480 	    // the list at ":help :bar".
2481 	    case CMD_aboveleft:
2482 	    case CMD_and:
2483 	    case CMD_belowright:
2484 	    case CMD_botright:
2485 	    case CMD_browse:
2486 	    case CMD_call:
2487 	    case CMD_confirm:
2488 	    case CMD_const:
2489 	    case CMD_delfunction:
2490 	    case CMD_djump:
2491 	    case CMD_dlist:
2492 	    case CMD_dsearch:
2493 	    case CMD_dsplit:
2494 	    case CMD_echo:
2495 	    case CMD_echoerr:
2496 	    case CMD_echomsg:
2497 	    case CMD_echon:
2498 	    case CMD_eval:
2499 	    case CMD_execute:
2500 	    case CMD_filter:
2501 	    case CMD_final:
2502 	    case CMD_help:
2503 	    case CMD_hide:
2504 	    case CMD_ijump:
2505 	    case CMD_ilist:
2506 	    case CMD_isearch:
2507 	    case CMD_isplit:
2508 	    case CMD_keepalt:
2509 	    case CMD_keepjumps:
2510 	    case CMD_keepmarks:
2511 	    case CMD_keeppatterns:
2512 	    case CMD_leftabove:
2513 	    case CMD_let:
2514 	    case CMD_lockmarks:
2515 	    case CMD_lockvar:
2516 	    case CMD_lua:
2517 	    case CMD_match:
2518 	    case CMD_mzscheme:
2519 	    case CMD_noautocmd:
2520 	    case CMD_noswapfile:
2521 	    case CMD_perl:
2522 	    case CMD_psearch:
2523 	    case CMD_py3:
2524 	    case CMD_python3:
2525 	    case CMD_python:
2526 	    case CMD_return:
2527 	    case CMD_rightbelow:
2528 	    case CMD_ruby:
2529 	    case CMD_silent:
2530 	    case CMD_smagic:
2531 	    case CMD_snomagic:
2532 	    case CMD_substitute:
2533 	    case CMD_syntax:
2534 	    case CMD_tab:
2535 	    case CMD_tcl:
2536 	    case CMD_throw:
2537 	    case CMD_tilde:
2538 	    case CMD_topleft:
2539 	    case CMD_unlet:
2540 	    case CMD_unlockvar:
2541 	    case CMD_var:
2542 	    case CMD_verbose:
2543 	    case CMD_vertical:
2544 	    case CMD_wincmd:
2545 				break;
2546 
2547 	    default:		goto doend;
2548 	}
2549     }
2550 #endif
2551 
2552     if (ea.argt & EX_XFILE)
2553     {
2554 	if (expand_filename(&ea, cmdlinep, &errormsg) == FAIL)
2555 	    goto doend;
2556     }
2557 
2558     /*
2559      * Accept buffer name.  Cannot be used at the same time with a buffer
2560      * number.  Don't do this for a user command.
2561      */
2562     if ((ea.argt & EX_BUFNAME) && *ea.arg != NUL && ea.addr_count == 0
2563 	    && !IS_USER_CMDIDX(ea.cmdidx))
2564     {
2565 	/*
2566 	 * :bdelete, :bwipeout and :bunload take several arguments, separated
2567 	 * by spaces: find next space (skipping over escaped characters).
2568 	 * The others take one argument: ignore trailing spaces.
2569 	 */
2570 	if (ea.cmdidx == CMD_bdelete || ea.cmdidx == CMD_bwipeout
2571 						  || ea.cmdidx == CMD_bunload)
2572 	    p = skiptowhite_esc(ea.arg);
2573 	else
2574 	{
2575 	    p = ea.arg + STRLEN(ea.arg);
2576 	    while (p > ea.arg && VIM_ISWHITE(p[-1]))
2577 		--p;
2578 	}
2579 	ea.line2 = buflist_findpat(ea.arg, p, (ea.argt & EX_BUFUNL) != 0,
2580 								FALSE, FALSE);
2581 	if (ea.line2 < 0)	    // failed
2582 	    goto doend;
2583 	ea.addr_count = 1;
2584 	ea.arg = skipwhite(p);
2585     }
2586 
2587     // The :try command saves the emsg_silent flag, reset it here when
2588     // ":silent! try" was used, it should only apply to :try itself.
2589     if (ea.cmdidx == CMD_try && cmdmod.cmod_did_esilent > 0)
2590     {
2591 	emsg_silent -= cmdmod.cmod_did_esilent;
2592 	if (emsg_silent < 0)
2593 	    emsg_silent = 0;
2594 	cmdmod.cmod_did_esilent = 0;
2595     }
2596 
2597 /*
2598  * 7. Execute the command.
2599  */
2600 
2601     if (IS_USER_CMDIDX(ea.cmdidx))
2602     {
2603 	/*
2604 	 * Execute a user-defined command.
2605 	 */
2606 	do_ucmd(&ea);
2607     }
2608     else
2609     {
2610 	/*
2611 	 * Call the function to execute the builtin command.
2612 	 */
2613 	ea.errmsg = NULL;
2614 	(cmdnames[ea.cmdidx].cmd_func)(&ea);
2615 	if (ea.errmsg != NULL)
2616 	    errormsg = ea.errmsg;
2617     }
2618 
2619 #ifdef FEAT_EVAL
2620     // Set flag that any command was executed, used by ex_vim9script().
2621     // Not if this was a command that wasn't executed or :endif.
2622     if (getline_equal(ea.getline, ea.cookie, getsourceline)
2623 	    && current_sctx.sc_sid > 0
2624 	    && ea.cmdidx != CMD_endif
2625 	    && (cstack->cs_idx < 0
2626 		    || (cstack->cs_flags[cstack->cs_idx] & CSF_ACTIVE)))
2627 	SCRIPT_ITEM(current_sctx.sc_sid)->sn_state = SN_STATE_HAD_COMMAND;
2628 
2629     /*
2630      * If the command just executed called do_cmdline(), any throw or ":return"
2631      * or ":finish" encountered there must also check the cstack of the still
2632      * active do_cmdline() that called this do_one_cmd().  Rethrow an uncaught
2633      * exception, or reanimate a returned function or finished script file and
2634      * return or finish it again.
2635      */
2636     if (need_rethrow)
2637 	do_throw(cstack);
2638     else if (check_cstack)
2639     {
2640 	if (source_finished(fgetline, cookie))
2641 	    do_finish(&ea, TRUE);
2642 	else if (getline_equal(fgetline, cookie, get_func_line)
2643 						   && current_func_returned())
2644 	    do_return(&ea, TRUE, FALSE, NULL);
2645     }
2646     need_rethrow = check_cstack = FALSE;
2647 #endif
2648 
2649 doend:
2650     if (curwin->w_cursor.lnum == 0)	// can happen with zero line number
2651     {
2652 	curwin->w_cursor.lnum = 1;
2653 	curwin->w_cursor.col = 0;
2654     }
2655 
2656     if (errormsg != NULL && *errormsg != NUL && !did_emsg)
2657     {
2658 	if (sourcing)
2659 	{
2660 	    if (errormsg != (char *)IObuff)
2661 	    {
2662 		STRCPY(IObuff, errormsg);
2663 		errormsg = (char *)IObuff;
2664 	    }
2665 	    append_command(*cmdlinep);
2666 	}
2667 	emsg(errormsg);
2668     }
2669 #ifdef FEAT_EVAL
2670     do_errthrow(cstack,
2671 	    (ea.cmdidx != CMD_SIZE && !IS_USER_CMDIDX(ea.cmdidx))
2672 			? cmdnames[(int)ea.cmdidx].cmd_name : (char_u *)NULL);
2673 
2674     if (did_set_expr_line)
2675 	set_expr_line(NULL, NULL);
2676 #endif
2677 
2678     undo_cmdmod(&cmdmod);
2679     cmdmod = save_cmdmod;
2680     reg_executing = save_reg_executing;
2681 
2682     if (ea.nextcmd && *ea.nextcmd == NUL)	// not really a next command
2683 	ea.nextcmd = NULL;
2684 
2685 #ifdef FEAT_EVAL
2686     --ex_nesting_level;
2687     vim_free(ea.cmdline_tofree);
2688 #endif
2689 
2690     return ea.nextcmd;
2691 }
2692 #if (_MSC_VER == 1200)
2693  #pragma optimize( "", on )
2694 #endif
2695 
2696 static char ex_error_buf[MSG_BUF_LEN];
2697 
2698 /*
2699  * Return an error message with argument included.
2700  * Uses a static buffer, only the last error will be kept.
2701  * "msg" will be translated, caller should use N_().
2702  */
2703      char *
2704 ex_errmsg(char *msg, char_u *arg)
2705 {
2706     vim_snprintf(ex_error_buf, MSG_BUF_LEN, _(msg), arg);
2707     return ex_error_buf;
2708 }
2709 
2710 /*
2711  * Check for an Ex command with optional tail.
2712  * If there is a match advance "pp" to the argument and return TRUE.
2713  * If "noparen" is TRUE do not recognize the command followed by "(".
2714  */
2715     static int
2716 checkforcmd_opt(
2717     char_u	**pp,		// start of command
2718     char	*cmd,		// name of command
2719     int		len,		// required length
2720     int		noparen)
2721 {
2722     int		i;
2723 
2724     for (i = 0; cmd[i] != NUL; ++i)
2725 	if (((char_u *)cmd)[i] != (*pp)[i])
2726 	    break;
2727     if (i >= len && !isalpha((*pp)[i])
2728 			   && (*pp)[i] != '_' && (!noparen || (*pp)[i] != '('))
2729     {
2730 	*pp = skipwhite(*pp + i);
2731 	return TRUE;
2732     }
2733     return FALSE;
2734 }
2735 
2736 /*
2737  * Check for an Ex command with optional tail.
2738  * If there is a match advance "pp" to the argument and return TRUE.
2739  */
2740     int
2741 checkforcmd(
2742     char_u	**pp,		// start of command
2743     char	*cmd,		// name of command
2744     int		len)		// required length
2745 {
2746     return checkforcmd_opt(pp, cmd, len, FALSE);
2747 }
2748 
2749 /*
2750  * Check for an Ex command with optional tail, not followed by "(".
2751  * If there is a match advance "pp" to the argument and return TRUE.
2752  */
2753     int
2754 checkforcmd_noparen(
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, TRUE);
2760 }
2761 
2762 /*
2763  * Parse and skip over command modifiers:
2764  * - update eap->cmd
2765  * - store flags in "cmod".
2766  * - Set ex_pressedreturn for an empty command line.
2767  * When "skip_only" is TRUE the global variables are not changed, except for
2768  * "cmdmod".
2769  * When "skip_only" is FALSE then undo_cmdmod() must be called later to free
2770  * any cmod_filter_regmatch.regprog.
2771  * Call apply_cmdmod() to get the side effects of the modifiers:
2772  * - Increment "sandbox" for ":sandbox"
2773  * - set p_verbose for ":verbose"
2774  * - set msg_silent for ":silent"
2775  * - set 'eventignore' to "all" for ":noautocmd"
2776  * Return FAIL when the command is not to be executed.
2777  * May set "errormsg" to an error message.
2778  */
2779     int
2780 parse_command_modifiers(
2781 	exarg_T	    *eap,
2782 	char	    **errormsg,
2783 	cmdmod_T    *cmod,
2784 	int	    skip_only)
2785 {
2786     char_u  *p;
2787     int	    starts_with_colon = FALSE;
2788 
2789     CLEAR_POINTER(cmod);
2790 
2791     // Repeat until no more command modifiers are found.
2792     for (;;)
2793     {
2794 	while (*eap->cmd == ' ' || *eap->cmd == '\t' || *eap->cmd == ':')
2795 	{
2796 	    if (*eap->cmd == ':')
2797 		starts_with_colon = TRUE;
2798 	    ++eap->cmd;
2799 	}
2800 
2801 	// in ex mode, an empty line works like :+
2802 	if (*eap->cmd == NUL && exmode_active
2803 		   && (getline_equal(eap->getline, eap->cookie, getexmodeline)
2804 		       || getline_equal(eap->getline, eap->cookie, getexline))
2805 			&& curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count)
2806 	{
2807 	    eap->cmd = (char_u *)"+";
2808 	    if (!skip_only)
2809 		ex_pressedreturn = TRUE;
2810 	}
2811 
2812 	// ignore comment and empty lines
2813 	if (comment_start(eap->cmd, starts_with_colon))
2814 	{
2815 	    // a comment ends at a NL
2816 	    if (eap->nextcmd == NULL)
2817 	    {
2818 		eap->nextcmd = vim_strchr(eap->cmd, '\n');
2819 		if (eap->nextcmd != NULL)
2820 		    ++eap->nextcmd;
2821 	    }
2822 	    return FAIL;
2823 	}
2824 	if (*eap->cmd == NUL)
2825 	{
2826 	    if (!skip_only)
2827 		ex_pressedreturn = TRUE;
2828 	    return FAIL;
2829 	}
2830 
2831 	p = skip_range(eap->cmd, TRUE, NULL);
2832 
2833 	// In Vim9 script a variable can shadow a command modifier:
2834 	//   verbose = 123
2835 	//   verbose += 123
2836 	//   silent! verbose = func()
2837 	//   verbose.member = 2
2838 	//   verbose[expr] = 2
2839 	// But not:
2840 	//   verbose [a, b] = list
2841 	if (in_vim9script())
2842 	{
2843 	    char_u *s, *n;
2844 
2845 	    for (s = p; ASCII_ISALPHA(*s); ++s)
2846 		;
2847 	    n = skipwhite(s);
2848 	    if (vim_strchr((char_u *)".=", *n) != NULL
2849 		    || *s == '['
2850 		    || (*n != NUL && n[1] == '='))
2851 		break;
2852 	}
2853 
2854 	switch (*p)
2855 	{
2856 	    // When adding an entry, also modify cmd_exists().
2857 	    case 'a':	if (!checkforcmd_noparen(&eap->cmd, "aboveleft", 3))
2858 			    break;
2859 			cmod->cmod_split |= WSP_ABOVE;
2860 			continue;
2861 
2862 	    case 'b':	if (checkforcmd_noparen(&eap->cmd, "belowright", 3))
2863 			{
2864 			    cmod->cmod_split |= WSP_BELOW;
2865 			    continue;
2866 			}
2867 			if (checkforcmd_opt(&eap->cmd, "browse", 3, TRUE))
2868 			{
2869 #ifdef FEAT_BROWSE_CMD
2870 			    cmod->cmod_flags |= CMOD_BROWSE;
2871 #endif
2872 			    continue;
2873 			}
2874 			if (!checkforcmd_noparen(&eap->cmd, "botright", 2))
2875 			    break;
2876 			cmod->cmod_split |= WSP_BOT;
2877 			continue;
2878 
2879 	    case 'c':	if (!checkforcmd_opt(&eap->cmd, "confirm", 4, TRUE))
2880 			    break;
2881 #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
2882 			cmod->cmod_flags |= CMOD_CONFIRM;
2883 #endif
2884 			continue;
2885 
2886 	    case 'k':	if (checkforcmd_noparen(&eap->cmd, "keepmarks", 3))
2887 			{
2888 			    cmod->cmod_flags |= CMOD_KEEPMARKS;
2889 			    continue;
2890 			}
2891 			if (checkforcmd_noparen(&eap->cmd, "keepalt", 5))
2892 			{
2893 			    cmod->cmod_flags |= CMOD_KEEPALT;
2894 			    continue;
2895 			}
2896 			if (checkforcmd_noparen(&eap->cmd, "keeppatterns", 5))
2897 			{
2898 			    cmod->cmod_flags |= CMOD_KEEPPATTERNS;
2899 			    continue;
2900 			}
2901 			if (!checkforcmd_noparen(&eap->cmd, "keepjumps", 5))
2902 			    break;
2903 			cmod->cmod_flags |= CMOD_KEEPJUMPS;
2904 			continue;
2905 
2906 	    case 'f':	// only accept ":filter {pat} cmd"
2907 			{
2908 			    char_u  *reg_pat;
2909 			    char_u  *nulp = NULL;
2910 			    int	    c = 0;
2911 
2912 			    if (!checkforcmd_noparen(&p, "filter", 4)
2913 						|| *p == NUL || ends_excmd(*p))
2914 				break;
2915 			    if (*p == '!')
2916 			    {
2917 				cmod->cmod_filter_force = TRUE;
2918 				p = skipwhite(p + 1);
2919 				if (*p == NUL || ends_excmd(*p))
2920 				    break;
2921 			    }
2922 #ifdef FEAT_EVAL
2923 			    // Avoid that "filter(arg)" is recognized.
2924 			    if (in_vim9script() && !VIM_ISWHITE(p[-1]))
2925 				break;
2926 #endif
2927 			    if (skip_only)
2928 				p = skip_vimgrep_pat(p, NULL, NULL);
2929 			    else
2930 				// NOTE: This puts a NUL after the pattern.
2931 				p = skip_vimgrep_pat_ext(p, &reg_pat, NULL,
2932 								    &nulp, &c);
2933 			    if (p == NULL || *p == NUL)
2934 				break;
2935 			    if (!skip_only)
2936 			    {
2937 				cmod->cmod_filter_regmatch.regprog =
2938 						vim_regcomp(reg_pat, RE_MAGIC);
2939 				if (cmod->cmod_filter_regmatch.regprog == NULL)
2940 				    break;
2941 				// restore the character overwritten by NUL
2942 				if (nulp != NULL)
2943 				    *nulp = c;
2944 			    }
2945 			    eap->cmd = p;
2946 			    continue;
2947 			}
2948 
2949 			// ":hide" and ":hide | cmd" are not modifiers
2950 	    case 'h':	if (p != eap->cmd || !checkforcmd_noparen(&p, "hide", 3)
2951 					       || *p == NUL || ends_excmd(*p))
2952 			    break;
2953 			eap->cmd = p;
2954 			cmod->cmod_flags |= CMOD_HIDE;
2955 			continue;
2956 
2957 	    case 'l':	if (checkforcmd_noparen(&eap->cmd, "lockmarks", 3))
2958 			{
2959 			    cmod->cmod_flags |= CMOD_LOCKMARKS;
2960 			    continue;
2961 			}
2962 			if (checkforcmd_noparen(&eap->cmd, "legacy", 3))
2963 			{
2964 			    if (ends_excmd2(p, eap->cmd))
2965 			    {
2966 				*errormsg =
2967 				      _(e_legacy_must_be_followed_by_command);
2968 				return FAIL;
2969 			    }
2970 			    cmod->cmod_flags |= CMOD_LEGACY;
2971 			    continue;
2972 			}
2973 
2974 			if (!checkforcmd_noparen(&eap->cmd, "leftabove", 5))
2975 			    break;
2976 			cmod->cmod_split |= WSP_ABOVE;
2977 			continue;
2978 
2979 	    case 'n':	if (checkforcmd_noparen(&eap->cmd, "noautocmd", 3))
2980 			{
2981 			    cmod->cmod_flags |= CMOD_NOAUTOCMD;
2982 			    continue;
2983 			}
2984 			if (!checkforcmd_noparen(&eap->cmd, "noswapfile", 3))
2985 			    break;
2986 			cmod->cmod_flags |= CMOD_NOSWAPFILE;
2987 			continue;
2988 
2989 	    case 'r':	if (!checkforcmd_noparen(&eap->cmd, "rightbelow", 6))
2990 			    break;
2991 			cmod->cmod_split |= WSP_BELOW;
2992 			continue;
2993 
2994 	    case 's':	if (checkforcmd_noparen(&eap->cmd, "sandbox", 3))
2995 			{
2996 			    cmod->cmod_flags |= CMOD_SANDBOX;
2997 			    continue;
2998 			}
2999 			if (!checkforcmd_noparen(&eap->cmd, "silent", 3))
3000 			    break;
3001 			cmod->cmod_flags |= CMOD_SILENT;
3002 			if (*eap->cmd == '!' && !VIM_ISWHITE(eap->cmd[-1]))
3003 			{
3004 			    // ":silent!", but not "silent !cmd"
3005 			    eap->cmd = skipwhite(eap->cmd + 1);
3006 			    cmod->cmod_flags |= CMOD_ERRSILENT;
3007 			}
3008 			continue;
3009 
3010 	    case 't':	if (checkforcmd_noparen(&p, "tab", 3))
3011 			{
3012 			    if (!skip_only)
3013 			    {
3014 				long tabnr = get_address(eap, &eap->cmd,
3015 						    ADDR_TABS, eap->skip,
3016 						    skip_only, FALSE, 1);
3017 				if (tabnr == MAXLNUM)
3018 				    cmod->cmod_tab = tabpage_index(curtab) + 1;
3019 				else
3020 				{
3021 				    if (tabnr < 0 || tabnr > LAST_TAB_NR)
3022 				    {
3023 					*errormsg = _(e_invalid_range);
3024 					return FAIL;
3025 				    }
3026 				    cmod->cmod_tab = tabnr + 1;
3027 				}
3028 			    }
3029 			    eap->cmd = p;
3030 			    continue;
3031 			}
3032 			if (!checkforcmd_noparen(&eap->cmd, "topleft", 2))
3033 			    break;
3034 			cmod->cmod_split |= WSP_TOP;
3035 			continue;
3036 
3037 	    case 'u':	if (!checkforcmd_noparen(&eap->cmd, "unsilent", 3))
3038 			    break;
3039 			cmod->cmod_flags |= CMOD_UNSILENT;
3040 			continue;
3041 
3042 	    case 'v':	if (checkforcmd_noparen(&eap->cmd, "vertical", 4))
3043 			{
3044 			    cmod->cmod_split |= WSP_VERT;
3045 			    continue;
3046 			}
3047 			if (checkforcmd_noparen(&eap->cmd, "vim9cmd", 4))
3048 			{
3049 			    if (ends_excmd2(p, eap->cmd))
3050 			    {
3051 				*errormsg =
3052 				      _(e_vim9cmd_must_be_followed_by_command);
3053 				return FAIL;
3054 			    }
3055 			    cmod->cmod_flags |= CMOD_VIM9CMD;
3056 			    continue;
3057 			}
3058 			if (!checkforcmd_noparen(&p, "verbose", 4))
3059 			    break;
3060 			if (vim_isdigit(*eap->cmd))
3061 			    cmod->cmod_verbose = atoi((char *)eap->cmd);
3062 			else
3063 			    cmod->cmod_verbose = 1;
3064 			eap->cmd = p;
3065 			continue;
3066 	}
3067 	break;
3068     }
3069 
3070     return OK;
3071 }
3072 
3073 /*
3074  * Return TRUE if "cmod" has anything set.
3075  */
3076     int
3077 has_cmdmod(cmdmod_T *cmod, int ignore_silent)
3078 {
3079     return (cmod->cmod_flags != 0 && (!ignore_silent
3080 		|| (cmod->cmod_flags
3081 		      & ~(CMOD_SILENT | CMOD_ERRSILENT | CMOD_UNSILENT)) != 0))
3082 	    || cmod->cmod_split != 0
3083 	    || cmod->cmod_verbose != 0
3084 	    || cmod->cmod_tab != 0
3085 	    || cmod->cmod_filter_regmatch.regprog != NULL;
3086 }
3087 
3088 /*
3089  * If Vim9 script and "cmdmod" has anything set give an error and return TRUE.
3090  */
3091     int
3092 cmdmod_error(int ignore_silent)
3093 {
3094     if (in_vim9script() && has_cmdmod(&cmdmod, ignore_silent))
3095     {
3096 	emsg(_(e_misplaced_command_modifier));
3097 	return TRUE;
3098     }
3099     return FALSE;
3100 }
3101 
3102 /*
3103  * Apply the command modifiers.  Saves current state in "cmdmod", call
3104  * undo_cmdmod() later.
3105  */
3106     void
3107 apply_cmdmod(cmdmod_T *cmod)
3108 {
3109 #ifdef HAVE_SANDBOX
3110     if ((cmod->cmod_flags & CMOD_SANDBOX) && !cmod->cmod_did_sandbox)
3111     {
3112 	++sandbox;
3113 	cmod->cmod_did_sandbox = TRUE;
3114     }
3115 #endif
3116     if (cmod->cmod_verbose > 0)
3117     {
3118 	if (cmod->cmod_verbose_save == 0)
3119 	    cmod->cmod_verbose_save = p_verbose + 1;
3120 	p_verbose = cmod->cmod_verbose;
3121     }
3122 
3123     if ((cmod->cmod_flags & (CMOD_SILENT | CMOD_UNSILENT))
3124 	    && cmod->cmod_save_msg_silent == 0)
3125     {
3126 	cmod->cmod_save_msg_silent = msg_silent + 1;
3127 	cmod->cmod_save_msg_scroll = msg_scroll;
3128     }
3129     if (cmod->cmod_flags & CMOD_SILENT)
3130 	++msg_silent;
3131     if (cmod->cmod_flags & CMOD_UNSILENT)
3132 	msg_silent = 0;
3133 
3134     if (cmod->cmod_flags & CMOD_ERRSILENT)
3135     {
3136 	++emsg_silent;
3137 	++cmod->cmod_did_esilent;
3138     }
3139 
3140     if ((cmod->cmod_flags & CMOD_NOAUTOCMD) && cmod->cmod_save_ei == NULL)
3141     {
3142 	// Set 'eventignore' to "all".
3143 	// First save the existing option value for restoring it later.
3144 	cmod->cmod_save_ei = vim_strsave(p_ei);
3145 	set_string_option_direct((char_u *)"ei", -1,
3146 					  (char_u *)"all", OPT_FREE, SID_NONE);
3147     }
3148 }
3149 
3150 /*
3151  * Undo and free contents of "cmod".
3152  */
3153     void
3154 undo_cmdmod(cmdmod_T *cmod)
3155 {
3156     if (cmod->cmod_verbose_save > 0)
3157     {
3158 	p_verbose = cmod->cmod_verbose_save - 1;
3159 	cmod->cmod_verbose_save = 0;
3160     }
3161 
3162 #ifdef HAVE_SANDBOX
3163     if (cmod->cmod_did_sandbox)
3164     {
3165 	--sandbox;
3166 	cmod->cmod_did_sandbox = FALSE;
3167     }
3168 #endif
3169 
3170     if (cmod->cmod_save_ei != NULL)
3171     {
3172 	// Restore 'eventignore' to the value before ":noautocmd".
3173 	set_string_option_direct((char_u *)"ei", -1, cmod->cmod_save_ei,
3174 							   OPT_FREE, SID_NONE);
3175 	free_string_option(cmod->cmod_save_ei);
3176 	cmod->cmod_save_ei = NULL;
3177     }
3178 
3179     vim_regfree(cmod->cmod_filter_regmatch.regprog);
3180 
3181     if (cmod->cmod_save_msg_silent > 0)
3182     {
3183 	// messages could be enabled for a serious error, need to check if the
3184 	// counters don't become negative
3185 	if (!did_emsg || msg_silent > cmod->cmod_save_msg_silent - 1)
3186 	    msg_silent = cmod->cmod_save_msg_silent - 1;
3187 	emsg_silent -= cmod->cmod_did_esilent;
3188 	if (emsg_silent < 0)
3189 	    emsg_silent = 0;
3190 	// Restore msg_scroll, it's set by file I/O commands, even when no
3191 	// message is actually displayed.
3192 	msg_scroll = cmod->cmod_save_msg_scroll;
3193 
3194 	// "silent reg" or "silent echo x" inside "redir" leaves msg_col
3195 	// somewhere in the line.  Put it back in the first column.
3196 	if (redirecting())
3197 	    msg_col = 0;
3198 
3199 	cmod->cmod_save_msg_silent = 0;
3200 	cmod->cmod_did_esilent = 0;
3201     }
3202 }
3203 
3204 /*
3205  * Parse the address range, if any, in "eap".
3206  * May set the last search pattern, unless "silent" is TRUE.
3207  * Return FAIL and set "errormsg" or return OK.
3208  */
3209     int
3210 parse_cmd_address(exarg_T *eap, char **errormsg, int silent)
3211 {
3212     int		address_count = 1;
3213     linenr_T	lnum;
3214 
3215     // Repeat for all ',' or ';' separated addresses.
3216     for (;;)
3217     {
3218 	eap->line1 = eap->line2;
3219 	eap->line2 = default_address(eap);
3220 	eap->cmd = skipwhite(eap->cmd);
3221 	lnum = get_address(eap, &eap->cmd, eap->addr_type, eap->skip, silent,
3222 					eap->addr_count == 0, address_count++);
3223 	if (eap->cmd == NULL)	// error detected
3224 	    return FAIL;
3225 	if (lnum == MAXLNUM)
3226 	{
3227 	    if (*eap->cmd == '%')   // '%' - all lines
3228 	    {
3229 		++eap->cmd;
3230 		switch (eap->addr_type)
3231 		{
3232 		    case ADDR_LINES:
3233 		    case ADDR_OTHER:
3234 			eap->line1 = 1;
3235 			eap->line2 = curbuf->b_ml.ml_line_count;
3236 			break;
3237 		    case ADDR_LOADED_BUFFERS:
3238 			{
3239 			    buf_T	*buf = firstbuf;
3240 
3241 			    while (buf->b_next != NULL
3242 						  && buf->b_ml.ml_mfp == NULL)
3243 				buf = buf->b_next;
3244 			    eap->line1 = buf->b_fnum;
3245 			    buf = lastbuf;
3246 			    while (buf->b_prev != NULL
3247 						  && buf->b_ml.ml_mfp == NULL)
3248 				buf = buf->b_prev;
3249 			    eap->line2 = buf->b_fnum;
3250 			    break;
3251 			}
3252 		    case ADDR_BUFFERS:
3253 			eap->line1 = firstbuf->b_fnum;
3254 			eap->line2 = lastbuf->b_fnum;
3255 			break;
3256 		    case ADDR_WINDOWS:
3257 		    case ADDR_TABS:
3258 			if (IS_USER_CMDIDX(eap->cmdidx))
3259 			{
3260 			    eap->line1 = 1;
3261 			    eap->line2 = eap->addr_type == ADDR_WINDOWS
3262 						  ? LAST_WIN_NR : LAST_TAB_NR;
3263 			}
3264 			else
3265 			{
3266 			    // there is no Vim command which uses '%' and
3267 			    // ADDR_WINDOWS or ADDR_TABS
3268 			    *errormsg = _(e_invalid_range);
3269 			    return FAIL;
3270 			}
3271 			break;
3272 		    case ADDR_TABS_RELATIVE:
3273 		    case ADDR_UNSIGNED:
3274 		    case ADDR_QUICKFIX:
3275 			*errormsg = _(e_invalid_range);
3276 			return FAIL;
3277 		    case ADDR_ARGUMENTS:
3278 			if (ARGCOUNT == 0)
3279 			    eap->line1 = eap->line2 = 0;
3280 			else
3281 			{
3282 			    eap->line1 = 1;
3283 			    eap->line2 = ARGCOUNT;
3284 			}
3285 			break;
3286 		    case ADDR_QUICKFIX_VALID:
3287 #ifdef FEAT_QUICKFIX
3288 			eap->line1 = 1;
3289 			eap->line2 = qf_get_valid_size(eap);
3290 			if (eap->line2 == 0)
3291 			    eap->line2 = 1;
3292 #endif
3293 			break;
3294 		    case ADDR_NONE:
3295 			// Will give an error later if a range is found.
3296 			break;
3297 		}
3298 		++eap->addr_count;
3299 	    }
3300 	    else if (*eap->cmd == '*' && vim_strchr(p_cpo, CPO_STAR) == NULL)
3301 	    {
3302 		pos_T	    *fp;
3303 
3304 		// '*' - visual area
3305 		if (eap->addr_type != ADDR_LINES)
3306 		{
3307 		    *errormsg = _(e_invalid_range);
3308 		    return FAIL;
3309 		}
3310 
3311 		++eap->cmd;
3312 		if (!eap->skip)
3313 		{
3314 		    fp = getmark('<', FALSE);
3315 		    if (check_mark(fp) == FAIL)
3316 			return FAIL;
3317 		    eap->line1 = fp->lnum;
3318 		    fp = getmark('>', FALSE);
3319 		    if (check_mark(fp) == FAIL)
3320 			return FAIL;
3321 		    eap->line2 = fp->lnum;
3322 		    ++eap->addr_count;
3323 		}
3324 	    }
3325 	}
3326 	else
3327 	    eap->line2 = lnum;
3328 	eap->addr_count++;
3329 
3330 	if (*eap->cmd == ';')
3331 	{
3332 	    if (!eap->skip)
3333 	    {
3334 		curwin->w_cursor.lnum = eap->line2;
3335 		// Don't leave the cursor on an illegal line or column, but do
3336 		// accept zero as address, so 0;/PATTERN/ works correctly.
3337 		if (eap->line2 > 0)
3338 		    check_cursor();
3339 	    }
3340 	}
3341 	else if (*eap->cmd != ',')
3342 	    break;
3343 	++eap->cmd;
3344     }
3345 
3346     // One address given: set start and end lines.
3347     if (eap->addr_count == 1)
3348     {
3349 	eap->line1 = eap->line2;
3350 	// ... but only implicit: really no address given
3351 	if (lnum == MAXLNUM)
3352 	    eap->addr_count = 0;
3353     }
3354     return OK;
3355 }
3356 
3357 /*
3358  * Append "cmd" to the error message in IObuff.
3359  * Takes care of limiting the length and handling 0xa0, which would be
3360  * invisible otherwise.
3361  */
3362     static void
3363 append_command(char_u *cmd)
3364 {
3365     char_u *s = cmd;
3366     char_u *d;
3367 
3368     STRCAT(IObuff, ": ");
3369     d = IObuff + STRLEN(IObuff);
3370     while (*s != NUL && d - IObuff < IOSIZE - 7)
3371     {
3372 	if (enc_utf8 ? (s[0] == 0xc2 && s[1] == 0xa0) : *s == 0xa0)
3373 	{
3374 	    s += enc_utf8 ? 2 : 1;
3375 	    STRCPY(d, "<a0>");
3376 	    d += 4;
3377 	}
3378 	else
3379 	    MB_COPY_CHAR(s, d);
3380     }
3381     *d = NUL;
3382 }
3383 
3384 /*
3385  * If "start" points "&opt", "&l:opt", "&g:opt" or "$ENV" return a pointer to
3386  * the name.  Otherwise just return "start".
3387  */
3388     char_u *
3389 skip_option_env_lead(char_u *start)
3390 {
3391     char_u *name = start;
3392 
3393     if (*start == '&')
3394     {
3395 	if ((start[1] == 'l' || start[1] == 'g') && start[2] == ':')
3396 	    name += 3;
3397 	else
3398 	    name += 1;
3399     }
3400     else if (*start == '$')
3401 	name += 1;
3402     return name;
3403 }
3404 
3405 /*
3406  * Find an Ex command by its name, either built-in or user.
3407  * Start of the name can be found at eap->cmd.
3408  * Sets eap->cmdidx and returns a pointer to char after the command name.
3409  * "full" is set to TRUE if the whole command name matched.
3410  *
3411  * If "lookup" is not NULL recognize expression without "eval" or "call" and
3412  * assignment without "let".  Sets eap->cmdidx to the command while returning
3413  * "eap->cmd".
3414  *
3415  * Returns NULL for an ambiguous user command.
3416  */
3417     char_u *
3418 find_ex_command(
3419 	exarg_T *eap,
3420 	int	*full UNUSED,
3421 	int	(*lookup)(char_u *, size_t, int cmd, cctx_T *) UNUSED,
3422 	cctx_T	*cctx UNUSED)
3423 {
3424     int		len;
3425     char_u	*p;
3426     int		i;
3427 #ifndef FEAT_EVAL
3428     int		vim9 = FALSE;
3429 #else
3430     int		vim9 = in_vim9script();
3431 
3432     /*
3433      * Recognize a Vim9 script function/method call and assignment:
3434      * "lvar = value", "lvar(arg)", "[1, 2 3]->Func()"
3435      */
3436     p = eap->cmd;
3437     if (lookup != NULL)
3438     {
3439 	char_u *pskip = skip_option_env_lead(eap->cmd);
3440 
3441 	if (vim_strchr((char_u *)"{('[\"@&$", *p) != NULL
3442 	       || ((p = to_name_const_end(pskip)) > eap->cmd && *p != NUL))
3443 	{
3444 	    int	    oplen;
3445 	    int	    heredoc;
3446 	    char_u  *swp;
3447 
3448 	    if (*eap->cmd == '&'
3449 		    || *eap->cmd == '$'
3450 		    || (eap->cmd[0] == '@'
3451 					&& (valid_yank_reg(eap->cmd[1], FALSE)
3452 						       || eap->cmd[1] == '@')))
3453 	    {
3454 		if (*eap->cmd == '&')
3455 		{
3456 		    p = eap->cmd + 1;
3457 		    if (STRNCMP("l:", p, 2) == 0 || STRNCMP("g:", p, 2) == 0)
3458 			p += 2;
3459 		    p = to_name_end(p, FALSE);
3460 		}
3461 		else if (*eap->cmd == '$')
3462 		    p = to_name_end(eap->cmd + 1, FALSE);
3463 		else
3464 		    p = eap->cmd + 2;
3465 		if (ends_excmd(*skipwhite(p)))
3466 		{
3467 		    // "&option <NL>", "$ENV <NL>" and "@r <NL>" are the start
3468 		    // of an expression.
3469 		    eap->cmdidx = CMD_eval;
3470 		    return eap->cmd;
3471 		}
3472 		// "&option" can be followed by "->" or "=", check below
3473 	    }
3474 
3475 	    swp = skipwhite(p);
3476 
3477 	    if (
3478 		// "(..." is an expression.
3479 		// "funcname(" is always a function call.
3480 		*p == '('
3481 		    || (p == eap->cmd
3482 			? (
3483 			    // "{..." is a dict expression or block start.
3484 			    *eap->cmd == '{'
3485 			    // "'string'->func()" is an expression.
3486 			 || *eap->cmd == '\''
3487 			    // '"string"->func()' is an expression.
3488 			 || *eap->cmd == '"'
3489 			    // "g:varname" is an expression.
3490 			 || eap->cmd[1] == ':'
3491 			    )
3492 			    // "varname->func()" is an expression.
3493 			: (*swp == '-' && swp[1] == '>')))
3494 	    {
3495 		if (*eap->cmd == '{' && ends_excmd(*skipwhite(eap->cmd + 1)))
3496 		{
3497 		    // "{" by itself is the start of a block.
3498 		    eap->cmdidx = CMD_block;
3499 		    return eap->cmd + 1;
3500 		}
3501 		eap->cmdidx = CMD_eval;
3502 		return eap->cmd;
3503 	    }
3504 
3505 	    if (p != eap->cmd && (
3506 			    // "varname[]" is an expression.
3507 			    *p == '['
3508 			    // "varname.key" is an expression.
3509 			 || (*p == '.' && (ASCII_ISALPHA(p[1])
3510 							     || p[1] == '_'))))
3511 	    {
3512 		char_u	*after = eap->cmd;
3513 
3514 		// When followed by "=" or "+=" then it is an assignment.
3515 		// Skip over the whole thing, it can be:
3516 		//	name.member = val
3517 		//	name[a : b] = val
3518 		//	name[idx] = val
3519 		//	name[idx].member = val
3520 		//	etc.
3521 		eap->cmdidx = CMD_eval;
3522 		++emsg_silent;
3523 		if (skip_expr(&after, NULL) == OK)
3524 		{
3525 		    after = skipwhite(after);
3526 		    if (*after == '=' || (*after != NUL && after[1] == '=')
3527 					 || (after[0] == '.' && after[1] == '.'
3528 							   && after[2] == '='))
3529 			eap->cmdidx = CMD_var;
3530 		}
3531 		--emsg_silent;
3532 		return eap->cmd;
3533 	    }
3534 
3535 	    // "[...]->Method()" is a list expression, but "[a, b] = Func()" is
3536 	    // an assignment.
3537 	    // If there is no line break inside the "[...]" then "p" is
3538 	    // advanced to after the "]" by to_name_const_end(): check if a "="
3539 	    // follows.
3540 	    // If "[...]" has a line break "p" still points at the "[" and it
3541 	    // can't be an assignment.
3542 	    if (*eap->cmd == '[')
3543 	    {
3544 		char_u	    *eq;
3545 
3546 		p = to_name_const_end(eap->cmd);
3547 		if (p == eap->cmd && *p == '[')
3548 		{
3549 		    int count = 0;
3550 		    int	semicolon = FALSE;
3551 
3552 		    p = skip_var_list(eap->cmd, TRUE, &count, &semicolon, TRUE);
3553 		}
3554 		eq = p;
3555 		if (eq != NULL)
3556 		{
3557 		    eq = skipwhite(eq);
3558 		    if (vim_strchr((char_u *)"+-*/%", *eq) != NULL)
3559 			++eq;
3560 		}
3561 		if (p == NULL || p == eap->cmd || *eq != '=')
3562 		{
3563 		    eap->cmdidx = CMD_eval;
3564 		    return eap->cmd;
3565 		}
3566 		if (p > eap->cmd && *eq == '=')
3567 		{
3568 		    eap->cmdidx = CMD_var;
3569 		    return eap->cmd;
3570 		}
3571 	    }
3572 
3573 	    // Recognize an assignment if we recognize the variable name:
3574 	    // "g:var = expr"
3575 	    // "@r = expr"
3576 	    // "&opt = expr"
3577 	    // "var = expr"  where "var" is a variable name or we are skipping
3578 	    // (variable declaration might have been skipped).
3579 	    oplen = assignment_len(skipwhite(p), &heredoc);
3580 	    if (oplen > 0)
3581 	    {
3582 		if (((p - eap->cmd) > 2 && eap->cmd[1] == ':')
3583 			|| *eap->cmd == '&'
3584 			|| *eap->cmd == '$'
3585 			|| *eap->cmd == '@'
3586 			|| eap->skip
3587 			|| lookup(eap->cmd, p - eap->cmd, TRUE, cctx) == OK)
3588 		{
3589 		    eap->cmdidx = CMD_var;
3590 		    return eap->cmd;
3591 		}
3592 	    }
3593 
3594 	    // Recognize using a type for a w:, b:, t: or g: variable:
3595 	    // "w:varname: number = 123".
3596 	    if (eap->cmd[1] == ':' && *p == ':')
3597 	    {
3598 		eap->cmdidx = CMD_eval;
3599 		return eap->cmd;
3600 	    }
3601 	}
3602 
3603 	// If it is an ID it might be a variable with an operator on the next
3604 	// line, if the variable exists it can't be an Ex command.
3605 	if (p > eap->cmd && ends_excmd(*skipwhite(p))
3606 		&& (lookup(eap->cmd, p - eap->cmd, TRUE, cctx) == OK
3607 		    || (ASCII_ISALPHA(eap->cmd[0]) && eap->cmd[1] == ':')))
3608 	{
3609 	    eap->cmdidx = CMD_eval;
3610 	    return eap->cmd;
3611 	}
3612 
3613 	// Check for "++nr" and "--nr".
3614 	if (p == eap->cmd && p[0] == p[1] && (*p == '+' || *p == '-'))
3615 	{
3616 	    eap->cmdidx = *p == '+' ? CMD_increment : CMD_decrement;
3617 	    return eap->cmd + 2;
3618 	}
3619     }
3620 #endif
3621 
3622     /*
3623      * Isolate the command and search for it in the command table.
3624      * Exceptions:
3625      * - The 'k' command can directly be followed by any character.
3626      *   But it is not used in Vim9 script.
3627      * - the 's' command can be followed directly by 'c', 'g', 'i', 'I' or 'r'
3628      *	    but :sre[wind] is another command, as are :scr[iptnames],
3629      *	    :scs[cope], :sim[alt], :sig[ns] and :sil[ent].
3630      * - the "d" command can directly be followed by 'l' or 'p' flag.
3631      */
3632     p = eap->cmd;
3633     if (!vim9 && *p == 'k')
3634     {
3635 	eap->cmdidx = CMD_k;
3636 	++p;
3637     }
3638     else if (!vim9
3639 	    && p[0] == 's'
3640 	    && ((p[1] == 'c' && (p[2] == NUL || (p[2] != 's' && p[2] != 'r'
3641 			&& (p[3] == NUL || (p[3] != 'i' && p[4] != 'p')))))
3642 		|| p[1] == 'g'
3643 		|| (p[1] == 'i' && p[2] != 'm' && p[2] != 'l' && p[2] != 'g')
3644 		|| p[1] == 'I'
3645 		|| (p[1] == 'r' && p[2] != 'e')))
3646     {
3647 	eap->cmdidx = CMD_substitute;
3648 	++p;
3649     }
3650     else
3651     {
3652 	while (ASCII_ISALPHA(*p))
3653 	    ++p;
3654 	// for python 3.x support ":py3", ":python3", ":py3file", etc.
3655 	if (eap->cmd[0] == 'p' && eap->cmd[1] == 'y')
3656 	{
3657 	    while (ASCII_ISALNUM(*p))
3658 		++p;
3659 	}
3660 	else if (*p == '9' && STRNCMP("vim9", eap->cmd, 4) == 0)
3661 	{
3662 	    // include "9" for "vim9*" commands; "vim9cmd" and "vim9script".
3663 	    ++p;
3664 	    while (ASCII_ISALPHA(*p))
3665 		++p;
3666 	}
3667 
3668 	// check for non-alpha command
3669 	if (p == eap->cmd && vim_strchr((char_u *)"@*!=><&~#}", *p) != NULL)
3670 	    ++p;
3671 	len = (int)(p - eap->cmd);
3672 	if (!vim9 && *eap->cmd == 'd' && (p[-1] == 'l' || p[-1] == 'p'))
3673 	{
3674 	    // Check for ":dl", ":dell", etc. to ":deletel": that's
3675 	    // :delete with the 'l' flag.  Same for 'p'.
3676 	    for (i = 0; i < len; ++i)
3677 		if (eap->cmd[i] != ((char_u *)"delete")[i])
3678 		    break;
3679 	    if (i == len - 1)
3680 	    {
3681 		--len;
3682 		if (p[-1] == 'l')
3683 		    eap->flags |= EXFLAG_LIST;
3684 		else
3685 		    eap->flags |= EXFLAG_PRINT;
3686 	    }
3687 	}
3688 
3689 	if (ASCII_ISLOWER(eap->cmd[0]))
3690 	{
3691 	    int c1 = eap->cmd[0];
3692 	    int c2 = len == 1 ? NUL : eap->cmd[1];
3693 
3694 	    if (command_count != (int)CMD_SIZE)
3695 	    {
3696 		iemsg(_("E943: Command table needs to be updated, run 'make cmdidxs'"));
3697 		getout(1);
3698 	    }
3699 
3700 	    // Use a precomputed index for fast look-up in cmdnames[]
3701 	    // taking into account the first 2 letters of eap->cmd.
3702 	    eap->cmdidx = cmdidxs1[CharOrdLow(c1)];
3703 	    if (ASCII_ISLOWER(c2))
3704 		eap->cmdidx += cmdidxs2[CharOrdLow(c1)][CharOrdLow(c2)];
3705 	}
3706 	else if (ASCII_ISUPPER(eap->cmd[0]))
3707 	    eap->cmdidx = CMD_Next;
3708 	else
3709 	    eap->cmdidx = CMD_bang;
3710 
3711 	for ( ; (int)eap->cmdidx < (int)CMD_SIZE;
3712 			       eap->cmdidx = (cmdidx_T)((int)eap->cmdidx + 1))
3713 	    if (STRNCMP(cmdnames[(int)eap->cmdidx].cmd_name, (char *)eap->cmd,
3714 							    (size_t)len) == 0)
3715 	    {
3716 #ifdef FEAT_EVAL
3717 		if (full != NULL
3718 			   && cmdnames[(int)eap->cmdidx].cmd_name[len] == NUL)
3719 		    *full = TRUE;
3720 #endif
3721 		break;
3722 	    }
3723 
3724 	// Not not recognize ":*" as the star command unless '*' is in
3725 	// 'cpoptions'.
3726 	if (eap->cmdidx == CMD_star && vim_strchr(p_cpo, CPO_STAR) == NULL)
3727 	    p = eap->cmd;
3728 
3729 	// Look for a user defined command as a last resort.  Let ":Print" be
3730 	// overruled by a user defined command.
3731 	if ((eap->cmdidx == CMD_SIZE || eap->cmdidx == CMD_Print)
3732 		&& *eap->cmd >= 'A' && *eap->cmd <= 'Z')
3733 	{
3734 	    // User defined commands may contain digits.
3735 	    while (ASCII_ISALNUM(*p))
3736 		++p;
3737 	    p = find_ucmd(eap, p, full, NULL, NULL);
3738 	}
3739 	if (p == NULL || p == eap->cmd)
3740 	    eap->cmdidx = CMD_SIZE;
3741     }
3742 
3743     // ":fina" means ":finally" for backwards compatibility.
3744     if (eap->cmdidx == CMD_final && p - eap->cmd == 4)
3745 	eap->cmdidx = CMD_finally;
3746 
3747 #ifdef FEAT_EVAL
3748     if (eap->cmdidx < CMD_SIZE
3749 	    && vim9
3750 	    && !IS_WHITE_OR_NUL(*p) && *p != '\n' && *p != '!' && *p != '|'
3751 	    && (eap->cmdidx < 0 ||
3752 		(cmdnames[eap->cmdidx].cmd_argt & EX_NONWHITE_OK) == 0))
3753     {
3754 	char_u *cmd = vim_strnsave(eap->cmd, p - eap->cmd);
3755 
3756 	semsg(_(e_command_str_not_followed_by_white_space_str), cmd, eap->cmd);
3757 	eap->cmdidx = CMD_SIZE;
3758 	vim_free(cmd);
3759     }
3760 #endif
3761 
3762     return p;
3763 }
3764 
3765 #if defined(FEAT_EVAL) || defined(PROTO)
3766 static struct cmdmod
3767 {
3768     char	*name;
3769     int		minlen;
3770     int		has_count;  // :123verbose  :3tab
3771 } cmdmods[] = {
3772     {"aboveleft", 3, FALSE},
3773     {"belowright", 3, FALSE},
3774     {"botright", 2, FALSE},
3775     {"browse", 3, FALSE},
3776     {"confirm", 4, FALSE},
3777     {"filter", 4, FALSE},
3778     {"hide", 3, FALSE},
3779     {"keepalt", 5, FALSE},
3780     {"keepjumps", 5, FALSE},
3781     {"keepmarks", 3, FALSE},
3782     {"keeppatterns", 5, FALSE},
3783     {"leftabove", 5, FALSE},
3784     {"lockmarks", 3, FALSE},
3785     {"noautocmd", 3, FALSE},
3786     {"noswapfile", 3, FALSE},
3787     {"rightbelow", 6, FALSE},
3788     {"sandbox", 3, FALSE},
3789     {"silent", 3, FALSE},
3790     {"tab", 3, TRUE},
3791     {"topleft", 2, FALSE},
3792     {"unsilent", 3, FALSE},
3793     {"verbose", 4, TRUE},
3794     {"vertical", 4, FALSE},
3795 };
3796 
3797 /*
3798  * Return length of a command modifier (including optional count).
3799  * Return zero when it's not a modifier.
3800  */
3801     int
3802 modifier_len(char_u *cmd)
3803 {
3804     int		i, j;
3805     char_u	*p = cmd;
3806 
3807     if (VIM_ISDIGIT(*cmd))
3808 	p = skipwhite(skipdigits(cmd + 1));
3809     for (i = 0; i < (int)ARRAY_LENGTH(cmdmods); ++i)
3810     {
3811 	for (j = 0; p[j] != NUL; ++j)
3812 	    if (p[j] != cmdmods[i].name[j])
3813 		break;
3814 	if (!ASCII_ISALPHA(p[j]) && j >= cmdmods[i].minlen
3815 					&& (p == cmd || cmdmods[i].has_count))
3816 	    return j + (int)(p - cmd);
3817     }
3818     return 0;
3819 }
3820 
3821 /*
3822  * Return > 0 if an Ex command "name" exists.
3823  * Return 2 if there is an exact match.
3824  * Return 3 if there is an ambiguous match.
3825  */
3826     int
3827 cmd_exists(char_u *name)
3828 {
3829     exarg_T	ea;
3830     int		full = FALSE;
3831     int		i;
3832     int		j;
3833     char_u	*p;
3834 
3835     // Check command modifiers.
3836     for (i = 0; i < (int)ARRAY_LENGTH(cmdmods); ++i)
3837     {
3838 	for (j = 0; name[j] != NUL; ++j)
3839 	    if (name[j] != cmdmods[i].name[j])
3840 		break;
3841 	if (name[j] == NUL && j >= cmdmods[i].minlen)
3842 	    return (cmdmods[i].name[j] == NUL ? 2 : 1);
3843     }
3844 
3845     // Check built-in commands and user defined commands.
3846     // For ":2match" and ":3match" we need to skip the number.
3847     ea.cmd = (*name == '2' || *name == '3') ? name + 1 : name;
3848     ea.cmdidx = (cmdidx_T)0;
3849     p = find_ex_command(&ea, &full, NULL, NULL);
3850     if (p == NULL)
3851 	return 3;
3852     if (vim_isdigit(*name) && ea.cmdidx != CMD_match)
3853 	return 0;
3854     if (*skipwhite(p) != NUL)
3855 	return 0;	// trailing garbage
3856     return (ea.cmdidx == CMD_SIZE ? 0 : (full ? 2 : 1));
3857 }
3858 
3859 /*
3860  * "fullcommand" function
3861  */
3862     void
3863 f_fullcommand(typval_T *argvars, typval_T *rettv)
3864 {
3865     exarg_T  ea;
3866     char_u   *name;
3867     char_u   *p;
3868 
3869     rettv->v_type = VAR_STRING;
3870     rettv->vval.v_string = NULL;
3871 
3872     if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
3873 	return;
3874 
3875     name = argvars[0].vval.v_string;
3876     if (name == NULL)
3877 	return;
3878 
3879     while (*name != NUL && *name == ':')
3880 	name++;
3881     name = skip_range(name, TRUE, NULL);
3882 
3883     ea.cmd = (*name == '2' || *name == '3') ? name + 1 : name;
3884     ea.cmdidx = (cmdidx_T)0;
3885     ea.addr_count = 0;
3886     p = find_ex_command(&ea, NULL, NULL, NULL);
3887     if (p == NULL || ea.cmdidx == CMD_SIZE)
3888 	return;
3889     if (in_vim9script())
3890     {
3891 	int	     res;
3892 
3893 	++emsg_silent;
3894 	res = not_in_vim9(&ea);
3895 	--emsg_silent;
3896 
3897 	if (res == FAIL)
3898 	    return;
3899     }
3900 
3901     rettv->vval.v_string = vim_strsave(IS_USER_CMDIDX(ea.cmdidx)
3902 				 ? get_user_command_name(ea.useridx, ea.cmdidx)
3903 				 : cmdnames[ea.cmdidx].cmd_name);
3904 }
3905 #endif
3906 
3907     cmdidx_T
3908 excmd_get_cmdidx(char_u *cmd, int len)
3909 {
3910     cmdidx_T idx;
3911 
3912     for (idx = (cmdidx_T)0; (int)idx < (int)CMD_SIZE;
3913 	    idx = (cmdidx_T)((int)idx + 1))
3914 	if (STRNCMP(cmdnames[(int)idx].cmd_name, cmd, (size_t)len) == 0)
3915 	    break;
3916 
3917     return idx;
3918 }
3919 
3920     long
3921 excmd_get_argt(cmdidx_T idx)
3922 {
3923     return (long)cmdnames[(int)idx].cmd_argt;
3924 }
3925 
3926 /*
3927  * Skip a range specifier of the form: addr [,addr] [;addr] ..
3928  *
3929  * Backslashed delimiters after / or ? will be skipped, and commands will
3930  * not be expanded between /'s and ?'s or after "'".
3931  *
3932  * Also skip white space and ":" characters after the range.
3933  * Returns the "cmd" pointer advanced to beyond the range.
3934  */
3935     char_u *
3936 skip_range(
3937     char_u	*cmd,
3938     int		skip_star,	// skip "*" used for Visual range
3939     int		*ctx)		// pointer to xp_context or NULL
3940 {
3941     unsigned	delim;
3942 
3943     while (vim_strchr((char_u *)" \t0123456789.$%'/?-+,;\\", *cmd) != NULL)
3944     {
3945 	if (*cmd == '\\')
3946 	{
3947 	    if (cmd[1] == '?' || cmd[1] == '/' || cmd[1] == '&')
3948 		++cmd;
3949 	    else
3950 		break;
3951 	}
3952 	else if (*cmd == '\'')
3953 	{
3954 	    if (*++cmd == NUL && ctx != NULL)
3955 		*ctx = EXPAND_NOTHING;
3956 	}
3957 	else if (*cmd == '/' || *cmd == '?')
3958 	{
3959 	    delim = *cmd++;
3960 	    while (*cmd != NUL && *cmd != delim)
3961 		if (*cmd++ == '\\' && *cmd != NUL)
3962 		    ++cmd;
3963 	    if (*cmd == NUL && ctx != NULL)
3964 		*ctx = EXPAND_NOTHING;
3965 	}
3966 	if (*cmd != NUL)
3967 	    ++cmd;
3968     }
3969 
3970     // Skip ":" and white space.
3971     while (*cmd == ':')
3972 	cmd = skipwhite(cmd + 1);
3973 
3974     // Skip "*" used for Visual range.
3975     if (skip_star && *cmd == '*' && vim_strchr(p_cpo, CPO_STAR) == NULL)
3976 	cmd = skipwhite(cmd + 1);
3977 
3978     return cmd;
3979 }
3980 
3981     static void
3982 addr_error(cmd_addr_T addr_type)
3983 {
3984     if (addr_type == ADDR_NONE)
3985 	emsg(_(e_norange));
3986     else
3987 	emsg(_(e_invalid_range));
3988 }
3989 
3990 /*
3991  * Return the default address for an address type.
3992  */
3993     static linenr_T
3994 default_address(exarg_T *eap)
3995 {
3996     linenr_T lnum = 0;
3997 
3998     switch (eap->addr_type)
3999     {
4000 	case ADDR_LINES:
4001 	case ADDR_OTHER:
4002 	    // Default is the cursor line number.  Avoid using an invalid
4003 	    // line number though.
4004 	    if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
4005 		lnum = curbuf->b_ml.ml_line_count;
4006 	    else
4007 		lnum = curwin->w_cursor.lnum;
4008 	    break;
4009 	case ADDR_WINDOWS:
4010 	    lnum = CURRENT_WIN_NR;
4011 	    break;
4012 	case ADDR_ARGUMENTS:
4013 	    lnum = curwin->w_arg_idx + 1;
4014 	    if (lnum > ARGCOUNT)
4015 		lnum = ARGCOUNT;
4016 	    break;
4017 	case ADDR_LOADED_BUFFERS:
4018 	case ADDR_BUFFERS:
4019 	    lnum = curbuf->b_fnum;
4020 	    break;
4021 	case ADDR_TABS:
4022 	    lnum = CURRENT_TAB_NR;
4023 	    break;
4024 	case ADDR_TABS_RELATIVE:
4025 	case ADDR_UNSIGNED:
4026 	    lnum = 1;
4027 	    break;
4028 	case ADDR_QUICKFIX:
4029 #ifdef FEAT_QUICKFIX
4030 	    lnum = qf_get_cur_idx(eap);
4031 #endif
4032 	    break;
4033 	case ADDR_QUICKFIX_VALID:
4034 #ifdef FEAT_QUICKFIX
4035 	    lnum = qf_get_cur_valid_idx(eap);
4036 #endif
4037 	    break;
4038 	case ADDR_NONE:
4039 	    // Will give an error later if a range is found.
4040 	    break;
4041     }
4042     return lnum;
4043 }
4044 
4045 /*
4046  * Get a single EX address.
4047  *
4048  * Set ptr to the next character after the part that was interpreted.
4049  * Set ptr to NULL when an error is encountered.
4050  * This may set the last used search pattern.
4051  *
4052  * Return MAXLNUM when no Ex address was found.
4053  */
4054     static linenr_T
4055 get_address(
4056     exarg_T	*eap UNUSED,
4057     char_u	**ptr,
4058     cmd_addr_T	addr_type,
4059     int		skip,		// only skip the address, don't use it
4060     int		silent,		// no errors or side effects
4061     int		to_other_file,  // flag: may jump to other file
4062     int		address_count UNUSED) // 1 for first address, >1 after comma
4063 {
4064     int		c;
4065     int		i;
4066     long	n;
4067     char_u	*cmd;
4068     pos_T	pos;
4069     pos_T	*fp;
4070     linenr_T	lnum;
4071     buf_T	*buf;
4072 
4073     cmd = skipwhite(*ptr);
4074     lnum = MAXLNUM;
4075     do
4076     {
4077 	switch (*cmd)
4078 	{
4079 	    case '.':			    // '.' - Cursor position
4080 		++cmd;
4081 		switch (addr_type)
4082 		{
4083 		    case ADDR_LINES:
4084 		    case ADDR_OTHER:
4085 			lnum = curwin->w_cursor.lnum;
4086 			break;
4087 		    case ADDR_WINDOWS:
4088 			lnum = CURRENT_WIN_NR;
4089 			break;
4090 		    case ADDR_ARGUMENTS:
4091 			lnum = curwin->w_arg_idx + 1;
4092 			break;
4093 		    case ADDR_LOADED_BUFFERS:
4094 		    case ADDR_BUFFERS:
4095 			lnum = curbuf->b_fnum;
4096 			break;
4097 		    case ADDR_TABS:
4098 			lnum = CURRENT_TAB_NR;
4099 			break;
4100 		    case ADDR_NONE:
4101 		    case ADDR_TABS_RELATIVE:
4102 		    case ADDR_UNSIGNED:
4103 			addr_error(addr_type);
4104 			cmd = NULL;
4105 			goto error;
4106 			break;
4107 		    case ADDR_QUICKFIX:
4108 #ifdef FEAT_QUICKFIX
4109 			lnum = qf_get_cur_idx(eap);
4110 #endif
4111 			break;
4112 		    case ADDR_QUICKFIX_VALID:
4113 #ifdef FEAT_QUICKFIX
4114 			lnum = qf_get_cur_valid_idx(eap);
4115 #endif
4116 			break;
4117 		}
4118 		break;
4119 
4120 	    case '$':			    // '$' - last line
4121 		++cmd;
4122 		switch (addr_type)
4123 		{
4124 		    case ADDR_LINES:
4125 		    case ADDR_OTHER:
4126 			lnum = curbuf->b_ml.ml_line_count;
4127 			break;
4128 		    case ADDR_WINDOWS:
4129 			lnum = LAST_WIN_NR;
4130 			break;
4131 		    case ADDR_ARGUMENTS:
4132 			lnum = ARGCOUNT;
4133 			break;
4134 		    case ADDR_LOADED_BUFFERS:
4135 			buf = lastbuf;
4136 			while (buf->b_ml.ml_mfp == NULL)
4137 			{
4138 			    if (buf->b_prev == NULL)
4139 				break;
4140 			    buf = buf->b_prev;
4141 			}
4142 			lnum = buf->b_fnum;
4143 			break;
4144 		    case ADDR_BUFFERS:
4145 			lnum = lastbuf->b_fnum;
4146 			break;
4147 		    case ADDR_TABS:
4148 			lnum = LAST_TAB_NR;
4149 			break;
4150 		    case ADDR_NONE:
4151 		    case ADDR_TABS_RELATIVE:
4152 		    case ADDR_UNSIGNED:
4153 			addr_error(addr_type);
4154 			cmd = NULL;
4155 			goto error;
4156 			break;
4157 		    case ADDR_QUICKFIX:
4158 #ifdef FEAT_QUICKFIX
4159 			lnum = qf_get_size(eap);
4160 			if (lnum == 0)
4161 			    lnum = 1;
4162 #endif
4163 			break;
4164 		    case ADDR_QUICKFIX_VALID:
4165 #ifdef FEAT_QUICKFIX
4166 			lnum = qf_get_valid_size(eap);
4167 			if (lnum == 0)
4168 			    lnum = 1;
4169 #endif
4170 			break;
4171 		}
4172 		break;
4173 
4174 	    case '\'':			    // ''' - mark
4175 		if (*++cmd == NUL)
4176 		{
4177 		    cmd = NULL;
4178 		    goto error;
4179 		}
4180 		if (addr_type != ADDR_LINES)
4181 		{
4182 		    addr_error(addr_type);
4183 		    cmd = NULL;
4184 		    goto error;
4185 		}
4186 		if (skip)
4187 		    ++cmd;
4188 		else
4189 		{
4190 		    // Only accept a mark in another file when it is
4191 		    // used by itself: ":'M".
4192 		    fp = getmark(*cmd, to_other_file && cmd[1] == NUL);
4193 		    ++cmd;
4194 		    if (fp == (pos_T *)-1)
4195 			// Jumped to another file.
4196 			lnum = curwin->w_cursor.lnum;
4197 		    else
4198 		    {
4199 			if (check_mark(fp) == FAIL)
4200 			{
4201 			    cmd = NULL;
4202 			    goto error;
4203 			}
4204 			lnum = fp->lnum;
4205 		    }
4206 		}
4207 		break;
4208 
4209 	    case '/':
4210 	    case '?':			// '/' or '?' - search
4211 		c = *cmd++;
4212 		if (addr_type != ADDR_LINES)
4213 		{
4214 		    addr_error(addr_type);
4215 		    cmd = NULL;
4216 		    goto error;
4217 		}
4218 		if (skip)	// skip "/pat/"
4219 		{
4220 		    cmd = skip_regexp(cmd, c, magic_isset());
4221 		    if (*cmd == c)
4222 			++cmd;
4223 		}
4224 		else
4225 		{
4226 		    int flags;
4227 
4228 		    pos = curwin->w_cursor; // save curwin->w_cursor
4229 
4230 		    // When '/' or '?' follows another address, start from
4231 		    // there.
4232 		    if (lnum != MAXLNUM)
4233 			curwin->w_cursor.lnum = lnum;
4234 
4235 		    // Start a forward search at the end of the line (unless
4236 		    // before the first line).
4237 		    // Start a backward search at the start of the line.
4238 		    // This makes sure we never match in the current
4239 		    // line, and can match anywhere in the
4240 		    // next/previous line.
4241 		    if (c == '/' && curwin->w_cursor.lnum > 0)
4242 			curwin->w_cursor.col = MAXCOL;
4243 		    else
4244 			curwin->w_cursor.col = 0;
4245 		    searchcmdlen = 0;
4246 		    flags = silent ? 0 : SEARCH_HIS | SEARCH_MSG;
4247 		    if (!do_search(NULL, c, c, cmd, 1L, flags, NULL))
4248 		    {
4249 			curwin->w_cursor = pos;
4250 			cmd = NULL;
4251 			goto error;
4252 		    }
4253 		    lnum = curwin->w_cursor.lnum;
4254 		    curwin->w_cursor = pos;
4255 		    // adjust command string pointer
4256 		    cmd += searchcmdlen;
4257 		}
4258 		break;
4259 
4260 	    case '\\':		    // "\?", "\/" or "\&", repeat search
4261 		++cmd;
4262 		if (addr_type != ADDR_LINES)
4263 		{
4264 		    addr_error(addr_type);
4265 		    cmd = NULL;
4266 		    goto error;
4267 		}
4268 		if (*cmd == '&')
4269 		    i = RE_SUBST;
4270 		else if (*cmd == '?' || *cmd == '/')
4271 		    i = RE_SEARCH;
4272 		else
4273 		{
4274 		    emsg(_(e_backslash_should_be_followed_by));
4275 		    cmd = NULL;
4276 		    goto error;
4277 		}
4278 
4279 		if (!skip)
4280 		{
4281 		    /*
4282 		     * When search follows another address, start from
4283 		     * there.
4284 		     */
4285 		    if (lnum != MAXLNUM)
4286 			pos.lnum = lnum;
4287 		    else
4288 			pos.lnum = curwin->w_cursor.lnum;
4289 
4290 		    /*
4291 		     * Start the search just like for the above
4292 		     * do_search().
4293 		     */
4294 		    if (*cmd != '?')
4295 			pos.col = MAXCOL;
4296 		    else
4297 			pos.col = 0;
4298 		    pos.coladd = 0;
4299 		    if (searchit(curwin, curbuf, &pos, NULL,
4300 				*cmd == '?' ? BACKWARD : FORWARD,
4301 				(char_u *)"", 1L, SEARCH_MSG, i, NULL) != FAIL)
4302 			lnum = pos.lnum;
4303 		    else
4304 		    {
4305 			cmd = NULL;
4306 			goto error;
4307 		    }
4308 		}
4309 		++cmd;
4310 		break;
4311 
4312 	    default:
4313 		if (VIM_ISDIGIT(*cmd))	// absolute line number
4314 		    lnum = getdigits(&cmd);
4315 	}
4316 
4317 	for (;;)
4318 	{
4319 	    cmd = skipwhite(cmd);
4320 	    if (*cmd != '-' && *cmd != '+' && !VIM_ISDIGIT(*cmd))
4321 		break;
4322 
4323 	    if (lnum == MAXLNUM)
4324 	    {
4325 		switch (addr_type)
4326 		{
4327 		    case ADDR_LINES:
4328 		    case ADDR_OTHER:
4329 			// "+1" is same as ".+1"
4330 			lnum = curwin->w_cursor.lnum;
4331 			break;
4332 		    case ADDR_WINDOWS:
4333 			lnum = CURRENT_WIN_NR;
4334 			break;
4335 		    case ADDR_ARGUMENTS:
4336 			lnum = curwin->w_arg_idx + 1;
4337 			break;
4338 		    case ADDR_LOADED_BUFFERS:
4339 		    case ADDR_BUFFERS:
4340 			lnum = curbuf->b_fnum;
4341 			break;
4342 		    case ADDR_TABS:
4343 			lnum = CURRENT_TAB_NR;
4344 			break;
4345 		    case ADDR_TABS_RELATIVE:
4346 			lnum = 1;
4347 			break;
4348 		    case ADDR_QUICKFIX:
4349 #ifdef FEAT_QUICKFIX
4350 			lnum = qf_get_cur_idx(eap);
4351 #endif
4352 			break;
4353 		    case ADDR_QUICKFIX_VALID:
4354 #ifdef FEAT_QUICKFIX
4355 			lnum = qf_get_cur_valid_idx(eap);
4356 #endif
4357 			break;
4358 		    case ADDR_NONE:
4359 		    case ADDR_UNSIGNED:
4360 			lnum = 0;
4361 			break;
4362 		}
4363 	    }
4364 
4365 	    if (VIM_ISDIGIT(*cmd))
4366 		i = '+';		// "number" is same as "+number"
4367 	    else
4368 		i = *cmd++;
4369 	    if (!VIM_ISDIGIT(*cmd))	// '+' is '+1', but '+0' is not '+1'
4370 		n = 1;
4371 	    else
4372 		n = getdigits(&cmd);
4373 
4374 	    if (addr_type == ADDR_TABS_RELATIVE)
4375 	    {
4376 		emsg(_(e_invalid_range));
4377 		cmd = NULL;
4378 		goto error;
4379 	    }
4380 	    else if (addr_type == ADDR_LOADED_BUFFERS
4381 		    || addr_type == ADDR_BUFFERS)
4382 		lnum = compute_buffer_local_count(
4383 				    addr_type, lnum, (i == '-') ? -1 * n : n);
4384 	    else
4385 	    {
4386 #ifdef FEAT_FOLDING
4387 		// Relative line addressing, need to adjust for folded lines
4388 		// now, but only do it after the first address.
4389 		if (addr_type == ADDR_LINES && (i == '-' || i == '+')
4390 			&& address_count >= 2)
4391 		    (void)hasFolding(lnum, NULL, &lnum);
4392 #endif
4393 		if (i == '-')
4394 		    lnum -= n;
4395 		else
4396 		    lnum += n;
4397 	    }
4398 	}
4399     } while (*cmd == '/' || *cmd == '?');
4400 
4401 error:
4402     *ptr = cmd;
4403     return lnum;
4404 }
4405 
4406 /*
4407  * Set eap->line1 and eap->line2 to the whole range.
4408  * Used for commands with the EX_DFLALL flag and no range given.
4409  */
4410     static void
4411 address_default_all(exarg_T *eap)
4412 {
4413     eap->line1 = 1;
4414     switch (eap->addr_type)
4415     {
4416 	case ADDR_LINES:
4417 	case ADDR_OTHER:
4418 	    eap->line2 = curbuf->b_ml.ml_line_count;
4419 	    break;
4420 	case ADDR_LOADED_BUFFERS:
4421 	    {
4422 		buf_T *buf = firstbuf;
4423 
4424 		while (buf->b_next != NULL && buf->b_ml.ml_mfp == NULL)
4425 		    buf = buf->b_next;
4426 		eap->line1 = buf->b_fnum;
4427 		buf = lastbuf;
4428 		while (buf->b_prev != NULL && buf->b_ml.ml_mfp == NULL)
4429 		    buf = buf->b_prev;
4430 		eap->line2 = buf->b_fnum;
4431 	    }
4432 	    break;
4433 	case ADDR_BUFFERS:
4434 	    eap->line1 = firstbuf->b_fnum;
4435 	    eap->line2 = lastbuf->b_fnum;
4436 	    break;
4437 	case ADDR_WINDOWS:
4438 	    eap->line2 = LAST_WIN_NR;
4439 	    break;
4440 	case ADDR_TABS:
4441 	    eap->line2 = LAST_TAB_NR;
4442 	    break;
4443 	case ADDR_TABS_RELATIVE:
4444 	    eap->line2 = 1;
4445 	    break;
4446 	case ADDR_ARGUMENTS:
4447 	    if (ARGCOUNT == 0)
4448 		eap->line1 = eap->line2 = 0;
4449 	    else
4450 		eap->line2 = ARGCOUNT;
4451 	    break;
4452 	case ADDR_QUICKFIX_VALID:
4453 #ifdef FEAT_QUICKFIX
4454 	    eap->line2 = qf_get_valid_size(eap);
4455 	    if (eap->line2 == 0)
4456 		eap->line2 = 1;
4457 #endif
4458 	    break;
4459 	case ADDR_NONE:
4460 	case ADDR_UNSIGNED:
4461 	case ADDR_QUICKFIX:
4462 	    iemsg(_("INTERNAL: Cannot use EX_DFLALL with ADDR_NONE, ADDR_UNSIGNED or ADDR_QUICKFIX"));
4463 	    break;
4464     }
4465 }
4466 
4467 
4468 /*
4469  * Get flags from an Ex command argument.
4470  */
4471     static void
4472 get_flags(exarg_T *eap)
4473 {
4474     while (vim_strchr((char_u *)"lp#", *eap->arg) != NULL)
4475     {
4476 	if (*eap->arg == 'l')
4477 	    eap->flags |= EXFLAG_LIST;
4478 	else if (*eap->arg == 'p')
4479 	    eap->flags |= EXFLAG_PRINT;
4480 	else
4481 	    eap->flags |= EXFLAG_NR;
4482 	eap->arg = skipwhite(eap->arg + 1);
4483     }
4484 }
4485 
4486 /*
4487  * Function called for command which is Not Implemented.  NI!
4488  */
4489     void
4490 ex_ni(exarg_T *eap)
4491 {
4492     if (!eap->skip)
4493 	eap->errmsg =
4494 		_("E319: Sorry, the command is not available in this version");
4495 }
4496 
4497 #ifdef HAVE_EX_SCRIPT_NI
4498 /*
4499  * Function called for script command which is Not Implemented.  NI!
4500  * Skips over ":perl <<EOF" constructs.
4501  */
4502     static void
4503 ex_script_ni(exarg_T *eap)
4504 {
4505     if (!eap->skip)
4506 	ex_ni(eap);
4507     else
4508 	vim_free(script_get(eap, eap->arg));
4509 }
4510 #endif
4511 
4512 /*
4513  * Check range in Ex command for validity.
4514  * Return NULL when valid, error message when invalid.
4515  */
4516     static char *
4517 invalid_range(exarg_T *eap)
4518 {
4519     buf_T	*buf;
4520 
4521     if (       eap->line1 < 0
4522 	    || eap->line2 < 0
4523 	    || eap->line1 > eap->line2)
4524 	return _(e_invalid_range);
4525 
4526     if (eap->argt & EX_RANGE)
4527     {
4528 	switch (eap->addr_type)
4529 	{
4530 	    case ADDR_LINES:
4531 		if (eap->line2 > curbuf->b_ml.ml_line_count
4532 #ifdef FEAT_DIFF
4533 			    + (eap->cmdidx == CMD_diffget)
4534 #endif
4535 		   )
4536 		    return _(e_invalid_range);
4537 		break;
4538 	    case ADDR_ARGUMENTS:
4539 		// add 1 if ARGCOUNT is 0
4540 		if (eap->line2 > ARGCOUNT + (!ARGCOUNT))
4541 		    return _(e_invalid_range);
4542 		break;
4543 	    case ADDR_BUFFERS:
4544 		// Only a boundary check, not whether the buffers actually
4545 		// exist.
4546 		if (eap->line1 < 1 || eap->line2 > get_highest_fnum())
4547 		    return _(e_invalid_range);
4548 		break;
4549 	    case ADDR_LOADED_BUFFERS:
4550 		buf = firstbuf;
4551 		while (buf->b_ml.ml_mfp == NULL)
4552 		{
4553 		    if (buf->b_next == NULL)
4554 			return _(e_invalid_range);
4555 		    buf = buf->b_next;
4556 		}
4557 		if (eap->line1 < buf->b_fnum)
4558 		    return _(e_invalid_range);
4559 		buf = lastbuf;
4560 		while (buf->b_ml.ml_mfp == NULL)
4561 		{
4562 		    if (buf->b_prev == NULL)
4563 			return _(e_invalid_range);
4564 		    buf = buf->b_prev;
4565 		}
4566 		if (eap->line2 > buf->b_fnum)
4567 		    return _(e_invalid_range);
4568 		break;
4569 	    case ADDR_WINDOWS:
4570 		if (eap->line2 > LAST_WIN_NR)
4571 		    return _(e_invalid_range);
4572 		break;
4573 	    case ADDR_TABS:
4574 		if (eap->line2 > LAST_TAB_NR)
4575 		    return _(e_invalid_range);
4576 		break;
4577 	    case ADDR_TABS_RELATIVE:
4578 	    case ADDR_OTHER:
4579 		// Any range is OK.
4580 		break;
4581 	    case ADDR_QUICKFIX:
4582 #ifdef FEAT_QUICKFIX
4583 		// No error for value that is too big, will use the last entry.
4584 		if (eap->line2 <= 0)
4585 		    return _(e_invalid_range);
4586 #endif
4587 		break;
4588 	    case ADDR_QUICKFIX_VALID:
4589 #ifdef FEAT_QUICKFIX
4590 		if ((eap->line2 != 1 && eap->line2 > qf_get_valid_size(eap))
4591 			|| eap->line2 < 0)
4592 		    return _(e_invalid_range);
4593 #endif
4594 		break;
4595 	    case ADDR_UNSIGNED:
4596 	    case ADDR_NONE:
4597 		// Will give an error elsewhere.
4598 		break;
4599 	}
4600     }
4601     return NULL;
4602 }
4603 
4604 /*
4605  * Correct the range for zero line number, if required.
4606  */
4607     static void
4608 correct_range(exarg_T *eap)
4609 {
4610     if (!(eap->argt & EX_ZEROR))	    // zero in range not allowed
4611     {
4612 	if (eap->line1 == 0)
4613 	    eap->line1 = 1;
4614 	if (eap->line2 == 0)
4615 	    eap->line2 = 1;
4616     }
4617 }
4618 
4619 #ifdef FEAT_QUICKFIX
4620 /*
4621  * For a ":vimgrep" or ":vimgrepadd" command return a pointer past the
4622  * pattern.  Otherwise return eap->arg.
4623  */
4624     static char_u *
4625 skip_grep_pat(exarg_T *eap)
4626 {
4627     char_u	*p = eap->arg;
4628 
4629     if (*p != NUL && (eap->cmdidx == CMD_vimgrep || eap->cmdidx == CMD_lvimgrep
4630 		|| eap->cmdidx == CMD_vimgrepadd
4631 		|| eap->cmdidx == CMD_lvimgrepadd
4632 		|| grep_internal(eap->cmdidx)))
4633     {
4634 	p = skip_vimgrep_pat(p, NULL, NULL);
4635 	if (p == NULL)
4636 	    p = eap->arg;
4637     }
4638     return p;
4639 }
4640 
4641 /*
4642  * For the ":make" and ":grep" commands insert the 'makeprg'/'grepprg' option
4643  * in the command line, so that things like % get expanded.
4644  */
4645     static char_u *
4646 replace_makeprg(exarg_T *eap, char_u *p, char_u **cmdlinep)
4647 {
4648     char_u	*new_cmdline;
4649     char_u	*program;
4650     char_u	*pos;
4651     char_u	*ptr;
4652     int		len;
4653     int		i;
4654 
4655     /*
4656      * Don't do it when ":vimgrep" is used for ":grep".
4657      */
4658     if ((eap->cmdidx == CMD_make || eap->cmdidx == CMD_lmake
4659 		     || eap->cmdidx == CMD_grep || eap->cmdidx == CMD_lgrep
4660 		     || eap->cmdidx == CMD_grepadd
4661 		     || eap->cmdidx == CMD_lgrepadd)
4662 	    && !grep_internal(eap->cmdidx))
4663     {
4664 	if (eap->cmdidx == CMD_grep || eap->cmdidx == CMD_lgrep
4665 	    || eap->cmdidx == CMD_grepadd || eap->cmdidx == CMD_lgrepadd)
4666 	{
4667 	    if (*curbuf->b_p_gp == NUL)
4668 		program = p_gp;
4669 	    else
4670 		program = curbuf->b_p_gp;
4671 	}
4672 	else
4673 	{
4674 	    if (*curbuf->b_p_mp == NUL)
4675 		program = p_mp;
4676 	    else
4677 		program = curbuf->b_p_mp;
4678 	}
4679 
4680 	p = skipwhite(p);
4681 
4682 	if ((pos = (char_u *)strstr((char *)program, "$*")) != NULL)
4683 	{
4684 	    // replace $* by given arguments
4685 	    i = 1;
4686 	    while ((pos = (char_u *)strstr((char *)pos + 2, "$*")) != NULL)
4687 		++i;
4688 	    len = (int)STRLEN(p);
4689 	    new_cmdline = alloc(STRLEN(program) + i * (len - 2) + 1);
4690 	    if (new_cmdline == NULL)
4691 		return NULL;			// out of memory
4692 	    ptr = new_cmdline;
4693 	    while ((pos = (char_u *)strstr((char *)program, "$*")) != NULL)
4694 	    {
4695 		i = (int)(pos - program);
4696 		STRNCPY(ptr, program, i);
4697 		STRCPY(ptr += i, p);
4698 		ptr += len;
4699 		program = pos + 2;
4700 	    }
4701 	    STRCPY(ptr, program);
4702 	}
4703 	else
4704 	{
4705 	    new_cmdline = alloc(STRLEN(program) + STRLEN(p) + 2);
4706 	    if (new_cmdline == NULL)
4707 		return NULL;			// out of memory
4708 	    STRCPY(new_cmdline, program);
4709 	    STRCAT(new_cmdline, " ");
4710 	    STRCAT(new_cmdline, p);
4711 	}
4712 	msg_make(p);
4713 
4714 	// 'eap->cmd' is not set here, because it is not used at CMD_make
4715 	vim_free(*cmdlinep);
4716 	*cmdlinep = new_cmdline;
4717 	p = new_cmdline;
4718     }
4719     return p;
4720 }
4721 #endif
4722 
4723 /*
4724  * Expand file name in Ex command argument.
4725  * When an error is detected, "errormsgp" is set to a non-NULL pointer.
4726  * Return FAIL for failure, OK otherwise.
4727  */
4728     int
4729 expand_filename(
4730     exarg_T	*eap,
4731     char_u	**cmdlinep,
4732     char	**errormsgp)
4733 {
4734     int		has_wildcards;	// need to expand wildcards
4735     char_u	*repl;
4736     int		srclen;
4737     char_u	*p;
4738     int		n;
4739     int		escaped;
4740 
4741 #ifdef FEAT_QUICKFIX
4742     // Skip a regexp pattern for ":vimgrep[add] pat file..."
4743     p = skip_grep_pat(eap);
4744 #else
4745     p = eap->arg;
4746 #endif
4747 
4748     /*
4749      * Decide to expand wildcards *before* replacing '%', '#', etc.  If
4750      * the file name contains a wildcard it should not cause expanding.
4751      * (it will be expanded anyway if there is a wildcard before replacing).
4752      */
4753     has_wildcards = mch_has_wildcard(p);
4754     while (*p != NUL)
4755     {
4756 #ifdef FEAT_EVAL
4757 	// Skip over `=expr`, wildcards in it are not expanded.
4758 	if (p[0] == '`' && p[1] == '=')
4759 	{
4760 	    p += 2;
4761 	    (void)skip_expr(&p, NULL);
4762 	    if (*p == '`')
4763 		++p;
4764 	    continue;
4765 	}
4766 #endif
4767 	/*
4768 	 * Quick check if this cannot be the start of a special string.
4769 	 * Also removes backslash before '%', '#' and '<'.
4770 	 */
4771 	if (vim_strchr((char_u *)"%#<", *p) == NULL)
4772 	{
4773 	    ++p;
4774 	    continue;
4775 	}
4776 
4777 	/*
4778 	 * Try to find a match at this position.
4779 	 */
4780 	repl = eval_vars(p, eap->arg, &srclen, &(eap->do_ecmd_lnum),
4781 							 errormsgp, &escaped);
4782 	if (*errormsgp != NULL)		// error detected
4783 	    return FAIL;
4784 	if (repl == NULL)		// no match found
4785 	{
4786 	    p += srclen;
4787 	    continue;
4788 	}
4789 
4790 	// Wildcards won't be expanded below, the replacement is taken
4791 	// literally.  But do expand "~/file", "~user/file" and "$HOME/file".
4792 	if (vim_strchr(repl, '$') != NULL || vim_strchr(repl, '~') != NULL)
4793 	{
4794 	    char_u *l = repl;
4795 
4796 	    repl = expand_env_save(repl);
4797 	    vim_free(l);
4798 	}
4799 
4800 	// Need to escape white space et al. with a backslash.
4801 	// Don't do this for:
4802 	// - replacement that already has been escaped: "##"
4803 	// - shell commands (may have to use quotes instead).
4804 	// - non-unix systems when there is a single argument (spaces don't
4805 	//   separate arguments then).
4806 	if (!eap->usefilter
4807 		&& !escaped
4808 		&& eap->cmdidx != CMD_bang
4809 		&& eap->cmdidx != CMD_grep
4810 		&& eap->cmdidx != CMD_grepadd
4811 		&& eap->cmdidx != CMD_hardcopy
4812 		&& eap->cmdidx != CMD_lgrep
4813 		&& eap->cmdidx != CMD_lgrepadd
4814 		&& eap->cmdidx != CMD_lmake
4815 		&& eap->cmdidx != CMD_make
4816 		&& eap->cmdidx != CMD_terminal
4817 #ifndef UNIX
4818 		&& !(eap->argt & EX_NOSPC)
4819 #endif
4820 		)
4821 	{
4822 	    char_u	*l;
4823 #ifdef BACKSLASH_IN_FILENAME
4824 	    // Don't escape a backslash here, because rem_backslash() doesn't
4825 	    // remove it later.
4826 	    static char_u *nobslash = (char_u *)" \t\"|";
4827 # define ESCAPE_CHARS nobslash
4828 #else
4829 # define ESCAPE_CHARS escape_chars
4830 #endif
4831 
4832 	    for (l = repl; *l; ++l)
4833 		if (vim_strchr(ESCAPE_CHARS, *l) != NULL)
4834 		{
4835 		    l = vim_strsave_escaped(repl, ESCAPE_CHARS);
4836 		    if (l != NULL)
4837 		    {
4838 			vim_free(repl);
4839 			repl = l;
4840 		    }
4841 		    break;
4842 		}
4843 	}
4844 
4845 	// For a shell command a '!' must be escaped.
4846 	if ((eap->usefilter || eap->cmdidx == CMD_bang
4847 						|| eap->cmdidx == CMD_terminal)
4848 			    && vim_strpbrk(repl, (char_u *)"!") != NULL)
4849 	{
4850 	    char_u	*l;
4851 
4852 	    l = vim_strsave_escaped(repl, (char_u *)"!");
4853 	    if (l != NULL)
4854 	    {
4855 		vim_free(repl);
4856 		repl = l;
4857 	    }
4858 	}
4859 
4860 	p = repl_cmdline(eap, p, srclen, repl, cmdlinep);
4861 	vim_free(repl);
4862 	if (p == NULL)
4863 	    return FAIL;
4864     }
4865 
4866     /*
4867      * One file argument: Expand wildcards.
4868      * Don't do this with ":r !command" or ":w !command".
4869      */
4870     if ((eap->argt & EX_NOSPC) && !eap->usefilter)
4871     {
4872 	/*
4873 	 * May do this twice:
4874 	 * 1. Replace environment variables.
4875 	 * 2. Replace any other wildcards, remove backslashes.
4876 	 */
4877 	for (n = 1; n <= 2; ++n)
4878 	{
4879 	    if (n == 2)
4880 	    {
4881 		/*
4882 		 * Halve the number of backslashes (this is Vi compatible).
4883 		 * For Unix and OS/2, when wildcards are expanded, this is
4884 		 * done by ExpandOne() below.
4885 		 */
4886 #if defined(UNIX)
4887 		if (!has_wildcards)
4888 #endif
4889 		    backslash_halve(eap->arg);
4890 	    }
4891 
4892 	    if (has_wildcards)
4893 	    {
4894 		if (n == 1)
4895 		{
4896 		    /*
4897 		     * First loop: May expand environment variables.  This
4898 		     * can be done much faster with expand_env() than with
4899 		     * something else (e.g., calling a shell).
4900 		     * After expanding environment variables, check again
4901 		     * if there are still wildcards present.
4902 		     */
4903 		    if (vim_strchr(eap->arg, '$') != NULL
4904 			    || vim_strchr(eap->arg, '~') != NULL)
4905 		    {
4906 			expand_env_esc(eap->arg, NameBuff, MAXPATHL,
4907 							    TRUE, TRUE, NULL);
4908 			has_wildcards = mch_has_wildcard(NameBuff);
4909 			p = NameBuff;
4910 		    }
4911 		    else
4912 			p = NULL;
4913 		}
4914 		else // n == 2
4915 		{
4916 		    expand_T	xpc;
4917 		    int		options = WILD_LIST_NOTFOUND
4918 					       | WILD_NOERROR | WILD_ADD_SLASH;
4919 
4920 		    ExpandInit(&xpc);
4921 		    xpc.xp_context = EXPAND_FILES;
4922 		    if (p_wic)
4923 			options += WILD_ICASE;
4924 		    p = ExpandOne(&xpc, eap->arg, NULL,
4925 						   options, WILD_EXPAND_FREE);
4926 		    if (p == NULL)
4927 			return FAIL;
4928 		}
4929 		if (p != NULL)
4930 		{
4931 		    (void)repl_cmdline(eap, eap->arg, (int)STRLEN(eap->arg),
4932 								 p, cmdlinep);
4933 		    if (n == 2)	// p came from ExpandOne()
4934 			vim_free(p);
4935 		}
4936 	    }
4937 	}
4938     }
4939     return OK;
4940 }
4941 
4942 /*
4943  * Replace part of the command line, keeping eap->cmd, eap->arg and
4944  * eap->nextcmd correct.
4945  * "src" points to the part that is to be replaced, of length "srclen".
4946  * "repl" is the replacement string.
4947  * Returns a pointer to the character after the replaced string.
4948  * Returns NULL for failure.
4949  */
4950     static char_u *
4951 repl_cmdline(
4952     exarg_T	*eap,
4953     char_u	*src,
4954     int		srclen,
4955     char_u	*repl,
4956     char_u	**cmdlinep)
4957 {
4958     int		len;
4959     int		i;
4960     char_u	*new_cmdline;
4961 
4962     /*
4963      * The new command line is build in new_cmdline[].
4964      * First allocate it.
4965      * Careful: a "+cmd" argument may have been NUL terminated.
4966      */
4967     len = (int)STRLEN(repl);
4968     i = (int)(src - *cmdlinep) + (int)STRLEN(src + srclen) + len + 3;
4969     if (eap->nextcmd != NULL)
4970 	i += (int)STRLEN(eap->nextcmd);// add space for next command
4971     if ((new_cmdline = alloc(i)) == NULL)
4972 	return NULL;			// out of memory!
4973 
4974     /*
4975      * Copy the stuff before the expanded part.
4976      * Copy the expanded stuff.
4977      * Copy what came after the expanded part.
4978      * Copy the next commands, if there are any.
4979      */
4980     i = (int)(src - *cmdlinep);	// length of part before match
4981     mch_memmove(new_cmdline, *cmdlinep, (size_t)i);
4982 
4983     mch_memmove(new_cmdline + i, repl, (size_t)len);
4984     i += len;				// remember the end of the string
4985     STRCPY(new_cmdline + i, src + srclen);
4986     src = new_cmdline + i;		// remember where to continue
4987 
4988     if (eap->nextcmd != NULL)		// append next command
4989     {
4990 	i = (int)STRLEN(new_cmdline) + 1;
4991 	STRCPY(new_cmdline + i, eap->nextcmd);
4992 	eap->nextcmd = new_cmdline + i;
4993     }
4994     eap->cmd = new_cmdline + (eap->cmd - *cmdlinep);
4995     eap->arg = new_cmdline + (eap->arg - *cmdlinep);
4996     if (eap->do_ecmd_cmd != NULL && eap->do_ecmd_cmd != dollar_command)
4997 	eap->do_ecmd_cmd = new_cmdline + (eap->do_ecmd_cmd - *cmdlinep);
4998     vim_free(*cmdlinep);
4999     *cmdlinep = new_cmdline;
5000 
5001     return src;
5002 }
5003 
5004 /*
5005  * Check for '|' to separate commands and '"' to start comments.
5006  */
5007     void
5008 separate_nextcmd(exarg_T *eap)
5009 {
5010     char_u	*p;
5011 
5012 #ifdef FEAT_QUICKFIX
5013     p = skip_grep_pat(eap);
5014 #else
5015     p = eap->arg;
5016 #endif
5017 
5018     for ( ; *p; MB_PTR_ADV(p))
5019     {
5020 	if (*p == Ctrl_V)
5021 	{
5022 	    if (eap->argt & (EX_CTRLV | EX_XFILE))
5023 		++p;		// skip CTRL-V and next char
5024 	    else
5025 				// remove CTRL-V and skip next char
5026 		STRMOVE(p, p + 1);
5027 	    if (*p == NUL)		// stop at NUL after CTRL-V
5028 		break;
5029 	}
5030 
5031 #ifdef FEAT_EVAL
5032 	// Skip over `=expr` when wildcards are expanded.
5033 	else if (p[0] == '`' && p[1] == '=' && (eap->argt & EX_XFILE))
5034 	{
5035 	    p += 2;
5036 	    (void)skip_expr(&p, NULL);
5037 	    if (*p == NUL)		// stop at NUL after CTRL-V
5038 		break;
5039 	}
5040 #endif
5041 
5042 	// Check for '"': start of comment or '|': next command
5043 	// :@" and :*" do not start a comment!
5044 	// :redir @" doesn't either.
5045 	else if ((*p == '"'
5046 #ifdef FEAT_EVAL
5047 		    && !in_vim9script()
5048 #endif
5049 		    && !(eap->argt & EX_NOTRLCOM)
5050 		    && ((eap->cmdidx != CMD_at && eap->cmdidx != CMD_star)
5051 							      || p != eap->arg)
5052 		    && (eap->cmdidx != CMD_redir
5053 					 || p != eap->arg + 1 || p[-1] != '@'))
5054 #ifdef FEAT_EVAL
5055 		|| (*p == '#'
5056 		    && in_vim9script()
5057 		    && !(eap->argt & EX_NOTRLCOM)
5058 		    && p > eap->cmd && VIM_ISWHITE(p[-1]))
5059 #endif
5060 		|| *p == '|' || *p == '\n')
5061 	{
5062 	    /*
5063 	     * We remove the '\' before the '|', unless EX_CTRLV is used
5064 	     * AND 'b' is present in 'cpoptions'.
5065 	     */
5066 	    if ((vim_strchr(p_cpo, CPO_BAR) == NULL
5067 			      || !(eap->argt & EX_CTRLV)) && *(p - 1) == '\\')
5068 	    {
5069 		STRMOVE(p - 1, p);	// remove the '\'
5070 		--p;
5071 	    }
5072 	    else
5073 	    {
5074 		eap->nextcmd = check_nextcmd(p);
5075 		*p = NUL;
5076 		break;
5077 	    }
5078 	}
5079     }
5080 
5081     if (!(eap->argt & EX_NOTRLCOM))	// remove trailing spaces
5082 	del_trailing_spaces(eap->arg);
5083 }
5084 
5085 /*
5086  * get + command from ex argument
5087  */
5088     static char_u *
5089 getargcmd(char_u **argp)
5090 {
5091     char_u *arg = *argp;
5092     char_u *command = NULL;
5093 
5094     if (*arg == '+')	    // +[command]
5095     {
5096 	++arg;
5097 	if (vim_isspace(*arg) || *arg == NUL)
5098 	    command = dollar_command;
5099 	else
5100 	{
5101 	    command = arg;
5102 	    arg = skip_cmd_arg(command, TRUE);
5103 	    if (*arg != NUL)
5104 		*arg++ = NUL;		// terminate command with NUL
5105 	}
5106 
5107 	arg = skipwhite(arg);	// skip over spaces
5108 	*argp = arg;
5109     }
5110     return command;
5111 }
5112 
5113 /*
5114  * Find end of "+command" argument.  Skip over "\ " and "\\".
5115  */
5116     char_u *
5117 skip_cmd_arg(
5118     char_u *p,
5119     int	   rembs)	// TRUE to halve the number of backslashes
5120 {
5121     while (*p && !vim_isspace(*p))
5122     {
5123 	if (*p == '\\' && p[1] != NUL)
5124 	{
5125 	    if (rembs)
5126 		STRMOVE(p, p + 1);
5127 	    else
5128 		++p;
5129 	}
5130 	MB_PTR_ADV(p);
5131     }
5132     return p;
5133 }
5134 
5135     int
5136 get_bad_opt(char_u *p, exarg_T *eap)
5137 {
5138     if (STRICMP(p, "keep") == 0)
5139 	eap->bad_char = BAD_KEEP;
5140     else if (STRICMP(p, "drop") == 0)
5141 	eap->bad_char = BAD_DROP;
5142     else if (MB_BYTE2LEN(*p) == 1 && p[1] == NUL)
5143 	eap->bad_char = *p;
5144     else
5145 	return FAIL;
5146     return OK;
5147 }
5148 
5149 /*
5150  * Get "++opt=arg" argument.
5151  * Return FAIL or OK.
5152  */
5153     static int
5154 getargopt(exarg_T *eap)
5155 {
5156     char_u	*arg = eap->arg + 2;
5157     int		*pp = NULL;
5158     int		bad_char_idx;
5159     char_u	*p;
5160 
5161     // ":edit ++[no]bin[ary] file"
5162     if (STRNCMP(arg, "bin", 3) == 0 || STRNCMP(arg, "nobin", 5) == 0)
5163     {
5164 	if (*arg == 'n')
5165 	{
5166 	    arg += 2;
5167 	    eap->force_bin = FORCE_NOBIN;
5168 	}
5169 	else
5170 	    eap->force_bin = FORCE_BIN;
5171 	if (!checkforcmd(&arg, "binary", 3))
5172 	    return FAIL;
5173 	eap->arg = skipwhite(arg);
5174 	return OK;
5175     }
5176 
5177     // ":read ++edit file"
5178     if (STRNCMP(arg, "edit", 4) == 0)
5179     {
5180 	eap->read_edit = TRUE;
5181 	eap->arg = skipwhite(arg + 4);
5182 	return OK;
5183     }
5184 
5185     if (STRNCMP(arg, "ff", 2) == 0)
5186     {
5187 	arg += 2;
5188 	pp = &eap->force_ff;
5189     }
5190     else if (STRNCMP(arg, "fileformat", 10) == 0)
5191     {
5192 	arg += 10;
5193 	pp = &eap->force_ff;
5194     }
5195     else if (STRNCMP(arg, "enc", 3) == 0)
5196     {
5197 	if (STRNCMP(arg, "encoding", 8) == 0)
5198 	    arg += 8;
5199 	else
5200 	    arg += 3;
5201 	pp = &eap->force_enc;
5202     }
5203     else if (STRNCMP(arg, "bad", 3) == 0)
5204     {
5205 	arg += 3;
5206 	pp = &bad_char_idx;
5207     }
5208 
5209     if (pp == NULL || *arg != '=')
5210 	return FAIL;
5211 
5212     ++arg;
5213     *pp = (int)(arg - eap->cmd);
5214     arg = skip_cmd_arg(arg, FALSE);
5215     eap->arg = skipwhite(arg);
5216     *arg = NUL;
5217 
5218     if (pp == &eap->force_ff)
5219     {
5220 	if (check_ff_value(eap->cmd + eap->force_ff) == FAIL)
5221 	    return FAIL;
5222 	eap->force_ff = eap->cmd[eap->force_ff];
5223     }
5224     else if (pp == &eap->force_enc)
5225     {
5226 	// Make 'fileencoding' lower case.
5227 	for (p = eap->cmd + eap->force_enc; *p != NUL; ++p)
5228 	    *p = TOLOWER_ASC(*p);
5229     }
5230     else
5231     {
5232 	// Check ++bad= argument.  Must be a single-byte character, "keep" or
5233 	// "drop".
5234 	if (get_bad_opt(eap->cmd + bad_char_idx, eap) == FAIL)
5235 	    return FAIL;
5236     }
5237 
5238     return OK;
5239 }
5240 
5241     static void
5242 ex_autocmd(exarg_T *eap)
5243 {
5244     /*
5245      * Disallow autocommands from .exrc and .vimrc in current
5246      * directory for security reasons.
5247      */
5248     if (secure)
5249     {
5250 	secure = 2;
5251 	eap->errmsg =
5252 	      _(e_command_not_allowed_from_vimrc_in_current_dir_or_tag_search);
5253     }
5254     else if (eap->cmdidx == CMD_autocmd)
5255 	do_autocmd(eap, eap->arg, eap->forceit);
5256     else
5257 	do_augroup(eap->arg, eap->forceit);
5258 }
5259 
5260 /*
5261  * ":doautocmd": Apply the automatic commands to the current buffer.
5262  */
5263     static void
5264 ex_doautocmd(exarg_T *eap)
5265 {
5266     char_u	*arg = eap->arg;
5267     int		call_do_modelines = check_nomodeline(&arg);
5268     int		did_aucmd;
5269 
5270     (void)do_doautocmd(arg, TRUE, &did_aucmd);
5271     // Only when there is no <nomodeline>.
5272     if (call_do_modelines && did_aucmd)
5273 	do_modelines(0);
5274 }
5275 
5276 /*
5277  * :[N]bunload[!] [N] [bufname] unload buffer
5278  * :[N]bdelete[!] [N] [bufname] delete buffer from buffer list
5279  * :[N]bwipeout[!] [N] [bufname] delete buffer really
5280  */
5281     static void
5282 ex_bunload(exarg_T *eap)
5283 {
5284     if (ERROR_IF_ANY_POPUP_WINDOW)
5285 	return;
5286     eap->errmsg = do_bufdel(
5287 	    eap->cmdidx == CMD_bdelete ? DOBUF_DEL
5288 		: eap->cmdidx == CMD_bwipeout ? DOBUF_WIPE
5289 		: DOBUF_UNLOAD, eap->arg,
5290 	    eap->addr_count, (int)eap->line1, (int)eap->line2, eap->forceit);
5291 }
5292 
5293 /*
5294  * :[N]buffer [N]	to buffer N
5295  * :[N]sbuffer [N]	to buffer N
5296  */
5297     static void
5298 ex_buffer(exarg_T *eap)
5299 {
5300     if (ERROR_IF_ANY_POPUP_WINDOW)
5301 	return;
5302     if (*eap->arg)
5303 	eap->errmsg = ex_errmsg(e_trailing_arg, eap->arg);
5304     else
5305     {
5306 	if (eap->addr_count == 0)	// default is current buffer
5307 	    goto_buffer(eap, DOBUF_CURRENT, FORWARD, 0);
5308 	else
5309 	    goto_buffer(eap, DOBUF_FIRST, FORWARD, (int)eap->line2);
5310 	if (eap->do_ecmd_cmd != NULL)
5311 	    do_cmd_argument(eap->do_ecmd_cmd);
5312     }
5313 }
5314 
5315 /*
5316  * :[N]bmodified [N]	to next mod. buffer
5317  * :[N]sbmodified [N]	to next mod. buffer
5318  */
5319     static void
5320 ex_bmodified(exarg_T *eap)
5321 {
5322     goto_buffer(eap, DOBUF_MOD, FORWARD, (int)eap->line2);
5323     if (eap->do_ecmd_cmd != NULL)
5324 	do_cmd_argument(eap->do_ecmd_cmd);
5325 }
5326 
5327 /*
5328  * :[N]bnext [N]	to next buffer
5329  * :[N]sbnext [N]	split and to next buffer
5330  */
5331     static void
5332 ex_bnext(exarg_T *eap)
5333 {
5334     if (ERROR_IF_ANY_POPUP_WINDOW)
5335 	return;
5336 
5337     goto_buffer(eap, DOBUF_CURRENT, FORWARD, (int)eap->line2);
5338     if (eap->do_ecmd_cmd != NULL)
5339 	do_cmd_argument(eap->do_ecmd_cmd);
5340 }
5341 
5342 /*
5343  * :[N]bNext [N]	to previous buffer
5344  * :[N]bprevious [N]	to previous buffer
5345  * :[N]sbNext [N]	split and to previous buffer
5346  * :[N]sbprevious [N]	split and to previous buffer
5347  */
5348     static void
5349 ex_bprevious(exarg_T *eap)
5350 {
5351     if (ERROR_IF_ANY_POPUP_WINDOW)
5352 	return;
5353 
5354     goto_buffer(eap, DOBUF_CURRENT, BACKWARD, (int)eap->line2);
5355     if (eap->do_ecmd_cmd != NULL)
5356 	do_cmd_argument(eap->do_ecmd_cmd);
5357 }
5358 
5359 /*
5360  * :brewind		to first buffer
5361  * :bfirst		to first buffer
5362  * :sbrewind		split and to first buffer
5363  * :sbfirst		split and to first buffer
5364  */
5365     static void
5366 ex_brewind(exarg_T *eap)
5367 {
5368     if (ERROR_IF_ANY_POPUP_WINDOW)
5369 	return;
5370 
5371     goto_buffer(eap, DOBUF_FIRST, FORWARD, 0);
5372     if (eap->do_ecmd_cmd != NULL)
5373 	do_cmd_argument(eap->do_ecmd_cmd);
5374 }
5375 
5376 /*
5377  * :blast		to last buffer
5378  * :sblast		split and to last buffer
5379  */
5380     static void
5381 ex_blast(exarg_T *eap)
5382 {
5383     if (ERROR_IF_ANY_POPUP_WINDOW)
5384 	return;
5385 
5386     goto_buffer(eap, DOBUF_LAST, BACKWARD, 0);
5387     if (eap->do_ecmd_cmd != NULL)
5388 	do_cmd_argument(eap->do_ecmd_cmd);
5389 }
5390 
5391 /*
5392  * Check if "c" ends an Ex command.
5393  * In Vim9 script does not check for white space before #.
5394  */
5395     int
5396 ends_excmd(int c)
5397 {
5398     int comment_char = '"';
5399 
5400 #ifdef FEAT_EVAL
5401     if (in_vim9script())
5402 	comment_char = '#';
5403 #endif
5404     return (c == NUL || c == '|' || c == comment_char || c == '\n');
5405 }
5406 
5407 /*
5408  * Like ends_excmd() but checks that a # in Vim9 script either has "cmd" equal
5409  * to "cmd_start" or has a white space character before it.
5410  */
5411     int
5412 ends_excmd2(char_u *cmd_start UNUSED, char_u *cmd)
5413 {
5414     int c = *cmd;
5415 
5416     if (c == NUL || c == '|' || c == '\n')
5417 	return TRUE;
5418 #ifdef FEAT_EVAL
5419     if (in_vim9script())
5420 	//  # starts a comment, #{ might be a mistake, #{{ can start a fold
5421 	return c == '#' && (cmd[1] != '{' || cmd[2] == '{')
5422 				 && (cmd == cmd_start || VIM_ISWHITE(cmd[-1]));
5423 #endif
5424     return c == '"';
5425 }
5426 
5427 #if defined(FEAT_SYN_HL) || defined(FEAT_SEARCH_EXTRA) || defined(FEAT_EVAL) \
5428 	|| defined(PROTO)
5429 /*
5430  * Return the next command, after the first '|' or '\n'.
5431  * Return NULL if not found.
5432  */
5433     char_u *
5434 find_nextcmd(char_u *p)
5435 {
5436     while (*p != '|' && *p != '\n')
5437     {
5438 	if (*p == NUL)
5439 	    return NULL;
5440 	++p;
5441     }
5442     return (p + 1);
5443 }
5444 #endif
5445 
5446 /*
5447  * Check if *p is a separator between Ex commands, skipping over white space.
5448  * Return NULL if it isn't, the following character if it is.
5449  */
5450     char_u *
5451 check_nextcmd(char_u *p)
5452 {
5453     char_u *s = skipwhite(p);
5454 
5455     if (*s == '|' || *s == '\n')
5456 	return (s + 1);
5457     else
5458 	return NULL;
5459 }
5460 
5461 /*
5462  * If "eap->nextcmd" is not set, check for a next command at "p".
5463  */
5464     void
5465 set_nextcmd(exarg_T *eap, char_u *arg)
5466 {
5467     char_u *p = check_nextcmd(arg);
5468 
5469     if (eap->nextcmd == NULL)
5470 	eap->nextcmd = p;
5471     else if (p != NULL)
5472 	// cannot use "| command" inside a  {} block
5473 	semsg(_(e_cannot_use_bar_to_separate_commands_here_str), arg);
5474 }
5475 
5476 /*
5477  * - if there are more files to edit
5478  * - and this is the last window
5479  * - and forceit not used
5480  * - and not repeated twice on a row
5481  *    return FAIL and give error message if 'message' TRUE
5482  * return OK otherwise
5483  */
5484     static int
5485 check_more(
5486     int message,	    // when FALSE check only, no messages
5487     int forceit)
5488 {
5489     int	    n = ARGCOUNT - curwin->w_arg_idx - 1;
5490 
5491     if (!forceit && only_one_window()
5492 	    && ARGCOUNT > 1 && !arg_had_last && n > 0 && quitmore == 0)
5493     {
5494 	if (message)
5495 	{
5496 #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
5497 	    if ((p_confirm || (cmdmod.cmod_flags & CMOD_CONFIRM))
5498 						    && curbuf->b_fname != NULL)
5499 	    {
5500 		char_u	buff[DIALOG_MSG_SIZE];
5501 
5502 		vim_snprintf((char *)buff, DIALOG_MSG_SIZE,
5503 			NGETTEXT("%d more file to edit.  Quit anyway?",
5504 			    "%d more files to edit.  Quit anyway?", n), n);
5505 		if (vim_dialog_yesno(VIM_QUESTION, NULL, buff, 1) == VIM_YES)
5506 		    return OK;
5507 		return FAIL;
5508 	    }
5509 #endif
5510 	    semsg(NGETTEXT("E173: %d more file to edit",
5511 			"E173: %d more files to edit", n), n);
5512 	    quitmore = 2;	    // next try to quit is allowed
5513 	}
5514 	return FAIL;
5515     }
5516     return OK;
5517 }
5518 
5519 /*
5520  * Function given to ExpandGeneric() to obtain the list of command names.
5521  */
5522     char_u *
5523 get_command_name(expand_T *xp UNUSED, int idx)
5524 {
5525     if (idx >= (int)CMD_SIZE)
5526 	return expand_user_command_name(idx);
5527     return cmdnames[idx].cmd_name;
5528 }
5529 
5530     static void
5531 ex_colorscheme(exarg_T *eap)
5532 {
5533     if (*eap->arg == NUL)
5534     {
5535 #ifdef FEAT_EVAL
5536 	char_u *expr = vim_strsave((char_u *)"g:colors_name");
5537 	char_u *p = NULL;
5538 
5539 	if (expr != NULL)
5540 	{
5541 	    ++emsg_off;
5542 	    p = eval_to_string(expr, FALSE);
5543 	    --emsg_off;
5544 	    vim_free(expr);
5545 	}
5546 	if (p != NULL)
5547 	{
5548 	    msg((char *)p);
5549 	    vim_free(p);
5550 	}
5551 	else
5552 	    msg("default");
5553 #else
5554 	msg(_("unknown"));
5555 #endif
5556     }
5557     else if (load_colors(eap->arg) == FAIL)
5558 	semsg(_("E185: Cannot find color scheme '%s'"), eap->arg);
5559 
5560 #ifdef FEAT_VTP
5561     else if (has_vtp_working())
5562     {
5563 	// background color change requires clear + redraw
5564 	update_screen(CLEAR);
5565 	redrawcmd();
5566     }
5567 #endif
5568 }
5569 
5570     static void
5571 ex_highlight(exarg_T *eap)
5572 {
5573     if (*eap->arg == NUL && eap->cmd[2] == '!')
5574 	msg(_("Greetings, Vim user!"));
5575     do_highlight(eap->arg, eap->forceit, FALSE);
5576 }
5577 
5578 
5579 /*
5580  * Call this function if we thought we were going to exit, but we won't
5581  * (because of an error).  May need to restore the terminal mode.
5582  */
5583     void
5584 not_exiting(void)
5585 {
5586     exiting = FALSE;
5587     settmode(TMODE_RAW);
5588 }
5589 
5590     int
5591 before_quit_autocmds(win_T *wp, int quit_all, int forceit)
5592 {
5593     apply_autocmds(EVENT_QUITPRE, NULL, NULL, FALSE, wp->w_buffer);
5594 
5595     // Bail out when autocommands closed the window.
5596     // Refuse to quit when the buffer in the last window is being closed (can
5597     // only happen in autocommands).
5598     if (!win_valid(wp)
5599 	    || curbuf_locked()
5600 	    || (wp->w_buffer->b_nwindows == 1 && wp->w_buffer->b_locked > 0))
5601 	return TRUE;
5602 
5603     if (quit_all || (check_more(FALSE, forceit) == OK && only_one_window()))
5604     {
5605 	apply_autocmds(EVENT_EXITPRE, NULL, NULL, FALSE, curbuf);
5606 	// Refuse to quit when locked or when the window was closed or the
5607 	// buffer in the last window is being closed (can only happen in
5608 	// autocommands).
5609 	if (!win_valid(wp) || curbuf_locked()
5610 			  || (curbuf->b_nwindows == 1 && curbuf->b_locked > 0))
5611 	    return TRUE;
5612     }
5613 
5614     return FALSE;
5615 }
5616 
5617 /*
5618  * ":quit": quit current window, quit Vim if the last window is closed.
5619  * ":{nr}quit": quit window {nr}
5620  * Also used when closing a terminal window that's the last one.
5621  */
5622     void
5623 ex_quit(exarg_T *eap)
5624 {
5625     win_T	*wp;
5626 
5627 #ifdef FEAT_CMDWIN
5628     if (cmdwin_type != 0)
5629     {
5630 	cmdwin_result = Ctrl_C;
5631 	return;
5632     }
5633 #endif
5634     // Don't quit while editing the command line.
5635     if (text_locked())
5636     {
5637 	text_locked_msg();
5638 	return;
5639     }
5640     if (eap->addr_count > 0)
5641     {
5642 	int	wnr = eap->line2;
5643 
5644 	for (wp = firstwin; wp->w_next != NULL; wp = wp->w_next)
5645 	    if (--wnr <= 0)
5646 		break;
5647     }
5648     else
5649 	wp = curwin;
5650 
5651     // Refuse to quit when locked.
5652     if (curbuf_locked())
5653 	return;
5654 
5655     // Trigger QuitPre and maybe ExitPre
5656     if (before_quit_autocmds(wp, FALSE, eap->forceit))
5657 	return;
5658 
5659 #ifdef FEAT_NETBEANS_INTG
5660     netbeansForcedQuit = eap->forceit;
5661 #endif
5662 
5663     /*
5664      * If there is only one relevant window we will exit.
5665      */
5666     if (check_more(FALSE, eap->forceit) == OK && only_one_window())
5667 	exiting = TRUE;
5668     if ((!buf_hide(wp->w_buffer)
5669 		&& check_changed(wp->w_buffer, (p_awa ? CCGD_AW : 0)
5670 				       | (eap->forceit ? CCGD_FORCEIT : 0)
5671 				       | CCGD_EXCMD))
5672 	    || check_more(TRUE, eap->forceit) == FAIL
5673 	    || (only_one_window() && check_changed_any(eap->forceit, TRUE)))
5674     {
5675 	not_exiting();
5676     }
5677     else
5678     {
5679 	// quit last window
5680 	// Note: only_one_window() returns true, even so a help window is
5681 	// still open. In that case only quit, if no address has been
5682 	// specified. Example:
5683 	// :h|wincmd w|1q     - don't quit
5684 	// :h|wincmd w|q      - quit
5685 	if (only_one_window() && (ONE_WINDOW || eap->addr_count == 0))
5686 	    getout(0);
5687 	not_exiting();
5688 #ifdef FEAT_GUI
5689 	need_mouse_correct = TRUE;
5690 #endif
5691 	// close window; may free buffer
5692 	win_close(wp, !buf_hide(wp->w_buffer) || eap->forceit);
5693     }
5694 }
5695 
5696 /*
5697  * ":cquit".
5698  */
5699     static void
5700 ex_cquit(exarg_T *eap UNUSED)
5701 {
5702     // this does not always pass on the exit code to the Manx compiler. why?
5703     getout(eap->addr_count > 0 ? (int)eap->line2 : EXIT_FAILURE);
5704 }
5705 
5706 /*
5707  * ":qall": try to quit all windows
5708  */
5709     static void
5710 ex_quit_all(exarg_T *eap)
5711 {
5712 # ifdef FEAT_CMDWIN
5713     if (cmdwin_type != 0)
5714     {
5715 	if (eap->forceit)
5716 	    cmdwin_result = K_XF1;	// ex_window() takes care of this
5717 	else
5718 	    cmdwin_result = K_XF2;
5719 	return;
5720     }
5721 # endif
5722 
5723     // Don't quit while editing the command line.
5724     if (text_locked())
5725     {
5726 	text_locked_msg();
5727 	return;
5728     }
5729 
5730     if (before_quit_autocmds(curwin, TRUE, eap->forceit))
5731 	return;
5732 
5733     exiting = TRUE;
5734     if (eap->forceit || !check_changed_any(FALSE, FALSE))
5735 	getout(0);
5736     not_exiting();
5737 }
5738 
5739 /*
5740  * ":close": close current window, unless it is the last one
5741  */
5742     static void
5743 ex_close(exarg_T *eap)
5744 {
5745     win_T	*win;
5746     int		winnr = 0;
5747 #ifdef FEAT_CMDWIN
5748     if (cmdwin_type != 0)
5749 	cmdwin_result = Ctrl_C;
5750     else
5751 #endif
5752 	if (!text_locked() && !curbuf_locked())
5753 	{
5754 	    if (eap->addr_count == 0)
5755 		ex_win_close(eap->forceit, curwin, NULL);
5756 	    else
5757 	    {
5758 		FOR_ALL_WINDOWS(win)
5759 		{
5760 		    winnr++;
5761 		    if (winnr == eap->line2)
5762 			break;
5763 		}
5764 		if (win == NULL)
5765 		    win = lastwin;
5766 		ex_win_close(eap->forceit, win, NULL);
5767 	    }
5768 	}
5769 }
5770 
5771 #ifdef FEAT_QUICKFIX
5772 /*
5773  * ":pclose": Close any preview window.
5774  */
5775     static void
5776 ex_pclose(exarg_T *eap)
5777 {
5778     win_T	*win;
5779 
5780     // First close any normal window.
5781     FOR_ALL_WINDOWS(win)
5782 	if (win->w_p_pvw)
5783 	{
5784 	    ex_win_close(eap->forceit, win, NULL);
5785 	    return;
5786 	}
5787 # ifdef FEAT_PROP_POPUP
5788     // Also when 'previewpopup' is empty, it might have been cleared.
5789     popup_close_preview();
5790 # endif
5791 }
5792 #endif
5793 
5794 /*
5795  * Close window "win" and take care of handling closing the last window for a
5796  * modified buffer.
5797  */
5798     static void
5799 ex_win_close(
5800     int		forceit,
5801     win_T	*win,
5802     tabpage_T	*tp)		// NULL or the tab page "win" is in
5803 {
5804     int		need_hide;
5805     buf_T	*buf = win->w_buffer;
5806 
5807     // Never close the autocommand window.
5808     if (win == aucmd_win)
5809     {
5810 	emsg(_(e_autocmd_close));
5811 	return;
5812     }
5813 
5814     need_hide = (bufIsChanged(buf) && buf->b_nwindows <= 1);
5815     if (need_hide && !buf_hide(buf) && !forceit)
5816     {
5817 #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
5818 	if ((p_confirm || (cmdmod.cmod_flags & CMOD_CONFIRM)) && p_write)
5819 	{
5820 	    bufref_T bufref;
5821 
5822 	    set_bufref(&bufref, buf);
5823 	    dialog_changed(buf, FALSE);
5824 	    if (bufref_valid(&bufref) && bufIsChanged(buf))
5825 		return;
5826 	    need_hide = FALSE;
5827 	}
5828 	else
5829 #endif
5830 	{
5831 	    no_write_message();
5832 	    return;
5833 	}
5834     }
5835 
5836 #ifdef FEAT_GUI
5837     need_mouse_correct = TRUE;
5838 #endif
5839 
5840     // free buffer when not hiding it or when it's a scratch buffer
5841     if (tp == NULL)
5842 	win_close(win, !need_hide && !buf_hide(buf));
5843     else
5844 	win_close_othertab(win, !need_hide && !buf_hide(buf), tp);
5845 }
5846 
5847 /*
5848  * Handle the argument for a tabpage related ex command.
5849  * Returns a tabpage number.
5850  * When an error is encountered then eap->errmsg is set.
5851  */
5852     static int
5853 get_tabpage_arg(exarg_T *eap)
5854 {
5855     int tab_number;
5856     int unaccept_arg0 = (eap->cmdidx == CMD_tabmove) ? 0 : 1;
5857 
5858     if (eap->arg && *eap->arg != NUL)
5859     {
5860 	char_u *p = eap->arg;
5861 	char_u *p_save;
5862 	int    relative = 0; // argument +N/-N means: go to N places to the
5863 			     // right/left relative to the current position.
5864 
5865 	if (*p == '-')
5866 	{
5867 	    relative = -1;
5868 	    p++;
5869 	}
5870 	else if (*p == '+')
5871 	{
5872 	    relative = 1;
5873 	    p++;
5874 	}
5875 
5876 	p_save = p;
5877 	tab_number = getdigits(&p);
5878 
5879 	if (relative == 0)
5880 	{
5881 	    if (STRCMP(p, "$") == 0)
5882 		tab_number = LAST_TAB_NR;
5883 	    else if (STRCMP(p, "#") == 0)
5884 		if (valid_tabpage(lastused_tabpage))
5885 		    tab_number = tabpage_index(lastused_tabpage);
5886 		else
5887 		{
5888 		    eap->errmsg = ex_errmsg(e_invargval, eap->arg);
5889 		    tab_number = 0;
5890 		    goto theend;
5891 		}
5892 	    else if (p == p_save || *p_save == '-' || *p != NUL
5893 		    || tab_number > LAST_TAB_NR)
5894 	    {
5895 		// No numbers as argument.
5896 		eap->errmsg = ex_errmsg(e_invarg2, eap->arg);
5897 		goto theend;
5898 	    }
5899 	}
5900 	else
5901 	{
5902 	    if (*p_save == NUL)
5903 		tab_number = 1;
5904 	    else if (p == p_save || *p_save == '-' || *p != NUL
5905 		    || tab_number == 0)
5906 	    {
5907 		// No numbers as argument.
5908 		eap->errmsg = ex_errmsg(e_invarg2, eap->arg);
5909 		goto theend;
5910 	    }
5911 	    tab_number = tab_number * relative + tabpage_index(curtab);
5912 	    if (!unaccept_arg0 && relative == -1)
5913 		--tab_number;
5914 	}
5915 	if (tab_number < unaccept_arg0 || tab_number > LAST_TAB_NR)
5916 	    eap->errmsg = ex_errmsg(e_invarg2, eap->arg);
5917     }
5918     else if (eap->addr_count > 0)
5919     {
5920 	if (unaccept_arg0 && eap->line2 == 0)
5921 	{
5922 	    eap->errmsg = _(e_invalid_range);
5923 	    tab_number = 0;
5924 	}
5925 	else
5926 	{
5927 	    tab_number = eap->line2;
5928 	    if (!unaccept_arg0 && *skipwhite(*eap->cmdlinep) == '-')
5929 	    {
5930 		--tab_number;
5931 		if (tab_number < unaccept_arg0)
5932 		    eap->errmsg = _(e_invalid_range);
5933 	    }
5934 	}
5935     }
5936     else
5937     {
5938 	switch (eap->cmdidx)
5939 	{
5940 	case CMD_tabnext:
5941 	    tab_number = tabpage_index(curtab) + 1;
5942 	    if (tab_number > LAST_TAB_NR)
5943 		tab_number = 1;
5944 	    break;
5945 	case CMD_tabmove:
5946 	    tab_number = LAST_TAB_NR;
5947 	    break;
5948 	default:
5949 	    tab_number = tabpage_index(curtab);
5950 	}
5951     }
5952 
5953 theend:
5954     return tab_number;
5955 }
5956 
5957 /*
5958  * ":tabclose": close current tab page, unless it is the last one.
5959  * ":tabclose N": close tab page N.
5960  */
5961     static void
5962 ex_tabclose(exarg_T *eap)
5963 {
5964     tabpage_T	*tp;
5965     int		tab_number;
5966 
5967 # ifdef FEAT_CMDWIN
5968     if (cmdwin_type != 0)
5969 	cmdwin_result = K_IGNORE;
5970     else
5971 # endif
5972 	if (first_tabpage->tp_next == NULL)
5973 	    emsg(_("E784: Cannot close last tab page"));
5974 	else
5975 	{
5976 	    tab_number = get_tabpage_arg(eap);
5977 	    if (eap->errmsg == NULL)
5978 	    {
5979 		tp = find_tabpage(tab_number);
5980 		if (tp == NULL)
5981 		{
5982 		    beep_flush();
5983 		    return;
5984 		}
5985 		if (tp != curtab)
5986 		{
5987 		    tabpage_close_other(tp, eap->forceit);
5988 		    return;
5989 		}
5990 		else if (!text_locked() && !curbuf_locked())
5991 		    tabpage_close(eap->forceit);
5992 	    }
5993 	}
5994 }
5995 
5996 /*
5997  * ":tabonly": close all tab pages except the current one
5998  */
5999     static void
6000 ex_tabonly(exarg_T *eap)
6001 {
6002     tabpage_T	*tp;
6003     int		done;
6004     int		tab_number;
6005 
6006 # ifdef FEAT_CMDWIN
6007     if (cmdwin_type != 0)
6008 	cmdwin_result = K_IGNORE;
6009     else
6010 # endif
6011 	if (first_tabpage->tp_next == NULL)
6012 	    msg(_("Already only one tab page"));
6013 	else
6014 	{
6015 	    tab_number = get_tabpage_arg(eap);
6016 	    if (eap->errmsg == NULL)
6017 	    {
6018 		goto_tabpage(tab_number);
6019 		// Repeat this up to a 1000 times, because autocommands may
6020 		// mess up the lists.
6021 		for (done = 0; done < 1000; ++done)
6022 		{
6023 		    FOR_ALL_TABPAGES(tp)
6024 			if (tp->tp_topframe != topframe)
6025 			{
6026 			    tabpage_close_other(tp, eap->forceit);
6027 			    // if we failed to close it quit
6028 			    if (valid_tabpage(tp))
6029 				done = 1000;
6030 			    // start over, "tp" is now invalid
6031 			    break;
6032 			}
6033 		    if (first_tabpage->tp_next == NULL)
6034 			break;
6035 		}
6036 	    }
6037 	}
6038 }
6039 
6040 /*
6041  * Close the current tab page.
6042  */
6043     void
6044 tabpage_close(int forceit)
6045 {
6046     // First close all the windows but the current one.  If that worked then
6047     // close the last window in this tab, that will close it.
6048     if (!ONE_WINDOW)
6049 	close_others(TRUE, forceit);
6050     if (ONE_WINDOW)
6051 	ex_win_close(forceit, curwin, NULL);
6052 # ifdef FEAT_GUI
6053     need_mouse_correct = TRUE;
6054 # endif
6055 }
6056 
6057 /*
6058  * Close tab page "tp", which is not the current tab page.
6059  * Note that autocommands may make "tp" invalid.
6060  * Also takes care of the tab pages line disappearing when closing the
6061  * last-but-one tab page.
6062  */
6063     void
6064 tabpage_close_other(tabpage_T *tp, int forceit)
6065 {
6066     int		done = 0;
6067     win_T	*wp;
6068     int		h = tabline_height();
6069 
6070     // Limit to 1000 windows, autocommands may add a window while we close
6071     // one.  OK, so I'm paranoid...
6072     while (++done < 1000)
6073     {
6074 	wp = tp->tp_firstwin;
6075 	ex_win_close(forceit, wp, tp);
6076 
6077 	// Autocommands may delete the tab page under our fingers and we may
6078 	// fail to close a window with a modified buffer.
6079 	if (!valid_tabpage(tp) || tp->tp_firstwin == wp)
6080 	    break;
6081     }
6082 
6083     apply_autocmds(EVENT_TABCLOSED, NULL, NULL, FALSE, curbuf);
6084 
6085     redraw_tabline = TRUE;
6086     if (h != tabline_height())
6087 	shell_new_rows();
6088 }
6089 
6090 /*
6091  * ":only".
6092  */
6093     static void
6094 ex_only(exarg_T *eap)
6095 {
6096     win_T   *wp;
6097     int	    wnr;
6098 # ifdef FEAT_GUI
6099     need_mouse_correct = TRUE;
6100 # endif
6101     if (eap->addr_count > 0)
6102     {
6103 	wnr = eap->line2;
6104 	for (wp = firstwin; --wnr > 0; )
6105 	{
6106 	    if (wp->w_next == NULL)
6107 		break;
6108 	    else
6109 		wp = wp->w_next;
6110 	}
6111 	win_goto(wp);
6112     }
6113     close_others(TRUE, eap->forceit);
6114 }
6115 
6116     static void
6117 ex_hide(exarg_T *eap UNUSED)
6118 {
6119     // ":hide" or ":hide | cmd": hide current window
6120     if (!eap->skip)
6121     {
6122 #ifdef FEAT_GUI
6123 	need_mouse_correct = TRUE;
6124 #endif
6125 	if (eap->addr_count == 0)
6126 	    win_close(curwin, FALSE);	// don't free buffer
6127 	else
6128 	{
6129 	    int	winnr = 0;
6130 	    win_T	*win;
6131 
6132 	    FOR_ALL_WINDOWS(win)
6133 	    {
6134 		winnr++;
6135 		if (winnr == eap->line2)
6136 		    break;
6137 	    }
6138 	    if (win == NULL)
6139 		win = lastwin;
6140 	    win_close(win, FALSE);
6141 	}
6142     }
6143 }
6144 
6145 /*
6146  * ":stop" and ":suspend": Suspend Vim.
6147  */
6148     static void
6149 ex_stop(exarg_T *eap)
6150 {
6151     /*
6152      * Disallow suspending for "rvim".
6153      */
6154     if (!check_restricted())
6155     {
6156 	if (!eap->forceit)
6157 	    autowrite_all();
6158 	apply_autocmds(EVENT_VIMSUSPEND, NULL, NULL, FALSE, NULL);
6159 	windgoto((int)Rows - 1, 0);
6160 	out_char('\n');
6161 	out_flush();
6162 	stoptermcap();
6163 	out_flush();		// needed for SUN to restore xterm buffer
6164 #ifdef FEAT_TITLE
6165 	mch_restore_title(SAVE_RESTORE_BOTH);	// restore window titles
6166 #endif
6167 	ui_suspend();		// call machine specific function
6168 #ifdef FEAT_TITLE
6169 	maketitle();
6170 	resettitle();		// force updating the title
6171 #endif
6172 	starttermcap();
6173 	scroll_start();		// scroll screen before redrawing
6174 	redraw_later_clear();
6175 	shell_resized();	// may have resized window
6176 	apply_autocmds(EVENT_VIMRESUME, NULL, NULL, FALSE, NULL);
6177     }
6178 }
6179 
6180 /*
6181  * ":exit", ":xit" and ":wq": Write file and quit the current window.
6182  */
6183     static void
6184 ex_exit(exarg_T *eap)
6185 {
6186 #ifdef FEAT_EVAL
6187     if (not_in_vim9(eap) == FAIL)
6188 	return;
6189 #endif
6190 #ifdef FEAT_CMDWIN
6191     if (cmdwin_type != 0)
6192     {
6193 	cmdwin_result = Ctrl_C;
6194 	return;
6195     }
6196 #endif
6197     // Don't quit while editing the command line.
6198     if (text_locked())
6199     {
6200 	text_locked_msg();
6201 	return;
6202     }
6203 
6204     /*
6205      * we plan to exit if there is only one relevant window
6206      */
6207     if (check_more(FALSE, eap->forceit) == OK && only_one_window())
6208 	exiting = TRUE;
6209 
6210     // Write the buffer for ":wq" or when it was changed.
6211     // Trigger QuitPre and ExitPre.
6212     // Check if we can exit now, after autocommands have changed things.
6213     if (((eap->cmdidx == CMD_wq || curbufIsChanged()) && do_write(eap) == FAIL)
6214 	    || before_quit_autocmds(curwin, FALSE, eap->forceit)
6215 	    || check_more(TRUE, eap->forceit) == FAIL
6216 	    || (only_one_window() && check_changed_any(eap->forceit, FALSE)))
6217     {
6218 	not_exiting();
6219     }
6220     else
6221     {
6222 	if (only_one_window())	    // quit last window, exit Vim
6223 	    getout(0);
6224 	not_exiting();
6225 # ifdef FEAT_GUI
6226 	need_mouse_correct = TRUE;
6227 # endif
6228 	// Quit current window, may free the buffer.
6229 	win_close(curwin, !buf_hide(curwin->w_buffer));
6230     }
6231 }
6232 
6233 /*
6234  * ":print", ":list", ":number".
6235  */
6236     static void
6237 ex_print(exarg_T *eap)
6238 {
6239     if (curbuf->b_ml.ml_flags & ML_EMPTY)
6240 	emsg(_(e_emptybuf));
6241     else
6242     {
6243 	for ( ;!got_int; ui_breakcheck())
6244 	{
6245 	    print_line(eap->line1,
6246 		    (eap->cmdidx == CMD_number || eap->cmdidx == CMD_pound
6247 						 || (eap->flags & EXFLAG_NR)),
6248 		    eap->cmdidx == CMD_list || (eap->flags & EXFLAG_LIST));
6249 	    if (++eap->line1 > eap->line2)
6250 		break;
6251 	    out_flush();	    // show one line at a time
6252 	}
6253 	setpcmark();
6254 	// put cursor at last line
6255 	curwin->w_cursor.lnum = eap->line2;
6256 	beginline(BL_SOL | BL_FIX);
6257     }
6258 
6259     ex_no_reprint = TRUE;
6260 }
6261 
6262 #ifdef FEAT_BYTEOFF
6263     static void
6264 ex_goto(exarg_T *eap)
6265 {
6266     goto_byte(eap->line2);
6267 }
6268 #endif
6269 
6270 /*
6271  * ":shell".
6272  */
6273     static void
6274 ex_shell(exarg_T *eap UNUSED)
6275 {
6276     do_shell(NULL, 0);
6277 }
6278 
6279 #if defined(HAVE_DROP_FILE) || defined(PROTO)
6280 
6281 static int drop_busy = FALSE;
6282 static int drop_filec;
6283 static char_u **drop_filev = NULL;
6284 static int drop_split;
6285 static void (*drop_callback)(void *);
6286 static void *drop_cookie;
6287 
6288     static void
6289 handle_drop_internal(void)
6290 {
6291     exarg_T	ea;
6292     int		save_msg_scroll = msg_scroll;
6293 
6294     // Setting the argument list may cause screen updates and being called
6295     // recursively.  Avoid that by setting drop_busy.
6296     drop_busy = TRUE;
6297 
6298     // Check whether the current buffer is changed. If so, we will need
6299     // to split the current window or data could be lost.
6300     // We don't need to check if the 'hidden' option is set, as in this
6301     // case the buffer won't be lost.
6302     if (!buf_hide(curbuf) && !drop_split)
6303     {
6304 	++emsg_off;
6305 	drop_split = check_changed(curbuf, CCGD_AW);
6306 	--emsg_off;
6307     }
6308     if (drop_split)
6309     {
6310 	if (win_split(0, 0) == FAIL)
6311 	    return;
6312 	RESET_BINDING(curwin);
6313 
6314 	// When splitting the window, create a new alist.  Otherwise the
6315 	// existing one is overwritten.
6316 	alist_unlink(curwin->w_alist);
6317 	alist_new();
6318     }
6319 
6320     /*
6321      * Set up the new argument list.
6322      */
6323     alist_set(ALIST(curwin), drop_filec, drop_filev, FALSE, NULL, 0);
6324 
6325     /*
6326      * Move to the first file.
6327      */
6328     // Fake up a minimal "next" command for do_argfile()
6329     CLEAR_FIELD(ea);
6330     ea.cmd = (char_u *)"next";
6331     do_argfile(&ea, 0);
6332 
6333     // do_ecmd() may set need_start_insertmode, but since we never left Insert
6334     // mode that is not needed here.
6335     need_start_insertmode = FALSE;
6336 
6337     // Restore msg_scroll, otherwise a following command may cause scrolling
6338     // unexpectedly.  The screen will be redrawn by the caller, thus
6339     // msg_scroll being set by displaying a message is irrelevant.
6340     msg_scroll = save_msg_scroll;
6341 
6342     if (drop_callback != NULL)
6343 	drop_callback(drop_cookie);
6344 
6345     drop_filev = NULL;
6346     drop_busy = FALSE;
6347 }
6348 
6349 /*
6350  * Handle a file drop. The code is here because a drop is *nearly* like an
6351  * :args command, but not quite (we have a list of exact filenames, so we
6352  * don't want to (a) parse a command line, or (b) expand wildcards). So the
6353  * code is very similar to :args and hence needs access to a lot of the static
6354  * functions in this file.
6355  *
6356  * The "filev" list must have been allocated using alloc(), as should each item
6357  * in the list. This function takes over responsibility for freeing the "filev"
6358  * list.
6359  */
6360     void
6361 handle_drop(
6362     int		filec,		// the number of files dropped
6363     char_u	**filev,	// the list of files dropped
6364     int		split,		// force splitting the window
6365     void	(*callback)(void *), // to be called after setting the argument
6366 				     // list
6367     void	*cookie)	// argument for "callback" (allocated)
6368 {
6369     // Cannot handle recursive drops, finish the pending one.
6370     if (drop_busy)
6371     {
6372 	FreeWild(filec, filev);
6373 	vim_free(cookie);
6374 	return;
6375     }
6376 
6377     // When calling handle_drop() more than once in a row we only use the last
6378     // one.
6379     if (drop_filev != NULL)
6380     {
6381 	FreeWild(drop_filec, drop_filev);
6382 	vim_free(drop_cookie);
6383     }
6384 
6385     drop_filec = filec;
6386     drop_filev = filev;
6387     drop_split = split;
6388     drop_callback = callback;
6389     drop_cookie = cookie;
6390 
6391     // Postpone this when:
6392     // - editing the command line
6393     // - not possible to change the current buffer
6394     // - updating the screen
6395     // As it may change buffers and window structures that are in use and cause
6396     // freed memory to be used.
6397     if (text_locked() || curbuf_locked() || updating_screen)
6398 	return;
6399 
6400     handle_drop_internal();
6401 }
6402 
6403 /*
6404  * To be called when text is unlocked, curbuf is unlocked or updating_screen is
6405  * reset: Handle a postponed drop.
6406  */
6407     void
6408 handle_any_postponed_drop(void)
6409 {
6410     if (!drop_busy && drop_filev != NULL
6411 	     && !text_locked() && !curbuf_locked() && !updating_screen)
6412 	handle_drop_internal();
6413 }
6414 #endif
6415 
6416 /*
6417  * ":preserve".
6418  */
6419     static void
6420 ex_preserve(exarg_T *eap UNUSED)
6421 {
6422     curbuf->b_flags |= BF_PRESERVED;
6423     ml_preserve(curbuf, TRUE);
6424 }
6425 
6426 /*
6427  * ":recover".
6428  */
6429     static void
6430 ex_recover(exarg_T *eap)
6431 {
6432     // Set recoverymode right away to avoid the ATTENTION prompt.
6433     recoverymode = TRUE;
6434     if (!check_changed(curbuf, (p_awa ? CCGD_AW : 0)
6435 			     | CCGD_MULTWIN
6436 			     | (eap->forceit ? CCGD_FORCEIT : 0)
6437 			     | CCGD_EXCMD)
6438 
6439 	    && (*eap->arg == NUL
6440 			     || setfname(curbuf, eap->arg, NULL, TRUE) == OK))
6441 	ml_recover(TRUE);
6442     recoverymode = FALSE;
6443 }
6444 
6445 /*
6446  * Command modifier used in a wrong way.
6447  */
6448     static void
6449 ex_wrongmodifier(exarg_T *eap)
6450 {
6451     eap->errmsg = _(e_invalid_command);
6452 }
6453 
6454 /*
6455  * :sview [+command] file	split window with new file, read-only
6456  * :split [[+command] file]	split window with current or new file
6457  * :vsplit [[+command] file]	split window vertically with current or new file
6458  * :new [[+command] file]	split window with no or new file
6459  * :vnew [[+command] file]	split vertically window with no or new file
6460  * :sfind [+command] file	split window with file in 'path'
6461  *
6462  * :tabedit			open new Tab page with empty window
6463  * :tabedit [+command] file	open new Tab page and edit "file"
6464  * :tabnew [[+command] file]	just like :tabedit
6465  * :tabfind [+command] file	open new Tab page and find "file"
6466  */
6467     void
6468 ex_splitview(exarg_T *eap)
6469 {
6470     win_T	*old_curwin = curwin;
6471 #if defined(FEAT_SEARCHPATH) || defined(FEAT_BROWSE)
6472     char_u	*fname = NULL;
6473 #endif
6474 #ifdef FEAT_BROWSE
6475     char_u	dot_path[] = ".";
6476     int		save_cmod_flags = cmdmod.cmod_flags;
6477 #endif
6478     int		use_tab = eap->cmdidx == CMD_tabedit
6479 		       || eap->cmdidx == CMD_tabfind
6480 		       || eap->cmdidx == CMD_tabnew;
6481 
6482     if (ERROR_IF_ANY_POPUP_WINDOW)
6483 	return;
6484 
6485 #ifdef FEAT_GUI
6486     need_mouse_correct = TRUE;
6487 #endif
6488 
6489 #ifdef FEAT_QUICKFIX
6490     // A ":split" in the quickfix window works like ":new".  Don't want two
6491     // quickfix windows.  But it's OK when doing ":tab split".
6492     if (bt_quickfix(curbuf) && cmdmod.cmod_tab == 0)
6493     {
6494 	if (eap->cmdidx == CMD_split)
6495 	    eap->cmdidx = CMD_new;
6496 	if (eap->cmdidx == CMD_vsplit)
6497 	    eap->cmdidx = CMD_vnew;
6498     }
6499 #endif
6500 
6501 #ifdef FEAT_SEARCHPATH
6502     if (eap->cmdidx == CMD_sfind || eap->cmdidx == CMD_tabfind)
6503     {
6504 	fname = find_file_in_path(eap->arg, (int)STRLEN(eap->arg),
6505 					  FNAME_MESS, TRUE, curbuf->b_ffname);
6506 	if (fname == NULL)
6507 	    goto theend;
6508 	eap->arg = fname;
6509     }
6510 # ifdef FEAT_BROWSE
6511     else
6512 # endif
6513 #endif
6514 #ifdef FEAT_BROWSE
6515     if ((cmdmod.cmod_flags & CMOD_BROWSE)
6516 	    && eap->cmdidx != CMD_vnew
6517 	    && eap->cmdidx != CMD_new)
6518     {
6519 	if (
6520 # ifdef FEAT_GUI
6521 	    !gui.in_use &&
6522 # endif
6523 		au_has_group((char_u *)"FileExplorer"))
6524 	{
6525 	    // No browsing supported but we do have the file explorer:
6526 	    // Edit the directory.
6527 	    if (*eap->arg == NUL || !mch_isdir(eap->arg))
6528 		eap->arg = dot_path;
6529 	}
6530 	else
6531 	{
6532 	    fname = do_browse(0, (char_u *)(use_tab
6533 			? _("Edit File in new tab page")
6534 			: _("Edit File in new window")),
6535 					  eap->arg, NULL, NULL, NULL, curbuf);
6536 	    if (fname == NULL)
6537 		goto theend;
6538 	    eap->arg = fname;
6539 	}
6540     }
6541     cmdmod.cmod_flags &= ~CMOD_BROWSE;	// Don't browse again in do_ecmd().
6542 #endif
6543 
6544     /*
6545      * Either open new tab page or split the window.
6546      */
6547     if (use_tab)
6548     {
6549 	if (win_new_tabpage(cmdmod.cmod_tab != 0 ? cmdmod.cmod_tab
6550 			 : eap->addr_count == 0 ? 0
6551 					       : (int)eap->line2 + 1) != FAIL)
6552 	{
6553 	    do_exedit(eap, old_curwin);
6554 
6555 	    // set the alternate buffer for the window we came from
6556 	    if (curwin != old_curwin
6557 		    && win_valid(old_curwin)
6558 		    && old_curwin->w_buffer != curbuf
6559 		    && (cmdmod.cmod_flags & CMOD_KEEPALT) == 0)
6560 		old_curwin->w_alt_fnum = curbuf->b_fnum;
6561 	}
6562     }
6563     else if (win_split(eap->addr_count > 0 ? (int)eap->line2 : 0,
6564 				     *eap->cmd == 'v' ? WSP_VERT : 0) != FAIL)
6565     {
6566 	// Reset 'scrollbind' when editing another file, but keep it when
6567 	// doing ":split" without arguments.
6568 	if (*eap->arg != NUL)
6569 	    RESET_BINDING(curwin);
6570 	else
6571 	    do_check_scrollbind(FALSE);
6572 	do_exedit(eap, old_curwin);
6573     }
6574 
6575 # ifdef FEAT_BROWSE
6576     cmdmod.cmod_flags = save_cmod_flags;
6577 # endif
6578 
6579 # if defined(FEAT_SEARCHPATH) || defined(FEAT_BROWSE)
6580 theend:
6581     vim_free(fname);
6582 # endif
6583 }
6584 
6585 /*
6586  * Open a new tab page.
6587  */
6588     void
6589 tabpage_new(void)
6590 {
6591     exarg_T	ea;
6592 
6593     CLEAR_FIELD(ea);
6594     ea.cmdidx = CMD_tabnew;
6595     ea.cmd = (char_u *)"tabn";
6596     ea.arg = (char_u *)"";
6597     ex_splitview(&ea);
6598 }
6599 
6600 /*
6601  * :tabnext command
6602  */
6603     static void
6604 ex_tabnext(exarg_T *eap)
6605 {
6606     int tab_number;
6607 
6608     if (ERROR_IF_POPUP_WINDOW)
6609 	return;
6610     switch (eap->cmdidx)
6611     {
6612 	case CMD_tabfirst:
6613 	case CMD_tabrewind:
6614 	    goto_tabpage(1);
6615 	    break;
6616 	case CMD_tablast:
6617 	    goto_tabpage(9999);
6618 	    break;
6619 	case CMD_tabprevious:
6620 	case CMD_tabNext:
6621 	    if (eap->arg && *eap->arg != NUL)
6622 	    {
6623 		char_u *p = eap->arg;
6624 		char_u *p_save = p;
6625 
6626 		tab_number = getdigits(&p);
6627 		if (p == p_save || *p_save == '-' || *p != NUL
6628 			    || tab_number == 0)
6629 		{
6630 		    // No numbers as argument.
6631 		    eap->errmsg = ex_errmsg(e_invarg2, eap->arg);
6632 		    return;
6633 		}
6634 	    }
6635 	    else
6636 	    {
6637 		if (eap->addr_count == 0)
6638 		    tab_number = 1;
6639 		else
6640 		{
6641 		    tab_number = eap->line2;
6642 		    if (tab_number < 1)
6643 		    {
6644 			eap->errmsg = _(e_invalid_range);
6645 			return;
6646 		    }
6647 		}
6648 	    }
6649 	    goto_tabpage(-tab_number);
6650 	    break;
6651 	default: // CMD_tabnext
6652 	    tab_number = get_tabpage_arg(eap);
6653 	    if (eap->errmsg == NULL)
6654 		goto_tabpage(tab_number);
6655 	    break;
6656     }
6657 }
6658 
6659 /*
6660  * :tabmove command
6661  */
6662     static void
6663 ex_tabmove(exarg_T *eap)
6664 {
6665     int tab_number;
6666 
6667     tab_number = get_tabpage_arg(eap);
6668     if (eap->errmsg == NULL)
6669 	tabpage_move(tab_number);
6670 }
6671 
6672 /*
6673  * :tabs command: List tabs and their contents.
6674  */
6675     static void
6676 ex_tabs(exarg_T *eap UNUSED)
6677 {
6678     tabpage_T	*tp;
6679     win_T	*wp;
6680     int		tabcount = 1;
6681 
6682     msg_start();
6683     msg_scroll = TRUE;
6684     for (tp = first_tabpage; tp != NULL && !got_int; tp = tp->tp_next)
6685     {
6686 	msg_putchar('\n');
6687 	vim_snprintf((char *)IObuff, IOSIZE, _("Tab page %d"), tabcount++);
6688 	msg_outtrans_attr(IObuff, HL_ATTR(HLF_T));
6689 	out_flush();	    // output one line at a time
6690 	ui_breakcheck();
6691 
6692 	if (tp  == curtab)
6693 	    wp = firstwin;
6694 	else
6695 	    wp = tp->tp_firstwin;
6696 	for ( ; wp != NULL && !got_int; wp = wp->w_next)
6697 	{
6698 	    msg_putchar('\n');
6699 	    msg_putchar(wp == curwin ? '>' : ' ');
6700 	    msg_putchar(' ');
6701 	    msg_putchar(bufIsChanged(wp->w_buffer) ? '+' : ' ');
6702 	    msg_putchar(' ');
6703 	    if (buf_spname(wp->w_buffer) != NULL)
6704 		vim_strncpy(IObuff, buf_spname(wp->w_buffer), IOSIZE - 1);
6705 	    else
6706 		home_replace(wp->w_buffer, wp->w_buffer->b_fname,
6707 							IObuff, IOSIZE, TRUE);
6708 	    msg_outtrans(IObuff);
6709 	    out_flush();	    // output one line at a time
6710 	    ui_breakcheck();
6711 	}
6712     }
6713 }
6714 
6715 /*
6716  * ":mode": Set screen mode.
6717  * If no argument given, just get the screen size and redraw.
6718  */
6719     static void
6720 ex_mode(exarg_T *eap)
6721 {
6722     if (*eap->arg == NUL)
6723 	shell_resized();
6724     else
6725 	emsg(_(e_screenmode));
6726 }
6727 
6728 /*
6729  * ":resize".
6730  * set, increment or decrement current window height
6731  */
6732     static void
6733 ex_resize(exarg_T *eap)
6734 {
6735     int		n;
6736     win_T	*wp = curwin;
6737 
6738     if (eap->addr_count > 0)
6739     {
6740 	n = eap->line2;
6741 	for (wp = firstwin; wp->w_next != NULL && --n > 0; wp = wp->w_next)
6742 	    ;
6743     }
6744 
6745 # ifdef FEAT_GUI
6746     need_mouse_correct = TRUE;
6747 # endif
6748     n = atol((char *)eap->arg);
6749     if (cmdmod.cmod_split & WSP_VERT)
6750     {
6751 	if (*eap->arg == '-' || *eap->arg == '+')
6752 	    n += wp->w_width;
6753 	else if (n == 0 && eap->arg[0] == NUL)	// default is very wide
6754 	    n = 9999;
6755 	win_setwidth_win((int)n, wp);
6756     }
6757     else
6758     {
6759 	if (*eap->arg == '-' || *eap->arg == '+')
6760 	    n += wp->w_height;
6761 	else if (n == 0 && eap->arg[0] == NUL)	// default is very high
6762 	    n = 9999;
6763 	win_setheight_win((int)n, wp);
6764     }
6765 }
6766 
6767 /*
6768  * ":find [+command] <file>" command.
6769  */
6770     static void
6771 ex_find(exarg_T *eap)
6772 {
6773 #ifdef FEAT_SEARCHPATH
6774     char_u	*fname;
6775     int		count;
6776 
6777     fname = find_file_in_path(eap->arg, (int)STRLEN(eap->arg), FNAME_MESS,
6778 						      TRUE, curbuf->b_ffname);
6779     if (eap->addr_count > 0)
6780     {
6781 	// Repeat finding the file "count" times.  This matters when it
6782 	// appears several times in the path.
6783 	count = eap->line2;
6784 	while (fname != NULL && --count > 0)
6785 	{
6786 	    vim_free(fname);
6787 	    fname = find_file_in_path(NULL, 0, FNAME_MESS,
6788 						     FALSE, curbuf->b_ffname);
6789 	}
6790     }
6791 
6792     if (fname != NULL)
6793     {
6794 	eap->arg = fname;
6795 #endif
6796 	do_exedit(eap, NULL);
6797 #ifdef FEAT_SEARCHPATH
6798 	vim_free(fname);
6799     }
6800 #endif
6801 }
6802 
6803 /*
6804  * ":open" simulation: for now just work like ":visual".
6805  */
6806     static void
6807 ex_open(exarg_T *eap)
6808 {
6809     regmatch_T	regmatch;
6810     char_u	*p;
6811 
6812 #ifdef FEAT_EVAL
6813     if (not_in_vim9(eap) == FAIL)
6814 	return;
6815 #endif
6816     curwin->w_cursor.lnum = eap->line2;
6817     beginline(BL_SOL | BL_FIX);
6818     if (*eap->arg == '/')
6819     {
6820 	// ":open /pattern/": put cursor in column found with pattern
6821 	++eap->arg;
6822 	p = skip_regexp(eap->arg, '/', magic_isset());
6823 	*p = NUL;
6824 	regmatch.regprog = vim_regcomp(eap->arg, magic_isset() ? RE_MAGIC : 0);
6825 	if (regmatch.regprog != NULL)
6826 	{
6827 	    regmatch.rm_ic = p_ic;
6828 	    p = ml_get_curline();
6829 	    if (vim_regexec(&regmatch, p, (colnr_T)0))
6830 		curwin->w_cursor.col = (colnr_T)(regmatch.startp[0] - p);
6831 	    else
6832 		emsg(_(e_nomatch));
6833 	    vim_regfree(regmatch.regprog);
6834 	}
6835 	// Move to the NUL, ignore any other arguments.
6836 	eap->arg += STRLEN(eap->arg);
6837     }
6838     check_cursor();
6839 
6840     eap->cmdidx = CMD_visual;
6841     do_exedit(eap, NULL);
6842 }
6843 
6844 /*
6845  * ":edit", ":badd", ":balt", ":visual".
6846  */
6847     static void
6848 ex_edit(exarg_T *eap)
6849 {
6850     do_exedit(eap, NULL);
6851 }
6852 
6853 /*
6854  * ":edit <file>" command and alike.
6855  */
6856     void
6857 do_exedit(
6858     exarg_T	*eap,
6859     win_T	*old_curwin)	    // curwin before doing a split or NULL
6860 {
6861     int		n;
6862     int		need_hide;
6863     int		exmode_was = exmode_active;
6864 
6865     if ((eap->cmdidx != CMD_pedit && ERROR_IF_POPUP_WINDOW)
6866 						 || ERROR_IF_TERM_POPUP_WINDOW)
6867 	return;
6868     /*
6869      * ":vi" command ends Ex mode.
6870      */
6871     if (exmode_active && (eap->cmdidx == CMD_visual
6872 						|| eap->cmdidx == CMD_view))
6873     {
6874 	exmode_active = FALSE;
6875 	ex_pressedreturn = FALSE;
6876 	if (*eap->arg == NUL)
6877 	{
6878 	    // Special case:  ":global/pat/visual\NLvi-commands"
6879 	    if (global_busy)
6880 	    {
6881 		int	rd = RedrawingDisabled;
6882 		int	nwr = no_wait_return;
6883 		int	ms = msg_scroll;
6884 #ifdef FEAT_GUI
6885 		int	he = hold_gui_events;
6886 #endif
6887 
6888 		if (eap->nextcmd != NULL)
6889 		{
6890 		    stuffReadbuff(eap->nextcmd);
6891 		    eap->nextcmd = NULL;
6892 		}
6893 
6894 		if (exmode_was != EXMODE_VIM)
6895 		    settmode(TMODE_RAW);
6896 		RedrawingDisabled = 0;
6897 		no_wait_return = 0;
6898 		need_wait_return = FALSE;
6899 		msg_scroll = 0;
6900 #ifdef FEAT_GUI
6901 		hold_gui_events = 0;
6902 #endif
6903 		must_redraw = CLEAR;
6904 		pending_exmode_active = TRUE;
6905 
6906 		main_loop(FALSE, TRUE);
6907 
6908 		pending_exmode_active = FALSE;
6909 		RedrawingDisabled = rd;
6910 		no_wait_return = nwr;
6911 		msg_scroll = ms;
6912 #ifdef FEAT_GUI
6913 		hold_gui_events = he;
6914 #endif
6915 	    }
6916 	    return;
6917 	}
6918     }
6919 
6920     if ((eap->cmdidx == CMD_new
6921 		|| eap->cmdidx == CMD_tabnew
6922 		|| eap->cmdidx == CMD_tabedit
6923 		|| eap->cmdidx == CMD_vnew) && *eap->arg == NUL)
6924     {
6925 	// ":new" or ":tabnew" without argument: edit an new empty buffer
6926 	setpcmark();
6927 	(void)do_ecmd(0, NULL, NULL, eap, ECMD_ONE,
6928 		      ECMD_HIDE + (eap->forceit ? ECMD_FORCEIT : 0),
6929 		      old_curwin == NULL ? curwin : NULL);
6930     }
6931     else if ((eap->cmdidx != CMD_split && eap->cmdidx != CMD_vsplit)
6932 	    || *eap->arg != NUL
6933 #ifdef FEAT_BROWSE
6934 	    || (cmdmod.cmod_flags & CMOD_BROWSE)
6935 #endif
6936 	    )
6937     {
6938 	// Can't edit another file when "curbuf_lock" is set.  Only ":edit"
6939 	// can bring us here, others are stopped earlier.
6940 	if (*eap->arg != NUL && curbuf_locked())
6941 	    return;
6942 
6943 	n = readonlymode;
6944 	if (eap->cmdidx == CMD_view || eap->cmdidx == CMD_sview)
6945 	    readonlymode = TRUE;
6946 	else if (eap->cmdidx == CMD_enew)
6947 	    readonlymode = FALSE;   // 'readonly' doesn't make sense in an
6948 				    // empty buffer
6949 	if (eap->cmdidx != CMD_balt && eap->cmdidx != CMD_badd)
6950 	    setpcmark();
6951 	if (do_ecmd(0, (eap->cmdidx == CMD_enew ? NULL : eap->arg),
6952 		    NULL, eap,
6953 		    // ":edit" goes to first line if Vi compatible
6954 		    (*eap->arg == NUL && eap->do_ecmd_lnum == 0
6955 				      && vim_strchr(p_cpo, CPO_GOTO1) != NULL)
6956 					       ? ECMD_ONE : eap->do_ecmd_lnum,
6957 		    (buf_hide(curbuf) ? ECMD_HIDE : 0)
6958 		    + (eap->forceit ? ECMD_FORCEIT : 0)
6959 		      // after a split we can use an existing buffer
6960 		    + (old_curwin != NULL ? ECMD_OLDBUF : 0)
6961 		    + (eap->cmdidx == CMD_badd ? ECMD_ADDBUF : 0)
6962 		    + (eap->cmdidx == CMD_balt ? ECMD_ALTBUF : 0)
6963 		    , old_curwin == NULL ? curwin : NULL) == FAIL)
6964 	{
6965 	    // Editing the file failed.  If the window was split, close it.
6966 	    if (old_curwin != NULL)
6967 	    {
6968 		need_hide = (curbufIsChanged() && curbuf->b_nwindows <= 1);
6969 		if (!need_hide || buf_hide(curbuf))
6970 		{
6971 #if defined(FEAT_EVAL)
6972 		    cleanup_T   cs;
6973 
6974 		    // Reset the error/interrupt/exception state here so that
6975 		    // aborting() returns FALSE when closing a window.
6976 		    enter_cleanup(&cs);
6977 #endif
6978 #ifdef FEAT_GUI
6979 		    need_mouse_correct = TRUE;
6980 #endif
6981 		    win_close(curwin, !need_hide && !buf_hide(curbuf));
6982 
6983 #if defined(FEAT_EVAL)
6984 		    // Restore the error/interrupt/exception state if not
6985 		    // discarded by a new aborting error, interrupt, or
6986 		    // uncaught exception.
6987 		    leave_cleanup(&cs);
6988 #endif
6989 		}
6990 	    }
6991 	}
6992 	else if (readonlymode && curbuf->b_nwindows == 1)
6993 	{
6994 	    // When editing an already visited buffer, 'readonly' won't be set
6995 	    // but the previous value is kept.  With ":view" and ":sview" we
6996 	    // want the  file to be readonly, except when another window is
6997 	    // editing the same buffer.
6998 	    curbuf->b_p_ro = TRUE;
6999 	}
7000 	readonlymode = n;
7001     }
7002     else
7003     {
7004 	if (eap->do_ecmd_cmd != NULL)
7005 	    do_cmd_argument(eap->do_ecmd_cmd);
7006 #ifdef FEAT_TITLE
7007 	n = curwin->w_arg_idx_invalid;
7008 #endif
7009 	check_arg_idx(curwin);
7010 #ifdef FEAT_TITLE
7011 	if (n != curwin->w_arg_idx_invalid)
7012 	    maketitle();
7013 #endif
7014     }
7015 
7016     /*
7017      * if ":split file" worked, set alternate file name in old window to new
7018      * file
7019      */
7020     if (old_curwin != NULL
7021 	    && *eap->arg != NUL
7022 	    && curwin != old_curwin
7023 	    && win_valid(old_curwin)
7024 	    && old_curwin->w_buffer != curbuf
7025 	    && (cmdmod.cmod_flags & CMOD_KEEPALT) == 0)
7026 	old_curwin->w_alt_fnum = curbuf->b_fnum;
7027 
7028     ex_no_reprint = TRUE;
7029 }
7030 
7031 #ifndef FEAT_GUI
7032 /*
7033  * ":gui" and ":gvim" when there is no GUI.
7034  */
7035     static void
7036 ex_nogui(exarg_T *eap)
7037 {
7038     eap->errmsg = _(e_gui_cannot_be_used_not_enabled_at_compile_time);
7039 }
7040 #endif
7041 
7042 #if defined(FEAT_GUI_MSWIN) && defined(FEAT_MENU) && defined(FEAT_TEAROFF)
7043     static void
7044 ex_tearoff(exarg_T *eap)
7045 {
7046     gui_make_tearoff(eap->arg);
7047 }
7048 #endif
7049 
7050 #if (defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_GTK) \
7051 	|| defined(FEAT_TERM_POPUP_MENU)) && defined(FEAT_MENU)
7052     static void
7053 ex_popup(exarg_T *eap)
7054 {
7055 # if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_GTK)
7056     if (gui.in_use)
7057 	gui_make_popup(eap->arg, eap->forceit);
7058 #  ifdef FEAT_TERM_POPUP_MENU
7059     else
7060 #  endif
7061 # endif
7062 # ifdef FEAT_TERM_POPUP_MENU
7063 	pum_make_popup(eap->arg, eap->forceit);
7064 # endif
7065 }
7066 #endif
7067 
7068     static void
7069 ex_swapname(exarg_T *eap UNUSED)
7070 {
7071     if (curbuf->b_ml.ml_mfp == NULL || curbuf->b_ml.ml_mfp->mf_fname == NULL)
7072 	msg(_("No swap file"));
7073     else
7074 	msg((char *)curbuf->b_ml.ml_mfp->mf_fname);
7075 }
7076 
7077 /*
7078  * ":syncbind" forces all 'scrollbind' windows to have the same relative
7079  * offset.
7080  * (1998-11-02 16:21:01  R. Edward Ralston <[email protected]>)
7081  */
7082     static void
7083 ex_syncbind(exarg_T *eap UNUSED)
7084 {
7085     win_T	*wp;
7086     win_T	*save_curwin = curwin;
7087     buf_T	*save_curbuf = curbuf;
7088     long	topline;
7089     long	y;
7090     linenr_T	old_linenr = curwin->w_cursor.lnum;
7091 
7092     setpcmark();
7093 
7094     /*
7095      * determine max topline
7096      */
7097     if (curwin->w_p_scb)
7098     {
7099 	topline = curwin->w_topline;
7100 	FOR_ALL_WINDOWS(wp)
7101 	{
7102 	    if (wp->w_p_scb && wp->w_buffer)
7103 	    {
7104 		y = wp->w_buffer->b_ml.ml_line_count - get_scrolloff_value();
7105 		if (topline > y)
7106 		    topline = y;
7107 	    }
7108 	}
7109 	if (topline < 1)
7110 	    topline = 1;
7111     }
7112     else
7113     {
7114 	topline = 1;
7115     }
7116 
7117 
7118     /*
7119      * Set all scrollbind windows to the same topline.
7120      */
7121     FOR_ALL_WINDOWS(curwin)
7122     {
7123 	if (curwin->w_p_scb)
7124 	{
7125 	    curbuf = curwin->w_buffer;
7126 	    y = topline - curwin->w_topline;
7127 	    if (y > 0)
7128 		scrollup(y, TRUE);
7129 	    else
7130 		scrolldown(-y, TRUE);
7131 	    curwin->w_scbind_pos = topline;
7132 	    redraw_later(VALID);
7133 	    cursor_correct();
7134 	    curwin->w_redr_status = TRUE;
7135 	}
7136     }
7137     curwin = save_curwin;
7138     curbuf = save_curbuf;
7139     if (curwin->w_p_scb)
7140     {
7141 	did_syncbind = TRUE;
7142 	checkpcmark();
7143 	if (old_linenr != curwin->w_cursor.lnum)
7144 	{
7145 	    char_u ctrl_o[2];
7146 
7147 	    ctrl_o[0] = Ctrl_O;
7148 	    ctrl_o[1] = 0;
7149 	    ins_typebuf(ctrl_o, REMAP_NONE, 0, TRUE, FALSE);
7150 	}
7151     }
7152 }
7153 
7154 
7155     static void
7156 ex_read(exarg_T *eap)
7157 {
7158     int		i;
7159     int		empty = (curbuf->b_ml.ml_flags & ML_EMPTY);
7160     linenr_T	lnum;
7161 
7162     if (eap->usefilter)			// :r!cmd
7163 	do_bang(1, eap, FALSE, FALSE, TRUE);
7164     else
7165     {
7166 	if (u_save(eap->line2, (linenr_T)(eap->line2 + 1)) == FAIL)
7167 	    return;
7168 
7169 #ifdef FEAT_BROWSE
7170 	if (cmdmod.cmod_flags & CMOD_BROWSE)
7171 	{
7172 	    char_u *browseFile;
7173 
7174 	    browseFile = do_browse(0, (char_u *)_("Append File"), eap->arg,
7175 						    NULL, NULL, NULL, curbuf);
7176 	    if (browseFile != NULL)
7177 	    {
7178 		i = readfile(browseFile, NULL,
7179 			  eap->line2, (linenr_T)0, (linenr_T)MAXLNUM, eap, 0);
7180 		vim_free(browseFile);
7181 	    }
7182 	    else
7183 		i = OK;
7184 	}
7185 	else
7186 #endif
7187 	     if (*eap->arg == NUL)
7188 	{
7189 	    if (check_fname() == FAIL)	// check for no file name
7190 		return;
7191 	    i = readfile(curbuf->b_ffname, curbuf->b_fname,
7192 			  eap->line2, (linenr_T)0, (linenr_T)MAXLNUM, eap, 0);
7193 	}
7194 	else
7195 	{
7196 	    if (vim_strchr(p_cpo, CPO_ALTREAD) != NULL)
7197 		(void)setaltfname(eap->arg, eap->arg, (linenr_T)1);
7198 	    i = readfile(eap->arg, NULL,
7199 			  eap->line2, (linenr_T)0, (linenr_T)MAXLNUM, eap, 0);
7200 
7201 	}
7202 	if (i != OK)
7203 	{
7204 #if defined(FEAT_EVAL)
7205 	    if (!aborting())
7206 #endif
7207 		semsg(_(e_notopen), eap->arg);
7208 	}
7209 	else
7210 	{
7211 	    if (empty && exmode_active)
7212 	    {
7213 		// Delete the empty line that remains.  Historically ex does
7214 		// this but vi doesn't.
7215 		if (eap->line2 == 0)
7216 		    lnum = curbuf->b_ml.ml_line_count;
7217 		else
7218 		    lnum = 1;
7219 		if (*ml_get(lnum) == NUL && u_savedel(lnum, 1L) == OK)
7220 		{
7221 		    ml_delete(lnum);
7222 		    if (curwin->w_cursor.lnum > 1
7223 					     && curwin->w_cursor.lnum >= lnum)
7224 			--curwin->w_cursor.lnum;
7225 		    deleted_lines_mark(lnum, 1L);
7226 		}
7227 	    }
7228 	    redraw_curbuf_later(VALID);
7229 	}
7230     }
7231 }
7232 
7233 static char_u	*prev_dir = NULL;
7234 
7235 #if defined(EXITFREE) || defined(PROTO)
7236     void
7237 free_cd_dir(void)
7238 {
7239     VIM_CLEAR(prev_dir);
7240     VIM_CLEAR(globaldir);
7241 }
7242 #endif
7243 
7244 /*
7245  * Get the previous directory for the given chdir scope.
7246  */
7247     static char_u *
7248 get_prevdir(cdscope_T scope)
7249 {
7250     if (scope == CDSCOPE_WINDOW)
7251 	return curwin->w_prevdir;
7252     else if (scope == CDSCOPE_TABPAGE)
7253 	return curtab->tp_prevdir;
7254     return prev_dir;
7255 }
7256 
7257 /*
7258  * Deal with the side effects of changing the current directory.
7259  * When 'scope' is CDSCOPE_TABPAGE then this was after an ":tcd" command.
7260  * When 'scope' is CDSCOPE_WINDOW then this was after an ":lcd" command.
7261  */
7262     void
7263 post_chdir(cdscope_T scope)
7264 {
7265     if (scope != CDSCOPE_WINDOW)
7266 	// Clear tab local directory for both :cd and :tcd
7267 	VIM_CLEAR(curtab->tp_localdir);
7268     VIM_CLEAR(curwin->w_localdir);
7269     if (scope != CDSCOPE_GLOBAL)
7270     {
7271 	char_u	*pdir = get_prevdir(scope);
7272 
7273 	// If still in the global directory, need to remember current
7274 	// directory as the global directory.
7275 	if (globaldir == NULL && pdir != NULL)
7276 	    globaldir = vim_strsave(pdir);
7277 
7278 	// Remember this local directory for the window.
7279 	if (mch_dirname(NameBuff, MAXPATHL) == OK)
7280 	{
7281 	    if (scope == CDSCOPE_TABPAGE)
7282 		curtab->tp_localdir = vim_strsave(NameBuff);
7283 	    else
7284 		curwin->w_localdir = vim_strsave(NameBuff);
7285 	}
7286     }
7287     else
7288     {
7289 	// We are now in the global directory, no need to remember its name.
7290 	VIM_CLEAR(globaldir);
7291     }
7292 
7293     shorten_fnames(TRUE);
7294 }
7295 
7296 /*
7297  * Change directory function used by :cd/:tcd/:lcd Ex commands and the
7298  * chdir() function.
7299  * scope == CDSCOPE_WINDOW: changes the window-local directory
7300  * scope == CDSCOPE_TABPAGE: changes the tab-local directory
7301  * Otherwise: changes the global directory
7302  * Returns TRUE if the directory is successfully changed.
7303  */
7304     int
7305 changedir_func(
7306 	char_u		*new_dir,
7307 	int		forceit,
7308 	cdscope_T	scope)
7309 {
7310     char_u	*tofree;
7311     char_u	*pdir = NULL;
7312     int		dir_differs;
7313     int		retval = FALSE;
7314 
7315     if (new_dir == NULL || allbuf_locked())
7316 	return FALSE;
7317 
7318     if (vim_strchr(p_cpo, CPO_CHDIR) != NULL && curbufIsChanged() && !forceit)
7319     {
7320 	emsg(_("E747: Cannot change directory, buffer is modified (add ! to override)"));
7321 	return FALSE;
7322     }
7323 
7324     // ":cd -": Change to previous directory
7325     if (STRCMP(new_dir, "-") == 0)
7326     {
7327 	pdir = get_prevdir(scope);
7328 	if (pdir == NULL)
7329 	{
7330 	    emsg(_("E186: No previous directory"));
7331 	    return FALSE;
7332 	}
7333 	new_dir = pdir;
7334     }
7335 
7336     // Free the previous directory
7337     tofree = get_prevdir(scope);
7338 
7339     // Save current directory for next ":cd -"
7340     if (mch_dirname(NameBuff, MAXPATHL) == OK)
7341 	pdir = vim_strsave(NameBuff);
7342     else
7343 	pdir = NULL;
7344     if (scope == CDSCOPE_WINDOW)
7345 	curwin->w_prevdir = pdir;
7346     else if (scope == CDSCOPE_TABPAGE)
7347 	curtab->tp_prevdir = pdir;
7348     else
7349 	prev_dir = pdir;
7350 
7351 #if defined(UNIX) || defined(VMS)
7352     // for UNIX ":cd" means: go to home directory
7353     if (*new_dir == NUL)
7354     {
7355 	// use NameBuff for home directory name
7356 # ifdef VMS
7357 	char_u	*p;
7358 
7359 	p = mch_getenv((char_u *)"SYS$LOGIN");
7360 	if (p == NULL || *p == NUL)	// empty is the same as not set
7361 	    NameBuff[0] = NUL;
7362 	else
7363 	    vim_strncpy(NameBuff, p, MAXPATHL - 1);
7364 # else
7365 	expand_env((char_u *)"$HOME", NameBuff, MAXPATHL);
7366 # endif
7367 	new_dir = NameBuff;
7368     }
7369 #endif
7370     dir_differs = new_dir == NULL || pdir == NULL
7371 	|| pathcmp((char *)pdir, (char *)new_dir, -1) != 0;
7372     if (new_dir == NULL || (dir_differs && vim_chdir(new_dir)))
7373 	emsg(_(e_failed));
7374     else
7375     {
7376 	char_u  *acmd_fname;
7377 
7378 	post_chdir(scope);
7379 
7380 	if (dir_differs)
7381 	{
7382 	    if (scope == CDSCOPE_WINDOW)
7383 		acmd_fname = (char_u *)"window";
7384 	    else if (scope == CDSCOPE_TABPAGE)
7385 		acmd_fname = (char_u *)"tabpage";
7386 	    else
7387 		acmd_fname = (char_u *)"global";
7388 	    apply_autocmds(EVENT_DIRCHANGED, acmd_fname, new_dir, FALSE,
7389 								curbuf);
7390 	}
7391 	retval = TRUE;
7392     }
7393     vim_free(tofree);
7394 
7395     return retval;
7396 }
7397 
7398 /*
7399  * ":cd", ":tcd", ":lcd", ":chdir" ":tchdir" and ":lchdir".
7400  */
7401     void
7402 ex_cd(exarg_T *eap)
7403 {
7404     char_u	*new_dir;
7405 
7406     new_dir = eap->arg;
7407 #if !defined(UNIX) && !defined(VMS)
7408     // for non-UNIX ":cd" means: print current directory
7409     if (*new_dir == NUL)
7410 	ex_pwd(NULL);
7411     else
7412 #endif
7413     {
7414 	cdscope_T	scope = CDSCOPE_GLOBAL;
7415 
7416 	if (eap->cmdidx == CMD_lcd || eap->cmdidx == CMD_lchdir)
7417 	    scope = CDSCOPE_WINDOW;
7418 	else if (eap->cmdidx == CMD_tcd || eap->cmdidx == CMD_tchdir)
7419 	    scope = CDSCOPE_TABPAGE;
7420 
7421 	if (changedir_func(new_dir, eap->forceit, scope))
7422 	{
7423 	    // Echo the new current directory if the command was typed.
7424 	    if (KeyTyped || p_verbose >= 5)
7425 		ex_pwd(eap);
7426 	}
7427     }
7428 }
7429 
7430 /*
7431  * ":pwd".
7432  */
7433     static void
7434 ex_pwd(exarg_T *eap UNUSED)
7435 {
7436     if (mch_dirname(NameBuff, MAXPATHL) == OK)
7437     {
7438 #ifdef BACKSLASH_IN_FILENAME
7439 	slash_adjust(NameBuff);
7440 #endif
7441 	if (p_verbose > 0)
7442 	{
7443 	    char *context = "global";
7444 
7445 	    if (curwin->w_localdir != NULL)
7446 		context = "window";
7447 	    else if (curtab->tp_localdir != NULL)
7448 		context = "tabpage";
7449 	    smsg("[%s] %s", context, (char *)NameBuff);
7450 	}
7451 	else
7452 	    msg((char *)NameBuff);
7453     }
7454     else
7455 	emsg(_("E187: Unknown"));
7456 }
7457 
7458 /*
7459  * ":=".
7460  */
7461     static void
7462 ex_equal(exarg_T *eap)
7463 {
7464     smsg("%ld", (long)eap->line2);
7465     ex_may_print(eap);
7466 }
7467 
7468     static void
7469 ex_sleep(exarg_T *eap)
7470 {
7471     int		n;
7472     long	len;
7473 
7474     if (cursor_valid())
7475     {
7476 	n = W_WINROW(curwin) + curwin->w_wrow - msg_scrolled;
7477 	if (n >= 0)
7478 	    windgoto((int)n, curwin->w_wincol + curwin->w_wcol);
7479     }
7480 
7481     len = eap->line2;
7482     switch (*eap->arg)
7483     {
7484 	case 'm': break;
7485 	case NUL: len *= 1000L; break;
7486 	default: semsg(_(e_invarg2), eap->arg); return;
7487     }
7488 
7489     // Hide the cursor if invoked with !
7490     do_sleep(len, eap->forceit);
7491 }
7492 
7493 /*
7494  * Sleep for "msec" milliseconds, but keep checking for a CTRL-C every second.
7495  * Hide the cursor if "hide_cursor" is TRUE.
7496  */
7497     void
7498 do_sleep(long msec, int hide_cursor)
7499 {
7500     long	done = 0;
7501     long	wait_now;
7502 # ifdef ELAPSED_FUNC
7503     elapsed_T	start_tv;
7504 
7505     // Remember at what time we started, so that we know how much longer we
7506     // should wait after waiting for a bit.
7507     ELAPSED_INIT(start_tv);
7508 # endif
7509 
7510     if (hide_cursor)
7511         cursor_sleep();
7512     else
7513         cursor_on();
7514 
7515     out_flush_cursor(FALSE, FALSE);
7516     while (!got_int && done < msec)
7517     {
7518 	wait_now = msec - done > 1000L ? 1000L : msec - done;
7519 #ifdef FEAT_TIMERS
7520 	{
7521 	    long    due_time = check_due_timer();
7522 
7523 	    if (due_time > 0 && due_time < wait_now)
7524 		wait_now = due_time;
7525 	}
7526 #endif
7527 #ifdef FEAT_JOB_CHANNEL
7528 	if (has_any_channel() && wait_now > 20L)
7529 	    wait_now = 20L;
7530 #endif
7531 #ifdef FEAT_SOUND
7532 	if (has_any_sound_callback() && wait_now > 20L)
7533 	    wait_now = 20L;
7534 #endif
7535 	ui_delay(wait_now, TRUE);
7536 
7537 #ifdef FEAT_JOB_CHANNEL
7538 	if (has_any_channel())
7539 	    ui_breakcheck_force(TRUE);
7540 	else
7541 #endif
7542 	    ui_breakcheck();
7543 #ifdef MESSAGE_QUEUE
7544 	// Process the netbeans and clientserver messages that may have been
7545 	// received in the call to ui_breakcheck() when the GUI is in use. This
7546 	// may occur when running a test case.
7547 	parse_queued_messages();
7548 #endif
7549 
7550 # ifdef ELAPSED_FUNC
7551 	// actual time passed
7552 	done = ELAPSED_FUNC(start_tv);
7553 # else
7554 	// guestimate time passed (will actually be more)
7555 	done += wait_now;
7556 # endif
7557     }
7558 
7559     // If CTRL-C was typed to interrupt the sleep, drop the CTRL-C from the
7560     // input buffer, otherwise a following call to input() fails.
7561     if (got_int)
7562 	(void)vpeekc();
7563 
7564     if (hide_cursor)
7565         cursor_unsleep();
7566 }
7567 
7568 /*
7569  * ":winsize" command (obsolete).
7570  */
7571     static void
7572 ex_winsize(exarg_T *eap)
7573 {
7574     int		w, h;
7575     char_u	*arg = eap->arg;
7576     char_u	*p;
7577 
7578     if (!isdigit(*arg))
7579     {
7580 	semsg(_(e_invarg2), arg);
7581 	return;
7582     }
7583     w = getdigits(&arg);
7584     arg = skipwhite(arg);
7585     p = arg;
7586     h = getdigits(&arg);
7587     if (*p != NUL && *arg == NUL)
7588 	set_shellsize(w, h, TRUE);
7589     else
7590 	emsg(_("E465: :winsize requires two number arguments"));
7591 }
7592 
7593     static void
7594 ex_wincmd(exarg_T *eap)
7595 {
7596     int		xchar = NUL;
7597     char_u	*p;
7598 
7599     if (*eap->arg == 'g' || *eap->arg == Ctrl_G)
7600     {
7601 	// CTRL-W g and CTRL-W CTRL-G  have an extra command character
7602 	if (eap->arg[1] == NUL)
7603 	{
7604 	    emsg(_(e_invarg));
7605 	    return;
7606 	}
7607 	xchar = eap->arg[1];
7608 	p = eap->arg + 2;
7609     }
7610     else
7611 	p = eap->arg + 1;
7612 
7613     set_nextcmd(eap, p);
7614     p = skipwhite(p);
7615     if (*p != NUL && *p != (
7616 #ifdef FEAT_EVAL
7617 	    in_vim9script() ? '#' :
7618 #endif
7619 		'"')
7620 	    && eap->nextcmd == NULL)
7621 	emsg(_(e_invarg));
7622     else if (!eap->skip)
7623     {
7624 	// Pass flags on for ":vertical wincmd ]".
7625 	postponed_split_flags = cmdmod.cmod_split;
7626 	postponed_split_tab = cmdmod.cmod_tab;
7627 	do_window(*eap->arg, eap->addr_count > 0 ? eap->line2 : 0L, xchar);
7628 	postponed_split_flags = 0;
7629 	postponed_split_tab = 0;
7630     }
7631 }
7632 
7633 #if defined(FEAT_GUI) || defined(UNIX) || defined(VMS) || defined(MSWIN)
7634 /*
7635  * ":winpos".
7636  */
7637     static void
7638 ex_winpos(exarg_T *eap)
7639 {
7640     int		x, y;
7641     char_u	*arg = eap->arg;
7642     char_u	*p;
7643 
7644     if (*arg == NUL)
7645     {
7646 # if defined(FEAT_GUI) || defined(MSWIN)
7647 #  ifdef VIMDLL
7648 	if (gui.in_use ? gui_mch_get_winpos(&x, &y) != FAIL :
7649 		mch_get_winpos(&x, &y) != FAIL)
7650 #  elif defined(FEAT_GUI)
7651 	if (gui.in_use && gui_mch_get_winpos(&x, &y) != FAIL)
7652 #  else
7653 	if (mch_get_winpos(&x, &y) != FAIL)
7654 #  endif
7655 	{
7656 	    sprintf((char *)IObuff, _("Window position: X %d, Y %d"), x, y);
7657 	    msg((char *)IObuff);
7658 	}
7659 	else
7660 # endif
7661 	    emsg(_("E188: Obtaining window position not implemented for this platform"));
7662     }
7663     else
7664     {
7665 	x = getdigits(&arg);
7666 	arg = skipwhite(arg);
7667 	p = arg;
7668 	y = getdigits(&arg);
7669 	if (*p == NUL || *arg != NUL)
7670 	{
7671 	    emsg(_("E466: :winpos requires two number arguments"));
7672 	    return;
7673 	}
7674 # ifdef FEAT_GUI
7675 	if (gui.in_use)
7676 	    gui_mch_set_winpos(x, y);
7677 	else if (gui.starting)
7678 	{
7679 	    // Remember the coordinates for when the window is opened.
7680 	    gui_win_x = x;
7681 	    gui_win_y = y;
7682 	}
7683 #  if defined(HAVE_TGETENT) || defined(VIMDLL)
7684 	else
7685 #  endif
7686 # endif
7687 # if defined(MSWIN) && (!defined(FEAT_GUI) || defined(VIMDLL))
7688 	    mch_set_winpos(x, y);
7689 # endif
7690 # ifdef HAVE_TGETENT
7691 	if (*T_CWP)
7692 	    term_set_winpos(x, y);
7693 # endif
7694     }
7695 }
7696 #endif
7697 
7698 /*
7699  * Handle command that work like operators: ":delete", ":yank", ":>" and ":<".
7700  */
7701     static void
7702 ex_operators(exarg_T *eap)
7703 {
7704     oparg_T	oa;
7705 
7706     clear_oparg(&oa);
7707     oa.regname = eap->regname;
7708     oa.start.lnum = eap->line1;
7709     oa.end.lnum = eap->line2;
7710     oa.line_count = eap->line2 - eap->line1 + 1;
7711     oa.motion_type = MLINE;
7712     virtual_op = FALSE;
7713     if (eap->cmdidx != CMD_yank)	// position cursor for undo
7714     {
7715 	setpcmark();
7716 	curwin->w_cursor.lnum = eap->line1;
7717 	beginline(BL_SOL | BL_FIX);
7718     }
7719 
7720     if (VIsual_active)
7721 	end_visual_mode();
7722 
7723     switch (eap->cmdidx)
7724     {
7725 	case CMD_delete:
7726 	    oa.op_type = OP_DELETE;
7727 	    op_delete(&oa);
7728 	    break;
7729 
7730 	case CMD_yank:
7731 	    oa.op_type = OP_YANK;
7732 	    (void)op_yank(&oa, FALSE, TRUE);
7733 	    break;
7734 
7735 	default:    // CMD_rshift or CMD_lshift
7736 	    if (
7737 #ifdef FEAT_RIGHTLEFT
7738 		(eap->cmdidx == CMD_rshift) ^ curwin->w_p_rl
7739 #else
7740 		eap->cmdidx == CMD_rshift
7741 #endif
7742 						)
7743 		oa.op_type = OP_RSHIFT;
7744 	    else
7745 		oa.op_type = OP_LSHIFT;
7746 	    op_shift(&oa, FALSE, eap->amount);
7747 	    break;
7748     }
7749     virtual_op = MAYBE;
7750     ex_may_print(eap);
7751 }
7752 
7753 /*
7754  * ":put".
7755  */
7756     static void
7757 ex_put(exarg_T *eap)
7758 {
7759     // ":0put" works like ":1put!".
7760     if (eap->line2 == 0)
7761     {
7762 	eap->line2 = 1;
7763 	eap->forceit = TRUE;
7764     }
7765     curwin->w_cursor.lnum = eap->line2;
7766     do_put(eap->regname, NULL, eap->forceit ? BACKWARD : FORWARD, 1L,
7767 						       PUT_LINE|PUT_CURSLINE);
7768 }
7769 
7770 /*
7771  * Handle ":copy" and ":move".
7772  */
7773     static void
7774 ex_copymove(exarg_T *eap)
7775 {
7776     long	n;
7777 
7778 #ifdef FEAT_EVAL
7779     if (not_in_vim9(eap) == FAIL)
7780 	return;
7781 #endif
7782     n = get_address(eap, &eap->arg, eap->addr_type, FALSE, FALSE, FALSE, 1);
7783     if (eap->arg == NULL)	    // error detected
7784     {
7785 	eap->nextcmd = NULL;
7786 	return;
7787     }
7788     get_flags(eap);
7789 
7790     /*
7791      * move or copy lines from 'eap->line1'-'eap->line2' to below line 'n'
7792      */
7793     if (n == MAXLNUM || n < 0 || n > curbuf->b_ml.ml_line_count)
7794     {
7795 	emsg(_(e_invalid_range));
7796 	return;
7797     }
7798 
7799     if (eap->cmdidx == CMD_move)
7800     {
7801 	if (do_move(eap->line1, eap->line2, n) == FAIL)
7802 	    return;
7803     }
7804     else
7805 	ex_copy(eap->line1, eap->line2, n);
7806     u_clearline();
7807     beginline(BL_SOL | BL_FIX);
7808     ex_may_print(eap);
7809 }
7810 
7811 /*
7812  * Print the current line if flags were given to the Ex command.
7813  */
7814     void
7815 ex_may_print(exarg_T *eap)
7816 {
7817     if (eap->flags != 0)
7818     {
7819 	print_line(curwin->w_cursor.lnum, (eap->flags & EXFLAG_NR),
7820 						  (eap->flags & EXFLAG_LIST));
7821 	ex_no_reprint = TRUE;
7822     }
7823 }
7824 
7825 /*
7826  * ":smagic" and ":snomagic".
7827  */
7828     static void
7829 ex_submagic(exarg_T *eap)
7830 {
7831     optmagic_T saved = magic_overruled;
7832 
7833     magic_overruled = eap->cmdidx == CMD_smagic
7834 					  ? OPTION_MAGIC_ON : OPTION_MAGIC_OFF;
7835     ex_substitute(eap);
7836     magic_overruled = saved;
7837 }
7838 
7839 /*
7840  * ":join".
7841  */
7842     static void
7843 ex_join(exarg_T *eap)
7844 {
7845     curwin->w_cursor.lnum = eap->line1;
7846     if (eap->line1 == eap->line2)
7847     {
7848 	if (eap->addr_count >= 2)   // :2,2join does nothing
7849 	    return;
7850 	if (eap->line2 == curbuf->b_ml.ml_line_count)
7851 	{
7852 	    beep_flush();
7853 	    return;
7854 	}
7855 	++eap->line2;
7856     }
7857     (void)do_join(eap->line2 - eap->line1 + 1, !eap->forceit, TRUE, TRUE, TRUE);
7858     beginline(BL_WHITE | BL_FIX);
7859     ex_may_print(eap);
7860 }
7861 
7862 /*
7863  * ":[addr]@r" or ":[addr]*r": execute register
7864  */
7865     static void
7866 ex_at(exarg_T *eap)
7867 {
7868     int		c;
7869     int		prev_len = typebuf.tb_len;
7870 
7871     curwin->w_cursor.lnum = eap->line2;
7872     check_cursor_col();
7873 
7874 #ifdef USE_ON_FLY_SCROLL
7875     dont_scroll = TRUE;		// disallow scrolling here
7876 #endif
7877 
7878     // get the register name.  No name means to use the previous one
7879     c = *eap->arg;
7880     if (c == NUL || (c == '*' && *eap->cmd == '*'))
7881 	c = '@';
7882     // Put the register in the typeahead buffer with the "silent" flag.
7883     if (do_execreg(c, TRUE, vim_strchr(p_cpo, CPO_EXECBUF) != NULL, TRUE)
7884 								      == FAIL)
7885     {
7886 	beep_flush();
7887     }
7888     else
7889     {
7890 	int	save_efr = exec_from_reg;
7891 
7892 	exec_from_reg = TRUE;
7893 
7894 	/*
7895 	 * Execute from the typeahead buffer.
7896 	 * Continue until the stuff buffer is empty and all added characters
7897 	 * have been consumed.
7898 	 */
7899 	while (!stuff_empty() || typebuf.tb_len > prev_len)
7900 	    (void)do_cmdline(NULL, getexline, NULL, DOCMD_NOWAIT|DOCMD_VERBOSE);
7901 
7902 	exec_from_reg = save_efr;
7903     }
7904 }
7905 
7906 /*
7907  * ":!".
7908  */
7909     static void
7910 ex_bang(exarg_T *eap)
7911 {
7912     do_bang(eap->addr_count, eap, eap->forceit, TRUE, TRUE);
7913 }
7914 
7915 /*
7916  * ":undo".
7917  */
7918     static void
7919 ex_undo(exarg_T *eap)
7920 {
7921     if (eap->addr_count == 1)	    // :undo 123
7922 	undo_time(eap->line2, FALSE, FALSE, TRUE);
7923     else
7924 	u_undo(1);
7925 }
7926 
7927 #ifdef FEAT_PERSISTENT_UNDO
7928     static void
7929 ex_wundo(exarg_T *eap)
7930 {
7931     char_u hash[UNDO_HASH_SIZE];
7932 
7933     u_compute_hash(hash);
7934     u_write_undo(eap->arg, eap->forceit, curbuf, hash);
7935 }
7936 
7937     static void
7938 ex_rundo(exarg_T *eap)
7939 {
7940     char_u hash[UNDO_HASH_SIZE];
7941 
7942     u_compute_hash(hash);
7943     u_read_undo(eap->arg, hash, NULL);
7944 }
7945 #endif
7946 
7947 /*
7948  * ":redo".
7949  */
7950     static void
7951 ex_redo(exarg_T *eap UNUSED)
7952 {
7953     u_redo(1);
7954 }
7955 
7956 /*
7957  * ":earlier" and ":later".
7958  */
7959     static void
7960 ex_later(exarg_T *eap)
7961 {
7962     long	count = 0;
7963     int		sec = FALSE;
7964     int		file = FALSE;
7965     char_u	*p = eap->arg;
7966 
7967     if (*p == NUL)
7968 	count = 1;
7969     else if (isdigit(*p))
7970     {
7971 	count = getdigits(&p);
7972 	switch (*p)
7973 	{
7974 	    case 's': ++p; sec = TRUE; break;
7975 	    case 'm': ++p; sec = TRUE; count *= 60; break;
7976 	    case 'h': ++p; sec = TRUE; count *= 60 * 60; break;
7977 	    case 'd': ++p; sec = TRUE; count *= 24 * 60 * 60; break;
7978 	    case 'f': ++p; file = TRUE; break;
7979 	}
7980     }
7981 
7982     if (*p != NUL)
7983 	semsg(_(e_invarg2), eap->arg);
7984     else
7985 	undo_time(eap->cmdidx == CMD_earlier ? -count : count,
7986 							    sec, file, FALSE);
7987 }
7988 
7989 /*
7990  * ":redir": start/stop redirection.
7991  */
7992     static void
7993 ex_redir(exarg_T *eap)
7994 {
7995     char	*mode;
7996     char_u	*fname;
7997     char_u	*arg = eap->arg;
7998 
7999 #ifdef FEAT_EVAL
8000     if (redir_execute)
8001     {
8002 	emsg(_("E930: Cannot use :redir inside execute()"));
8003 	return;
8004     }
8005 #endif
8006 
8007     if (STRICMP(eap->arg, "END") == 0)
8008 	close_redir();
8009     else
8010     {
8011 	if (*arg == '>')
8012 	{
8013 	    ++arg;
8014 	    if (*arg == '>')
8015 	    {
8016 		++arg;
8017 		mode = "a";
8018 	    }
8019 	    else
8020 		mode = "w";
8021 	    arg = skipwhite(arg);
8022 
8023 	    close_redir();
8024 
8025 	    // Expand environment variables and "~/".
8026 	    fname = expand_env_save(arg);
8027 	    if (fname == NULL)
8028 		return;
8029 #ifdef FEAT_BROWSE
8030 	    if (cmdmod.cmod_flags & CMOD_BROWSE)
8031 	    {
8032 		char_u	*browseFile;
8033 
8034 		browseFile = do_browse(BROWSE_SAVE,
8035 			(char_u *)_("Save Redirection"),
8036 			fname, NULL, NULL,
8037 			(char_u *)_(BROWSE_FILTER_ALL_FILES), curbuf);
8038 		if (browseFile == NULL)
8039 		    return;		// operation cancelled
8040 		vim_free(fname);
8041 		fname = browseFile;
8042 		eap->forceit = TRUE;	// since dialog already asked
8043 	    }
8044 #endif
8045 
8046 	    redir_fd = open_exfile(fname, eap->forceit, mode);
8047 	    vim_free(fname);
8048 	}
8049 #ifdef FEAT_EVAL
8050 	else if (*arg == '@')
8051 	{
8052 	    // redirect to a register a-z (resp. A-Z for appending)
8053 	    close_redir();
8054 	    ++arg;
8055 	    if (ASCII_ISALPHA(*arg)
8056 # ifdef FEAT_CLIPBOARD
8057 		    || *arg == '*'
8058 		    || *arg == '+'
8059 # endif
8060 		    || *arg == '"')
8061 	    {
8062 		redir_reg = *arg++;
8063 		if (*arg == '>' && arg[1] == '>')  // append
8064 		    arg += 2;
8065 		else
8066 		{
8067 		    // Can use both "@a" and "@a>".
8068 		    if (*arg == '>')
8069 			arg++;
8070 		    // Make register empty when not using @A-@Z and the
8071 		    // command is valid.
8072 		    if (*arg == NUL && !isupper(redir_reg))
8073 			write_reg_contents(redir_reg, (char_u *)"", -1, FALSE);
8074 		}
8075 	    }
8076 	    if (*arg != NUL)
8077 	    {
8078 		redir_reg = 0;
8079 		semsg(_(e_invarg2), eap->arg);
8080 	    }
8081 	}
8082 	else if (*arg == '=' && arg[1] == '>')
8083 	{
8084 	    int append;
8085 
8086 	    // redirect to a variable
8087 	    close_redir();
8088 	    arg += 2;
8089 
8090 	    if (*arg == '>')
8091 	    {
8092 		++arg;
8093 		append = TRUE;
8094 	    }
8095 	    else
8096 		append = FALSE;
8097 
8098 	    if (var_redir_start(skipwhite(arg), append) == OK)
8099 		redir_vname = 1;
8100 	}
8101 #endif
8102 
8103 	// TODO: redirect to a buffer
8104 
8105 	else
8106 	    semsg(_(e_invarg2), eap->arg);
8107     }
8108 
8109     // Make sure redirection is not off.  Can happen for cmdline completion
8110     // that indirectly invokes a command to catch its output.
8111     if (redir_fd != NULL
8112 #ifdef FEAT_EVAL
8113 			  || redir_reg || redir_vname
8114 #endif
8115 							)
8116 	redir_off = FALSE;
8117 }
8118 
8119 /*
8120  * ":redraw": force redraw
8121  */
8122     void
8123 ex_redraw(exarg_T *eap)
8124 {
8125     int		r = RedrawingDisabled;
8126     int		p = p_lz;
8127 
8128     RedrawingDisabled = 0;
8129     p_lz = FALSE;
8130     validate_cursor();
8131     update_topline();
8132     update_screen(eap->forceit ? CLEAR : VIsual_active ? INVERTED : 0);
8133 #ifdef FEAT_TITLE
8134     if (need_maketitle)
8135 	maketitle();
8136 #endif
8137 #if defined(MSWIN) && (!defined(FEAT_GUI_MSWIN) || defined(VIMDLL))
8138 # ifdef VIMDLL
8139     if (!gui.in_use)
8140 # endif
8141 	resize_console_buf();
8142 #endif
8143     RedrawingDisabled = r;
8144     p_lz = p;
8145 
8146     // Reset msg_didout, so that a message that's there is overwritten.
8147     msg_didout = FALSE;
8148     msg_col = 0;
8149 
8150     // No need to wait after an intentional redraw.
8151     need_wait_return = FALSE;
8152 
8153     out_flush();
8154 }
8155 
8156 /*
8157  * ":redrawstatus": force redraw of status line(s)
8158  */
8159     static void
8160 ex_redrawstatus(exarg_T *eap UNUSED)
8161 {
8162     int		r = RedrawingDisabled;
8163     int		p = p_lz;
8164 
8165     RedrawingDisabled = 0;
8166     p_lz = FALSE;
8167     if (eap->forceit)
8168 	status_redraw_all();
8169     else
8170 	status_redraw_curbuf();
8171     update_screen(VIsual_active ? INVERTED : 0);
8172     RedrawingDisabled = r;
8173     p_lz = p;
8174     out_flush();
8175 }
8176 
8177 /*
8178  * ":redrawtabline": force redraw of the tabline
8179  */
8180     static void
8181 ex_redrawtabline(exarg_T *eap UNUSED)
8182 {
8183     int		r = RedrawingDisabled;
8184     int		p = p_lz;
8185 
8186     RedrawingDisabled = 0;
8187     p_lz = FALSE;
8188 
8189     draw_tabline();
8190 
8191     RedrawingDisabled = r;
8192     p_lz = p;
8193     out_flush();
8194 }
8195 
8196     static void
8197 close_redir(void)
8198 {
8199     if (redir_fd != NULL)
8200     {
8201 	fclose(redir_fd);
8202 	redir_fd = NULL;
8203     }
8204 #ifdef FEAT_EVAL
8205     redir_reg = 0;
8206     if (redir_vname)
8207     {
8208 	var_redir_stop();
8209 	redir_vname = 0;
8210     }
8211 #endif
8212 }
8213 
8214 #if (defined(FEAT_SESSION) || defined(FEAT_EVAL)) || defined(PROTO)
8215     int
8216 vim_mkdir_emsg(char_u *name, int prot UNUSED)
8217 {
8218     if (vim_mkdir(name, prot) != 0)
8219     {
8220 	semsg(_("E739: Cannot create directory: %s"), name);
8221 	return FAIL;
8222     }
8223     return OK;
8224 }
8225 #endif
8226 
8227 /*
8228  * Open a file for writing for an Ex command, with some checks.
8229  * Return file descriptor, or NULL on failure.
8230  */
8231     FILE *
8232 open_exfile(
8233     char_u	*fname,
8234     int		forceit,
8235     char	*mode)	    // "w" for create new file or "a" for append
8236 {
8237     FILE	*fd;
8238 
8239 #ifdef UNIX
8240     // with Unix it is possible to open a directory
8241     if (mch_isdir(fname))
8242     {
8243 	semsg(_(e_src_is_directory), fname);
8244 	return NULL;
8245     }
8246 #endif
8247     if (!forceit && *mode != 'a' && vim_fexists(fname))
8248     {
8249 	semsg(_("E189: \"%s\" exists (add ! to override)"), fname);
8250 	return NULL;
8251     }
8252 
8253     if ((fd = mch_fopen((char *)fname, mode)) == NULL)
8254 	semsg(_("E190: Cannot open \"%s\" for writing"), fname);
8255 
8256     return fd;
8257 }
8258 
8259 /*
8260  * ":mark" and ":k".
8261  */
8262     static void
8263 ex_mark(exarg_T *eap)
8264 {
8265     pos_T	pos;
8266 
8267 #ifdef FEAT_EVAL
8268     if (not_in_vim9(eap) == FAIL)
8269 	return;
8270 #endif
8271     if (*eap->arg == NUL)		// No argument?
8272 	emsg(_(e_argreq));
8273     else if (eap->arg[1] != NUL)	// more than one character?
8274 	semsg(_(e_trailing_arg), eap->arg);
8275     else
8276     {
8277 	pos = curwin->w_cursor;		// save curwin->w_cursor
8278 	curwin->w_cursor.lnum = eap->line2;
8279 	beginline(BL_WHITE | BL_FIX);
8280 	if (setmark(*eap->arg) == FAIL)	// set mark
8281 	    emsg(_("E191: Argument must be a letter or forward/backward quote"));
8282 	curwin->w_cursor = pos;		// restore curwin->w_cursor
8283     }
8284 }
8285 
8286 /*
8287  * Update w_topline, w_leftcol and the cursor position.
8288  */
8289     void
8290 update_topline_cursor(void)
8291 {
8292     check_cursor();		// put cursor on valid line
8293     update_topline();
8294     if (!curwin->w_p_wrap)
8295 	validate_cursor();
8296     update_curswant();
8297 }
8298 
8299 /*
8300  * Save the current State and go to Normal mode.
8301  * Return TRUE if the typeahead could be saved.
8302  */
8303     int
8304 save_current_state(save_state_T *sst)
8305 {
8306     sst->save_msg_scroll = msg_scroll;
8307     sst->save_restart_edit = restart_edit;
8308     sst->save_msg_didout = msg_didout;
8309     sst->save_State = State;
8310     sst->save_insertmode = p_im;
8311     sst->save_finish_op = finish_op;
8312     sst->save_opcount = opcount;
8313     sst->save_reg_executing = reg_executing;
8314 
8315     msg_scroll = FALSE;		    // no msg scrolling in Normal mode
8316     restart_edit = 0;		    // don't go to Insert mode
8317     p_im = FALSE;		    // don't use 'insertmode'
8318 
8319     sst->save_script_version = current_sctx.sc_version;
8320     current_sctx.sc_version = 1;    // not in Vim9 script
8321 
8322     /*
8323      * Save the current typeahead.  This is required to allow using ":normal"
8324      * from an event handler and makes sure we don't hang when the argument
8325      * ends with half a command.
8326      */
8327     save_typeahead(&sst->tabuf);
8328     return sst->tabuf.typebuf_valid;
8329 }
8330 
8331     void
8332 restore_current_state(save_state_T *sst)
8333 {
8334     // Restore the previous typeahead.
8335     restore_typeahead(&sst->tabuf, FALSE);
8336 
8337     msg_scroll = sst->save_msg_scroll;
8338     restart_edit = sst->save_restart_edit;
8339     p_im = sst->save_insertmode;
8340     finish_op = sst->save_finish_op;
8341     opcount = sst->save_opcount;
8342     reg_executing = sst->save_reg_executing;
8343     msg_didout |= sst->save_msg_didout;	// don't reset msg_didout now
8344     current_sctx.sc_version = sst->save_script_version;
8345 
8346     // Restore the state (needed when called from a function executed for
8347     // 'indentexpr'). Update the mouse and cursor, they may have changed.
8348     State = sst->save_State;
8349 #ifdef CURSOR_SHAPE
8350     ui_cursor_shape();		// may show different cursor shape
8351 #endif
8352 }
8353 
8354 /*
8355  * ":normal[!] {commands}": Execute normal mode commands.
8356  */
8357     void
8358 ex_normal(exarg_T *eap)
8359 {
8360     save_state_T save_state;
8361     char_u	*arg = NULL;
8362     int		l;
8363     char_u	*p;
8364 
8365     if (ex_normal_lock > 0)
8366     {
8367 	emsg(_(e_secure));
8368 	return;
8369     }
8370     if (ex_normal_busy >= p_mmd)
8371     {
8372 	emsg(_("E192: Recursive use of :normal too deep"));
8373 	return;
8374     }
8375 
8376     /*
8377      * vgetc() expects a CSI and K_SPECIAL to have been escaped.  Don't do
8378      * this for the K_SPECIAL leading byte, otherwise special keys will not
8379      * work.
8380      */
8381     if (has_mbyte)
8382     {
8383 	int	len = 0;
8384 
8385 	// Count the number of characters to be escaped.
8386 	for (p = eap->arg; *p != NUL; ++p)
8387 	{
8388 #ifdef FEAT_GUI
8389 	    if (*p == CSI)  // leadbyte CSI
8390 		len += 2;
8391 #endif
8392 	    for (l = (*mb_ptr2len)(p) - 1; l > 0; --l)
8393 		if (*++p == K_SPECIAL	  // trailbyte K_SPECIAL or CSI
8394 #ifdef FEAT_GUI
8395 			|| *p == CSI
8396 #endif
8397 			)
8398 		    len += 2;
8399 	}
8400 	if (len > 0)
8401 	{
8402 	    arg = alloc(STRLEN(eap->arg) + len + 1);
8403 	    if (arg != NULL)
8404 	    {
8405 		len = 0;
8406 		for (p = eap->arg; *p != NUL; ++p)
8407 		{
8408 		    arg[len++] = *p;
8409 #ifdef FEAT_GUI
8410 		    if (*p == CSI)
8411 		    {
8412 			arg[len++] = KS_EXTRA;
8413 			arg[len++] = (int)KE_CSI;
8414 		    }
8415 #endif
8416 		    for (l = (*mb_ptr2len)(p) - 1; l > 0; --l)
8417 		    {
8418 			arg[len++] = *++p;
8419 			if (*p == K_SPECIAL)
8420 			{
8421 			    arg[len++] = KS_SPECIAL;
8422 			    arg[len++] = KE_FILLER;
8423 			}
8424 #ifdef FEAT_GUI
8425 			else if (*p == CSI)
8426 			{
8427 			    arg[len++] = KS_EXTRA;
8428 			    arg[len++] = (int)KE_CSI;
8429 			}
8430 #endif
8431 		    }
8432 		    arg[len] = NUL;
8433 		}
8434 	    }
8435 	}
8436     }
8437 
8438     ++ex_normal_busy;
8439     if (save_current_state(&save_state))
8440     {
8441 	/*
8442 	 * Repeat the :normal command for each line in the range.  When no
8443 	 * range given, execute it just once, without positioning the cursor
8444 	 * first.
8445 	 */
8446 	do
8447 	{
8448 	    if (eap->addr_count != 0)
8449 	    {
8450 		curwin->w_cursor.lnum = eap->line1++;
8451 		curwin->w_cursor.col = 0;
8452 		check_cursor_moved(curwin);
8453 	    }
8454 
8455 	    exec_normal_cmd(arg != NULL
8456 		     ? arg
8457 		     : eap->arg, eap->forceit ? REMAP_NONE : REMAP_YES, FALSE);
8458 	}
8459 	while (eap->addr_count > 0 && eap->line1 <= eap->line2 && !got_int);
8460     }
8461 
8462     // Might not return to the main loop when in an event handler.
8463     update_topline_cursor();
8464 
8465     restore_current_state(&save_state);
8466     --ex_normal_busy;
8467     setmouse();
8468 #ifdef CURSOR_SHAPE
8469     ui_cursor_shape();		// may show different cursor shape
8470 #endif
8471 
8472     vim_free(arg);
8473 }
8474 
8475 /*
8476  * ":startinsert", ":startreplace" and ":startgreplace"
8477  */
8478     static void
8479 ex_startinsert(exarg_T *eap)
8480 {
8481     if (eap->forceit)
8482     {
8483 	// cursor line can be zero on startup
8484 	if (!curwin->w_cursor.lnum)
8485 	    curwin->w_cursor.lnum = 1;
8486 	set_cursor_for_append_to_line();
8487     }
8488 #ifdef FEAT_TERMINAL
8489     // Ignore this when running in an active terminal.
8490     if (term_job_running(curbuf->b_term))
8491 	return;
8492 #endif
8493 
8494     // Ignore the command when already in Insert mode.  Inserting an
8495     // expression register that invokes a function can do this.
8496     if (State & INSERT)
8497 	return;
8498 
8499     if (eap->cmdidx == CMD_startinsert)
8500 	restart_edit = 'a';
8501     else if (eap->cmdidx == CMD_startreplace)
8502 	restart_edit = 'R';
8503     else
8504 	restart_edit = 'V';
8505 
8506     if (!eap->forceit)
8507     {
8508 	if (eap->cmdidx == CMD_startinsert)
8509 	    restart_edit = 'i';
8510 	curwin->w_curswant = 0;	    // avoid MAXCOL
8511     }
8512 
8513     if (VIsual_active)
8514 	showmode();
8515 }
8516 
8517 /*
8518  * ":stopinsert"
8519  */
8520     static void
8521 ex_stopinsert(exarg_T *eap UNUSED)
8522 {
8523     restart_edit = 0;
8524     stop_insert_mode = TRUE;
8525     clearmode();
8526 }
8527 
8528 /*
8529  * Execute normal mode command "cmd".
8530  * "remap" can be REMAP_NONE or REMAP_YES.
8531  */
8532     void
8533 exec_normal_cmd(char_u *cmd, int remap, int silent)
8534 {
8535     // Stuff the argument into the typeahead buffer.
8536     ins_typebuf(cmd, remap, 0, TRUE, silent);
8537     exec_normal(FALSE, FALSE, FALSE);
8538 }
8539 
8540 /*
8541  * Execute normal_cmd() until there is no typeahead left.
8542  * When "use_vpeekc" is TRUE use vpeekc() to check for available chars.
8543  */
8544     void
8545 exec_normal(int was_typed, int use_vpeekc, int may_use_terminal_loop UNUSED)
8546 {
8547     oparg_T	oa;
8548     int		c;
8549 
8550     // When calling vpeekc() from feedkeys() it will return Ctrl_C when there
8551     // is nothing to get, so also check for Ctrl_C.
8552     clear_oparg(&oa);
8553     finish_op = FALSE;
8554     while ((!stuff_empty()
8555 		|| ((was_typed || !typebuf_typed()) && typebuf.tb_len > 0)
8556 		|| (use_vpeekc && (c = vpeekc()) != NUL && c != Ctrl_C))
8557 	    && !got_int)
8558     {
8559 	update_topline_cursor();
8560 #ifdef FEAT_TERMINAL
8561 	if (may_use_terminal_loop && term_use_loop()
8562 		&& oa.op_type == OP_NOP && oa.regname == NUL
8563 		&& !VIsual_active)
8564 	{
8565 	    // If terminal_loop() returns OK we got a key that is handled
8566 	    // in Normal model.  With FAIL we first need to position the
8567 	    // cursor and the screen needs to be redrawn.
8568 	    if (terminal_loop(TRUE) == OK)
8569 		normal_cmd(&oa, TRUE);
8570 	}
8571 	else
8572 #endif
8573 	    // execute a Normal mode cmd
8574 	    normal_cmd(&oa, TRUE);
8575     }
8576 }
8577 
8578 #ifdef FEAT_FIND_ID
8579     static void
8580 ex_checkpath(exarg_T *eap)
8581 {
8582     find_pattern_in_path(NULL, 0, 0, FALSE, FALSE, CHECK_PATH, 1L,
8583 				   eap->forceit ? ACTION_SHOW_ALL : ACTION_SHOW,
8584 					      (linenr_T)1, (linenr_T)MAXLNUM);
8585 }
8586 
8587 #if defined(FEAT_QUICKFIX)
8588 /*
8589  * ":psearch"
8590  */
8591     static void
8592 ex_psearch(exarg_T *eap)
8593 {
8594     g_do_tagpreview = p_pvh;
8595     ex_findpat(eap);
8596     g_do_tagpreview = 0;
8597 }
8598 #endif
8599 
8600     static void
8601 ex_findpat(exarg_T *eap)
8602 {
8603     int		whole = TRUE;
8604     long	n;
8605     char_u	*p;
8606     int		action;
8607 
8608     switch (cmdnames[eap->cmdidx].cmd_name[2])
8609     {
8610 	case 'e':	// ":psearch", ":isearch" and ":dsearch"
8611 		if (cmdnames[eap->cmdidx].cmd_name[0] == 'p')
8612 		    action = ACTION_GOTO;
8613 		else
8614 		    action = ACTION_SHOW;
8615 		break;
8616 	case 'i':	// ":ilist" and ":dlist"
8617 		action = ACTION_SHOW_ALL;
8618 		break;
8619 	case 'u':	// ":ijump" and ":djump"
8620 		action = ACTION_GOTO;
8621 		break;
8622 	default:	// ":isplit" and ":dsplit"
8623 		action = ACTION_SPLIT;
8624 		break;
8625     }
8626 
8627     n = 1;
8628     if (vim_isdigit(*eap->arg))	// get count
8629     {
8630 	n = getdigits(&eap->arg);
8631 	eap->arg = skipwhite(eap->arg);
8632     }
8633     if (*eap->arg == '/')   // Match regexp, not just whole words
8634     {
8635 	whole = FALSE;
8636 	++eap->arg;
8637 	p = skip_regexp(eap->arg, '/', magic_isset());
8638 	if (*p)
8639 	{
8640 	    *p++ = NUL;
8641 	    p = skipwhite(p);
8642 
8643 	    // Check for trailing illegal characters
8644 	    if (!ends_excmd2(eap->arg, p))
8645 		eap->errmsg = ex_errmsg(e_trailing_arg, p);
8646 	    else
8647 		set_nextcmd(eap, p);
8648 	}
8649     }
8650     if (!eap->skip)
8651 	find_pattern_in_path(eap->arg, 0, (int)STRLEN(eap->arg),
8652 			    whole, !eap->forceit,
8653 			    *eap->cmd == 'd' ?	FIND_DEFINE : FIND_ANY,
8654 			    n, action, eap->line1, eap->line2);
8655 }
8656 #endif
8657 
8658 
8659 #ifdef FEAT_QUICKFIX
8660 /*
8661  * ":ptag", ":ptselect", ":ptjump", ":ptnext", etc.
8662  */
8663     static void
8664 ex_ptag(exarg_T *eap)
8665 {
8666     g_do_tagpreview = p_pvh;  // will be reset to 0 in ex_tag_cmd()
8667     ex_tag_cmd(eap, cmdnames[eap->cmdidx].cmd_name + 1);
8668 }
8669 
8670 /*
8671  * ":pedit"
8672  */
8673     static void
8674 ex_pedit(exarg_T *eap)
8675 {
8676     win_T	*curwin_save = curwin;
8677 
8678     if (ERROR_IF_ANY_POPUP_WINDOW)
8679 	return;
8680 
8681     // Open the preview window or popup and make it the current window.
8682     g_do_tagpreview = p_pvh;
8683     prepare_tagpreview(TRUE, TRUE, FALSE);
8684 
8685     // Edit the file.
8686     do_exedit(eap, NULL);
8687 
8688     if (curwin != curwin_save && win_valid(curwin_save))
8689     {
8690 	// Return cursor to where we were
8691 	validate_cursor();
8692 	redraw_later(VALID);
8693 	win_enter(curwin_save, TRUE);
8694     }
8695 # ifdef FEAT_PROP_POPUP
8696     else if (WIN_IS_POPUP(curwin))
8697     {
8698 	// can't keep focus in popup window
8699 	win_enter(firstwin, TRUE);
8700     }
8701 # endif
8702     g_do_tagpreview = 0;
8703 }
8704 #endif
8705 
8706 /*
8707  * ":stag", ":stselect" and ":stjump".
8708  */
8709     static void
8710 ex_stag(exarg_T *eap)
8711 {
8712     postponed_split = -1;
8713     postponed_split_flags = cmdmod.cmod_split;
8714     postponed_split_tab = cmdmod.cmod_tab;
8715     ex_tag_cmd(eap, cmdnames[eap->cmdidx].cmd_name + 1);
8716     postponed_split_flags = 0;
8717     postponed_split_tab = 0;
8718 }
8719 
8720 /*
8721  * ":tag", ":tselect", ":tjump", ":tnext", etc.
8722  */
8723     static void
8724 ex_tag(exarg_T *eap)
8725 {
8726     ex_tag_cmd(eap, cmdnames[eap->cmdidx].cmd_name);
8727 }
8728 
8729     static void
8730 ex_tag_cmd(exarg_T *eap, char_u *name)
8731 {
8732     int		cmd;
8733 
8734     switch (name[1])
8735     {
8736 	case 'j': cmd = DT_JUMP;	// ":tjump"
8737 		  break;
8738 	case 's': cmd = DT_SELECT;	// ":tselect"
8739 		  break;
8740 	case 'p': cmd = DT_PREV;	// ":tprevious"
8741 		  break;
8742 	case 'N': cmd = DT_PREV;	// ":tNext"
8743 		  break;
8744 	case 'n': cmd = DT_NEXT;	// ":tnext"
8745 		  break;
8746 	case 'o': cmd = DT_POP;		// ":pop"
8747 		  break;
8748 	case 'f':			// ":tfirst"
8749 	case 'r': cmd = DT_FIRST;	// ":trewind"
8750 		  break;
8751 	case 'l': cmd = DT_LAST;	// ":tlast"
8752 		  break;
8753 	default:			// ":tag"
8754 #ifdef FEAT_CSCOPE
8755 		  if (p_cst && *eap->arg != NUL)
8756 		  {
8757 		      ex_cstag(eap);
8758 		      return;
8759 		  }
8760 #endif
8761 		  cmd = DT_TAG;
8762 		  break;
8763     }
8764 
8765     if (name[0] == 'l')
8766     {
8767 #ifndef FEAT_QUICKFIX
8768 	ex_ni(eap);
8769 	return;
8770 #else
8771 	cmd = DT_LTAG;
8772 #endif
8773     }
8774 
8775     do_tag(eap->arg, cmd, eap->addr_count > 0 ? (int)eap->line2 : 1,
8776 							  eap->forceit, TRUE);
8777 }
8778 
8779 /*
8780  * Check "str" for starting with a special cmdline variable.
8781  * If found return one of the SPEC_ values and set "*usedlen" to the length of
8782  * the variable.  Otherwise return -1 and "*usedlen" is unchanged.
8783  */
8784     int
8785 find_cmdline_var(char_u *src, int *usedlen)
8786 {
8787     int		len;
8788     int		i;
8789     static char *(spec_str[]) = {
8790 		    "%",
8791 #define SPEC_PERC   0
8792 		    "#",
8793 #define SPEC_HASH   (SPEC_PERC + 1)
8794 		    "<cword>",		// cursor word
8795 #define SPEC_CWORD  (SPEC_HASH + 1)
8796 		    "<cWORD>",		// cursor WORD
8797 #define SPEC_CCWORD (SPEC_CWORD + 1)
8798 		    "<cexpr>",		// expr under cursor
8799 #define SPEC_CEXPR  (SPEC_CCWORD + 1)
8800 		    "<cfile>",		// cursor path name
8801 #define SPEC_CFILE  (SPEC_CEXPR + 1)
8802 		    "<sfile>",		// ":so" file name
8803 #define SPEC_SFILE  (SPEC_CFILE + 1)
8804 		    "<slnum>",		// ":so" file line number
8805 #define SPEC_SLNUM  (SPEC_SFILE + 1)
8806 		    "<stack>",		// call stack
8807 #define SPEC_STACK  (SPEC_SLNUM + 1)
8808 		    "<afile>",		// autocommand file name
8809 #define SPEC_AFILE  (SPEC_STACK + 1)
8810 		    "<abuf>",		// autocommand buffer number
8811 #define SPEC_ABUF   (SPEC_AFILE + 1)
8812 		    "<amatch>",		// autocommand match name
8813 #define SPEC_AMATCH (SPEC_ABUF + 1)
8814 		    "<sflnum>",		// script file line number
8815 #define SPEC_SFLNUM  (SPEC_AMATCH + 1)
8816 		    "<SID>",		// script ID: <SNR>123_
8817 #define SPEC_SID  (SPEC_SFLNUM + 1)
8818 #ifdef FEAT_CLIENTSERVER
8819 		    "<client>"
8820 # define SPEC_CLIENT (SPEC_SID + 1)
8821 #endif
8822     };
8823 
8824     for (i = 0; i < (int)ARRAY_LENGTH(spec_str); ++i)
8825     {
8826 	len = (int)STRLEN(spec_str[i]);
8827 	if (STRNCMP(src, spec_str[i], len) == 0)
8828 	{
8829 	    *usedlen = len;
8830 	    return i;
8831 	}
8832     }
8833     return -1;
8834 }
8835 
8836 /*
8837  * Evaluate cmdline variables.
8838  *
8839  * change "%"	    to curbuf->b_ffname
8840  *	  "#"	    to curwin->w_alt_fnum
8841  *	  "%%"	    to curwin->w_alt_fnum in Vim9 script
8842  *	  "<cword>" to word under the cursor
8843  *	  "<cWORD>" to WORD under the cursor
8844  *	  "<cexpr>" to C-expression under the cursor
8845  *	  "<cfile>" to path name under the cursor
8846  *	  "<sfile>" to sourced file name
8847  *	  "<stack>" to call stack
8848  *	  "<slnum>" to sourced file line number
8849  *	  "<afile>" to file name for autocommand
8850  *	  "<abuf>"  to buffer number for autocommand
8851  *	  "<amatch>" to matching name for autocommand
8852  *
8853  * When an error is detected, "errormsg" is set to a non-NULL pointer (may be
8854  * "" for error without a message) and NULL is returned.
8855  * Returns an allocated string if a valid match was found.
8856  * Returns NULL if no match was found.	"usedlen" then still contains the
8857  * number of characters to skip.
8858  */
8859     char_u *
8860 eval_vars(
8861     char_u	*src,		// pointer into commandline
8862     char_u	*srcstart,	// beginning of valid memory for src
8863     int		*usedlen,	// characters after src that are used
8864     linenr_T	*lnump,		// line number for :e command, or NULL
8865     char	**errormsg,	// pointer to error message
8866     int		*escaped)	// return value has escaped white space (can
8867 				// be NULL)
8868 {
8869     int		i;
8870     char_u	*s;
8871     char_u	*result;
8872     char_u	*resultbuf = NULL;
8873     int		resultlen;
8874     buf_T	*buf;
8875     int		valid = VALID_HEAD + VALID_PATH;    // assume valid result
8876     int		spec_idx;
8877     int		tilde_file = FALSE;
8878     int		skip_mod = FALSE;
8879     char_u	strbuf[30];
8880 
8881     *errormsg = NULL;
8882     if (escaped != NULL)
8883 	*escaped = FALSE;
8884 
8885     /*
8886      * Check if there is something to do.
8887      */
8888     spec_idx = find_cmdline_var(src, usedlen);
8889     if (spec_idx < 0)	// no match
8890     {
8891 	*usedlen = 1;
8892 	return NULL;
8893     }
8894 
8895     /*
8896      * Skip when preceded with a backslash "\%" and "\#".
8897      * Note: In "\\%" the % is also not recognized!
8898      */
8899     if (src > srcstart && src[-1] == '\\')
8900     {
8901 	*usedlen = 0;
8902 	STRMOVE(src - 1, src);	// remove backslash
8903 	return NULL;
8904     }
8905 
8906     /*
8907      * word or WORD under cursor
8908      */
8909     if (spec_idx == SPEC_CWORD || spec_idx == SPEC_CCWORD
8910 						     || spec_idx == SPEC_CEXPR)
8911     {
8912 	resultlen = find_ident_under_cursor(&result,
8913 		spec_idx == SPEC_CWORD ? (FIND_IDENT | FIND_STRING)
8914 	      : spec_idx == SPEC_CEXPR ? (FIND_IDENT | FIND_STRING | FIND_EVAL)
8915 	      : FIND_STRING);
8916 	if (resultlen == 0)
8917 	{
8918 	    *errormsg = "";
8919 	    return NULL;
8920 	}
8921     }
8922 
8923     /*
8924      * '#': Alternate file name
8925      * '%': Current file name
8926      *	    File name under the cursor
8927      *	    File name for autocommand
8928      *	and following modifiers
8929      */
8930     else
8931     {
8932 	int off = 0;
8933 
8934 	switch (spec_idx)
8935 	{
8936 	case SPEC_PERC:
8937 #ifdef FEAT_EVAL
8938 		if (!in_vim9script() || src[1] != '%')
8939 #endif
8940 		{
8941 		    // '%': current file
8942 		    if (curbuf->b_fname == NULL)
8943 		    {
8944 			result = (char_u *)"";
8945 			valid = 0;	    // Must have ":p:h" to be valid
8946 		    }
8947 		    else
8948 		    {
8949 			result = curbuf->b_fname;
8950 			tilde_file = STRCMP(result, "~") == 0;
8951 		    }
8952 		    break;
8953 		}
8954 #ifdef FEAT_EVAL
8955 		// "%%" alternate file
8956 		off = 1;
8957 #endif
8958 		// FALLTHROUGH
8959 	case SPEC_HASH:		// '#' or "#99": alternate file
8960 		if (off == 0 ? src[1] == '#' : src[2] == '%')
8961 		{
8962 		    // "##" or "%%%": the argument list
8963 		    result = arg_all();
8964 		    resultbuf = result;
8965 		    *usedlen = off + 2;
8966 		    if (escaped != NULL)
8967 			*escaped = TRUE;
8968 		    skip_mod = TRUE;
8969 		    break;
8970 		}
8971 		s = src + off + 1;
8972 		if (*s == '<')		// "#<99" uses v:oldfiles
8973 		    ++s;
8974 		i = (int)getdigits(&s);
8975 		if (s == src + off + 2 && src[off + 1] == '-')
8976 		    // just a minus sign, don't skip over it
8977 		    s--;
8978 		*usedlen = (int)(s - src); // length of what we expand
8979 
8980 		if (src[off + 1] == '<' && i != 0)
8981 		{
8982 		    if (*usedlen < off + 2)
8983 		    {
8984 			// Should we give an error message for #<text?
8985 			*usedlen = off + 1;
8986 			return NULL;
8987 		    }
8988 #ifdef FEAT_EVAL
8989 		    result = list_find_str(get_vim_var_list(VV_OLDFILES),
8990 								     (long)i);
8991 		    if (result == NULL)
8992 		    {
8993 			*errormsg = "";
8994 			return NULL;
8995 		    }
8996 #else
8997 		    *errormsg = _("E809: #< is not available without the +eval feature");
8998 		    return NULL;
8999 #endif
9000 		}
9001 		else
9002 		{
9003 		    if (i == 0 && src[off + 1] == '<' && *usedlen > off + 1)
9004 			*usedlen = off + 1;
9005 		    buf = buflist_findnr(i);
9006 		    if (buf == NULL)
9007 		    {
9008 			*errormsg = _("E194: No alternate file name to substitute for '#'");
9009 			return NULL;
9010 		    }
9011 		    if (lnump != NULL)
9012 			*lnump = ECMD_LAST;
9013 		    if (buf->b_fname == NULL)
9014 		    {
9015 			result = (char_u *)"";
9016 			valid = 0;	    // Must have ":p:h" to be valid
9017 		    }
9018 		    else
9019 		    {
9020 			result = buf->b_fname;
9021 			tilde_file = STRCMP(result, "~") == 0;
9022 		    }
9023 		}
9024 		break;
9025 
9026 #ifdef FEAT_SEARCHPATH
9027 	case SPEC_CFILE:	// file name under cursor
9028 		result = file_name_at_cursor(FNAME_MESS|FNAME_HYP, 1L, NULL);
9029 		if (result == NULL)
9030 		{
9031 		    *errormsg = "";
9032 		    return NULL;
9033 		}
9034 		resultbuf = result;	    // remember allocated string
9035 		break;
9036 #endif
9037 
9038 	case SPEC_AFILE:	// file name for autocommand
9039 		result = autocmd_fname;
9040 		if (result != NULL && !autocmd_fname_full)
9041 		{
9042 		    // Still need to turn the fname into a full path.  It is
9043 		    // postponed to avoid a delay when <afile> is not used.
9044 		    autocmd_fname_full = TRUE;
9045 		    result = FullName_save(autocmd_fname, FALSE);
9046 		    vim_free(autocmd_fname);
9047 		    autocmd_fname = result;
9048 		}
9049 		if (result == NULL)
9050 		{
9051 		    *errormsg = _("E495: no autocommand file name to substitute for \"<afile>\"");
9052 		    return NULL;
9053 		}
9054 		result = shorten_fname1(result);
9055 		break;
9056 
9057 	case SPEC_ABUF:		// buffer number for autocommand
9058 		if (autocmd_bufnr <= 0)
9059 		{
9060 		    *errormsg = _("E496: no autocommand buffer number to substitute for \"<abuf>\"");
9061 		    return NULL;
9062 		}
9063 		sprintf((char *)strbuf, "%d", autocmd_bufnr);
9064 		result = strbuf;
9065 		break;
9066 
9067 	case SPEC_AMATCH:	// match name for autocommand
9068 		result = autocmd_match;
9069 		if (result == NULL)
9070 		{
9071 		    *errormsg = _("E497: no autocommand match name to substitute for \"<amatch>\"");
9072 		    return NULL;
9073 		}
9074 		break;
9075 
9076 	case SPEC_SFILE:	// file name for ":so" command
9077 	case SPEC_STACK:	// call stack
9078 		result = estack_sfile(spec_idx == SPEC_SFILE
9079 						? ESTACK_SFILE : ESTACK_STACK);
9080 		if (result == NULL)
9081 		{
9082 		    *errormsg = spec_idx == SPEC_SFILE
9083 			? _("E498: no :source file name to substitute for \"<sfile>\"")
9084 			: _("E489: no call stack to substitute for \"<stack>\"");
9085 		    return NULL;
9086 		}
9087 		resultbuf = result;	    // remember allocated string
9088 		break;
9089 
9090 	case SPEC_SLNUM:	// line in file for ":so" command
9091 		if (SOURCING_NAME == NULL || SOURCING_LNUM == 0)
9092 		{
9093 		    *errormsg = _("E842: no line number to use for \"<slnum>\"");
9094 		    return NULL;
9095 		}
9096 		sprintf((char *)strbuf, "%ld", SOURCING_LNUM);
9097 		result = strbuf;
9098 		break;
9099 
9100 #ifdef FEAT_EVAL
9101 	case SPEC_SFLNUM:	// line in script file
9102 		if (current_sctx.sc_lnum + SOURCING_LNUM == 0)
9103 		{
9104 		    *errormsg = _("E961: no line number to use for \"<sflnum>\"");
9105 		    return NULL;
9106 		}
9107 		sprintf((char *)strbuf, "%ld",
9108 				 (long)(current_sctx.sc_lnum + SOURCING_LNUM));
9109 		result = strbuf;
9110 		break;
9111 
9112 	case SPEC_SID:
9113 		if (current_sctx.sc_sid <= 0)
9114 		{
9115 		    *errormsg = _(e_usingsid);
9116 		    return NULL;
9117 		}
9118 		sprintf((char *)strbuf, "<SNR>%d_", current_sctx.sc_sid);
9119 		result = strbuf;
9120 		break;
9121 #endif
9122 
9123 #ifdef FEAT_CLIENTSERVER
9124 	case SPEC_CLIENT:	// Source of last submitted input
9125 		sprintf((char *)strbuf, PRINTF_HEX_LONG_U,
9126 							(long_u)clientWindow);
9127 		result = strbuf;
9128 		break;
9129 #endif
9130 
9131 	default:
9132 		result = (char_u *)""; // avoid gcc warning
9133 		break;
9134 	}
9135 
9136 	resultlen = (int)STRLEN(result);	// length of new string
9137 	if (src[*usedlen] == '<')	// remove the file name extension
9138 	{
9139 	    ++*usedlen;
9140 	    if ((s = vim_strrchr(result, '.')) != NULL && s >= gettail(result))
9141 		resultlen = (int)(s - result);
9142 	}
9143 	else if (!skip_mod)
9144 	{
9145 	    valid |= modify_fname(src, tilde_file, usedlen, &result, &resultbuf,
9146 								  &resultlen);
9147 	    if (result == NULL)
9148 	    {
9149 		*errormsg = "";
9150 		return NULL;
9151 	    }
9152 	}
9153     }
9154 
9155     if (resultlen == 0 || valid != VALID_HEAD + VALID_PATH)
9156     {
9157 	if (valid != VALID_HEAD + VALID_PATH)
9158 	    // xgettext:no-c-format
9159 	    *errormsg = _("E499: Empty file name for '%' or '#', only works with \":p:h\"");
9160 	else
9161 	    *errormsg = _("E500: Evaluates to an empty string");
9162 	result = NULL;
9163     }
9164     else
9165 	result = vim_strnsave(result, resultlen);
9166     vim_free(resultbuf);
9167     return result;
9168 }
9169 
9170 /*
9171  * Expand the <sfile> string in "arg".
9172  *
9173  * Returns an allocated string, or NULL for any error.
9174  */
9175     char_u *
9176 expand_sfile(char_u *arg)
9177 {
9178     char	*errormsg;
9179     int		len;
9180     char_u	*result;
9181     char_u	*newres;
9182     char_u	*repl;
9183     int		srclen;
9184     char_u	*p;
9185 
9186     result = vim_strsave(arg);
9187     if (result == NULL)
9188 	return NULL;
9189 
9190     for (p = result; *p; )
9191     {
9192 	if (STRNCMP(p, "<sfile>", 7) != 0)
9193 	    ++p;
9194 	else
9195 	{
9196 	    // replace "<sfile>" with the sourced file name, and do ":" stuff
9197 	    repl = eval_vars(p, result, &srclen, NULL, &errormsg, NULL);
9198 	    if (errormsg != NULL)
9199 	    {
9200 		if (*errormsg)
9201 		    emsg(errormsg);
9202 		vim_free(result);
9203 		return NULL;
9204 	    }
9205 	    if (repl == NULL)		// no match (cannot happen)
9206 	    {
9207 		p += srclen;
9208 		continue;
9209 	    }
9210 	    len = (int)STRLEN(result) - srclen + (int)STRLEN(repl) + 1;
9211 	    newres = alloc(len);
9212 	    if (newres == NULL)
9213 	    {
9214 		vim_free(repl);
9215 		vim_free(result);
9216 		return NULL;
9217 	    }
9218 	    mch_memmove(newres, result, (size_t)(p - result));
9219 	    STRCPY(newres + (p - result), repl);
9220 	    len = (int)STRLEN(newres);
9221 	    STRCAT(newres, p + srclen);
9222 	    vim_free(repl);
9223 	    vim_free(result);
9224 	    result = newres;
9225 	    p = newres + len;		// continue after the match
9226 	}
9227     }
9228 
9229     return result;
9230 }
9231 
9232 #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG) || defined(PROTO)
9233 /*
9234  * Make a dialog message in "buff[DIALOG_MSG_SIZE]".
9235  * "format" must contain "%s".
9236  */
9237     void
9238 dialog_msg(char_u *buff, char *format, char_u *fname)
9239 {
9240     if (fname == NULL)
9241 	fname = (char_u *)_("Untitled");
9242     vim_snprintf((char *)buff, DIALOG_MSG_SIZE, format, fname);
9243 }
9244 #endif
9245 
9246 /*
9247  * ":behave {mswin,xterm}"
9248  */
9249     static void
9250 ex_behave(exarg_T *eap)
9251 {
9252     if (STRCMP(eap->arg, "mswin") == 0)
9253     {
9254 	set_option_value((char_u *)"selection", 0L, (char_u *)"exclusive", 0);
9255 	set_option_value((char_u *)"selectmode", 0L, (char_u *)"mouse,key", 0);
9256 	set_option_value((char_u *)"mousemodel", 0L, (char_u *)"popup", 0);
9257 	set_option_value((char_u *)"keymodel", 0L,
9258 					     (char_u *)"startsel,stopsel", 0);
9259     }
9260     else if (STRCMP(eap->arg, "xterm") == 0)
9261     {
9262 	set_option_value((char_u *)"selection", 0L, (char_u *)"inclusive", 0);
9263 	set_option_value((char_u *)"selectmode", 0L, (char_u *)"", 0);
9264 	set_option_value((char_u *)"mousemodel", 0L, (char_u *)"extend", 0);
9265 	set_option_value((char_u *)"keymodel", 0L, (char_u *)"", 0);
9266     }
9267     else
9268 	semsg(_(e_invarg2), eap->arg);
9269 }
9270 
9271 static int filetype_detect = FALSE;
9272 static int filetype_plugin = FALSE;
9273 static int filetype_indent = FALSE;
9274 
9275 /*
9276  * ":filetype [plugin] [indent] {on,off,detect}"
9277  * on: Load the filetype.vim file to install autocommands for file types.
9278  * off: Load the ftoff.vim file to remove all autocommands for file types.
9279  * plugin on: load filetype.vim and ftplugin.vim
9280  * plugin off: load ftplugof.vim
9281  * indent on: load filetype.vim and indent.vim
9282  * indent off: load indoff.vim
9283  */
9284     static void
9285 ex_filetype(exarg_T *eap)
9286 {
9287     char_u	*arg = eap->arg;
9288     int		plugin = FALSE;
9289     int		indent = FALSE;
9290 
9291     if (*eap->arg == NUL)
9292     {
9293 	// Print current status.
9294 	smsg("filetype detection:%s  plugin:%s  indent:%s",
9295 		filetype_detect ? "ON" : "OFF",
9296 		filetype_plugin ? (filetype_detect ? "ON" : "(on)") : "OFF",
9297 		filetype_indent ? (filetype_detect ? "ON" : "(on)") : "OFF");
9298 	return;
9299     }
9300 
9301     // Accept "plugin" and "indent" in any order.
9302     for (;;)
9303     {
9304 	if (STRNCMP(arg, "plugin", 6) == 0)
9305 	{
9306 	    plugin = TRUE;
9307 	    arg = skipwhite(arg + 6);
9308 	    continue;
9309 	}
9310 	if (STRNCMP(arg, "indent", 6) == 0)
9311 	{
9312 	    indent = TRUE;
9313 	    arg = skipwhite(arg + 6);
9314 	    continue;
9315 	}
9316 	break;
9317     }
9318     if (STRCMP(arg, "on") == 0 || STRCMP(arg, "detect") == 0)
9319     {
9320 	if (*arg == 'o' || !filetype_detect)
9321 	{
9322 	    source_runtime((char_u *)FILETYPE_FILE, DIP_ALL);
9323 	    filetype_detect = TRUE;
9324 	    if (plugin)
9325 	    {
9326 		source_runtime((char_u *)FTPLUGIN_FILE, DIP_ALL);
9327 		filetype_plugin = TRUE;
9328 	    }
9329 	    if (indent)
9330 	    {
9331 		source_runtime((char_u *)INDENT_FILE, DIP_ALL);
9332 		filetype_indent = TRUE;
9333 	    }
9334 	}
9335 	if (*arg == 'd')
9336 	{
9337 	    (void)do_doautocmd((char_u *)"filetypedetect BufRead", TRUE, NULL);
9338 	    do_modelines(0);
9339 	}
9340     }
9341     else if (STRCMP(arg, "off") == 0)
9342     {
9343 	if (plugin || indent)
9344 	{
9345 	    if (plugin)
9346 	    {
9347 		source_runtime((char_u *)FTPLUGOF_FILE, DIP_ALL);
9348 		filetype_plugin = FALSE;
9349 	    }
9350 	    if (indent)
9351 	    {
9352 		source_runtime((char_u *)INDOFF_FILE, DIP_ALL);
9353 		filetype_indent = FALSE;
9354 	    }
9355 	}
9356 	else
9357 	{
9358 	    source_runtime((char_u *)FTOFF_FILE, DIP_ALL);
9359 	    filetype_detect = FALSE;
9360 	}
9361     }
9362     else
9363 	semsg(_(e_invarg2), arg);
9364 }
9365 
9366 /*
9367  * ":setfiletype [FALLBACK] {name}"
9368  */
9369     static void
9370 ex_setfiletype(exarg_T *eap)
9371 {
9372     if (!did_filetype)
9373     {
9374 	char_u *arg = eap->arg;
9375 
9376 	if (STRNCMP(arg, "FALLBACK ", 9) == 0)
9377 	    arg += 9;
9378 
9379 	set_option_value((char_u *)"filetype", 0L, arg, OPT_LOCAL);
9380 	if (arg != eap->arg)
9381 	    did_filetype = FALSE;
9382     }
9383 }
9384 
9385     static void
9386 ex_digraphs(exarg_T *eap UNUSED)
9387 {
9388 #ifdef FEAT_DIGRAPHS
9389     if (*eap->arg != NUL)
9390 	putdigraph(eap->arg);
9391     else
9392 	listdigraphs(eap->forceit);
9393 #else
9394     emsg(_(e_no_digraphs_version));
9395 #endif
9396 }
9397 
9398 #if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
9399     void
9400 set_no_hlsearch(int flag)
9401 {
9402     no_hlsearch = flag;
9403 # ifdef FEAT_EVAL
9404     set_vim_var_nr(VV_HLSEARCH, !no_hlsearch && p_hls);
9405 # endif
9406 }
9407 
9408 /*
9409  * ":nohlsearch"
9410  */
9411     static void
9412 ex_nohlsearch(exarg_T *eap UNUSED)
9413 {
9414     set_no_hlsearch(TRUE);
9415     redraw_all_later(SOME_VALID);
9416 }
9417 #endif
9418 
9419 #ifdef FEAT_CRYPT
9420 /*
9421  * ":X": Get crypt key
9422  */
9423     static void
9424 ex_X(exarg_T *eap UNUSED)
9425 {
9426     crypt_check_current_method();
9427     (void)crypt_get_key(TRUE, TRUE);
9428 }
9429 #endif
9430 
9431 #ifdef FEAT_FOLDING
9432     static void
9433 ex_fold(exarg_T *eap)
9434 {
9435     if (foldManualAllowed(TRUE))
9436 	foldCreate(eap->line1, eap->line2);
9437 }
9438 
9439     static void
9440 ex_foldopen(exarg_T *eap)
9441 {
9442     opFoldRange(eap->line1, eap->line2, eap->cmdidx == CMD_foldopen,
9443 							 eap->forceit, FALSE);
9444 }
9445 
9446     static void
9447 ex_folddo(exarg_T *eap)
9448 {
9449     linenr_T	lnum;
9450 
9451 # ifdef FEAT_CLIPBOARD
9452     start_global_changes();
9453 # endif
9454 
9455     // First set the marks for all lines closed/open.
9456     for (lnum = eap->line1; lnum <= eap->line2; ++lnum)
9457 	if (hasFolding(lnum, NULL, NULL) == (eap->cmdidx == CMD_folddoclosed))
9458 	    ml_setmarked(lnum);
9459 
9460     // Execute the command on the marked lines.
9461     global_exe(eap->arg);
9462     ml_clearmarked();	   // clear rest of the marks
9463 # ifdef FEAT_CLIPBOARD
9464     end_global_changes();
9465 # endif
9466 }
9467 #endif
9468 
9469 #if defined(FEAT_QUICKFIX) || defined(PROTO)
9470 /*
9471  * Returns TRUE if the supplied Ex cmdidx is for a location list command
9472  * instead of a quickfix command.
9473  */
9474     int
9475 is_loclist_cmd(int cmdidx)
9476 {
9477     if (cmdidx < 0 || cmdidx >= CMD_SIZE)
9478 	return FALSE;
9479     return cmdnames[cmdidx].cmd_name[0] == 'l';
9480 }
9481 #endif
9482 
9483 #if defined(FEAT_TIMERS) || defined(PROTO)
9484     int
9485 get_pressedreturn(void)
9486 {
9487     return ex_pressedreturn;
9488 }
9489 
9490     void
9491 set_pressedreturn(int val)
9492 {
9493      ex_pressedreturn = val;
9494 }
9495 #endif
9496