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