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/// @file ex_cmds2.c
5///
6/// Some more functions for command line commands
7
8#include <assert.h>
9#include <inttypes.h>
10#include <stdbool.h>
11#include <string.h>
12#include <fcntl.h>
13
14#include "nvim/vim.h"
15#include "nvim/ascii.h"
16#ifdef HAVE_LOCALE_H
17# include <locale.h>
18#endif
19#include "nvim/ex_cmds2.h"
20#include "nvim/buffer.h"
21#include "nvim/change.h"
22#include "nvim/charset.h"
23#include "nvim/eval.h"
24#include "nvim/ex_cmds.h"
25#include "nvim/ex_docmd.h"
26#include "nvim/ex_eval.h"
27#include "nvim/ex_getln.h"
28#include "nvim/fileio.h"
29#include "nvim/getchar.h"
30#include "nvim/mark.h"
31#include "nvim/mbyte.h"
32#include "nvim/message.h"
33#include "nvim/misc1.h"
34#include "nvim/garray.h"
35#include "nvim/memory.h"
36#include "nvim/move.h"
37#include "nvim/normal.h"
38#include "nvim/ops.h"
39#include "nvim/option.h"
40#include "nvim/os_unix.h"
41#include "nvim/path.h"
42#include "nvim/quickfix.h"
43#include "nvim/regexp.h"
44#include "nvim/screen.h"
45#include "nvim/strings.h"
46#include "nvim/undo.h"
47#include "nvim/version.h"
48#include "nvim/window.h"
49#include "nvim/profile.h"
50#include "nvim/os/os.h"
51#include "nvim/os/shell.h"
52#include "nvim/os/fs_defs.h"
53#include "nvim/api/private/helpers.h"
54#include "nvim/api/private/defs.h"
55
56
57/// Growarray to store info about already sourced scripts.
58/// Also store the dev/ino, so that we don't have to stat() each
59/// script when going through the list.
60typedef struct scriptitem_S {
61 char_u *sn_name;
62 bool file_id_valid;
63 FileID file_id;
64 bool sn_prof_on; ///< true when script is/was profiled
65 bool sn_pr_force; ///< forceit: profile functions in this script
66 proftime_T sn_pr_child; ///< time set when going into first child
67 int sn_pr_nest; ///< nesting for sn_pr_child
68 // profiling the script as a whole
69 int sn_pr_count; ///< nr of times sourced
70 proftime_T sn_pr_total; ///< time spent in script + children
71 proftime_T sn_pr_self; ///< time spent in script itself
72 proftime_T sn_pr_start; ///< time at script start
73 proftime_T sn_pr_children; ///< time in children after script start
74 // profiling the script per line
75 garray_T sn_prl_ga; ///< things stored for every line
76 proftime_T sn_prl_start; ///< start time for current line
77 proftime_T sn_prl_children; ///< time spent in children for this line
78 proftime_T sn_prl_wait; ///< wait start time for current line
79 linenr_T sn_prl_idx; ///< index of line being timed; -1 if none
80 int sn_prl_execed; ///< line being timed was executed
81} scriptitem_T;
82
83static garray_T script_items = { 0, 0, sizeof(scriptitem_T), 4, NULL };
84#define SCRIPT_ITEM(id) (((scriptitem_T *)script_items.ga_data)[(id) - 1])
85
86// Struct used in sn_prl_ga for every line of a script.
87typedef struct sn_prl_S {
88 int snp_count; ///< nr of times line was executed
89 proftime_T sn_prl_total; ///< time spent in a line + children
90 proftime_T sn_prl_self; ///< time spent in a line itself
91} sn_prl_T;
92
93/// Structure used to store info for each sourced file.
94/// It is shared between do_source() and getsourceline().
95/// This is required, because it needs to be handed to do_cmdline() and
96/// sourcing can be done recursively.
97struct source_cookie {
98 FILE *fp; ///< opened file for sourcing
99 char_u *nextline; ///< if not NULL: line that was read ahead
100 int finished; ///< ":finish" used
101#if defined(USE_CRNL)
102 int fileformat; ///< EOL_UNKNOWN, EOL_UNIX or EOL_DOS
103 bool error; ///< true if LF found after CR-LF
104#endif
105 linenr_T breakpoint; ///< next line with breakpoint or zero
106 char_u *fname; ///< name of sourced file
107 int dbg_tick; ///< debug_tick when breakpoint was set
108 int level; ///< top nesting level of sourced file
109 vimconv_T conv; ///< type of conversion
110};
111
112# define PRL_ITEM(si, idx) (((sn_prl_T *)(si)->sn_prl_ga.ga_data)[(idx)])
113
114#ifdef INCLUDE_GENERATED_DECLARATIONS
115# include "ex_cmds2.c.generated.h"
116#endif
117
118/// batch mode debugging: don't save and restore typeahead.
119static bool debug_greedy = false;
120
121/// Debug mode. Repeatedly get Ex commands, until told to continue normal
122/// execution.
123void do_debug(char_u *cmd)
124{
125 int save_msg_scroll = msg_scroll;
126 int save_State = State;
127 int save_did_emsg = did_emsg;
128 const bool save_cmd_silent = cmd_silent;
129 int save_msg_silent = msg_silent;
130 int save_emsg_silent = emsg_silent;
131 int save_redir_off = redir_off;
132 tasave_T typeaheadbuf;
133 bool typeahead_saved = false;
134 int save_ignore_script = 0;
135 int save_ex_normal_busy;
136 int n;
137 char_u *cmdline = NULL;
138 char_u *p;
139 char *tail = NULL;
140 static int last_cmd = 0;
141#define CMD_CONT 1
142#define CMD_NEXT 2
143#define CMD_STEP 3
144#define CMD_FINISH 4
145#define CMD_QUIT 5
146#define CMD_INTERRUPT 6
147#define CMD_BACKTRACE 7
148#define CMD_FRAME 8
149#define CMD_UP 9
150#define CMD_DOWN 10
151
152
153 RedrawingDisabled++; // don't redisplay the window
154 no_wait_return++; // don't wait for return
155 did_emsg = false; // don't use error from debugged stuff
156 cmd_silent = false; // display commands
157 msg_silent = false; // display messages
158 emsg_silent = false; // display error messages
159 redir_off = true; // don't redirect debug commands
160
161 State = NORMAL;
162 debug_mode = true;
163
164 if (!debug_did_msg) {
165 MSG(_("Entering Debug mode. Type \"cont\" to continue."));
166 }
167 if (sourcing_name != NULL) {
168 msg(sourcing_name);
169 }
170 if (sourcing_lnum != 0) {
171 smsg(_("line %" PRId64 ": %s"), (int64_t)sourcing_lnum, cmd);
172 } else {
173 smsg(_("cmd: %s"), cmd);
174 }
175
176 // Repeat getting a command and executing it.
177 for (;; ) {
178 msg_scroll = true;
179 need_wait_return = false;
180 // Save the current typeahead buffer and replace it with an empty one.
181 // This makes sure we get input from the user here and don't interfere
182 // with the commands being executed. Reset "ex_normal_busy" to avoid
183 // the side effects of using ":normal". Save the stuff buffer and make
184 // it empty. Set ignore_script to avoid reading from script input.
185 save_ex_normal_busy = ex_normal_busy;
186 ex_normal_busy = 0;
187 if (!debug_greedy) {
188 save_typeahead(&typeaheadbuf);
189 typeahead_saved = true;
190 save_ignore_script = ignore_script;
191 ignore_script = true;
192 }
193
194 xfree(cmdline);
195 cmdline = (char_u *)getcmdline_prompt('>', NULL, 0, EXPAND_NOTHING, NULL,
196 CALLBACK_NONE);
197
198 if (typeahead_saved) {
199 restore_typeahead(&typeaheadbuf);
200 ignore_script = save_ignore_script;
201 }
202 ex_normal_busy = save_ex_normal_busy;
203
204 cmdline_row = msg_row;
205 msg_starthere();
206 if (cmdline != NULL) {
207 // If this is a debug command, set "last_cmd".
208 // If not, reset "last_cmd".
209 // For a blank line use previous command.
210 p = skipwhite(cmdline);
211 if (*p != NUL) {
212 switch (*p) {
213 case 'c': last_cmd = CMD_CONT;
214 tail = "ont";
215 break;
216 case 'n': last_cmd = CMD_NEXT;
217 tail = "ext";
218 break;
219 case 's': last_cmd = CMD_STEP;
220 tail = "tep";
221 break;
222 case 'f':
223 last_cmd = 0;
224 if (p[1] == 'r') {
225 last_cmd = CMD_FRAME;
226 tail = "rame";
227 } else {
228 last_cmd = CMD_FINISH;
229 tail = "inish";
230 }
231 break;
232 case 'q': last_cmd = CMD_QUIT;
233 tail = "uit";
234 break;
235 case 'i': last_cmd = CMD_INTERRUPT;
236 tail = "nterrupt";
237 break;
238 case 'b':
239 last_cmd = CMD_BACKTRACE;
240 if (p[1] == 't') {
241 tail = "t";
242 } else {
243 tail = "acktrace";
244 }
245 break;
246 case 'w':
247 last_cmd = CMD_BACKTRACE;
248 tail = "here";
249 break;
250 case 'u':
251 last_cmd = CMD_UP;
252 tail = "p";
253 break;
254 case 'd':
255 last_cmd = CMD_DOWN;
256 tail = "own";
257 break;
258 default: last_cmd = 0;
259 }
260 if (last_cmd != 0) {
261 // Check that the tail matches.
262 p++;
263 while (*p != NUL && *p == *tail) {
264 p++;
265 tail++;
266 }
267 if (ASCII_ISALPHA(*p) && last_cmd != CMD_FRAME) {
268 last_cmd = 0;
269 }
270 }
271 }
272
273 if (last_cmd != 0) {
274 // Execute debug command: decided where to break next and return.
275 switch (last_cmd) {
276 case CMD_CONT:
277 debug_break_level = -1;
278 break;
279 case CMD_NEXT:
280 debug_break_level = ex_nesting_level;
281 break;
282 case CMD_STEP:
283 debug_break_level = 9999;
284 break;
285 case CMD_FINISH:
286 debug_break_level = ex_nesting_level - 1;
287 break;
288 case CMD_QUIT:
289 got_int = true;
290 debug_break_level = -1;
291 break;
292 case CMD_INTERRUPT:
293 got_int = true;
294 debug_break_level = 9999;
295 // Do not repeat ">interrupt" cmd, continue stepping.
296 last_cmd = CMD_STEP;
297 break;
298 case CMD_BACKTRACE:
299 do_showbacktrace(cmd);
300 continue;
301 case CMD_FRAME:
302 if (*p == NUL) {
303 do_showbacktrace(cmd);
304 } else {
305 p = skipwhite(p);
306 do_setdebugtracelevel(p);
307 }
308 continue;
309 case CMD_UP:
310 debug_backtrace_level++;
311 do_checkbacktracelevel();
312 continue;
313 case CMD_DOWN:
314 debug_backtrace_level--;
315 do_checkbacktracelevel();
316 continue;
317 }
318 // Going out reset backtrace_level
319 debug_backtrace_level = 0;
320 break;
321 }
322
323 // don't debug this command
324 n = debug_break_level;
325 debug_break_level = -1;
326 (void)do_cmdline(cmdline, getexline, NULL,
327 DOCMD_VERBOSE|DOCMD_EXCRESET);
328 debug_break_level = n;
329 }
330 lines_left = (int)(Rows - 1);
331 }
332 xfree(cmdline);
333
334 RedrawingDisabled--;
335 no_wait_return--;
336 redraw_all_later(NOT_VALID);
337 need_wait_return = false;
338 msg_scroll = save_msg_scroll;
339 lines_left = (int)(Rows - 1);
340 State = save_State;
341 debug_mode = false;
342 did_emsg = save_did_emsg;
343 cmd_silent = save_cmd_silent;
344 msg_silent = save_msg_silent;
345 emsg_silent = save_emsg_silent;
346 redir_off = save_redir_off;
347
348 // Only print the message again when typing a command before coming back here.
349 debug_did_msg = true;
350}
351
352static int get_maxbacktrace_level(void)
353{
354 int maxbacktrace = 0;
355
356 if (sourcing_name != NULL) {
357 char *p = (char *)sourcing_name;
358 char *q;
359 while ((q = strstr(p, "..")) != NULL) {
360 p = q + 2;
361 maxbacktrace++;
362 }
363 }
364 return maxbacktrace;
365}
366
367static void do_setdebugtracelevel(char_u *arg)
368{
369 int level = atoi((char *)arg);
370 if (*arg == '+' || level < 0) {
371 debug_backtrace_level += level;
372 } else {
373 debug_backtrace_level = level;
374 }
375
376 do_checkbacktracelevel();
377}
378
379static void do_checkbacktracelevel(void)
380{
381 if (debug_backtrace_level < 0) {
382 debug_backtrace_level = 0;
383 MSG(_("frame is zero"));
384 } else {
385 int max = get_maxbacktrace_level();
386 if (debug_backtrace_level > max) {
387 debug_backtrace_level = max;
388 smsg(_("frame at highest level: %d"), max);
389 }
390 }
391}
392
393static void do_showbacktrace(char_u *cmd)
394{
395 if (sourcing_name != NULL) {
396 int i = 0;
397 int max = get_maxbacktrace_level();
398 char *cur = (char *)sourcing_name;
399 while (!got_int) {
400 char *next = strstr(cur, "..");
401 if (next != NULL) {
402 *next = NUL;
403 }
404 if (i == max - debug_backtrace_level) {
405 smsg("->%d %s", max - i, cur);
406 } else {
407 smsg(" %d %s", max - i, cur);
408 }
409 i++;
410 if (next == NULL) {
411 break;
412 }
413 *next = '.';
414 cur = next + 2;
415 }
416 }
417 if (sourcing_lnum != 0) {
418 smsg(_("line %" PRId64 ": %s"), (int64_t)sourcing_lnum, cmd);
419 } else {
420 smsg(_("cmd: %s"), cmd);
421 }
422}
423
424
425/// ":debug".
426void ex_debug(exarg_T *eap)
427{
428 int debug_break_level_save = debug_break_level;
429
430 debug_break_level = 9999;
431 do_cmdline_cmd((char *)eap->arg);
432 debug_break_level = debug_break_level_save;
433}
434
435static char_u *debug_breakpoint_name = NULL;
436static linenr_T debug_breakpoint_lnum;
437
438/// When debugging or a breakpoint is set on a skipped command, no debug prompt
439/// is shown by do_one_cmd(). This situation is indicated by debug_skipped, and
440/// debug_skipped_name is then set to the source name in the breakpoint case. If
441/// a skipped command decides itself that a debug prompt should be displayed, it
442/// can do so by calling dbg_check_skipped().
443static int debug_skipped;
444static char_u *debug_skipped_name;
445
446/// Go to debug mode when a breakpoint was encountered or "ex_nesting_level" is
447/// at or below the break level. But only when the line is actually
448/// executed. Return true and set breakpoint_name for skipped commands that
449/// decide to execute something themselves.
450/// Called from do_one_cmd() before executing a command.
451void dbg_check_breakpoint(exarg_T *eap)
452{
453 char_u *p;
454
455 debug_skipped = false;
456 if (debug_breakpoint_name != NULL) {
457 if (!eap->skip) {
458 // replace K_SNR with "<SNR>"
459 if (debug_breakpoint_name[0] == K_SPECIAL
460 && debug_breakpoint_name[1] == KS_EXTRA
461 && debug_breakpoint_name[2] == (int)KE_SNR) {
462 p = (char_u *)"<SNR>";
463 } else {
464 p = (char_u *)"";
465 }
466 smsg(_("Breakpoint in \"%s%s\" line %" PRId64),
467 p,
468 debug_breakpoint_name + (*p == NUL ? 0 : 3),
469 (int64_t)debug_breakpoint_lnum);
470 debug_breakpoint_name = NULL;
471 do_debug(eap->cmd);
472 } else {
473 debug_skipped = true;
474 debug_skipped_name = debug_breakpoint_name;
475 debug_breakpoint_name = NULL;
476 }
477 } else if (ex_nesting_level <= debug_break_level) {
478 if (!eap->skip) {
479 do_debug(eap->cmd);
480 } else {
481 debug_skipped = true;
482 debug_skipped_name = NULL;
483 }
484 }
485}
486
487/// Go to debug mode if skipped by dbg_check_breakpoint() because eap->skip was
488/// set.
489///
490/// @return true when the debug mode is entered this time.
491bool dbg_check_skipped(exarg_T *eap)
492{
493 int prev_got_int;
494
495 if (debug_skipped) {
496 // Save the value of got_int and reset it. We don't want a previous
497 // interruption cause flushing the input buffer.
498 prev_got_int = got_int;
499 got_int = false;
500 debug_breakpoint_name = debug_skipped_name;
501 // eap->skip is true
502 eap->skip = false;
503 dbg_check_breakpoint(eap);
504 eap->skip = true;
505 got_int |= prev_got_int;
506 return true;
507 }
508 return false;
509}
510
511/// The list of breakpoints: dbg_breakp.
512/// This is a grow-array of structs.
513struct debuggy {
514 int dbg_nr; ///< breakpoint number
515 int dbg_type; ///< DBG_FUNC or DBG_FILE
516 char_u *dbg_name; ///< function or file name
517 regprog_T *dbg_prog; ///< regexp program
518 linenr_T dbg_lnum; ///< line number in function or file
519 int dbg_forceit; ///< ! used
520};
521
522static garray_T dbg_breakp = { 0, 0, sizeof(struct debuggy), 4, NULL };
523#define BREAKP(idx) (((struct debuggy *)dbg_breakp.ga_data)[idx])
524#define DEBUGGY(gap, idx) (((struct debuggy *)gap->ga_data)[idx])
525static int last_breakp = 0; // nr of last defined breakpoint
526
527// Profiling uses file and func names similar to breakpoints.
528static garray_T prof_ga = { 0, 0, sizeof(struct debuggy), 4, NULL };
529#define DBG_FUNC 1
530#define DBG_FILE 2
531
532
533/// Parse the arguments of ":profile", ":breakadd" or ":breakdel" and put them
534/// in the entry just after the last one in dbg_breakp. Note that "dbg_name"
535/// is allocated.
536/// Returns FAIL for failure.
537///
538/// @param arg
539/// @param gap either &dbg_breakp or &prof_ga
540static int dbg_parsearg(char_u *arg, garray_T *gap)
541{
542 char_u *p = arg;
543 char_u *q;
544 struct debuggy *bp;
545 bool here = false;
546
547 ga_grow(gap, 1);
548
549 bp = &DEBUGGY(gap, gap->ga_len);
550
551 // Find "func" or "file".
552 if (STRNCMP(p, "func", 4) == 0) {
553 bp->dbg_type = DBG_FUNC;
554 } else if (STRNCMP(p, "file", 4) == 0) {
555 bp->dbg_type = DBG_FILE;
556 } else if (gap != &prof_ga && STRNCMP(p, "here", 4) == 0) {
557 if (curbuf->b_ffname == NULL) {
558 EMSG(_(e_noname));
559 return FAIL;
560 }
561 bp->dbg_type = DBG_FILE;
562 here = true;
563 } else {
564 EMSG2(_(e_invarg2), p);
565 return FAIL;
566 }
567 p = skipwhite(p + 4);
568
569 // Find optional line number.
570 if (here) {
571 bp->dbg_lnum = curwin->w_cursor.lnum;
572 } else if (gap != &prof_ga && ascii_isdigit(*p)) {
573 bp->dbg_lnum = getdigits_long(&p, true, 0);
574 p = skipwhite(p);
575 } else {
576 bp->dbg_lnum = 0;
577 }
578
579 // Find the function or file name. Don't accept a function name with ().
580 if ((!here && *p == NUL)
581 || (here && *p != NUL)
582 || (bp->dbg_type == DBG_FUNC && strstr((char *)p, "()") != NULL)) {
583 EMSG2(_(e_invarg2), arg);
584 return FAIL;
585 }
586
587 if (bp->dbg_type == DBG_FUNC) {
588 bp->dbg_name = vim_strsave(p);
589 } else if (here) {
590 bp->dbg_name = vim_strsave(curbuf->b_ffname);
591 } else {
592 // Expand the file name in the same way as do_source(). This means
593 // doing it twice, so that $DIR/file gets expanded when $DIR is
594 // "~/dir".
595 q = expand_env_save(p);
596 if (q == NULL) {
597 return FAIL;
598 }
599 p = expand_env_save(q);
600 xfree(q);
601 if (p == NULL) {
602 return FAIL;
603 }
604 if (*p != '*') {
605 bp->dbg_name = (char_u *)fix_fname((char *)p);
606 xfree(p);
607 } else {
608 bp->dbg_name = p;
609 }
610 }
611
612 if (bp->dbg_name == NULL) {
613 return FAIL;
614 }
615 return OK;
616}
617
618/// ":breakadd". Also used for ":profile".
619void ex_breakadd(exarg_T *eap)
620{
621 struct debuggy *bp;
622 char_u *pat;
623 garray_T *gap;
624
625 gap = &dbg_breakp;
626 if (eap->cmdidx == CMD_profile) {
627 gap = &prof_ga;
628 }
629
630 if (dbg_parsearg(eap->arg, gap) == OK) {
631 bp = &DEBUGGY(gap, gap->ga_len);
632 bp->dbg_forceit = eap->forceit;
633
634 pat = file_pat_to_reg_pat(bp->dbg_name, NULL, NULL, false);
635 if (pat != NULL) {
636 bp->dbg_prog = vim_regcomp(pat, RE_MAGIC + RE_STRING);
637 xfree(pat);
638 }
639 if (pat == NULL || bp->dbg_prog == NULL) {
640 xfree(bp->dbg_name);
641 } else {
642 if (bp->dbg_lnum == 0) { // default line number is 1
643 bp->dbg_lnum = 1;
644 }
645 if (eap->cmdidx != CMD_profile) {
646 DEBUGGY(gap, gap->ga_len).dbg_nr = ++last_breakp;
647 debug_tick++;
648 }
649 gap->ga_len++;
650 }
651 }
652}
653
654/// ":debuggreedy".
655void ex_debuggreedy(exarg_T *eap)
656{
657 if (eap->addr_count == 0 || eap->line2 != 0) {
658 debug_greedy = true;
659 } else {
660 debug_greedy = false;
661 }
662}
663
664/// ":breakdel" and ":profdel".
665void ex_breakdel(exarg_T *eap)
666{
667 struct debuggy *bp, *bpi;
668 int nr;
669 int todel = -1;
670 bool del_all = false;
671 linenr_T best_lnum = 0;
672 garray_T *gap;
673
674 gap = &dbg_breakp;
675 if (eap->cmdidx == CMD_profdel) {
676 gap = &prof_ga;
677 }
678
679 if (ascii_isdigit(*eap->arg)) {
680 // ":breakdel {nr}"
681 nr = atoi((char *)eap->arg);
682 for (int i = 0; i < gap->ga_len; i++) {
683 if (DEBUGGY(gap, i).dbg_nr == nr) {
684 todel = i;
685 break;
686 }
687 }
688 } else if (*eap->arg == '*') {
689 todel = 0;
690 del_all = true;
691 } else {
692 // ":breakdel {func|file} [lnum] {name}"
693 if (dbg_parsearg(eap->arg, gap) == FAIL) {
694 return;
695 }
696 bp = &DEBUGGY(gap, gap->ga_len);
697 for (int i = 0; i < gap->ga_len; i++) {
698 bpi = &DEBUGGY(gap, i);
699 if (bp->dbg_type == bpi->dbg_type
700 && STRCMP(bp->dbg_name, bpi->dbg_name) == 0
701 && (bp->dbg_lnum == bpi->dbg_lnum
702 || (bp->dbg_lnum == 0
703 && (best_lnum == 0
704 || bpi->dbg_lnum < best_lnum)))) {
705 todel = i;
706 best_lnum = bpi->dbg_lnum;
707 }
708 }
709 xfree(bp->dbg_name);
710 }
711
712 if (todel < 0) {
713 EMSG2(_("E161: Breakpoint not found: %s"), eap->arg);
714 } else {
715 while (!GA_EMPTY(gap)) {
716 xfree(DEBUGGY(gap, todel).dbg_name);
717 vim_regfree(DEBUGGY(gap, todel).dbg_prog);
718 gap->ga_len--;
719 if (todel < gap->ga_len) {
720 memmove(&DEBUGGY(gap, todel), &DEBUGGY(gap, todel + 1),
721 (size_t)(gap->ga_len - todel) * sizeof(struct debuggy));
722 }
723 if (eap->cmdidx == CMD_breakdel) {
724 debug_tick++;
725 }
726 if (!del_all) {
727 break;
728 }
729 }
730
731 // If all breakpoints were removed clear the array.
732 if (GA_EMPTY(gap)) {
733 ga_clear(gap);
734 }
735 }
736}
737
738/// ":breaklist".
739void ex_breaklist(exarg_T *eap)
740{
741 struct debuggy *bp;
742
743 if (GA_EMPTY(&dbg_breakp)) {
744 MSG(_("No breakpoints defined"));
745 } else {
746 for (int i = 0; i < dbg_breakp.ga_len; i++) {
747 bp = &BREAKP(i);
748 if (bp->dbg_type == DBG_FILE) {
749 home_replace(NULL, bp->dbg_name, NameBuff, MAXPATHL, true);
750 }
751 smsg(_("%3d %s %s line %" PRId64),
752 bp->dbg_nr,
753 bp->dbg_type == DBG_FUNC ? "func" : "file",
754 bp->dbg_type == DBG_FUNC ? bp->dbg_name : NameBuff,
755 (int64_t)bp->dbg_lnum);
756 }
757 }
758}
759
760/// Find a breakpoint for a function or sourced file.
761/// Returns line number at which to break; zero when no matching breakpoint.
762linenr_T
763dbg_find_breakpoint(
764 bool file, // true for a file, false for a function
765 char_u *fname, // file or function name
766 linenr_T after // after this line number
767)
768{
769 return debuggy_find(file, fname, after, &dbg_breakp, NULL);
770}
771
772/// @param file true for a file, false for a function
773/// @param fname file or function name
774/// @param fp[out] forceit
775///
776/// @returns true if profiling is on for a function or sourced file.
777bool has_profiling(bool file, char_u *fname, bool *fp)
778{
779 return debuggy_find(file, fname, (linenr_T)0, &prof_ga, fp)
780 != (linenr_T)0;
781}
782
783/// Common code for dbg_find_breakpoint() and has_profiling().
784static linenr_T
785debuggy_find(
786 bool file, // true for a file, false for a function
787 char_u *fname, // file or function name
788 linenr_T after, // after this line number
789 garray_T *gap, // either &dbg_breakp or &prof_ga
790 bool *fp // if not NULL: return forceit
791)
792{
793 struct debuggy *bp;
794 linenr_T lnum = 0;
795 char_u *name = fname;
796 int prev_got_int;
797
798 // Return quickly when there are no breakpoints.
799 if (GA_EMPTY(gap)) {
800 return (linenr_T)0;
801 }
802
803 // Replace K_SNR in function name with "<SNR>".
804 if (!file && fname[0] == K_SPECIAL) {
805 name = xmalloc(STRLEN(fname) + 3);
806 STRCPY(name, "<SNR>");
807 STRCPY(name + 5, fname + 3);
808 }
809
810 for (int i = 0; i < gap->ga_len; i++) {
811 // Skip entries that are not useful or are for a line that is beyond
812 // an already found breakpoint.
813 bp = &DEBUGGY(gap, i);
814 if ((bp->dbg_type == DBG_FILE) == file
815 && (gap == &prof_ga
816 || (bp->dbg_lnum > after && (lnum == 0 || bp->dbg_lnum < lnum)))) {
817 // Save the value of got_int and reset it. We don't want a
818 // previous interruption cancel matching, only hitting CTRL-C
819 // while matching should abort it.
820 prev_got_int = got_int;
821 got_int = false;
822 if (vim_regexec_prog(&bp->dbg_prog, false, name, (colnr_T)0)) {
823 lnum = bp->dbg_lnum;
824 if (fp != NULL) {
825 *fp = bp->dbg_forceit;
826 }
827 }
828 got_int |= prev_got_int;
829 }
830 }
831 if (name != fname) {
832 xfree(name);
833 }
834
835 return lnum;
836}
837
838/// Called when a breakpoint was encountered.
839void dbg_breakpoint(char_u *name, linenr_T lnum)
840{
841 // We need to check if this line is actually executed in do_one_cmd()
842 debug_breakpoint_name = name;
843 debug_breakpoint_lnum = lnum;
844}
845
846static char_u *profile_fname = NULL;
847
848/// ":profile cmd args"
849void ex_profile(exarg_T *eap)
850{
851 static proftime_T pause_time;
852
853 char_u *e;
854 int len;
855
856 e = skiptowhite(eap->arg);
857 len = (int)(e - eap->arg);
858 e = skipwhite(e);
859
860 if (len == 5 && STRNCMP(eap->arg, "start", 5) == 0 && *e != NUL) {
861 xfree(profile_fname);
862 profile_fname = expand_env_save_opt(e, true);
863 do_profiling = PROF_YES;
864 profile_set_wait(profile_zero());
865 set_vim_var_nr(VV_PROFILING, 1L);
866 } else if (do_profiling == PROF_NONE) {
867 EMSG(_("E750: First use \":profile start {fname}\""));
868 } else if (STRCMP(eap->arg, "stop") == 0) {
869 profile_dump();
870 do_profiling = PROF_NONE;
871 set_vim_var_nr(VV_PROFILING, 0L);
872 profile_reset();
873 } else if (STRCMP(eap->arg, "pause") == 0) {
874 if (do_profiling == PROF_YES) {
875 pause_time = profile_start();
876 }
877 do_profiling = PROF_PAUSED;
878 } else if (STRCMP(eap->arg, "continue") == 0) {
879 if (do_profiling == PROF_PAUSED) {
880 pause_time = profile_end(pause_time);
881 profile_set_wait(profile_add(profile_get_wait(), pause_time));
882 }
883 do_profiling = PROF_YES;
884 } else if (STRCMP(eap->arg, "dump") == 0) {
885 profile_dump();
886 } else {
887 // The rest is similar to ":breakadd".
888 ex_breakadd(eap);
889 }
890}
891
892void ex_python(exarg_T *eap)
893{
894 script_host_execute("python", eap);
895}
896
897void ex_pyfile(exarg_T *eap)
898{
899 script_host_execute_file("python", eap);
900}
901
902void ex_pydo(exarg_T *eap)
903{
904 script_host_do_range("python", eap);
905}
906
907void ex_ruby(exarg_T *eap)
908{
909 script_host_execute("ruby", eap);
910}
911
912void ex_rubyfile(exarg_T *eap)
913{
914 script_host_execute_file("ruby", eap);
915}
916
917void ex_rubydo(exarg_T *eap)
918{
919 script_host_do_range("ruby", eap);
920}
921
922void ex_python3(exarg_T *eap)
923{
924 script_host_execute("python3", eap);
925}
926
927void ex_py3file(exarg_T *eap)
928{
929 script_host_execute_file("python3", eap);
930}
931
932void ex_pydo3(exarg_T *eap)
933{
934 script_host_do_range("python3", eap);
935}
936
937// Command line expansion for :profile.
938static enum {
939 PEXP_SUBCMD, ///< expand :profile sub-commands
940 PEXP_FUNC ///< expand :profile func {funcname}
941} pexpand_what;
942
943static char *pexpand_cmds[] = {
944 "continue",
945 "dump",
946 "file",
947 "func",
948 "pause",
949 "start",
950 "stop",
951 NULL
952};
953
954/// Function given to ExpandGeneric() to obtain the profile command
955/// specific expansion.
956char_u *get_profile_name(expand_T *xp, int idx)
957{
958 switch (pexpand_what) {
959 case PEXP_SUBCMD:
960 return (char_u *)pexpand_cmds[idx];
961 // case PEXP_FUNC: TODO
962 default:
963 return NULL;
964 }
965}
966
967/// Handle command line completion for :profile command.
968void set_context_in_profile_cmd(expand_T *xp, const char *arg)
969{
970 // Default: expand subcommands.
971 xp->xp_context = EXPAND_PROFILE;
972 pexpand_what = PEXP_SUBCMD;
973 xp->xp_pattern = (char_u *)arg;
974
975 char_u *const end_subcmd = skiptowhite((const char_u *)arg);
976 if (*end_subcmd == NUL) {
977 return;
978 }
979
980 if ((const char *)end_subcmd - arg == 5 && strncmp(arg, "start", 5) == 0) {
981 xp->xp_context = EXPAND_FILES;
982 xp->xp_pattern = skipwhite((const char_u *)end_subcmd);
983 return;
984 }
985
986 // TODO(tarruda): expand function names after "func"
987 xp->xp_context = EXPAND_NOTHING;
988}
989
990/// Dump the profiling info.
991void profile_dump(void)
992{
993 FILE *fd;
994
995 if (profile_fname != NULL) {
996 fd = os_fopen((char *)profile_fname, "w");
997 if (fd == NULL) {
998 EMSG2(_(e_notopen), profile_fname);
999 } else {
1000 script_dump_profile(fd);
1001 func_dump_profile(fd);
1002 fclose(fd);
1003 }
1004 }
1005}
1006
1007/// Reset all profiling information.
1008static void profile_reset(void)
1009{
1010 // Reset sourced files.
1011 for (int id = 1; id <= script_items.ga_len; id++) {
1012 scriptitem_T *si = &SCRIPT_ITEM(id);
1013 if (si->sn_prof_on) {
1014 si->sn_prof_on = false;
1015 si->sn_pr_force = false;
1016 si->sn_pr_child = profile_zero();
1017 si->sn_pr_nest = 0;
1018 si->sn_pr_count = 0;
1019 si->sn_pr_total = profile_zero();
1020 si->sn_pr_self = profile_zero();
1021 si->sn_pr_start = profile_zero();
1022 si->sn_pr_children = profile_zero();
1023 ga_clear(&si->sn_prl_ga);
1024 si->sn_prl_start = profile_zero();
1025 si->sn_prl_children = profile_zero();
1026 si->sn_prl_wait = profile_zero();
1027 si->sn_prl_idx = -1;
1028 si->sn_prl_execed = 0;
1029 }
1030 }
1031
1032 // Reset functions.
1033 size_t n = func_hashtab.ht_used;
1034 hashitem_T *hi = func_hashtab.ht_array;
1035
1036 for (; n > (size_t)0; hi++) {
1037 if (!HASHITEM_EMPTY(hi)) {
1038 n--;
1039 ufunc_T *uf = HI2UF(hi);
1040 if (uf->uf_profiling) {
1041 uf->uf_profiling = 0;
1042 uf->uf_tm_count = 0;
1043 uf->uf_tm_total = profile_zero();
1044 uf->uf_tm_self = profile_zero();
1045 uf->uf_tm_children = profile_zero();
1046
1047 XFREE_CLEAR(uf->uf_tml_count);
1048 XFREE_CLEAR(uf->uf_tml_total);
1049 XFREE_CLEAR(uf->uf_tml_self);
1050
1051 uf->uf_tml_start = profile_zero();
1052 uf->uf_tml_children = profile_zero();
1053 uf->uf_tml_wait = profile_zero();
1054 uf->uf_tml_idx = -1;
1055 uf->uf_tml_execed = 0;
1056 }
1057 }
1058 }
1059
1060 XFREE_CLEAR(profile_fname);
1061}
1062
1063/// Start profiling a script.
1064static void profile_init(scriptitem_T *si)
1065{
1066 si->sn_pr_count = 0;
1067 si->sn_pr_total = profile_zero();
1068 si->sn_pr_self = profile_zero();
1069
1070 ga_init(&si->sn_prl_ga, sizeof(sn_prl_T), 100);
1071 si->sn_prl_idx = -1;
1072 si->sn_prof_on = true;
1073 si->sn_pr_nest = 0;
1074}
1075
1076/// Save time when starting to invoke another script or function.
1077void script_prof_save(
1078 proftime_T *tm // place to store wait time
1079)
1080{
1081 scriptitem_T *si;
1082
1083 if (current_sctx.sc_sid > 0 && current_sctx.sc_sid <= script_items.ga_len) {
1084 si = &SCRIPT_ITEM(current_sctx.sc_sid);
1085 if (si->sn_prof_on && si->sn_pr_nest++ == 0) {
1086 si->sn_pr_child = profile_start();
1087 }
1088 }
1089 *tm = profile_get_wait();
1090}
1091
1092/// Count time spent in children after invoking another script or function.
1093void script_prof_restore(proftime_T *tm)
1094{
1095 scriptitem_T *si;
1096
1097 if (current_sctx.sc_sid > 0 && current_sctx.sc_sid <= script_items.ga_len) {
1098 si = &SCRIPT_ITEM(current_sctx.sc_sid);
1099 if (si->sn_prof_on && --si->sn_pr_nest == 0) {
1100 si->sn_pr_child = profile_end(si->sn_pr_child);
1101 // don't count wait time
1102 si->sn_pr_child = profile_sub_wait(*tm, si->sn_pr_child);
1103 si->sn_pr_children = profile_add(si->sn_pr_children, si->sn_pr_child);
1104 si->sn_prl_children = profile_add(si->sn_prl_children, si->sn_pr_child);
1105 }
1106 }
1107}
1108
1109static proftime_T inchar_time;
1110
1111/// Called when starting to wait for the user to type a character.
1112void prof_inchar_enter(void)
1113{
1114 inchar_time = profile_start();
1115}
1116
1117/// Called when finished waiting for the user to type a character.
1118void prof_inchar_exit(void)
1119{
1120 inchar_time = profile_end(inchar_time);
1121 profile_set_wait(profile_add(profile_get_wait(), inchar_time));
1122}
1123
1124/// Dump the profiling results for all scripts in file "fd".
1125static void script_dump_profile(FILE *fd)
1126{
1127 scriptitem_T *si;
1128 FILE *sfd;
1129 sn_prl_T *pp;
1130
1131 for (int id = 1; id <= script_items.ga_len; id++) {
1132 si = &SCRIPT_ITEM(id);
1133 if (si->sn_prof_on) {
1134 fprintf(fd, "SCRIPT %s\n", si->sn_name);
1135 if (si->sn_pr_count == 1) {
1136 fprintf(fd, "Sourced 1 time\n");
1137 } else {
1138 fprintf(fd, "Sourced %d times\n", si->sn_pr_count);
1139 }
1140 fprintf(fd, "Total time: %s\n", profile_msg(si->sn_pr_total));
1141 fprintf(fd, " Self time: %s\n", profile_msg(si->sn_pr_self));
1142 fprintf(fd, "\n");
1143 fprintf(fd, "count total (s) self (s)\n");
1144
1145 sfd = os_fopen((char *)si->sn_name, "r");
1146 if (sfd == NULL) {
1147 fprintf(fd, "Cannot open file!\n");
1148 } else {
1149 // Keep going till the end of file, so that trailing
1150 // continuation lines are listed.
1151 for (int i = 0; ; i++) {
1152 if (vim_fgets(IObuff, IOSIZE, sfd)) {
1153 break;
1154 }
1155 // When a line has been truncated, append NL, taking care
1156 // of multi-byte characters .
1157 if (IObuff[IOSIZE - 2] != NUL && IObuff[IOSIZE - 2] != NL) {
1158 int n = IOSIZE - 2;
1159
1160 // Move to the first byte of this char.
1161 // utf_head_off() doesn't work, because it checks
1162 // for a truncated character.
1163 while (n > 0 && (IObuff[n] & 0xc0) == 0x80) {
1164 n--;
1165 }
1166
1167 IObuff[n] = NL;
1168 IObuff[n + 1] = NUL;
1169 }
1170 if (i < si->sn_prl_ga.ga_len
1171 && (pp = &PRL_ITEM(si, i))->snp_count > 0) {
1172 fprintf(fd, "%5d ", pp->snp_count);
1173 if (profile_equal(pp->sn_prl_total, pp->sn_prl_self)) {
1174 fprintf(fd, " ");
1175 } else {
1176 fprintf(fd, "%s ", profile_msg(pp->sn_prl_total));
1177 }
1178 fprintf(fd, "%s ", profile_msg(pp->sn_prl_self));
1179 } else {
1180 fprintf(fd, " ");
1181 }
1182 fprintf(fd, "%s", IObuff);
1183 }
1184 fclose(sfd);
1185 }
1186 fprintf(fd, "\n");
1187 }
1188 }
1189}
1190
1191/// Return true when a function defined in the current script should be
1192/// profiled.
1193bool prof_def_func(void)
1194{
1195 if (current_sctx.sc_sid > 0) {
1196 return SCRIPT_ITEM(current_sctx.sc_sid).sn_pr_force;
1197 }
1198 return false;
1199}
1200
1201/// If 'autowrite' option set, try to write the file.
1202/// Careful: autocommands may make "buf" invalid!
1203///
1204/// @return FAIL for failure, OK otherwise
1205int autowrite(buf_T *buf, int forceit)
1206{
1207 int r;
1208 bufref_T bufref;
1209
1210 if (!(p_aw || p_awa) || !p_write
1211 // never autowrite a "nofile" or "nowrite" buffer
1212 || bt_dontwrite(buf)
1213 || (!forceit && buf->b_p_ro) || buf->b_ffname == NULL) {
1214 return FAIL;
1215 }
1216 set_bufref(&bufref, buf);
1217 r = buf_write_all(buf, forceit);
1218
1219 // Writing may succeed but the buffer still changed, e.g., when there is a
1220 // conversion error. We do want to return FAIL then.
1221 if (bufref_valid(&bufref) && bufIsChanged(buf)) {
1222 r = FAIL;
1223 }
1224 return r;
1225}
1226
1227/// Flush all buffers, except the ones that are readonly or are never written.
1228void autowrite_all(void)
1229{
1230 if (!(p_aw || p_awa) || !p_write) {
1231 return;
1232 }
1233
1234 FOR_ALL_BUFFERS(buf) {
1235 if (bufIsChanged(buf) && !buf->b_p_ro && !bt_dontwrite(buf)) {
1236 bufref_T bufref;
1237 set_bufref(&bufref, buf);
1238 (void)buf_write_all(buf, false);
1239 // an autocommand may have deleted the buffer
1240 if (!bufref_valid(&bufref)) {
1241 buf = firstbuf;
1242 }
1243 }
1244 }
1245}
1246
1247/// Return true if buffer was changed and cannot be abandoned.
1248/// For flags use the CCGD_ values.
1249bool check_changed(buf_T *buf, int flags)
1250{
1251 int forceit = (flags & CCGD_FORCEIT);
1252 bufref_T bufref;
1253 set_bufref(&bufref, buf);
1254
1255 if (!forceit
1256 && bufIsChanged(buf)
1257 && ((flags & CCGD_MULTWIN) || buf->b_nwindows <= 1)
1258 && (!(flags & CCGD_AW) || autowrite(buf, forceit) == FAIL)) {
1259 if ((p_confirm || cmdmod.confirm) && p_write) {
1260 int count = 0;
1261
1262 if (flags & CCGD_ALLBUF) {
1263 FOR_ALL_BUFFERS(buf2) {
1264 if (bufIsChanged(buf2) && (buf2->b_ffname != NULL)) {
1265 count++;
1266 }
1267 }
1268 }
1269 if (!bufref_valid(&bufref)) {
1270 // Autocommand deleted buffer, oops! It's not changed now.
1271 return false;
1272 }
1273 dialog_changed(buf, count > 1);
1274 if (!bufref_valid(&bufref)) {
1275 // Autocommand deleted buffer, oops! It's not changed now.
1276 return false;
1277 }
1278 return bufIsChanged(buf);
1279 }
1280 if (flags & CCGD_EXCMD) {
1281 no_write_message();
1282 } else {
1283 no_write_message_nobang();
1284 }
1285 return true;
1286 }
1287 return false;
1288}
1289
1290
1291
1292/// Ask the user what to do when abandoning a changed buffer.
1293/// Must check 'write' option first!
1294///
1295/// @param buf
1296/// @param checkall may abandon all changed buffers
1297void dialog_changed(buf_T *buf, bool checkall)
1298{
1299 char_u buff[DIALOG_MSG_SIZE];
1300 int ret;
1301 // Init ea pseudo-structure, this is needed for the check_overwrite()
1302 // function.
1303 exarg_T ea = {
1304 .append = false,
1305 .forceit = false,
1306 };
1307
1308 dialog_msg(buff, _("Save changes to \"%s\"?"), buf->b_fname);
1309 if (checkall) {
1310 ret = vim_dialog_yesnoallcancel(VIM_QUESTION, NULL, buff, 1);
1311 } else {
1312 ret = vim_dialog_yesnocancel(VIM_QUESTION, NULL, buff, 1);
1313 }
1314
1315 if (ret == VIM_YES) {
1316 if (buf->b_fname != NULL
1317 && check_overwrite(&ea,
1318 buf,
1319 buf->b_fname,
1320 buf->b_ffname,
1321 false) == OK) {
1322 // didn't hit Cancel
1323 (void)buf_write_all(buf, false);
1324 }
1325 } else if (ret == VIM_NO) {
1326 unchanged(buf, true, false);
1327 } else if (ret == VIM_ALL) {
1328 // Write all modified files that can be written.
1329 // Skip readonly buffers, these need to be confirmed
1330 // individually.
1331 FOR_ALL_BUFFERS(buf2) {
1332 if (bufIsChanged(buf2) && (buf2->b_ffname != NULL) && !buf2->b_p_ro) {
1333 bufref_T bufref;
1334 set_bufref(&bufref, buf2);
1335
1336 if (buf2->b_fname != NULL
1337 && check_overwrite(&ea, buf2, buf2->b_fname,
1338 buf2->b_ffname, false) == OK) {
1339 // didn't hit Cancel
1340 (void)buf_write_all(buf2, false);
1341 }
1342 // an autocommand may have deleted the buffer
1343 if (!bufref_valid(&bufref)) {
1344 buf2 = firstbuf;
1345 }
1346 }
1347 }
1348 } else if (ret == VIM_DISCARDALL) {
1349 // mark all buffers as unchanged
1350 FOR_ALL_BUFFERS(buf2) {
1351 unchanged(buf2, true, false);
1352 }
1353 }
1354}
1355
1356/// Ask the user whether to close the terminal buffer or not.
1357///
1358/// @param buf The terminal buffer.
1359/// @return bool Whether to close the buffer or not.
1360bool dialog_close_terminal(buf_T *buf)
1361{
1362 char_u buff[DIALOG_MSG_SIZE];
1363
1364 dialog_msg(buff, _("Close \"%s\"?"),
1365 (buf->b_fname != NULL) ? buf->b_fname : (char_u *)"?");
1366
1367 int ret = vim_dialog_yesnocancel(VIM_QUESTION, NULL, buff, 1);
1368
1369 return (ret == VIM_YES) ? true : false;
1370}
1371
1372/// Return true if the buffer "buf" can be abandoned, either by making it
1373/// hidden, autowriting it or unloading it.
1374bool can_abandon(buf_T *buf, int forceit)
1375{
1376 return buf_hide(buf)
1377 || !bufIsChanged(buf)
1378 || buf->b_nwindows > 1
1379 || autowrite(buf, forceit) == OK
1380 || forceit;
1381}
1382
1383
1384/// Add a buffer number to "bufnrs", unless it's already there.
1385static void add_bufnum(int *bufnrs, int *bufnump, int nr)
1386{
1387 int i;
1388
1389 for (i = 0; i < *bufnump; i++) {
1390 if (bufnrs[i] == nr) {
1391 return;
1392 }
1393 }
1394 bufnrs[*bufnump] = nr;
1395 *bufnump = *bufnump + 1;
1396}
1397
1398/// Check if any buffer was changed and cannot be abandoned.
1399/// That changed buffer becomes the current buffer.
1400/// When "unload" is true the current buffer is unloaded instead of making it
1401/// hidden. This is used for ":q!".
1402///
1403/// @param[in] hidden specifies whether to check only hidden buffers.
1404/// @param[in] unload specifies whether to unload, instead of hide, the buffer.
1405///
1406/// @returns true if any buffer is changed and cannot be abandoned
1407bool check_changed_any(bool hidden, bool unload)
1408{
1409 bool ret = false;
1410 int save;
1411 int i;
1412 int bufnum = 0;
1413 size_t bufcount = 0;
1414 int *bufnrs;
1415
1416 FOR_ALL_BUFFERS(buf) {
1417 bufcount++;
1418 }
1419
1420 if (bufcount == 0) {
1421 return false;
1422 }
1423
1424 bufnrs = xmalloc(sizeof(*bufnrs) * bufcount);
1425
1426 // curbuf
1427 bufnrs[bufnum++] = curbuf->b_fnum;
1428 // buf in curtab
1429 FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
1430 if (wp->w_buffer != curbuf) {
1431 add_bufnum(bufnrs, &bufnum, wp->w_buffer->b_fnum);
1432 }
1433 }
1434
1435 // buf in other tab
1436 FOR_ALL_TABS(tp) {
1437 if (tp != curtab) {
1438 FOR_ALL_WINDOWS_IN_TAB(wp, tp) {
1439 add_bufnum(bufnrs, &bufnum, wp->w_buffer->b_fnum);
1440 }
1441 }
1442 }
1443
1444 // any other buf
1445 FOR_ALL_BUFFERS(buf) {
1446 add_bufnum(bufnrs, &bufnum, buf->b_fnum);
1447 }
1448
1449 buf_T *buf = NULL;
1450 for (i = 0; i < bufnum; i++) {
1451 buf = buflist_findnr(bufnrs[i]);
1452 if (buf == NULL) {
1453 continue;
1454 }
1455 if ((!hidden || buf->b_nwindows == 0) && bufIsChanged(buf)) {
1456 bufref_T bufref;
1457 set_bufref(&bufref, buf);
1458
1459 // Try auto-writing the buffer. If this fails but the buffer no
1460 // longer exists it's not changed, that's OK.
1461 if (check_changed(buf, (p_awa ? CCGD_AW : 0)
1462 | CCGD_MULTWIN
1463 | CCGD_ALLBUF) && bufref_valid(&bufref)) {
1464 break; // didn't save - still changes
1465 }
1466 }
1467 }
1468
1469 if (i >= bufnum) {
1470 goto theend;
1471 }
1472
1473 ret = true;
1474 exiting = false;
1475 // When ":confirm" used, don't give an error message.
1476 if (!(p_confirm || cmdmod.confirm)) {
1477 // There must be a wait_return for this message, do_buffer()
1478 // may cause a redraw. But wait_return() is a no-op when vgetc()
1479 // is busy (Quit used from window menu), then make sure we don't
1480 // cause a scroll up.
1481 if (vgetc_busy > 0) {
1482 msg_row = cmdline_row;
1483 msg_col = 0;
1484 msg_didout = false;
1485 }
1486 if (EMSG2(_("E162: No write since last change for buffer \"%s\""),
1487 buf_spname(buf) != NULL ? buf_spname(buf) : buf->b_fname)) {
1488 save = no_wait_return;
1489 no_wait_return = false;
1490 wait_return(false);
1491 no_wait_return = save;
1492 }
1493 }
1494
1495 // Try to find a window that contains the buffer.
1496 if (buf != curbuf) {
1497 FOR_ALL_TAB_WINDOWS(tp, wp) {
1498 if (wp->w_buffer == buf) {
1499 bufref_T bufref;
1500 set_bufref(&bufref, buf);
1501 goto_tabpage_win(tp, wp);
1502 // Paranoia: did autocmds wipe out the buffer with changes?
1503 if (!bufref_valid(&bufref)) {
1504 goto theend;
1505 }
1506 goto buf_found;
1507 }
1508 }
1509 }
1510buf_found:
1511
1512 // Open the changed buffer in the current window.
1513 if (buf != curbuf) {
1514 set_curbuf(buf, unload ? DOBUF_UNLOAD : DOBUF_GOTO);
1515 }
1516
1517theend:
1518 xfree(bufnrs);
1519 return ret;
1520}
1521
1522/// Return FAIL if there is no file name, OK if there is one.
1523/// Give error message for FAIL.
1524int check_fname(void)
1525{
1526 if (curbuf->b_ffname == NULL) {
1527 EMSG(_(e_noname));
1528 return FAIL;
1529 }
1530 return OK;
1531}
1532
1533/// Flush the contents of a buffer, unless it has no file name.
1534///
1535/// @return FAIL for failure, OK otherwise
1536int buf_write_all(buf_T *buf, int forceit)
1537{
1538 int retval;
1539 buf_T *old_curbuf = curbuf;
1540
1541 retval = (buf_write(buf, buf->b_ffname, buf->b_fname,
1542 (linenr_T)1, buf->b_ml.ml_line_count, NULL,
1543 false, forceit, true, false));
1544 if (curbuf != old_curbuf) {
1545 msg_source(HL_ATTR(HLF_W));
1546 MSG(_("Warning: Entered other buffer unexpectedly (check autocommands)"));
1547 }
1548 return retval;
1549}
1550
1551/// Code to handle the argument list.
1552
1553#define AL_SET 1
1554#define AL_ADD 2
1555#define AL_DEL 3
1556
1557/// Isolate one argument, taking backticks.
1558/// Changes the argument in-place, puts a NUL after it. Backticks remain.
1559/// Return a pointer to the start of the next argument.
1560static char_u *do_one_arg(char_u *str)
1561{
1562 char_u *p;
1563 bool inbacktick;
1564
1565 inbacktick = false;
1566 for (p = str; *str; str++) {
1567 // When the backslash is used for escaping the special meaning of a
1568 // character we need to keep it until wildcard expansion.
1569 if (rem_backslash(str)) {
1570 *p++ = *str++;
1571 *p++ = *str;
1572 } else {
1573 // An item ends at a space not in backticks
1574 if (!inbacktick && ascii_isspace(*str)) {
1575 break;
1576 }
1577 if (*str == '`') {
1578 inbacktick ^= true;
1579 }
1580 *p++ = *str;
1581 }
1582 }
1583 str = skipwhite(str);
1584 *p = NUL;
1585
1586 return str;
1587}
1588
1589/// Separate the arguments in "str" and return a list of pointers in the
1590/// growarray "gap".
1591static void get_arglist(garray_T *gap, char_u *str, int escaped)
1592{
1593 ga_init(gap, (int)sizeof(char_u *), 20);
1594 while (*str != NUL) {
1595 GA_APPEND(char_u *, gap, str);
1596
1597 // If str is escaped, don't handle backslashes or spaces
1598 if (!escaped) {
1599 return;
1600 }
1601
1602 // Isolate one argument, change it in-place, put a NUL after it.
1603 str = do_one_arg(str);
1604 }
1605}
1606
1607/// Parse a list of arguments (file names), expand them and return in
1608/// "fnames[fcountp]". When "wig" is true, removes files matching 'wildignore'.
1609///
1610/// @return FAIL or OK.
1611int get_arglist_exp(char_u *str, int *fcountp, char_u ***fnamesp, bool wig)
1612{
1613 garray_T ga;
1614 int i;
1615
1616 get_arglist(&ga, str, true);
1617
1618 if (wig) {
1619 i = expand_wildcards(ga.ga_len, (char_u **)ga.ga_data,
1620 fcountp, fnamesp, EW_FILE|EW_NOTFOUND);
1621 } else {
1622 i = gen_expand_wildcards(ga.ga_len, (char_u **)ga.ga_data,
1623 fcountp, fnamesp, EW_FILE|EW_NOTFOUND);
1624 }
1625
1626 ga_clear(&ga);
1627 return i;
1628}
1629
1630
1631/// @param str
1632/// @param what
1633/// AL_SET: Redefine the argument list to 'str'.
1634/// AL_ADD: add files in 'str' to the argument list after "after".
1635/// AL_DEL: remove files in 'str' from the argument list.
1636/// @param after
1637/// 0 means before first one
1638///
1639/// @return FAIL for failure, OK otherwise.
1640static int do_arglist(char_u *str, int what, int after)
1641{
1642 garray_T new_ga;
1643 int exp_count;
1644 char_u **exp_files;
1645 char_u *p;
1646 int match;
1647 int arg_escaped = true;
1648
1649 // Set default argument for ":argadd" command.
1650 if (what == AL_ADD && *str == NUL) {
1651 if (curbuf->b_ffname == NULL) {
1652 return FAIL;
1653 }
1654 str = curbuf->b_fname;
1655 arg_escaped = false;
1656 }
1657
1658 // Collect all file name arguments in "new_ga".
1659 get_arglist(&new_ga, str, arg_escaped);
1660
1661 if (what == AL_DEL) {
1662 regmatch_T regmatch;
1663 bool didone;
1664
1665 // Delete the items: use each item as a regexp and find a match in the
1666 // argument list.
1667 regmatch.rm_ic = p_fic; // ignore case when 'fileignorecase' is set
1668 for (int i = 0; i < new_ga.ga_len && !got_int; i++) {
1669 p = ((char_u **)new_ga.ga_data)[i];
1670 p = file_pat_to_reg_pat(p, NULL, NULL, false);
1671 if (p == NULL) {
1672 break;
1673 }
1674 regmatch.regprog = vim_regcomp(p, p_magic ? RE_MAGIC : 0);
1675 if (regmatch.regprog == NULL) {
1676 xfree(p);
1677 break;
1678 }
1679
1680 didone = false;
1681 for (match = 0; match < ARGCOUNT; match++) {
1682 if (vim_regexec(&regmatch, alist_name(&ARGLIST[match]),
1683 (colnr_T)0)) {
1684 didone = true;
1685 xfree(ARGLIST[match].ae_fname);
1686 memmove(ARGLIST + match, ARGLIST + match + 1,
1687 (size_t)(ARGCOUNT - match - 1) * sizeof(aentry_T));
1688 ALIST(curwin)->al_ga.ga_len--;
1689 if (curwin->w_arg_idx > match) {
1690 curwin->w_arg_idx--;
1691 }
1692 match--;
1693 }
1694 }
1695
1696 vim_regfree(regmatch.regprog);
1697 xfree(p);
1698 if (!didone) {
1699 EMSG2(_(e_nomatch2), ((char_u **)new_ga.ga_data)[i]);
1700 }
1701 }
1702 ga_clear(&new_ga);
1703 } else {
1704 int i = expand_wildcards(new_ga.ga_len, (char_u **)new_ga.ga_data,
1705 &exp_count, &exp_files,
1706 EW_DIR|EW_FILE|EW_ADDSLASH|EW_NOTFOUND);
1707 ga_clear(&new_ga);
1708 if (i == FAIL || exp_count == 0) {
1709 EMSG(_(e_nomatch));
1710 return FAIL;
1711 }
1712
1713 if (what == AL_ADD) {
1714 (void)alist_add_list(exp_count, exp_files, after);
1715 xfree(exp_files);
1716 } else { // what == AL_SET
1717 alist_set(ALIST(curwin), exp_count, exp_files, false, NULL, 0);
1718 }
1719 }
1720
1721 alist_check_arg_idx();
1722
1723 return OK;
1724}
1725
1726/// Check the validity of the arg_idx for each other window.
1727static void alist_check_arg_idx(void)
1728{
1729 FOR_ALL_TAB_WINDOWS(tp, win) {
1730 if (win->w_alist == curwin->w_alist) {
1731 check_arg_idx(win);
1732 }
1733 }
1734}
1735
1736/// Return true if window "win" is editing the file at the current argument
1737/// index.
1738static bool editing_arg_idx(win_T *win)
1739{
1740 return !(win->w_arg_idx >= WARGCOUNT(win)
1741 || (win->w_buffer->b_fnum
1742 != WARGLIST(win)[win->w_arg_idx].ae_fnum
1743 && (win->w_buffer->b_ffname == NULL
1744 || !(path_full_compare(
1745 alist_name(&WARGLIST(win)[win->w_arg_idx]),
1746 win->w_buffer->b_ffname, true) & kEqualFiles))));
1747}
1748
1749/// Check if window "win" is editing the w_arg_idx file in its argument list.
1750void check_arg_idx(win_T *win)
1751{
1752 if (WARGCOUNT(win) > 1 && !editing_arg_idx(win)) {
1753 // We are not editing the current entry in the argument list.
1754 // Set "arg_had_last" if we are editing the last one.
1755 win->w_arg_idx_invalid = true;
1756 if (win->w_arg_idx != WARGCOUNT(win) - 1
1757 && arg_had_last == false
1758 && ALIST(win) == &global_alist
1759 && GARGCOUNT > 0
1760 && win->w_arg_idx < GARGCOUNT
1761 && (win->w_buffer->b_fnum == GARGLIST[GARGCOUNT - 1].ae_fnum
1762 || (win->w_buffer->b_ffname != NULL
1763 && (path_full_compare(alist_name(&GARGLIST[GARGCOUNT - 1]),
1764 win->w_buffer->b_ffname, true)
1765 & kEqualFiles)))) {
1766 arg_had_last = true;
1767 }
1768 } else {
1769 // We are editing the current entry in the argument list.
1770 // Set "arg_had_last" if it's also the last one
1771 win->w_arg_idx_invalid = false;
1772 if (win->w_arg_idx == WARGCOUNT(win) - 1
1773 && win->w_alist == &global_alist) {
1774 arg_had_last = true;
1775 }
1776 }
1777}
1778
1779/// ":args", ":argslocal" and ":argsglobal".
1780void ex_args(exarg_T *eap)
1781{
1782 if (eap->cmdidx != CMD_args) {
1783 alist_unlink(ALIST(curwin));
1784 if (eap->cmdidx == CMD_argglobal) {
1785 ALIST(curwin) = &global_alist;
1786 } else { // eap->cmdidx == CMD_arglocal
1787 alist_new();
1788 }
1789 }
1790
1791 if (*eap->arg != NUL) {
1792 // ":args file ..": define new argument list, handle like ":next"
1793 // Also for ":argslocal file .." and ":argsglobal file ..".
1794 ex_next(eap);
1795 } else if (eap->cmdidx == CMD_args) {
1796 // ":args": list arguments.
1797 if (ARGCOUNT > 0) {
1798 char_u **items = xmalloc(sizeof(char_u *) * (size_t)ARGCOUNT);
1799 // Overwrite the command, for a short list there is no scrolling
1800 // required and no wait_return().
1801 gotocmdline(true);
1802 for (int i = 0; i < ARGCOUNT; i++) {
1803 items[i] = alist_name(&ARGLIST[i]);
1804 }
1805 list_in_columns(items, ARGCOUNT, curwin->w_arg_idx);
1806 xfree(items);
1807 }
1808 } else if (eap->cmdidx == CMD_arglocal) {
1809 garray_T *gap = &curwin->w_alist->al_ga;
1810
1811 // ":argslocal": make a local copy of the global argument list.
1812 ga_grow(gap, GARGCOUNT);
1813 for (int i = 0; i < GARGCOUNT; i++) {
1814 if (GARGLIST[i].ae_fname != NULL) {
1815 AARGLIST(curwin->w_alist)[gap->ga_len].ae_fname =
1816 vim_strsave(GARGLIST[i].ae_fname);
1817 AARGLIST(curwin->w_alist)[gap->ga_len].ae_fnum =
1818 GARGLIST[i].ae_fnum;
1819 gap->ga_len++;
1820 }
1821 }
1822 }
1823}
1824
1825/// ":previous", ":sprevious", ":Next" and ":sNext".
1826void ex_previous(exarg_T *eap)
1827{
1828 // If past the last one already, go to the last one.
1829 if (curwin->w_arg_idx - (int)eap->line2 >= ARGCOUNT) {
1830 do_argfile(eap, ARGCOUNT - 1);
1831 } else {
1832 do_argfile(eap, curwin->w_arg_idx - (int)eap->line2);
1833 }
1834}
1835
1836/// ":rewind", ":first", ":sfirst" and ":srewind".
1837void ex_rewind(exarg_T *eap)
1838{
1839 do_argfile(eap, 0);
1840}
1841
1842/// ":last" and ":slast".
1843void ex_last(exarg_T *eap)
1844{
1845 do_argfile(eap, ARGCOUNT - 1);
1846}
1847
1848/// ":argument" and ":sargument".
1849void ex_argument(exarg_T *eap)
1850{
1851 int i;
1852
1853 if (eap->addr_count > 0) {
1854 i = (int)eap->line2 - 1;
1855 } else {
1856 i = curwin->w_arg_idx;
1857 }
1858 do_argfile(eap, i);
1859}
1860
1861/// Edit file "argn" of the argument lists.
1862void do_argfile(exarg_T *eap, int argn)
1863{
1864 int other;
1865 char_u *p;
1866 int old_arg_idx = curwin->w_arg_idx;
1867
1868 if (argn < 0 || argn >= ARGCOUNT) {
1869 if (ARGCOUNT <= 1) {
1870 EMSG(_("E163: There is only one file to edit"));
1871 } else if (argn < 0) {
1872 EMSG(_("E164: Cannot go before first file"));
1873 } else {
1874 EMSG(_("E165: Cannot go beyond last file"));
1875 }
1876 } else {
1877 setpcmark();
1878
1879 // split window or create new tab page first
1880 if (*eap->cmd == 's' || cmdmod.tab != 0) {
1881 if (win_split(0, 0) == FAIL) {
1882 return;
1883 }
1884 RESET_BINDING(curwin);
1885 } else {
1886 // if 'hidden' set, only check for changed file when re-editing
1887 // the same buffer
1888 other = true;
1889 if (buf_hide(curbuf)) {
1890 p = (char_u *)fix_fname((char *)alist_name(&ARGLIST[argn]));
1891 other = otherfile(p);
1892 xfree(p);
1893 }
1894 if ((!buf_hide(curbuf) || !other)
1895 && check_changed(curbuf, CCGD_AW
1896 | (other ? 0 : CCGD_MULTWIN)
1897 | (eap->forceit ? CCGD_FORCEIT : 0)
1898 | CCGD_EXCMD)) {
1899 return;
1900 }
1901 }
1902
1903 curwin->w_arg_idx = argn;
1904 if (argn == ARGCOUNT - 1
1905 && curwin->w_alist == &global_alist) {
1906 arg_had_last = true;
1907 }
1908
1909 // Edit the file; always use the last known line number.
1910 // When it fails (e.g. Abort for already edited file) restore the
1911 // argument index.
1912 if (do_ecmd(0, alist_name(&ARGLIST[curwin->w_arg_idx]), NULL,
1913 eap, ECMD_LAST,
1914 (buf_hide(curwin->w_buffer) ? ECMD_HIDE : 0)
1915 + (eap->forceit ? ECMD_FORCEIT : 0), curwin) == FAIL) {
1916 curwin->w_arg_idx = old_arg_idx;
1917 } else if (eap->cmdidx != CMD_argdo) {
1918 // like Vi: set the mark where the cursor is in the file.
1919 setmark('\'');
1920 }
1921 }
1922}
1923
1924/// ":next", and commands that behave like it.
1925void ex_next(exarg_T *eap)
1926{
1927 int i;
1928
1929 // check for changed buffer now, if this fails the argument list is not
1930 // redefined.
1931 if (buf_hide(curbuf)
1932 || eap->cmdidx == CMD_snext
1933 || !check_changed(curbuf, CCGD_AW
1934 | (eap->forceit ? CCGD_FORCEIT : 0)
1935 | CCGD_EXCMD)) {
1936 if (*eap->arg != NUL) { // redefine file list
1937 if (do_arglist(eap->arg, AL_SET, 0) == FAIL) {
1938 return;
1939 }
1940 i = 0;
1941 } else {
1942 i = curwin->w_arg_idx + (int)eap->line2;
1943 }
1944 do_argfile(eap, i);
1945 }
1946}
1947
1948/// ":argedit"
1949void ex_argedit(exarg_T *eap)
1950{
1951 int i = eap->addr_count ? (int)eap->line2 : curwin->w_arg_idx + 1;
1952 // Whether curbuf will be reused, curbuf->b_ffname will be set.
1953 bool curbuf_is_reusable = curbuf_reusable();
1954
1955 if (do_arglist(eap->arg, AL_ADD, i) == FAIL) {
1956 return;
1957 }
1958 maketitle();
1959
1960 if (curwin->w_arg_idx == 0
1961 && (curbuf->b_ml.ml_flags & ML_EMPTY)
1962 && (curbuf->b_ffname == NULL || curbuf_is_reusable)) {
1963 i = 0;
1964 }
1965 // Edit the argument.
1966 if (i < ARGCOUNT) {
1967 do_argfile(eap, i);
1968 }
1969}
1970
1971/// ":argadd"
1972void ex_argadd(exarg_T *eap)
1973{
1974 do_arglist(eap->arg, AL_ADD,
1975 eap->addr_count > 0 ? (int)eap->line2 : curwin->w_arg_idx + 1);
1976 maketitle();
1977}
1978
1979/// ":argdelete"
1980void ex_argdelete(exarg_T *eap)
1981{
1982 if (eap->addr_count > 0) {
1983 // ":1,4argdel": Delete all arguments in the range.
1984 if (eap->line2 > ARGCOUNT) {
1985 eap->line2 = ARGCOUNT;
1986 }
1987 linenr_T n = eap->line2 - eap->line1 + 1;
1988 if (*eap->arg != NUL) {
1989 // Can't have both a range and an argument.
1990 EMSG(_(e_invarg));
1991 } else if (n <= 0) {
1992 // Don't give an error for ":%argdel" if the list is empty.
1993 if (eap->line1 != 1 || eap->line2 != 0) {
1994 EMSG(_(e_invrange));
1995 }
1996 } else {
1997 for (linenr_T i = eap->line1; i <= eap->line2; i++) {
1998 xfree(ARGLIST[i - 1].ae_fname);
1999 }
2000 memmove(ARGLIST + eap->line1 - 1, ARGLIST + eap->line2,
2001 (size_t)(ARGCOUNT - eap->line2) * sizeof(aentry_T));
2002 ALIST(curwin)->al_ga.ga_len -= (int)n;
2003 if (curwin->w_arg_idx >= eap->line2) {
2004 curwin->w_arg_idx -= (int)n;
2005 } else if (curwin->w_arg_idx > eap->line1) {
2006 curwin->w_arg_idx = (int)eap->line1;
2007 }
2008 if (ARGCOUNT == 0) {
2009 curwin->w_arg_idx = 0;
2010 } else if (curwin->w_arg_idx >= ARGCOUNT) {
2011 curwin->w_arg_idx = ARGCOUNT - 1;
2012 }
2013 }
2014 } else if (*eap->arg == NUL) {
2015 EMSG(_(e_argreq));
2016 } else {
2017 do_arglist(eap->arg, AL_DEL, 0);
2018 }
2019 maketitle();
2020}
2021
2022/// ":argdo", ":windo", ":bufdo", ":tabdo", ":cdo", ":ldo", ":cfdo" and ":lfdo"
2023void ex_listdo(exarg_T *eap)
2024{
2025 int i;
2026 win_T *wp;
2027 tabpage_T *tp;
2028 int next_fnum = 0;
2029 char_u *save_ei = NULL;
2030 char_u *p_shm_save;
2031
2032 if (eap->cmdidx != CMD_windo && eap->cmdidx != CMD_tabdo) {
2033 // Don't do syntax HL autocommands. Skipping the syntax file is a
2034 // great speed improvement.
2035 save_ei = au_event_disable(",Syntax");
2036 }
2037
2038 if (eap->cmdidx == CMD_windo
2039 || eap->cmdidx == CMD_tabdo
2040 || buf_hide(curbuf)
2041 || !check_changed(curbuf, CCGD_AW
2042 | (eap->forceit ? CCGD_FORCEIT : 0)
2043 | CCGD_EXCMD)) {
2044 i = 0;
2045 // start at the eap->line1 argument/window/buffer
2046 wp = firstwin;
2047 tp = first_tabpage;
2048 switch (eap->cmdidx) {
2049 case CMD_windo:
2050 for (; wp != NULL && i + 1 < eap->line1; wp = wp->w_next) {
2051 i++;
2052 }
2053 break;
2054 case CMD_tabdo:
2055 for (; tp != NULL && i + 1 < eap->line1; tp = tp->tp_next) {
2056 i++;
2057 }
2058 break;
2059 case CMD_argdo:
2060 i = (int)eap->line1 - 1;
2061 break;
2062 default:
2063 break;
2064 }
2065
2066 buf_T *buf = curbuf;
2067 size_t qf_size = 0;
2068
2069 // set pcmark now
2070 if (eap->cmdidx == CMD_bufdo) {
2071 // Advance to the first listed buffer after "eap->line1".
2072 for (buf = firstbuf;
2073 buf != NULL && (buf->b_fnum < eap->line1 || !buf->b_p_bl);
2074 buf = buf->b_next) {
2075 if (buf->b_fnum > eap->line2) {
2076 buf = NULL;
2077 break;
2078 }
2079 }
2080 if (buf != NULL) {
2081 goto_buffer(eap, DOBUF_FIRST, FORWARD, buf->b_fnum);
2082 }
2083 } else if (eap->cmdidx == CMD_cdo || eap->cmdidx == CMD_ldo
2084 || eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo) {
2085 qf_size = qf_get_size(eap);
2086 assert(eap->line1 >= 0);
2087 if (qf_size == 0 || (size_t)eap->line1 > qf_size) {
2088 buf = NULL;
2089 } else {
2090 ex_cc(eap);
2091
2092 buf = curbuf;
2093 i = (int)eap->line1 - 1;
2094 if (eap->addr_count <= 0) {
2095 // Default to all quickfix/location list entries.
2096 assert(qf_size < MAXLNUM);
2097 eap->line2 = (linenr_T)qf_size;
2098 }
2099 }
2100 } else {
2101 setpcmark();
2102 }
2103 listcmd_busy = true; // avoids setting pcmark below
2104
2105 while (!got_int && buf != NULL) {
2106 if (eap->cmdidx == CMD_argdo) {
2107 // go to argument "i"
2108 if (i == ARGCOUNT) {
2109 break;
2110 }
2111 // Don't call do_argfile() when already there, it will try
2112 // reloading the file.
2113 if (curwin->w_arg_idx != i || !editing_arg_idx(curwin)) {
2114 // Clear 'shm' to avoid that the file message overwrites
2115 // any output from the command.
2116 p_shm_save = vim_strsave(p_shm);
2117 set_option_value("shm", 0L, "", 0);
2118 do_argfile(eap, i);
2119 set_option_value("shm", 0L, (char *)p_shm_save, 0);
2120 xfree(p_shm_save);
2121 }
2122 if (curwin->w_arg_idx != i) {
2123 break;
2124 }
2125 } else if (eap->cmdidx == CMD_windo) {
2126 // go to window "wp"
2127 if (!win_valid(wp)) {
2128 break;
2129 }
2130 assert(wp);
2131 win_goto(wp);
2132 if (curwin != wp) {
2133 break; // something must be wrong
2134 }
2135 wp = curwin->w_next;
2136 } else if (eap->cmdidx == CMD_tabdo) {
2137 // go to window "tp"
2138 if (!valid_tabpage(tp)) {
2139 break;
2140 }
2141 assert(tp);
2142 goto_tabpage_tp(tp, true, true);
2143 tp = tp->tp_next;
2144 } else if (eap->cmdidx == CMD_bufdo) {
2145 // Remember the number of the next listed buffer, in case
2146 // ":bwipe" is used or autocommands do something strange.
2147 next_fnum = -1;
2148 for (buf_T *bp = curbuf->b_next; bp != NULL; bp = bp->b_next) {
2149 if (bp->b_p_bl) {
2150 next_fnum = bp->b_fnum;
2151 break;
2152 }
2153 }
2154 }
2155
2156 i++;
2157 // execute the command
2158 do_cmdline(eap->arg, eap->getline, eap->cookie,
2159 DOCMD_VERBOSE + DOCMD_NOWAIT);
2160
2161 if (eap->cmdidx == CMD_bufdo) {
2162 // Done?
2163 if (next_fnum < 0 || next_fnum > eap->line2) {
2164 break;
2165 }
2166
2167 // Check if the buffer still exists.
2168 bool buf_still_exists = false;
2169 FOR_ALL_BUFFERS(bp) {
2170 if (bp->b_fnum == next_fnum) {
2171 buf_still_exists = true;
2172 break;
2173 }
2174 }
2175 if (!buf_still_exists) {
2176 break;
2177 }
2178
2179 // Go to the next buffer. Clear 'shm' to avoid that the file
2180 // message overwrites any output from the command.
2181 p_shm_save = vim_strsave(p_shm);
2182 set_option_value("shm", 0L, "", 0);
2183 goto_buffer(eap, DOBUF_FIRST, FORWARD, next_fnum);
2184 set_option_value("shm", 0L, (char *)p_shm_save, 0);
2185 xfree(p_shm_save);
2186
2187 // If autocommands took us elsewhere, quit here.
2188 if (curbuf->b_fnum != next_fnum) {
2189 break;
2190 }
2191 }
2192
2193 if (eap->cmdidx == CMD_cdo || eap->cmdidx == CMD_ldo
2194 || eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo) {
2195 assert(i >= 0);
2196 if ((size_t)i >= qf_size || i >= eap->line2) {
2197 break;
2198 }
2199
2200 size_t qf_idx = qf_get_cur_idx(eap);
2201
2202 ex_cnext(eap);
2203
2204 // If jumping to the next quickfix entry fails, quit here.
2205 if (qf_get_cur_idx(eap) == qf_idx) {
2206 break;
2207 }
2208 }
2209
2210 if (eap->cmdidx == CMD_windo) {
2211 validate_cursor(); // cursor may have moved
2212 // required when 'scrollbind' has been set
2213 if (curwin->w_p_scb) {
2214 do_check_scrollbind(true);
2215 }
2216 }
2217 if (eap->cmdidx == CMD_windo || eap->cmdidx == CMD_tabdo) {
2218 if (i + 1 > eap->line2) {
2219 break;
2220 }
2221 }
2222 if (eap->cmdidx == CMD_argdo && i >= eap->line2) {
2223 break;
2224 }
2225 }
2226 listcmd_busy = false;
2227 }
2228
2229 if (save_ei != NULL) {
2230 au_event_restore(save_ei);
2231 apply_autocmds(EVENT_SYNTAX, curbuf->b_p_syn,
2232 curbuf->b_fname, true, curbuf);
2233 }
2234}
2235
2236/// Add files[count] to the arglist of the current window after arg "after".
2237/// The file names in files[count] must have been allocated and are taken over.
2238/// Files[] itself is not taken over.
2239///
2240/// @param after: where to add: 0 = before first one
2241///
2242/// @return index of first added argument
2243static int alist_add_list(int count, char_u **files, int after)
2244{
2245 int old_argcount = ARGCOUNT;
2246 ga_grow(&ALIST(curwin)->al_ga, count);
2247 {
2248 if (after < 0) {
2249 after = 0;
2250 }
2251 if (after > ARGCOUNT) {
2252 after = ARGCOUNT;
2253 }
2254 if (after < ARGCOUNT) {
2255 memmove(&(ARGLIST[after + count]), &(ARGLIST[after]),
2256 (size_t)(ARGCOUNT - after) * sizeof(aentry_T));
2257 }
2258 for (int i = 0; i < count; i++) {
2259 ARGLIST[after + i].ae_fname = files[i];
2260 ARGLIST[after + i].ae_fnum = buflist_add(files[i],
2261 BLN_LISTED | BLN_CURBUF);
2262 }
2263 ALIST(curwin)->al_ga.ga_len += count;
2264 if (old_argcount > 0 && curwin->w_arg_idx >= after) {
2265 curwin->w_arg_idx += count;
2266 }
2267 return after;
2268 }
2269}
2270
2271// Function given to ExpandGeneric() to obtain the possible arguments of the
2272// argedit and argdelete commands.
2273char_u *get_arglist_name(expand_T *xp FUNC_ATTR_UNUSED, int idx)
2274{
2275 if (idx >= ARGCOUNT) {
2276 return NULL;
2277 }
2278 return alist_name(&ARGLIST[idx]);
2279}
2280
2281/// ":compiler[!] {name}"
2282void ex_compiler(exarg_T *eap)
2283{
2284 char_u *buf;
2285 char_u *old_cur_comp = NULL;
2286 char_u *p;
2287
2288 if (*eap->arg == NUL) {
2289 // List all compiler scripts.
2290 do_cmdline_cmd("echo globpath(&rtp, 'compiler/*.vim')"); // NOLINT
2291 } else {
2292 size_t bufsize = STRLEN(eap->arg) + 14;
2293 buf = xmalloc(bufsize);
2294 if (eap->forceit) {
2295 // ":compiler! {name}" sets global options
2296 do_cmdline_cmd("command -nargs=* CompilerSet set <args>");
2297 } else {
2298 // ":compiler! {name}" sets local options.
2299 // To remain backwards compatible "current_compiler" is always
2300 // used. A user's compiler plugin may set it, the distributed
2301 // plugin will then skip the settings. Afterwards set
2302 // "b:current_compiler" and restore "current_compiler".
2303 // Explicitly prepend "g:" to make it work in a function.
2304 old_cur_comp = get_var_value("g:current_compiler");
2305 if (old_cur_comp != NULL) {
2306 old_cur_comp = vim_strsave(old_cur_comp);
2307 }
2308 do_cmdline_cmd("command -nargs=* CompilerSet setlocal <args>");
2309 }
2310 do_unlet(S_LEN("g:current_compiler"), true);
2311 do_unlet(S_LEN("b:current_compiler"), true);
2312
2313 snprintf((char *)buf, bufsize, "compiler/%s.vim", eap->arg);
2314 if (source_runtime(buf, DIP_ALL) == FAIL) {
2315 EMSG2(_("E666: compiler not supported: %s"), eap->arg);
2316 }
2317 xfree(buf);
2318
2319 do_cmdline_cmd(":delcommand CompilerSet");
2320
2321 // Set "b:current_compiler" from "current_compiler".
2322 p = get_var_value("g:current_compiler");
2323 if (p != NULL) {
2324 set_internal_string_var((char_u *)"b:current_compiler", p);
2325 }
2326
2327 // Restore "current_compiler" for ":compiler {name}".
2328 if (!eap->forceit) {
2329 if (old_cur_comp != NULL) {
2330 set_internal_string_var((char_u *)"g:current_compiler",
2331 old_cur_comp);
2332 xfree(old_cur_comp);
2333 } else {
2334 do_unlet(S_LEN("g:current_compiler"), true);
2335 }
2336 }
2337 }
2338}
2339
2340/// ":runtime [what] {name}"
2341void ex_runtime(exarg_T *eap)
2342{
2343 char_u *arg = eap->arg;
2344 char_u *p = skiptowhite(arg);
2345 ptrdiff_t len = p - arg;
2346 int flags = eap->forceit ? DIP_ALL : 0;
2347
2348 if (STRNCMP(arg, "START", len) == 0) {
2349 flags += DIP_START + DIP_NORTP;
2350 arg = skipwhite(arg + len);
2351 } else if (STRNCMP(arg, "OPT", len) == 0) {
2352 flags += DIP_OPT + DIP_NORTP;
2353 arg = skipwhite(arg + len);
2354 } else if (STRNCMP(arg, "PACK", len) == 0) {
2355 flags += DIP_START + DIP_OPT + DIP_NORTP;
2356 arg = skipwhite(arg + len);
2357 } else if (STRNCMP(arg, "ALL", len) == 0) {
2358 flags += DIP_START + DIP_OPT;
2359 arg = skipwhite(arg + len);
2360 }
2361
2362 source_runtime(arg, flags);
2363}
2364
2365
2366static void source_callback(char_u *fname, void *cookie)
2367{
2368 (void)do_source(fname, false, DOSO_NONE);
2369}
2370
2371/// Find the file "name" in all directories in "path" and invoke
2372/// "callback(fname, cookie)".
2373/// "name" can contain wildcards.
2374/// When "flags" has DIP_ALL: source all files, otherwise only the first one.
2375/// When "flags" has DIP_DIR: find directories instead of files.
2376/// When "flags" has DIP_ERR: give an error message if there is no match.
2377///
2378/// return FAIL when no file could be sourced, OK otherwise.
2379int do_in_path(char_u *path, char_u *name, int flags,
2380 DoInRuntimepathCB callback, void *cookie)
2381{
2382 char_u *tail;
2383 int num_files;
2384 char_u **files;
2385 int i;
2386 bool did_one = false;
2387
2388 // Make a copy of 'runtimepath'. Invoking the callback may change the
2389 // value.
2390 char_u *rtp_copy = vim_strsave(path);
2391 char_u *buf = xmallocz(MAXPATHL);
2392 {
2393 if (p_verbose > 1 && name != NULL) {
2394 verbose_enter();
2395 smsg(_("Searching for \"%s\" in \"%s\""),
2396 (char *)name, (char *)path);
2397 verbose_leave();
2398 }
2399
2400 // Loop over all entries in 'runtimepath'.
2401 char_u *rtp = rtp_copy;
2402 while (*rtp != NUL && ((flags & DIP_ALL) || !did_one)) {
2403 // Copy the path from 'runtimepath' to buf[].
2404 copy_option_part(&rtp, buf, MAXPATHL, ",");
2405 size_t buflen = STRLEN(buf);
2406
2407 // Skip after or non-after directories.
2408 if (flags & (DIP_NOAFTER | DIP_AFTER)) {
2409 bool is_after = buflen >= 5
2410 && STRCMP(buf + buflen - 5, "after") == 0;
2411
2412 if ((is_after && (flags & DIP_NOAFTER))
2413 || (!is_after && (flags & DIP_AFTER))) {
2414 continue;
2415 }
2416 }
2417
2418 if (name == NULL) {
2419 (*callback)(buf, (void *)&cookie);
2420 if (!did_one) {
2421 did_one = (cookie == NULL);
2422 }
2423 } else if (buflen + STRLEN(name) + 2 < MAXPATHL) {
2424 add_pathsep((char *)buf);
2425 tail = buf + STRLEN(buf);
2426
2427 // Loop over all patterns in "name"
2428 char_u *np = name;
2429 while (*np != NUL && ((flags & DIP_ALL) || !did_one)) {
2430 // Append the pattern from "name" to buf[].
2431 assert(MAXPATHL >= (tail - buf));
2432 copy_option_part(&np, tail, (size_t)(MAXPATHL - (tail - buf)),
2433 "\t ");
2434
2435 if (p_verbose > 2) {
2436 verbose_enter();
2437 smsg(_("Searching for \"%s\""), buf);
2438 verbose_leave();
2439 }
2440
2441 // Expand wildcards, invoke the callback for each match.
2442 if (gen_expand_wildcards(1, &buf, &num_files, &files,
2443 (flags & DIP_DIR) ? EW_DIR
2444 : EW_FILE) == OK) {
2445 for (i = 0; i < num_files; i++) {
2446 (*callback)(files[i], cookie);
2447 did_one = true;
2448 if (!(flags & DIP_ALL)) {
2449 break;
2450 }
2451 }
2452 FreeWild(num_files, files);
2453 }
2454 }
2455 }
2456 }
2457 }
2458 xfree(buf);
2459 xfree(rtp_copy);
2460 if (!did_one && name != NULL) {
2461 char *basepath = path == p_rtp ? "runtimepath" : "packpath";
2462
2463 if (flags & DIP_ERR) {
2464 EMSG3(_(e_dirnotf), basepath, name);
2465 } else if (p_verbose > 0) {
2466 verbose_enter();
2467 smsg(_("not found in '%s': \"%s\""), basepath, name);
2468 verbose_leave();
2469 }
2470 }
2471
2472
2473 return did_one ? OK : FAIL;
2474}
2475
2476/// Find "name" in "path". When found, invoke the callback function for
2477/// it: callback(fname, "cookie")
2478/// When "flags" has DIP_ALL repeat for all matches, otherwise only the first
2479/// one is used.
2480/// Returns OK when at least one match found, FAIL otherwise.
2481/// If "name" is NULL calls callback for each entry in "path". Cookie is
2482/// passed by reference in this case, setting it to NULL indicates that callback
2483/// has done its job.
2484int do_in_path_and_pp(char_u *path, char_u *name, int flags,
2485 DoInRuntimepathCB callback, void *cookie)
2486{
2487 int done = FAIL;
2488
2489 if ((flags & DIP_NORTP) == 0) {
2490 done = do_in_path(path, name, flags, callback, cookie);
2491 }
2492
2493 if ((done == FAIL || (flags & DIP_ALL)) && (flags & DIP_START)) {
2494 char *start_dir = "pack/*/start/*/%s"; // NOLINT
2495 size_t len = STRLEN(start_dir) + STRLEN(name);
2496 char_u *s = xmallocz(len);
2497
2498 vim_snprintf((char *)s, len, start_dir, name);
2499 done = do_in_path(p_pp, s, flags, callback, cookie);
2500
2501 xfree(s);
2502 }
2503
2504 if ((done == FAIL || (flags & DIP_ALL)) && (flags & DIP_OPT)) {
2505 char *opt_dir = "pack/*/opt/*/%s"; // NOLINT
2506 size_t len = STRLEN(opt_dir) + STRLEN(name);
2507 char_u *s = xmallocz(len);
2508
2509 vim_snprintf((char *)s, len, opt_dir, name);
2510 done = do_in_path(p_pp, s, flags, callback, cookie);
2511
2512 xfree(s);
2513 }
2514
2515 return done;
2516}
2517
2518/// Just like do_in_path_and_pp(), using 'runtimepath' for "path".
2519int do_in_runtimepath(char_u *name, int flags, DoInRuntimepathCB callback,
2520 void *cookie)
2521{
2522 return do_in_path_and_pp(p_rtp, name, flags, callback, cookie);
2523}
2524
2525/// Source the file "name" from all directories in 'runtimepath'.
2526/// "name" can contain wildcards.
2527/// When "flags" has DIP_ALL: source all files, otherwise only the first one.
2528///
2529/// return FAIL when no file could be sourced, OK otherwise.
2530int source_runtime(char_u *name, int flags)
2531{
2532 return source_in_path(p_rtp, name, flags);
2533}
2534
2535/// Just like source_runtime(), but use "path" instead of 'runtimepath'.
2536int source_in_path(char_u *path, char_u *name, int flags)
2537{
2538 return do_in_path_and_pp(path, name, flags, source_callback, NULL);
2539}
2540
2541// Expand wildcards in "pat" and invoke do_source() for each match.
2542static void source_all_matches(char_u *pat)
2543{
2544 int num_files;
2545 char_u **files;
2546
2547 if (gen_expand_wildcards(1, &pat, &num_files, &files, EW_FILE) == OK) {
2548 for (int i = 0; i < num_files; i++) {
2549 (void)do_source(files[i], false, DOSO_NONE);
2550 }
2551 FreeWild(num_files, files);
2552 }
2553}
2554
2555/// Add the package directory to 'runtimepath'
2556static int add_pack_dir_to_rtp(char_u *fname)
2557{
2558 char_u *p4, *p3, *p2, *p1, *p;
2559 char_u *buf = NULL;
2560 char *afterdir = NULL;
2561 int retval = FAIL;
2562
2563 p4 = p3 = p2 = p1 = get_past_head(fname);
2564 for (p = p1; *p; MB_PTR_ADV(p)) {
2565 if (vim_ispathsep_nocolon(*p)) {
2566 p4 = p3; p3 = p2; p2 = p1; p1 = p;
2567 }
2568 }
2569
2570 // now we have:
2571 // rtp/pack/name/start/name
2572 // p4 p3 p2 p1
2573 //
2574 // find the part up to "pack" in 'runtimepath'
2575 p4++; // append pathsep in order to expand symlink
2576 char_u c = *p4;
2577 *p4 = NUL;
2578 char *const ffname = fix_fname((char *)fname);
2579 *p4 = c;
2580
2581 if (ffname == NULL) {
2582 return FAIL;
2583 }
2584
2585 // Find "ffname" in "p_rtp", ignoring '/' vs '\' differences
2586 // Also stop at the first "after" directory
2587 size_t fname_len = strlen(ffname);
2588 buf = try_malloc(MAXPATHL);
2589 if (buf == NULL) {
2590 goto theend;
2591 }
2592 const char *insp = NULL;
2593 const char *after_insp = NULL;
2594 for (const char *entry = (const char *)p_rtp; *entry != NUL; ) {
2595 const char *cur_entry = entry;
2596
2597 copy_option_part((char_u **)&entry, buf, MAXPATHL, ",");
2598 if (insp == NULL) {
2599 add_pathsep((char *)buf);
2600 char *const rtp_ffname = fix_fname((char *)buf);
2601 if (rtp_ffname == NULL) {
2602 goto theend;
2603 }
2604 bool match = path_fnamencmp(rtp_ffname, ffname, fname_len) == 0;
2605 xfree(rtp_ffname);
2606 if (match) {
2607 // Insert "ffname" after this entry (and comma).
2608 insp = entry;
2609 }
2610 }
2611
2612 if ((p = (char_u *)strstr((char *)buf, "after")) != NULL
2613 && p > buf
2614 && vim_ispathsep(p[-1])
2615 && (vim_ispathsep(p[5]) || p[5] == NUL || p[5] == ',')) {
2616 if (insp == NULL) {
2617 // Did not find "ffname" before the first "after" directory,
2618 // insert it before this entry.
2619 insp = cur_entry;
2620 }
2621 after_insp = cur_entry;
2622 break;
2623 }
2624 }
2625
2626 if (insp == NULL) {
2627 // Both "fname" and "after" not found, append at the end.
2628 insp = (const char *)p_rtp + STRLEN(p_rtp);
2629 }
2630
2631 // check if rtp/pack/name/start/name/after exists
2632 afterdir = concat_fnames((char *)fname, "after", true);
2633 size_t afterlen = 0;
2634 if (os_isdir((char_u *)afterdir)) {
2635 afterlen = strlen(afterdir) + 1; // add one for comma
2636 }
2637
2638 const size_t oldlen = STRLEN(p_rtp);
2639 const size_t addlen = STRLEN(fname) + 1; // add one for comma
2640 const size_t new_rtp_capacity = oldlen + addlen + afterlen + 1;
2641 // add one for NUL ------------------------------------------^
2642 char *const new_rtp = try_malloc(new_rtp_capacity);
2643 if (new_rtp == NULL) {
2644 goto theend;
2645 }
2646
2647 // We now have 'rtp' parts: {keep}{keep_after}{rest}.
2648 // Create new_rtp, first: {keep},{fname}
2649 size_t keep = (size_t)(insp - (const char *)p_rtp);
2650 memmove(new_rtp, p_rtp, keep);
2651 size_t new_rtp_len = keep;
2652 if (*insp == NUL) {
2653 new_rtp[new_rtp_len++] = ','; // add comma before
2654 }
2655 memmove(new_rtp + new_rtp_len, fname, addlen - 1);
2656 new_rtp_len += addlen - 1;
2657 if (*insp != NUL) {
2658 new_rtp[new_rtp_len++] = ','; // add comma after
2659 }
2660
2661 if (afterlen > 0 && after_insp != NULL) {
2662 size_t keep_after = (size_t)(after_insp - (const char *)p_rtp);
2663
2664 // Add to new_rtp: {keep},{fname}{keep_after},{afterdir}
2665 memmove(new_rtp + new_rtp_len, p_rtp + keep, keep_after - keep);
2666 new_rtp_len += keep_after - keep;
2667 memmove(new_rtp + new_rtp_len, afterdir, afterlen - 1);
2668 new_rtp_len += afterlen - 1;
2669 new_rtp[new_rtp_len++] = ',';
2670 keep = keep_after;
2671 }
2672
2673 if (p_rtp[keep] != NUL) {
2674 // Append rest: {keep},{fname}{keep_after},{afterdir}{rest}
2675 memmove(new_rtp + new_rtp_len, p_rtp + keep, oldlen - keep + 1);
2676 } else {
2677 new_rtp[new_rtp_len] = NUL;
2678 }
2679
2680 if (afterlen > 0 && after_insp == NULL) {
2681 // Append afterdir when "after" was not found:
2682 // {keep},{fname}{rest},{afterdir}
2683 xstrlcat(new_rtp, ",", new_rtp_capacity);
2684 xstrlcat(new_rtp, afterdir, new_rtp_capacity);
2685 }
2686
2687 set_option_value("rtp", 0L, new_rtp, 0);
2688 xfree(new_rtp);
2689 retval = OK;
2690
2691theend:
2692 xfree(buf);
2693 xfree(ffname);
2694 xfree(afterdir);
2695 return retval;
2696}
2697
2698/// Load scripts in "plugin" and "ftdetect" directories of the package.
2699static int load_pack_plugin(char_u *fname)
2700{
2701 static const char *plugpat = "%s/plugin/**/*.vim"; // NOLINT
2702 static const char *ftpat = "%s/ftdetect/*.vim"; // NOLINT
2703
2704 int retval = FAIL;
2705 char *const ffname = fix_fname((char *)fname);
2706 size_t len = strlen(ffname) + STRLEN(ftpat);
2707 char_u *pat = try_malloc(len + 1);
2708 if (pat == NULL) {
2709 goto theend;
2710 }
2711 vim_snprintf((char *)pat, len, plugpat, ffname);
2712 source_all_matches(pat);
2713
2714 char_u *cmd = vim_strsave((char_u *)"g:did_load_filetypes");
2715
2716 // If runtime/filetype.vim wasn't loaded yet, the scripts will be
2717 // found when it loads.
2718 if (eval_to_number(cmd) > 0) {
2719 do_cmdline_cmd("augroup filetypedetect");
2720 vim_snprintf((char *)pat, len, ftpat, ffname);
2721 source_all_matches(pat);
2722 do_cmdline_cmd("augroup END");
2723 }
2724 xfree(cmd);
2725 xfree(pat);
2726 retval = OK;
2727
2728theend:
2729 xfree(ffname);
2730
2731 return retval;
2732}
2733
2734// used for "cookie" of add_pack_plugin()
2735static int APP_ADD_DIR;
2736static int APP_LOAD;
2737static int APP_BOTH;
2738
2739static void add_pack_plugin(char_u *fname, void *cookie)
2740{
2741 if (cookie != &APP_LOAD) {
2742 char *buf = xmalloc(MAXPATHL);
2743 bool found = false;
2744
2745 const char *p = (const char *)p_rtp;
2746 while (*p != NUL) {
2747 copy_option_part((char_u **)&p, (char_u *)buf, MAXPATHL, ",");
2748 if (path_fnamecmp(buf, (char *)fname) == 0) {
2749 found = true;
2750 break;
2751 }
2752 }
2753 xfree(buf);
2754 if (!found) {
2755 // directory is not yet in 'runtimepath', add it
2756 if (add_pack_dir_to_rtp(fname) == FAIL) {
2757 return;
2758 }
2759 }
2760 }
2761
2762 if (cookie != &APP_ADD_DIR) {
2763 load_pack_plugin(fname);
2764 }
2765}
2766
2767/// Add all packages in the "start" directory to 'runtimepath'.
2768void add_pack_start_dirs(void)
2769{
2770 do_in_path(p_pp, (char_u *)"pack/*/start/*", DIP_ALL + DIP_DIR, // NOLINT
2771 add_pack_plugin, &APP_ADD_DIR);
2772}
2773
2774/// Load plugins from all packages in the "start" directory.
2775void load_start_packages(void)
2776{
2777 did_source_packages = true;
2778 do_in_path(p_pp, (char_u *)"pack/*/start/*", DIP_ALL + DIP_DIR, // NOLINT
2779 add_pack_plugin, &APP_LOAD);
2780}
2781
2782// ":packloadall"
2783// Find plugins in the package directories and source them.
2784void ex_packloadall(exarg_T *eap)
2785{
2786 if (!did_source_packages || eap->forceit) {
2787 // First do a round to add all directories to 'runtimepath', then load
2788 // the plugins. This allows for plugins to use an autoload directory
2789 // of another plugin.
2790 add_pack_start_dirs();
2791 load_start_packages();
2792 }
2793}
2794
2795/// ":packadd[!] {name}"
2796void ex_packadd(exarg_T *eap)
2797{
2798 static const char *plugpat = "pack/*/%s/%s"; // NOLINT
2799 int res = OK;
2800
2801 // Round 1: use "start", round 2: use "opt".
2802 for (int round = 1; round <= 2; round++) {
2803 // Only look under "start" when loading packages wasn't done yet.
2804 if (round == 1 && did_source_packages) {
2805 continue;
2806 }
2807
2808 const size_t len = STRLEN(plugpat) + STRLEN(eap->arg) + 5;
2809 char *pat = xmallocz(len);
2810 vim_snprintf(pat, len, plugpat, round == 1 ? "start" : "opt", eap->arg);
2811 // The first round don't give a "not found" error, in the second round
2812 // only when nothing was found in the first round.
2813 res = do_in_path(p_pp, (char_u *)pat,
2814 DIP_ALL + DIP_DIR
2815 + (round == 2 && res == FAIL ? DIP_ERR : 0),
2816 add_pack_plugin, eap->forceit ? &APP_ADD_DIR : &APP_BOTH);
2817 xfree(pat);
2818 }
2819}
2820
2821/// ":options"
2822void ex_options(exarg_T *eap)
2823{
2824 os_setenv("OPTWIN_CMD", cmdmod.tab ? "tab" : "", 1);
2825 os_setenv("OPTWIN_CMD",
2826 cmdmod.tab ? "tab" :
2827 (cmdmod.split & WSP_VERT) ? "vert" : "", 1);
2828 cmd_source((char_u *)SYS_OPTWIN_FILE, NULL);
2829}
2830
2831// Detect Python 3 or 2, and initialize 'pyxversion'.
2832void init_pyxversion(void)
2833{
2834 if (p_pyx == 0) {
2835 if (eval_has_provider("python3")) {
2836 p_pyx = 3;
2837 } else if (eval_has_provider("python")) {
2838 p_pyx = 2;
2839 }
2840 }
2841}
2842
2843// Does a file contain one of the following strings at the beginning of any
2844// line?
2845// "#!(any string)python2" => returns 2
2846// "#!(any string)python3" => returns 3
2847// "# requires python 2.x" => returns 2
2848// "# requires python 3.x" => returns 3
2849// otherwise return 0.
2850static int requires_py_version(char_u *filename)
2851{
2852 FILE *file;
2853 int requires_py_version = 0;
2854 int i, lines;
2855
2856 lines = (int)p_mls;
2857 if (lines < 0) {
2858 lines = 5;
2859 }
2860
2861 file = os_fopen((char *)filename, "r");
2862 if (file != NULL) {
2863 for (i = 0; i < lines; i++) {
2864 if (vim_fgets(IObuff, IOSIZE, file)) {
2865 break;
2866 }
2867 if (i == 0 && IObuff[0] == '#' && IObuff[1] == '!') {
2868 // Check shebang.
2869 if (strstr((char *)IObuff + 2, "python2") != NULL) {
2870 requires_py_version = 2;
2871 break;
2872 }
2873 if (strstr((char *)IObuff + 2, "python3") != NULL) {
2874 requires_py_version = 3;
2875 break;
2876 }
2877 }
2878 IObuff[21] = '\0';
2879 if (STRCMP("# requires python 2.x", IObuff) == 0) {
2880 requires_py_version = 2;
2881 break;
2882 }
2883 if (STRCMP("# requires python 3.x", IObuff) == 0) {
2884 requires_py_version = 3;
2885 break;
2886 }
2887 }
2888 fclose(file);
2889 }
2890 return requires_py_version;
2891}
2892
2893
2894// Source a python file using the requested python version.
2895static void source_pyx_file(exarg_T *eap, char_u *fname)
2896{
2897 exarg_T ex;
2898 long int v = requires_py_version(fname);
2899
2900 init_pyxversion();
2901 if (v == 0) {
2902 // user didn't choose a preference, 'pyx' is used
2903 v = p_pyx;
2904 }
2905
2906 // now source, if required python version is not supported show
2907 // unobtrusive message.
2908 if (eap == NULL) {
2909 memset(&ex, 0, sizeof(ex));
2910 } else {
2911 ex = *eap;
2912 }
2913 ex.arg = fname;
2914 ex.cmd = (char_u *)(v == 2 ? "pyfile" : "pyfile3");
2915
2916 if (v == 2) {
2917 ex_pyfile(&ex);
2918 } else {
2919 ex_py3file(&ex);
2920 }
2921}
2922
2923// ":pyxfile {fname}"
2924void ex_pyxfile(exarg_T *eap)
2925{
2926 source_pyx_file(eap, eap->arg);
2927}
2928
2929// ":pyx"
2930void ex_pyx(exarg_T *eap)
2931{
2932 init_pyxversion();
2933 if (p_pyx == 2) {
2934 ex_python(eap);
2935 } else {
2936 ex_python3(eap);
2937 }
2938}
2939
2940// ":pyxdo"
2941void ex_pyxdo(exarg_T *eap)
2942{
2943 init_pyxversion();
2944 if (p_pyx == 2) {
2945 ex_pydo(eap);
2946 } else {
2947 ex_pydo3(eap);
2948 }
2949}
2950
2951/// ":source {fname}"
2952void ex_source(exarg_T *eap)
2953{
2954 cmd_source(eap->arg, eap);
2955}
2956
2957static void cmd_source(char_u *fname, exarg_T *eap)
2958{
2959 if (*fname == NUL) {
2960 EMSG(_(e_argreq));
2961 } else if (eap != NULL && eap->forceit) {
2962 // ":source!": read Normal mode commands
2963 // Need to execute the commands directly. This is required at least
2964 // for:
2965 // - ":g" command busy
2966 // - after ":argdo", ":windo" or ":bufdo"
2967 // - another command follows
2968 // - inside a loop
2969 openscript(fname, global_busy || listcmd_busy || eap->nextcmd != NULL
2970 || eap->cstack->cs_idx >= 0);
2971
2972 // ":source" read ex commands
2973 } else if (do_source(fname, false, DOSO_NONE) == FAIL) {
2974 EMSG2(_(e_notopen), fname);
2975 }
2976}
2977
2978/// ":source" and associated commands.
2979///
2980/// @return address holding the next breakpoint line for a source cookie
2981linenr_T *source_breakpoint(void *cookie)
2982{
2983 return &((struct source_cookie *)cookie)->breakpoint;
2984}
2985
2986/// Return the address holding the debug tick for a source cookie.
2987int *source_dbg_tick(void *cookie)
2988{
2989 return &((struct source_cookie *)cookie)->dbg_tick;
2990}
2991
2992/// Return the nesting level for a source cookie.
2993int source_level(void *cookie)
2994{
2995 return ((struct source_cookie *)cookie)->level;
2996}
2997
2998/// Special function to open a file without handle inheritance.
2999/// If possible the handle is closed on exec().
3000static FILE *fopen_noinh_readbin(char *filename)
3001{
3002#ifdef WIN32
3003 int fd_tmp = os_open(filename, O_RDONLY | O_BINARY | O_NOINHERIT, 0);
3004#else
3005 int fd_tmp = os_open(filename, O_RDONLY, 0);
3006#endif
3007
3008 if (fd_tmp < 0) {
3009 return NULL;
3010 }
3011
3012 (void)os_set_cloexec(fd_tmp);
3013
3014 return fdopen(fd_tmp, READBIN);
3015}
3016
3017
3018/// Read the file "fname" and execute its lines as EX commands.
3019///
3020/// This function may be called recursively!
3021///
3022/// @param fname
3023/// @param check_other check for .vimrc and _vimrc
3024/// @param is_vimrc DOSO_ value
3025///
3026/// @return FAIL if file could not be opened, OK otherwise
3027int do_source(char_u *fname, int check_other, int is_vimrc)
3028{
3029 struct source_cookie cookie;
3030 char_u *save_sourcing_name;
3031 linenr_T save_sourcing_lnum;
3032 char_u *p;
3033 char_u *fname_exp;
3034 char_u *firstline = NULL;
3035 int retval = FAIL;
3036 static scid_T last_current_SID = 0;
3037 static int last_current_SID_seq = 0;
3038 void *save_funccalp;
3039 int save_debug_break_level = debug_break_level;
3040 scriptitem_T *si = NULL;
3041 proftime_T wait_start;
3042 bool trigger_source_post = false;
3043
3044 p = expand_env_save(fname);
3045 if (p == NULL) {
3046 return retval;
3047 }
3048 fname_exp = (char_u *)fix_fname((char *)p);
3049 xfree(p);
3050 if (fname_exp == NULL) {
3051 return retval;
3052 }
3053 if (os_isdir(fname_exp)) {
3054 smsg(_("Cannot source a directory: \"%s\""), fname);
3055 goto theend;
3056 }
3057
3058 // Apply SourceCmd autocommands, they should get the file and source it.
3059 if (has_autocmd(EVENT_SOURCECMD, fname_exp, NULL)
3060 && apply_autocmds(EVENT_SOURCECMD, fname_exp, fname_exp,
3061 false, curbuf)) {
3062 retval = aborting() ? FAIL : OK;
3063 if (retval == OK) {
3064 // Apply SourcePost autocommands.
3065 apply_autocmds(EVENT_SOURCEPOST, fname_exp, fname_exp, false, curbuf);
3066 }
3067 goto theend;
3068 }
3069
3070 // Apply SourcePre autocommands, they may get the file.
3071 apply_autocmds(EVENT_SOURCEPRE, fname_exp, fname_exp, false, curbuf);
3072
3073 cookie.fp = fopen_noinh_readbin((char *)fname_exp);
3074 if (cookie.fp == NULL && check_other) {
3075 // Try again, replacing file name ".vimrc" by "_vimrc" or vice versa,
3076 // and ".exrc" by "_exrc" or vice versa.
3077 p = path_tail(fname_exp);
3078 if ((*p == '.' || *p == '_')
3079 && (STRICMP(p + 1, "nvimrc") == 0 || STRICMP(p + 1, "exrc") == 0)) {
3080 *p = (*p == '_') ? '.' : '_';
3081 cookie.fp = fopen_noinh_readbin((char *)fname_exp);
3082 }
3083 }
3084
3085 if (cookie.fp == NULL) {
3086 if (p_verbose > 0) {
3087 verbose_enter();
3088 if (sourcing_name == NULL) {
3089 smsg(_("could not source \"%s\""), fname);
3090 } else {
3091 smsg(_("line %" PRId64 ": could not source \"%s\""),
3092 (int64_t)sourcing_lnum, fname);
3093 }
3094 verbose_leave();
3095 }
3096 goto theend;
3097 }
3098
3099 // The file exists.
3100 // - In verbose mode, give a message.
3101 // - For a vimrc file, may want to call vimrc_found().
3102 if (p_verbose > 1) {
3103 verbose_enter();
3104 if (sourcing_name == NULL) {
3105 smsg(_("sourcing \"%s\""), fname);
3106 } else {
3107 smsg(_("line %" PRId64 ": sourcing \"%s\""),
3108 (int64_t)sourcing_lnum, fname);
3109 }
3110 verbose_leave();
3111 }
3112 if (is_vimrc == DOSO_VIMRC) {
3113 vimrc_found(fname_exp, (char_u *)"MYVIMRC");
3114 }
3115
3116#ifdef USE_CRNL
3117 // If no automatic file format: Set default to CR-NL.
3118 if (*p_ffs == NUL) {
3119 cookie.fileformat = EOL_DOS;
3120 } else {
3121 cookie.fileformat = EOL_UNKNOWN;
3122 }
3123 cookie.error = false;
3124#endif
3125
3126 cookie.nextline = NULL;
3127 cookie.finished = false;
3128
3129 // Check if this script has a breakpoint.
3130 cookie.breakpoint = dbg_find_breakpoint(true, fname_exp, (linenr_T)0);
3131 cookie.fname = fname_exp;
3132 cookie.dbg_tick = debug_tick;
3133
3134 cookie.level = ex_nesting_level;
3135
3136 // Keep the sourcing name/lnum, for recursive calls.
3137 save_sourcing_name = sourcing_name;
3138 sourcing_name = fname_exp;
3139 save_sourcing_lnum = sourcing_lnum;
3140 sourcing_lnum = 0;
3141
3142 // start measuring script load time if --startuptime was passed and
3143 // time_fd was successfully opened afterwards.
3144 proftime_T rel_time;
3145 proftime_T start_time;
3146 FILE * const l_time_fd = time_fd;
3147 if (l_time_fd != NULL) {
3148 time_push(&rel_time, &start_time);
3149 }
3150
3151 const int l_do_profiling = do_profiling;
3152 if (l_do_profiling == PROF_YES) {
3153 prof_child_enter(&wait_start); // entering a child now
3154 }
3155
3156 // Don't use local function variables, if called from a function.
3157 // Also starts profiling timer for nested script.
3158 save_funccalp = save_funccal();
3159
3160 // Check if this script was sourced before to finds its SID.
3161 // If it's new, generate a new SID.
3162 // Always use a new sequence number.
3163 const sctx_T save_current_sctx = current_sctx;
3164 current_sctx.sc_seq = ++last_current_SID_seq;
3165 current_sctx.sc_lnum = 0;
3166 FileID file_id;
3167 bool file_id_ok = os_fileid((char *)fname_exp, &file_id);
3168 assert(script_items.ga_len >= 0);
3169 for (current_sctx.sc_sid = script_items.ga_len; current_sctx.sc_sid > 0;
3170 current_sctx.sc_sid--) {
3171 si = &SCRIPT_ITEM(current_sctx.sc_sid);
3172 // Compare dev/ino when possible, it catches symbolic links.
3173 // Also compare file names, the inode may change when the file was edited.
3174 bool file_id_equal = file_id_ok && si->file_id_valid
3175 && os_fileid_equal(&(si->file_id), &file_id);
3176 if (si->sn_name != NULL
3177 && (file_id_equal || fnamecmp(si->sn_name, fname_exp) == 0)) {
3178 break;
3179 }
3180 }
3181 if (current_sctx.sc_sid == 0) {
3182 current_sctx.sc_sid = ++last_current_SID;
3183 ga_grow(&script_items, (int)(current_sctx.sc_sid - script_items.ga_len));
3184 while (script_items.ga_len < current_sctx.sc_sid) {
3185 script_items.ga_len++;
3186 SCRIPT_ITEM(script_items.ga_len).sn_name = NULL;
3187 SCRIPT_ITEM(script_items.ga_len).sn_prof_on = false;
3188 }
3189 si = &SCRIPT_ITEM(current_sctx.sc_sid);
3190 si->sn_name = fname_exp;
3191 fname_exp = vim_strsave(si->sn_name); // used for autocmd
3192 if (file_id_ok) {
3193 si->file_id_valid = true;
3194 si->file_id = file_id;
3195 } else {
3196 si->file_id_valid = false;
3197 }
3198
3199 // Allocate the local script variables to use for this script.
3200 new_script_vars(current_sctx.sc_sid);
3201 }
3202
3203 if (l_do_profiling == PROF_YES) {
3204 bool forceit;
3205
3206 // Check if we do profiling for this script.
3207 if (!si->sn_prof_on && has_profiling(true, si->sn_name, &forceit)) {
3208 profile_init(si);
3209 si->sn_pr_force = forceit;
3210 }
3211 if (si->sn_prof_on) {
3212 si->sn_pr_count++;
3213 si->sn_pr_start = profile_start();
3214 si->sn_pr_children = profile_zero();
3215 }
3216 }
3217
3218 cookie.conv.vc_type = CONV_NONE; // no conversion
3219
3220 // Read the first line so we can check for a UTF-8 BOM.
3221 firstline = getsourceline(0, (void *)&cookie, 0);
3222 if (firstline != NULL && STRLEN(firstline) >= 3 && firstline[0] == 0xef
3223 && firstline[1] == 0xbb && firstline[2] == 0xbf) {
3224 // Found BOM; setup conversion, skip over BOM and recode the line.
3225 convert_setup(&cookie.conv, (char_u *)"utf-8", p_enc);
3226 p = string_convert(&cookie.conv, firstline + 3, NULL);
3227 if (p == NULL) {
3228 p = vim_strsave(firstline + 3);
3229 }
3230 xfree(firstline);
3231 firstline = p;
3232 }
3233
3234 // Call do_cmdline, which will call getsourceline() to get the lines.
3235 do_cmdline(firstline, getsourceline, (void *)&cookie,
3236 DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_REPEAT);
3237 retval = OK;
3238
3239 if (l_do_profiling == PROF_YES) {
3240 // Get "si" again, "script_items" may have been reallocated.
3241 si = &SCRIPT_ITEM(current_sctx.sc_sid);
3242 if (si->sn_prof_on) {
3243 si->sn_pr_start = profile_end(si->sn_pr_start);
3244 si->sn_pr_start = profile_sub_wait(wait_start, si->sn_pr_start);
3245 si->sn_pr_total = profile_add(si->sn_pr_total, si->sn_pr_start);
3246 si->sn_pr_self = profile_self(si->sn_pr_self, si->sn_pr_start,
3247 si->sn_pr_children);
3248 }
3249 }
3250
3251 if (got_int) {
3252 EMSG(_(e_interr));
3253 }
3254 sourcing_name = save_sourcing_name;
3255 sourcing_lnum = save_sourcing_lnum;
3256 if (p_verbose > 1) {
3257 verbose_enter();
3258 smsg(_("finished sourcing %s"), fname);
3259 if (sourcing_name != NULL) {
3260 smsg(_("continuing in %s"), sourcing_name);
3261 }
3262 verbose_leave();
3263 }
3264
3265 if (l_time_fd != NULL) {
3266 vim_snprintf((char *)IObuff, IOSIZE, "sourcing %s", fname);
3267 time_msg((char *)IObuff, &start_time);
3268 time_pop(rel_time);
3269 }
3270
3271 if (!got_int) {
3272 trigger_source_post = true;
3273 }
3274
3275 // After a "finish" in debug mode, need to break at first command of next
3276 // sourced file.
3277 if (save_debug_break_level > ex_nesting_level
3278 && debug_break_level == ex_nesting_level) {
3279 debug_break_level++;
3280 }
3281
3282 current_sctx = save_current_sctx;
3283 restore_funccal(save_funccalp);
3284 if (l_do_profiling == PROF_YES) {
3285 prof_child_exit(&wait_start); // leaving a child now
3286 }
3287 fclose(cookie.fp);
3288 xfree(cookie.nextline);
3289 xfree(firstline);
3290 convert_setup(&cookie.conv, NULL, NULL);
3291
3292 if (trigger_source_post) {
3293 apply_autocmds(EVENT_SOURCEPOST, fname_exp, fname_exp, false, curbuf);
3294 }
3295
3296theend:
3297 xfree(fname_exp);
3298 return retval;
3299}
3300
3301
3302/// ":scriptnames"
3303void ex_scriptnames(exarg_T *eap)
3304{
3305 if (eap->addr_count > 0) {
3306 // :script {scriptId}: edit the script
3307 if (eap->line2 < 1 || eap->line2 > script_items.ga_len) {
3308 EMSG(_(e_invarg));
3309 } else {
3310 eap->arg = SCRIPT_ITEM(eap->line2).sn_name;
3311 do_exedit(eap, NULL);
3312 }
3313 return;
3314 }
3315
3316 for (int i = 1; i <= script_items.ga_len && !got_int; i++) {
3317 if (SCRIPT_ITEM(i).sn_name != NULL) {
3318 home_replace(NULL, SCRIPT_ITEM(i).sn_name,
3319 NameBuff, MAXPATHL, true);
3320 smsg("%3d: %s", i, NameBuff);
3321 }
3322 }
3323}
3324
3325# if defined(BACKSLASH_IN_FILENAME)
3326/// Fix slashes in the list of script names for 'shellslash'.
3327void scriptnames_slash_adjust(void)
3328{
3329 for (int i = 1; i <= script_items.ga_len; i++) {
3330 if (SCRIPT_ITEM(i).sn_name != NULL) {
3331 slash_adjust(SCRIPT_ITEM(i).sn_name);
3332 }
3333 }
3334}
3335
3336# endif
3337
3338/// Get a pointer to a script name. Used for ":verbose set".
3339char_u *get_scriptname(LastSet last_set, bool *should_free)
3340{
3341 *should_free = false;
3342
3343 switch (last_set.script_ctx.sc_sid) {
3344 case SID_MODELINE:
3345 return (char_u *)_("modeline");
3346 case SID_CMDARG:
3347 return (char_u *)_("--cmd argument");
3348 case SID_CARG:
3349 return (char_u *)_("-c argument");
3350 case SID_ENV:
3351 return (char_u *)_("environment variable");
3352 case SID_ERROR:
3353 return (char_u *)_("error handler");
3354 case SID_LUA:
3355 return (char_u *)_("Lua");
3356 case SID_API_CLIENT:
3357 vim_snprintf((char *)IObuff, IOSIZE,
3358 _("API client (channel id %" PRIu64 ")"),
3359 last_set.channel_id);
3360 return IObuff;
3361 default:
3362 *should_free = true;
3363 return home_replace_save(NULL,
3364 SCRIPT_ITEM(last_set.script_ctx.sc_sid).sn_name);
3365 }
3366}
3367
3368# if defined(EXITFREE)
3369void free_scriptnames(void)
3370{
3371 profile_reset();
3372
3373# define FREE_SCRIPTNAME(item) xfree((item)->sn_name)
3374 GA_DEEP_CLEAR(&script_items, scriptitem_T, FREE_SCRIPTNAME);
3375}
3376# endif
3377
3378
3379/// Get one full line from a sourced file.
3380/// Called by do_cmdline() when it's called from do_source().
3381///
3382/// @return pointer to the line in allocated memory, or NULL for end-of-file or
3383/// some error.
3384char_u *getsourceline(int c, void *cookie, int indent)
3385{
3386 struct source_cookie *sp = (struct source_cookie *)cookie;
3387 char_u *line;
3388 char_u *p;
3389
3390 // If breakpoints have been added/deleted need to check for it.
3391 if (sp->dbg_tick < debug_tick) {
3392 sp->breakpoint = dbg_find_breakpoint(true, sp->fname, sourcing_lnum);
3393 sp->dbg_tick = debug_tick;
3394 }
3395 if (do_profiling == PROF_YES) {
3396 script_line_end();
3397 }
3398 // Get current line. If there is a read-ahead line, use it, otherwise get
3399 // one now.
3400 if (sp->finished) {
3401 line = NULL;
3402 } else if (sp->nextline == NULL) {
3403 line = get_one_sourceline(sp);
3404 } else {
3405 line = sp->nextline;
3406 sp->nextline = NULL;
3407 sourcing_lnum++;
3408 }
3409 if (line != NULL && do_profiling == PROF_YES) {
3410 script_line_start();
3411 }
3412
3413 // Only concatenate lines starting with a \ when 'cpoptions' doesn't
3414 // contain the 'C' flag.
3415 if (line != NULL && (vim_strchr(p_cpo, CPO_CONCAT) == NULL)) {
3416 // compensate for the one line read-ahead
3417 sourcing_lnum--;
3418
3419 // Get the next line and concatenate it when it starts with a
3420 // backslash. We always need to read the next line, keep it in
3421 // sp->nextline.
3422 // Also check for a comment in between continuation lines: "\ .
3423 sp->nextline = get_one_sourceline(sp);
3424 if (sp->nextline != NULL
3425 && (*(p = skipwhite(sp->nextline)) == '\\'
3426 || (p[0] == '"' && p[1] == '\\' && p[2] == ' '))) {
3427 garray_T ga;
3428
3429 ga_init(&ga, (int)sizeof(char_u), 400);
3430 ga_concat(&ga, line);
3431 if (*p == '\\') {
3432 ga_concat(&ga, p + 1);
3433 }
3434 for (;; ) {
3435 xfree(sp->nextline);
3436 sp->nextline = get_one_sourceline(sp);
3437 if (sp->nextline == NULL) {
3438 break;
3439 }
3440 p = skipwhite(sp->nextline);
3441 if (*p == '\\') {
3442 // Adjust the growsize to the current length to speed up
3443 // concatenating many lines.
3444 if (ga.ga_len > 400) {
3445 ga_set_growsize(&ga, (ga.ga_len > 8000) ? 8000 : ga.ga_len);
3446 }
3447 ga_concat(&ga, p + 1);
3448 } else if (p[0] != '"' || p[1] != '\\' || p[2] != ' ') {
3449 break;
3450 }
3451 }
3452 ga_append(&ga, NUL);
3453 xfree(line);
3454 line = ga.ga_data;
3455 }
3456 }
3457
3458 if (line != NULL && sp->conv.vc_type != CONV_NONE) {
3459 char_u *s;
3460
3461 // Convert the encoding of the script line.
3462 s = string_convert(&sp->conv, line, NULL);
3463 if (s != NULL) {
3464 xfree(line);
3465 line = s;
3466 }
3467 }
3468
3469 // Did we encounter a breakpoint?
3470 if (sp->breakpoint != 0 && sp->breakpoint <= sourcing_lnum) {
3471 dbg_breakpoint(sp->fname, sourcing_lnum);
3472 // Find next breakpoint.
3473 sp->breakpoint = dbg_find_breakpoint(true, sp->fname, sourcing_lnum);
3474 sp->dbg_tick = debug_tick;
3475 }
3476
3477 return line;
3478}
3479
3480static char_u *get_one_sourceline(struct source_cookie *sp)
3481{
3482 garray_T ga;
3483 int len;
3484 int c;
3485 char_u *buf;
3486#ifdef USE_CRNL
3487 int has_cr; // CR-LF found
3488#endif
3489 bool have_read = false;
3490
3491 // use a growarray to store the sourced line
3492 ga_init(&ga, 1, 250);
3493
3494 // Loop until there is a finished line (or end-of-file).
3495 sourcing_lnum++;
3496 for (;; ) {
3497 // make room to read at least 120 (more) characters
3498 ga_grow(&ga, 120);
3499 buf = (char_u *)ga.ga_data;
3500
3501retry:
3502 errno = 0;
3503 if (fgets((char *)buf + ga.ga_len, ga.ga_maxlen - ga.ga_len,
3504 sp->fp) == NULL) {
3505 if (errno == EINTR) {
3506 goto retry;
3507 }
3508
3509 break;
3510 }
3511 len = ga.ga_len + (int)STRLEN(buf + ga.ga_len);
3512#ifdef USE_CRNL
3513 // Ignore a trailing CTRL-Z, when in Dos mode. Only recognize the
3514 // CTRL-Z by its own, or after a NL.
3515 if ((len == 1 || (len >= 2 && buf[len - 2] == '\n'))
3516 && sp->fileformat == EOL_DOS
3517 && buf[len - 1] == Ctrl_Z) {
3518 buf[len - 1] = NUL;
3519 break;
3520 }
3521#endif
3522
3523 have_read = true;
3524 ga.ga_len = len;
3525
3526 // If the line was longer than the buffer, read more.
3527 if (ga.ga_maxlen - ga.ga_len == 1 && buf[len - 1] != '\n') {
3528 continue;
3529 }
3530
3531 if (len >= 1 && buf[len - 1] == '\n') { // remove trailing NL
3532#ifdef USE_CRNL
3533 has_cr = (len >= 2 && buf[len - 2] == '\r');
3534 if (sp->fileformat == EOL_UNKNOWN) {
3535 if (has_cr) {
3536 sp->fileformat = EOL_DOS;
3537 } else {
3538 sp->fileformat = EOL_UNIX;
3539 }
3540 }
3541
3542 if (sp->fileformat == EOL_DOS) {
3543 if (has_cr) { // replace trailing CR
3544 buf[len - 2] = '\n';
3545 len--;
3546 ga.ga_len--;
3547 } else { // lines like ":map xx yy^M" will have failed
3548 if (!sp->error) {
3549 msg_source(HL_ATTR(HLF_W));
3550 EMSG(_("W15: Warning: Wrong line separator, ^M may be missing"));
3551 }
3552 sp->error = true;
3553 sp->fileformat = EOL_UNIX;
3554 }
3555 }
3556#endif
3557 // The '\n' is escaped if there is an odd number of ^V's just
3558 // before it, first set "c" just before the 'V's and then check
3559 // len&c parities (is faster than ((len-c)%2 == 0)) -- Acevedo
3560 for (c = len - 2; c >= 0 && buf[c] == Ctrl_V; c--) {}
3561 if ((len & 1) != (c & 1)) { // escaped NL, read more
3562 sourcing_lnum++;
3563 continue;
3564 }
3565
3566 buf[len - 1] = NUL; // remove the NL
3567 }
3568
3569 // Check for ^C here now and then, so recursive :so can be broken.
3570 line_breakcheck();
3571 break;
3572 }
3573
3574 if (have_read) {
3575 return (char_u *)ga.ga_data;
3576 }
3577
3578 xfree(ga.ga_data);
3579 return NULL;
3580}
3581
3582/// Called when starting to read a script line.
3583/// "sourcing_lnum" must be correct!
3584/// When skipping lines it may not actually be executed, but we won't find out
3585/// until later and we need to store the time now.
3586void script_line_start(void)
3587{
3588 scriptitem_T *si;
3589 sn_prl_T *pp;
3590
3591 if (current_sctx.sc_sid <= 0 || current_sctx.sc_sid > script_items.ga_len) {
3592 return;
3593 }
3594 si = &SCRIPT_ITEM(current_sctx.sc_sid);
3595 if (si->sn_prof_on && sourcing_lnum >= 1) {
3596 // Grow the array before starting the timer, so that the time spent
3597 // here isn't counted.
3598 (void)ga_grow(&si->sn_prl_ga,
3599 (int)(sourcing_lnum - si->sn_prl_ga.ga_len));
3600 si->sn_prl_idx = sourcing_lnum - 1;
3601 while (si->sn_prl_ga.ga_len <= si->sn_prl_idx
3602 && si->sn_prl_ga.ga_len < si->sn_prl_ga.ga_maxlen) {
3603 // Zero counters for a line that was not used before.
3604 pp = &PRL_ITEM(si, si->sn_prl_ga.ga_len);
3605 pp->snp_count = 0;
3606 pp->sn_prl_total = profile_zero();
3607 pp->sn_prl_self = profile_zero();
3608 si->sn_prl_ga.ga_len++;
3609 }
3610 si->sn_prl_execed = false;
3611 si->sn_prl_start = profile_start();
3612 si->sn_prl_children = profile_zero();
3613 si->sn_prl_wait = profile_get_wait();
3614 }
3615}
3616
3617/// Called when actually executing a function line.
3618void script_line_exec(void)
3619{
3620 scriptitem_T *si;
3621
3622 if (current_sctx.sc_sid <= 0 || current_sctx.sc_sid > script_items.ga_len) {
3623 return;
3624 }
3625 si = &SCRIPT_ITEM(current_sctx.sc_sid);
3626 if (si->sn_prof_on && si->sn_prl_idx >= 0) {
3627 si->sn_prl_execed = true;
3628 }
3629}
3630
3631/// Called when done with a function line.
3632void script_line_end(void)
3633{
3634 scriptitem_T *si;
3635 sn_prl_T *pp;
3636
3637 if (current_sctx.sc_sid <= 0 || current_sctx.sc_sid > script_items.ga_len) {
3638 return;
3639 }
3640 si = &SCRIPT_ITEM(current_sctx.sc_sid);
3641 if (si->sn_prof_on && si->sn_prl_idx >= 0
3642 && si->sn_prl_idx < si->sn_prl_ga.ga_len) {
3643 if (si->sn_prl_execed) {
3644 pp = &PRL_ITEM(si, si->sn_prl_idx);
3645 pp->snp_count++;
3646 si->sn_prl_start = profile_end(si->sn_prl_start);
3647 si->sn_prl_start = profile_sub_wait(si->sn_prl_wait, si->sn_prl_start);
3648 pp->sn_prl_total = profile_add(pp->sn_prl_total, si->sn_prl_start);
3649 pp->sn_prl_self = profile_self(pp->sn_prl_self, si->sn_prl_start,
3650 si->sn_prl_children);
3651 }
3652 si->sn_prl_idx = -1;
3653 }
3654}
3655
3656/// ":scriptencoding": Set encoding conversion for a sourced script.
3657/// Without the multi-byte feature it's simply ignored.
3658void ex_scriptencoding(exarg_T *eap)
3659{
3660 struct source_cookie *sp;
3661 char_u *name;
3662
3663 if (!getline_equal(eap->getline, eap->cookie, getsourceline)) {
3664 EMSG(_("E167: :scriptencoding used outside of a sourced file"));
3665 return;
3666 }
3667
3668 if (*eap->arg != NUL) {
3669 name = enc_canonize(eap->arg);
3670 } else {
3671 name = eap->arg;
3672 }
3673
3674 // Setup for conversion from the specified encoding to 'encoding'.
3675 sp = (struct source_cookie *)getline_cookie(eap->getline, eap->cookie);
3676 convert_setup(&sp->conv, name, p_enc);
3677
3678 if (name != eap->arg) {
3679 xfree(name);
3680 }
3681}
3682
3683/// ":finish": Mark a sourced file as finished.
3684void ex_finish(exarg_T *eap)
3685{
3686 if (getline_equal(eap->getline, eap->cookie, getsourceline)) {
3687 do_finish(eap, false);
3688 } else {
3689 EMSG(_("E168: :finish used outside of a sourced file"));
3690 }
3691}
3692
3693/// Mark a sourced file as finished. Possibly makes the ":finish" pending.
3694/// Also called for a pending finish at the ":endtry" or after returning from
3695/// an extra do_cmdline(). "reanimate" is used in the latter case.
3696void do_finish(exarg_T *eap, int reanimate)
3697{
3698 int idx;
3699
3700 if (reanimate) {
3701 ((struct source_cookie *)getline_cookie(eap->getline,
3702 eap->cookie))->finished = false;
3703 }
3704
3705 // Cleanup (and inactivate) conditionals, but stop when a try conditional
3706 // not in its finally clause (which then is to be executed next) is found.
3707 // In this case, make the ":finish" pending for execution at the ":endtry".
3708 // Otherwise, finish normally.
3709 idx = cleanup_conditionals(eap->cstack, 0, true);
3710 if (idx >= 0) {
3711 eap->cstack->cs_pending[idx] = CSTP_FINISH;
3712 report_make_pending(CSTP_FINISH, NULL);
3713 } else {
3714 ((struct source_cookie *)getline_cookie(eap->getline,
3715 eap->cookie))->finished = true;
3716 }
3717}
3718
3719
3720/// Return true when a sourced file had the ":finish" command: Don't give error
3721/// message for missing ":endif".
3722/// Return false when not sourcing a file.
3723bool source_finished(LineGetter fgetline, void *cookie)
3724{
3725 return getline_equal(fgetline, cookie, getsourceline)
3726 && ((struct source_cookie *)getline_cookie(
3727 fgetline, cookie))->finished;
3728}
3729
3730/// ":checktime [buffer]"
3731void ex_checktime(exarg_T *eap)
3732{
3733 buf_T *buf;
3734 int save_no_check_timestamps = no_check_timestamps;
3735
3736 no_check_timestamps = 0;
3737 if (eap->addr_count == 0) { // default is all buffers
3738 check_timestamps(false);
3739 } else {
3740 buf = buflist_findnr((int)eap->line2);
3741 if (buf != NULL) { // cannot happen?
3742 (void)buf_check_timestamp(buf, false);
3743 }
3744 }
3745 no_check_timestamps = save_no_check_timestamps;
3746}
3747
3748#if defined(HAVE_LOCALE_H)
3749# define HAVE_GET_LOCALE_VAL
3750
3751static char *get_locale_val(int what)
3752{
3753 // Obtain the locale value from the libraries.
3754 char *loc = setlocale(what, NULL);
3755
3756 return loc;
3757}
3758#endif
3759
3760// Return true when "lang" starts with a valid language name.
3761// Rejects NULL, empty string, "C", "C.UTF-8" and others.
3762static bool is_valid_mess_lang(char *lang)
3763{
3764 return lang != NULL && ASCII_ISALPHA(lang[0]) && ASCII_ISALPHA(lang[1]);
3765}
3766
3767/// Obtain the current messages language. Used to set the default for
3768/// 'helplang'. May return NULL or an empty string.
3769char *get_mess_lang(void)
3770{
3771 char *p;
3772
3773# ifdef HAVE_GET_LOCALE_VAL
3774# if defined(LC_MESSAGES)
3775 p = get_locale_val(LC_MESSAGES);
3776# else
3777 // This is necessary for Win32, where LC_MESSAGES is not defined and $LANG
3778 // may be set to the LCID number. LC_COLLATE is the best guess, LC_TIME
3779 // and LC_MONETARY may be set differently for a Japanese working in the
3780 // US.
3781 p = get_locale_val(LC_COLLATE);
3782# endif
3783# else
3784 p = os_getenv("LC_ALL");
3785 if (!is_valid_mess_lang(p)) {
3786 p = os_getenv("LC_MESSAGES");
3787 if (!is_valid_mess_lang(p)) {
3788 p = os_getenv("LANG");
3789 }
3790 }
3791# endif
3792 return is_valid_mess_lang(p) ? p : NULL;
3793}
3794
3795// Complicated #if; matches with where get_mess_env() is used below.
3796#ifdef HAVE_WORKING_LIBINTL
3797/// Get the language used for messages from the environment.
3798static char_u *get_mess_env(void)
3799{
3800 char_u *p;
3801
3802 p = (char_u *)os_getenv("LC_ALL");
3803 if (p == NULL) {
3804 p = (char_u *)os_getenv("LC_MESSAGES");
3805 if (p == NULL) {
3806 p = (char_u *)os_getenv("LANG");
3807 if (p != NULL && ascii_isdigit(*p)) {
3808 p = NULL; // ignore something like "1043"
3809 }
3810# ifdef HAVE_GET_LOCALE_VAL
3811 if (p == NULL) {
3812 p = (char_u *)get_locale_val(LC_CTYPE);
3813 }
3814# endif
3815 }
3816 }
3817 return p;
3818}
3819
3820#endif
3821
3822
3823/// Set the "v:lang" variable according to the current locale setting.
3824/// Also do "v:lc_time"and "v:ctype".
3825void set_lang_var(void)
3826{
3827 const char *loc;
3828
3829# ifdef HAVE_GET_LOCALE_VAL
3830 loc = get_locale_val(LC_CTYPE);
3831# else
3832 // setlocale() not supported: use the default value
3833 loc = "C";
3834# endif
3835 set_vim_var_string(VV_CTYPE, loc, -1);
3836
3837 // When LC_MESSAGES isn't defined use the value from $LC_MESSAGES, fall
3838 // back to LC_CTYPE if it's empty.
3839# ifdef HAVE_WORKING_LIBINTL
3840 loc = (char *)get_mess_env();
3841# elif defined(LC_MESSAGES)
3842 loc = get_locale_val(LC_MESSAGES);
3843# else
3844 // In Windows LC_MESSAGES is not defined fallback to LC_CTYPE
3845 loc = get_locale_val(LC_CTYPE);
3846# endif
3847 set_vim_var_string(VV_LANG, loc, -1);
3848
3849# ifdef HAVE_GET_LOCALE_VAL
3850 loc = get_locale_val(LC_TIME);
3851# endif
3852 set_vim_var_string(VV_LC_TIME, loc, -1);
3853}
3854
3855#ifdef HAVE_WORKING_LIBINTL
3856///
3857/// ":language": Set the language (locale).
3858///
3859/// @param eap
3860///
3861void ex_language(exarg_T *eap)
3862{
3863 char *loc;
3864 char_u *p;
3865 char_u *name;
3866 int what = LC_ALL;
3867 char *whatstr = "";
3868#ifdef LC_MESSAGES
3869# define VIM_LC_MESSAGES LC_MESSAGES
3870#else
3871# define VIM_LC_MESSAGES 6789
3872#endif
3873
3874 name = eap->arg;
3875
3876 // Check for "messages {name}", "ctype {name}" or "time {name}" argument.
3877 // Allow abbreviation, but require at least 3 characters to avoid
3878 // confusion with a two letter language name "me" or "ct".
3879 p = skiptowhite(eap->arg);
3880 if ((*p == NUL || ascii_iswhite(*p)) && p - eap->arg >= 3) {
3881 if (STRNICMP(eap->arg, "messages", p - eap->arg) == 0) {
3882 what = VIM_LC_MESSAGES;
3883 name = skipwhite(p);
3884 whatstr = "messages ";
3885 } else if (STRNICMP(eap->arg, "ctype", p - eap->arg) == 0) {
3886 what = LC_CTYPE;
3887 name = skipwhite(p);
3888 whatstr = "ctype ";
3889 } else if (STRNICMP(eap->arg, "time", p - eap->arg) == 0) {
3890 what = LC_TIME;
3891 name = skipwhite(p);
3892 whatstr = "time ";
3893 }
3894 }
3895
3896 if (*name == NUL) {
3897#ifdef HAVE_WORKING_LIBINTL
3898 if (what == VIM_LC_MESSAGES) {
3899 p = get_mess_env();
3900 } else {
3901#endif
3902 p = (char_u *)setlocale(what, NULL);
3903#ifdef HAVE_WORKING_LIBINTL
3904 }
3905#endif
3906 if (p == NULL || *p == NUL) {
3907 p = (char_u *)"Unknown";
3908 }
3909 smsg(_("Current %slanguage: \"%s\""), whatstr, p);
3910 } else {
3911#ifndef LC_MESSAGES
3912 if (what == VIM_LC_MESSAGES) {
3913 loc = "";
3914 } else {
3915#endif
3916 loc = setlocale(what, (char *)name);
3917#ifdef LC_NUMERIC
3918 // Make sure strtod() uses a decimal point, not a comma.
3919 setlocale(LC_NUMERIC, "C");
3920#endif
3921#ifndef LC_MESSAGES
3922 }
3923#endif
3924 if (loc == NULL) {
3925 EMSG2(_("E197: Cannot set language to \"%s\""), name);
3926 } else {
3927#ifdef HAVE_NL_MSG_CAT_CNTR
3928 // Need to do this for GNU gettext, otherwise cached translations
3929 // will be used again.
3930 extern int _nl_msg_cat_cntr;
3931
3932 _nl_msg_cat_cntr++;
3933#endif
3934 // Reset $LC_ALL, otherwise it would overrule everything.
3935 os_setenv("LC_ALL", "", 1);
3936
3937 if (what != LC_TIME) {
3938 // Tell gettext() what to translate to. It apparently doesn't
3939 // use the currently effective locale.
3940 if (what == LC_ALL) {
3941 os_setenv("LANG", (char *)name, 1);
3942
3943 // Clear $LANGUAGE because GNU gettext uses it.
3944 os_setenv("LANGUAGE", "", 1);
3945 }
3946 if (what != LC_CTYPE) {
3947 os_setenv("LC_MESSAGES", (char *)name, 1);
3948 set_helplang_default((char *)name);
3949 }
3950 }
3951
3952 // Set v:lang, v:lc_time and v:ctype to the final result.
3953 set_lang_var();
3954 maketitle();
3955 }
3956 }
3957}
3958
3959
3960static char_u **locales = NULL; // Array of all available locales
3961
3962#ifndef WIN32
3963static bool did_init_locales = false;
3964
3965/// Return an array of strings for all available locales + NULL for the
3966/// last element. Return NULL in case of error.
3967static char_u **find_locales(void)
3968{
3969 garray_T locales_ga;
3970 char_u *loc;
3971 char *saveptr = NULL;
3972
3973 // Find all available locales by running command "locale -a". If this
3974 // doesn't work we won't have completion.
3975 char_u *locale_a = get_cmd_output((char_u *)"locale -a", NULL,
3976 kShellOptSilent, NULL);
3977 if (locale_a == NULL) {
3978 return NULL;
3979 }
3980 ga_init(&locales_ga, sizeof(char_u *), 20);
3981
3982 // Transform locale_a string where each locale is separated by "\n"
3983 // into an array of locale strings.
3984 loc = (char_u *)os_strtok((char *)locale_a, "\n", &saveptr);
3985
3986 while (loc != NULL) {
3987 loc = vim_strsave(loc);
3988 GA_APPEND(char_u *, &locales_ga, loc);
3989 loc = (char_u *)os_strtok(NULL, "\n", &saveptr);
3990 }
3991 xfree(locale_a);
3992 // Guarantee that .ga_data is NULL terminated
3993 ga_grow(&locales_ga, 1);
3994 ((char_u **)locales_ga.ga_data)[locales_ga.ga_len] = NULL;
3995 return (char_u **)locales_ga.ga_data;
3996}
3997#endif
3998
3999/// Lazy initialization of all available locales.
4000static void init_locales(void)
4001{
4002#ifndef WIN32
4003 if (!did_init_locales) {
4004 did_init_locales = true;
4005 locales = find_locales();
4006 }
4007#endif
4008}
4009
4010# if defined(EXITFREE)
4011void free_locales(void)
4012{
4013 int i;
4014 if (locales != NULL) {
4015 for (i = 0; locales[i] != NULL; i++) {
4016 xfree(locales[i]);
4017 }
4018 XFREE_CLEAR(locales);
4019 }
4020}
4021
4022# endif
4023
4024/// Function given to ExpandGeneric() to obtain the possible arguments of the
4025/// ":language" command.
4026char_u *get_lang_arg(expand_T *xp, int idx)
4027{
4028 if (idx == 0) {
4029 return (char_u *)"messages";
4030 }
4031 if (idx == 1) {
4032 return (char_u *)"ctype";
4033 }
4034 if (idx == 2) {
4035 return (char_u *)"time";
4036 }
4037
4038 init_locales();
4039 if (locales == NULL) {
4040 return NULL;
4041 }
4042 return locales[idx - 3];
4043}
4044
4045/// Function given to ExpandGeneric() to obtain the available locales.
4046char_u *get_locales(expand_T *xp, int idx)
4047{
4048 init_locales();
4049 if (locales == NULL) {
4050 return NULL;
4051 }
4052 return locales[idx];
4053}
4054
4055#endif
4056
4057
4058static void script_host_execute(char *name, exarg_T *eap)
4059{
4060 size_t len;
4061 char *const script = script_get(eap, &len);
4062
4063 if (script != NULL) {
4064 list_T *const args = tv_list_alloc(3);
4065 // script
4066 tv_list_append_allocated_string(args, script);
4067 // current range
4068 tv_list_append_number(args, (int)eap->line1);
4069 tv_list_append_number(args, (int)eap->line2);
4070
4071 (void)eval_call_provider(name, "execute", args);
4072 }
4073}
4074
4075static void script_host_execute_file(char *name, exarg_T *eap)
4076{
4077 uint8_t buffer[MAXPATHL];
4078 vim_FullName((char *)eap->arg, (char *)buffer, sizeof(buffer), false);
4079
4080 list_T *args = tv_list_alloc(3);
4081 // filename
4082 tv_list_append_string(args, (const char *)buffer, -1);
4083 // current range
4084 tv_list_append_number(args, (int)eap->line1);
4085 tv_list_append_number(args, (int)eap->line2);
4086 (void)eval_call_provider(name, "execute_file", args);
4087}
4088
4089static void script_host_do_range(char *name, exarg_T *eap)
4090{
4091 list_T *args = tv_list_alloc(3);
4092 tv_list_append_number(args, (int)eap->line1);
4093 tv_list_append_number(args, (int)eap->line2);
4094 tv_list_append_string(args, (const char *)eap->arg, -1);
4095 (void)eval_call_provider(name, "do_range", args);
4096}
4097
4098/// ":drop"
4099/// Opens the first argument in a window. When there are two or more arguments
4100/// the argument list is redefined.
4101void ex_drop(exarg_T *eap)
4102{
4103 bool split = false;
4104 buf_T *buf;
4105
4106 // Check if the first argument is already being edited in a window. If
4107 // so, jump to that window.
4108 // We would actually need to check all arguments, but that's complicated
4109 // and mostly only one file is dropped.
4110 // This also ignores wildcards, since it is very unlikely the user is
4111 // editing a file name with a wildcard character.
4112 do_arglist(eap->arg, AL_SET, 0);
4113
4114 // Expanding wildcards may result in an empty argument list. E.g. when
4115 // editing "foo.pyc" and ".pyc" is in 'wildignore'. Assume that we
4116 // already did an error message for this.
4117 if (ARGCOUNT == 0) {
4118 return;
4119 }
4120
4121 if (cmdmod.tab) {
4122 // ":tab drop file ...": open a tab for each argument that isn't
4123 // edited in a window yet. It's like ":tab all" but without closing
4124 // windows or tabs.
4125 ex_all(eap);
4126 } else {
4127 // ":drop file ...": Edit the first argument. Jump to an existing
4128 // window if possible, edit in current window if the current buffer
4129 // can be abandoned, otherwise open a new window.
4130 buf = buflist_findnr(ARGLIST[0].ae_fnum);
4131
4132 FOR_ALL_TAB_WINDOWS(tp, wp) {
4133 if (wp->w_buffer == buf) {
4134 goto_tabpage_win(tp, wp);
4135 curwin->w_arg_idx = 0;
4136 return;
4137 }
4138 }
4139
4140 // Check whether the current buffer is changed. If so, we will need
4141 // to split the current window or data could be lost.
4142 // Skip the check if the 'hidden' option is set, as in this case the
4143 // buffer won't be lost.
4144 if (!buf_hide(curbuf)) {
4145 emsg_off++;
4146 split = check_changed(curbuf, CCGD_AW | CCGD_EXCMD);
4147 emsg_off--;
4148 }
4149
4150 // Fake a ":sfirst" or ":first" command edit the first argument.
4151 if (split) {
4152 eap->cmdidx = CMD_sfirst;
4153 eap->cmd[0] = 's';
4154 } else {
4155 eap->cmdidx = CMD_first;
4156 }
4157 ex_rewind(eap);
4158 }
4159}
4160