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