blob: 30efcdf83da2fd08eb03a59f0fd8d15dab117368 [file] [log] [blame]
Bram Moolenaaredf3f972016-08-29 22:49:24 +02001/* vi:set ts=8 sts=4 sw=4 noet:
Bram Moolenaar071d4272004-06-13 20:20:40 +00002 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * ex_cmds2.c: some more functions for command line commands
12 */
13
Bram Moolenaar071d4272004-06-13 20:20:40 +000014#include "vim.h"
Bram Moolenaar071d4272004-06-13 20:20:40 +000015#include "version.h"
16
Bram Moolenaarf28dbce2016-01-29 22:03:47 +010017static void cmd_source(char_u *fname, exarg_T *eap);
Bram Moolenaar071d4272004-06-13 20:20:40 +000018
Bram Moolenaar05159a02005-02-26 23:04:13 +000019#ifdef FEAT_EVAL
Bram Moolenaar8cd06ca2005-02-28 22:44:58 +000020/* Growarray to store info about already sourced scripts.
Bram Moolenaar05159a02005-02-26 23:04:13 +000021 * For Unix also store the dev/ino, so that we don't have to stat() each
22 * script when going through the list. */
23typedef struct scriptitem_S
24{
25 char_u *sn_name;
26# ifdef UNIX
Bram Moolenaarbf0c4522009-05-16 19:16:33 +000027 int sn_dev_valid;
28 dev_t sn_dev;
Bram Moolenaar05159a02005-02-26 23:04:13 +000029 ino_t sn_ino;
30# endif
31# ifdef FEAT_PROFILE
32 int sn_prof_on; /* TRUE when script is/was profiled */
Bram Moolenaar8cd06ca2005-02-28 22:44:58 +000033 int sn_pr_force; /* forceit: profile functions in this script */
Bram Moolenaar05159a02005-02-26 23:04:13 +000034 proftime_T sn_pr_child; /* time set when going into first child */
35 int sn_pr_nest; /* nesting for sn_pr_child */
36 /* profiling the script as a whole */
37 int sn_pr_count; /* nr of times sourced */
Bram Moolenaar8c8de832008-06-24 22:58:06 +000038 proftime_T sn_pr_total; /* time spent in script + children */
39 proftime_T sn_pr_self; /* time spent in script itself */
Bram Moolenaar05159a02005-02-26 23:04:13 +000040 proftime_T sn_pr_start; /* time at script start */
41 proftime_T sn_pr_children; /* time in children after script start */
42 /* profiling the script per line */
43 garray_T sn_prl_ga; /* things stored for every line */
44 proftime_T sn_prl_start; /* start time for current line */
45 proftime_T sn_prl_children; /* time spent in children for this line */
46 proftime_T sn_prl_wait; /* wait start time for current line */
47 int sn_prl_idx; /* index of line being timed; -1 if none */
48 int sn_prl_execed; /* line being timed was executed */
49# endif
50} scriptitem_T;
51
52static garray_T script_items = {0, 0, sizeof(scriptitem_T), 4, NULL};
53#define SCRIPT_ITEM(id) (((scriptitem_T *)script_items.ga_data)[(id) - 1])
54
55# ifdef FEAT_PROFILE
56/* Struct used in sn_prl_ga for every line of a script. */
57typedef struct sn_prl_S
58{
59 int snp_count; /* nr of times line was executed */
Bram Moolenaar8c8de832008-06-24 22:58:06 +000060 proftime_T sn_prl_total; /* time spent in a line + children */
61 proftime_T sn_prl_self; /* time spent in a line itself */
Bram Moolenaar05159a02005-02-26 23:04:13 +000062} sn_prl_T;
63
64# define PRL_ITEM(si, idx) (((sn_prl_T *)(si)->sn_prl_ga.ga_data)[(idx)])
65# endif
66#endif
67
Bram Moolenaar071d4272004-06-13 20:20:40 +000068#if defined(FEAT_EVAL) || defined(PROTO)
69static int debug_greedy = FALSE; /* batch mode debugging: don't save
70 and restore typeahead. */
Bram Moolenaarf1f60f82016-01-16 15:40:53 +010071static void do_setdebugtracelevel(char_u *arg);
72static void do_checkbacktracelevel(void);
73static void do_showbacktrace(char_u *cmd);
Bram Moolenaar071d4272004-06-13 20:20:40 +000074
Bram Moolenaarc6f9f732018-02-11 19:06:26 +010075static char_u *debug_oldval = NULL; /* old and newval for debug expressions */
76static char_u *debug_newval = NULL;
77static int debug_expr = 0; /* use debug_expr */
78
79 int
80has_watchexpr(void)
81{
82 return debug_expr;
83}
84
Bram Moolenaar071d4272004-06-13 20:20:40 +000085/*
86 * do_debug(): Debug mode.
87 * Repeatedly get Ex commands, until told to continue normal execution.
88 */
89 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +010090do_debug(char_u *cmd)
Bram Moolenaar071d4272004-06-13 20:20:40 +000091{
92 int save_msg_scroll = msg_scroll;
93 int save_State = State;
94 int save_did_emsg = did_emsg;
95 int save_cmd_silent = cmd_silent;
96 int save_msg_silent = msg_silent;
97 int save_emsg_silent = emsg_silent;
98 int save_redir_off = redir_off;
99 tasave_T typeaheadbuf;
Bram Moolenaaree3f7a52008-01-01 13:17:56 +0000100 int typeahead_saved = FALSE;
Bram Moolenaar383c6f52008-01-04 15:01:07 +0000101 int save_ignore_script = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000102 int save_ex_normal_busy;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000103 int n;
104 char_u *cmdline = NULL;
105 char_u *p;
106 char *tail = NULL;
107 static int last_cmd = 0;
108#define CMD_CONT 1
109#define CMD_NEXT 2
110#define CMD_STEP 3
111#define CMD_FINISH 4
112#define CMD_QUIT 5
113#define CMD_INTERRUPT 6
Bram Moolenaarf1f60f82016-01-16 15:40:53 +0100114#define CMD_BACKTRACE 7
115#define CMD_FRAME 8
116#define CMD_UP 9
117#define CMD_DOWN 10
Bram Moolenaar071d4272004-06-13 20:20:40 +0000118
119#ifdef ALWAYS_USE_GUI
120 /* Can't do this when there is no terminal for input/output. */
121 if (!gui.in_use)
122 {
123 /* Break as soon as possible. */
124 debug_break_level = 9999;
125 return;
126 }
127#endif
128
129 /* Make sure we are in raw mode and start termcap mode. Might have side
130 * effects... */
131 settmode(TMODE_RAW);
132 starttermcap();
133
134 ++RedrawingDisabled; /* don't redisplay the window */
135 ++no_wait_return; /* don't wait for return */
136 did_emsg = FALSE; /* don't use error from debugged stuff */
137 cmd_silent = FALSE; /* display commands */
138 msg_silent = FALSE; /* display messages */
139 emsg_silent = FALSE; /* display error messages */
140 redir_off = TRUE; /* don't redirect debug commands */
141
142 State = NORMAL;
Bram Moolenaard99388b2017-10-26 14:28:32 +0200143 debug_mode = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000144
145 if (!debug_did_msg)
Bram Moolenaar32526b32019-01-19 17:43:09 +0100146 msg(_("Entering Debug mode. Type \"cont\" to continue."));
Bram Moolenaarc6f9f732018-02-11 19:06:26 +0100147 if (debug_oldval != NULL)
148 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100149 smsg(_("Oldval = \"%s\""), debug_oldval);
Bram Moolenaarc6f9f732018-02-11 19:06:26 +0100150 vim_free(debug_oldval);
151 debug_oldval = NULL;
152 }
153 if (debug_newval != NULL)
154 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100155 smsg(_("Newval = \"%s\""), debug_newval);
Bram Moolenaarc6f9f732018-02-11 19:06:26 +0100156 vim_free(debug_newval);
157 debug_newval = NULL;
158 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000159 if (sourcing_name != NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +0100160 msg((char *)sourcing_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000161 if (sourcing_lnum != 0)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100162 smsg(_("line %ld: %s"), (long)sourcing_lnum, cmd);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000163 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100164 smsg(_("cmd: %s"), cmd);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000165 /*
166 * Repeat getting a command and executing it.
167 */
168 for (;;)
169 {
170 msg_scroll = TRUE;
171 need_wait_return = FALSE;
Bram Moolenaar85b11762016-02-27 18:13:23 +0100172
Bram Moolenaar071d4272004-06-13 20:20:40 +0000173 /* Save the current typeahead buffer and replace it with an empty one.
174 * This makes sure we get input from the user here and don't interfere
175 * with the commands being executed. Reset "ex_normal_busy" to avoid
176 * the side effects of using ":normal". Save the stuff buffer and make
Bram Moolenaaree3f7a52008-01-01 13:17:56 +0000177 * it empty. Set ignore_script to avoid reading from script input. */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000178 save_ex_normal_busy = ex_normal_busy;
179 ex_normal_busy = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000180 if (!debug_greedy)
Bram Moolenaaree3f7a52008-01-01 13:17:56 +0000181 {
Bram Moolenaar071d4272004-06-13 20:20:40 +0000182 save_typeahead(&typeaheadbuf);
Bram Moolenaaree3f7a52008-01-01 13:17:56 +0000183 typeahead_saved = TRUE;
184 save_ignore_script = ignore_script;
185 ignore_script = TRUE;
186 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000187
Bram Moolenaardc303bc2016-05-17 17:45:38 +0200188 vim_free(cmdline);
Bram Moolenaarbfd8fc02005-09-20 23:22:24 +0000189 cmdline = getcmdline_prompt('>', NULL, 0, EXPAND_NOTHING, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000190
Bram Moolenaaree3f7a52008-01-01 13:17:56 +0000191 if (typeahead_saved)
192 {
Bram Moolenaar071d4272004-06-13 20:20:40 +0000193 restore_typeahead(&typeaheadbuf);
Bram Moolenaaree3f7a52008-01-01 13:17:56 +0000194 ignore_script = save_ignore_script;
195 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000196 ex_normal_busy = save_ex_normal_busy;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000197
198 cmdline_row = msg_row;
Bram Moolenaarf1f60f82016-01-16 15:40:53 +0100199 msg_starthere();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000200 if (cmdline != NULL)
201 {
202 /* If this is a debug command, set "last_cmd".
203 * If not, reset "last_cmd".
204 * For a blank line use previous command. */
205 p = skipwhite(cmdline);
206 if (*p != NUL)
207 {
208 switch (*p)
209 {
210 case 'c': last_cmd = CMD_CONT;
211 tail = "ont";
212 break;
213 case 'n': last_cmd = CMD_NEXT;
214 tail = "ext";
215 break;
216 case 's': last_cmd = CMD_STEP;
217 tail = "tep";
218 break;
Bram Moolenaarf1f60f82016-01-16 15:40:53 +0100219 case 'f':
220 last_cmd = 0;
221 if (p[1] == 'r')
222 {
223 last_cmd = CMD_FRAME;
224 tail = "rame";
225 }
226 else
227 {
228 last_cmd = CMD_FINISH;
229 tail = "inish";
230 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000231 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;
Bram Moolenaarf1f60f82016-01-16 15:40:53 +0100238 case 'b': last_cmd = CMD_BACKTRACE;
239 if (p[1] == 't')
240 tail = "t";
241 else
242 tail = "acktrace";
243 break;
244 case 'w': last_cmd = CMD_BACKTRACE;
245 tail = "here";
246 break;
247 case 'u': last_cmd = CMD_UP;
248 tail = "p";
249 break;
250 case 'd': last_cmd = CMD_DOWN;
251 tail = "own";
252 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000253 default: last_cmd = 0;
254 }
255 if (last_cmd != 0)
256 {
257 /* Check that the tail matches. */
258 ++p;
259 while (*p != NUL && *p == *tail)
260 {
261 ++p;
262 ++tail;
263 }
Bram Moolenaarf1f60f82016-01-16 15:40:53 +0100264 if (ASCII_ISALPHA(*p) && last_cmd != CMD_FRAME)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000265 last_cmd = 0;
266 }
267 }
268
269 if (last_cmd != 0)
270 {
271 /* Execute debug command: decided where to break next and
272 * return. */
273 switch (last_cmd)
274 {
275 case CMD_CONT:
276 debug_break_level = -1;
277 break;
278 case CMD_NEXT:
279 debug_break_level = ex_nesting_level;
280 break;
281 case CMD_STEP:
282 debug_break_level = 9999;
283 break;
284 case CMD_FINISH:
285 debug_break_level = ex_nesting_level - 1;
286 break;
287 case CMD_QUIT:
288 got_int = TRUE;
289 debug_break_level = -1;
290 break;
291 case CMD_INTERRUPT:
292 got_int = TRUE;
293 debug_break_level = 9999;
294 /* Do not repeat ">interrupt" cmd, continue stepping. */
295 last_cmd = CMD_STEP;
296 break;
Bram Moolenaarf1f60f82016-01-16 15:40:53 +0100297 case CMD_BACKTRACE:
298 do_showbacktrace(cmd);
299 continue;
300 case CMD_FRAME:
301 if (*p == NUL)
302 {
303 do_showbacktrace(cmd);
304 }
305 else
306 {
307 p = skipwhite(p);
308 do_setdebugtracelevel(p);
309 }
310 continue;
311 case CMD_UP:
312 debug_backtrace_level++;
313 do_checkbacktracelevel();
314 continue;
315 case CMD_DOWN:
316 debug_backtrace_level--;
317 do_checkbacktracelevel();
318 continue;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000319 }
Bram Moolenaarf1f60f82016-01-16 15:40:53 +0100320 /* Going out reset backtrace_level */
321 debug_backtrace_level = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000322 break;
323 }
324
325 /* don't debug this command */
326 n = debug_break_level;
327 debug_break_level = -1;
328 (void)do_cmdline(cmdline, getexline, NULL,
329 DOCMD_VERBOSE|DOCMD_EXCRESET);
330 debug_break_level = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000331 }
332 lines_left = Rows - 1;
333 }
334 vim_free(cmdline);
335
336 --RedrawingDisabled;
337 --no_wait_return;
338 redraw_all_later(NOT_VALID);
339 need_wait_return = FALSE;
340 msg_scroll = save_msg_scroll;
341 lines_left = Rows - 1;
342 State = save_State;
Bram Moolenaard99388b2017-10-26 14:28:32 +0200343 debug_mode = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000344 did_emsg = save_did_emsg;
345 cmd_silent = save_cmd_silent;
346 msg_silent = save_msg_silent;
347 emsg_silent = save_emsg_silent;
348 redir_off = save_redir_off;
349
350 /* Only print the message again when typing a command before coming back
351 * here. */
352 debug_did_msg = TRUE;
353}
354
Bram Moolenaarf1f60f82016-01-16 15:40:53 +0100355 static int
356get_maxbacktrace_level(void)
357{
358 char *p, *q;
Bram Moolenaardc633cf2016-04-23 14:33:19 +0200359 int maxbacktrace = 0;
Bram Moolenaarf1f60f82016-01-16 15:40:53 +0100360
Bram Moolenaarf1f60f82016-01-16 15:40:53 +0100361 if (sourcing_name != NULL)
362 {
363 p = (char *)sourcing_name;
364 while ((q = strstr(p, "..")) != NULL)
365 {
366 p = q + 2;
367 maxbacktrace++;
368 }
369 }
370 return maxbacktrace;
371}
372
373 static void
374do_setdebugtracelevel(char_u *arg)
375{
376 int level;
377
378 level = atoi((char *)arg);
379 if (*arg == '+' || level < 0)
380 debug_backtrace_level += level;
381 else
382 debug_backtrace_level = level;
383
384 do_checkbacktracelevel();
385}
386
387 static void
388do_checkbacktracelevel(void)
389{
390 if (debug_backtrace_level < 0)
391 {
392 debug_backtrace_level = 0;
Bram Moolenaar32526b32019-01-19 17:43:09 +0100393 msg(_("frame is zero"));
Bram Moolenaarf1f60f82016-01-16 15:40:53 +0100394 }
395 else
396 {
397 int max = get_maxbacktrace_level();
398
399 if (debug_backtrace_level > max)
400 {
401 debug_backtrace_level = max;
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100402 smsg(_("frame at highest level: %d"), max);
Bram Moolenaarf1f60f82016-01-16 15:40:53 +0100403 }
404 }
405}
406
407 static void
408do_showbacktrace(char_u *cmd)
409{
410 char *cur;
411 char *next;
412 int i = 0;
413 int max = get_maxbacktrace_level();
414
415 if (sourcing_name != NULL)
416 {
417 cur = (char *)sourcing_name;
418 while (!got_int)
419 {
420 next = strstr(cur, "..");
421 if (next != NULL)
422 *next = NUL;
423 if (i == max - debug_backtrace_level)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100424 smsg("->%d %s", max - i, cur);
Bram Moolenaarf1f60f82016-01-16 15:40:53 +0100425 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100426 smsg(" %d %s", max - i, cur);
Bram Moolenaarf1f60f82016-01-16 15:40:53 +0100427 ++i;
428 if (next == NULL)
429 break;
430 *next = '.';
431 cur = next + 2;
432 }
433 }
434 if (sourcing_lnum != 0)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100435 smsg(_("line %ld: %s"), (long)sourcing_lnum, cmd);
Bram Moolenaarf1f60f82016-01-16 15:40:53 +0100436 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100437 smsg(_("cmd: %s"), cmd);
Bram Moolenaarf1f60f82016-01-16 15:40:53 +0100438}
439
Bram Moolenaar071d4272004-06-13 20:20:40 +0000440/*
441 * ":debug".
442 */
443 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +0100444ex_debug(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000445{
446 int debug_break_level_save = debug_break_level;
447
448 debug_break_level = 9999;
449 do_cmdline_cmd(eap->arg);
450 debug_break_level = debug_break_level_save;
451}
452
453static char_u *debug_breakpoint_name = NULL;
454static linenr_T debug_breakpoint_lnum;
455
456/*
457 * When debugging or a breakpoint is set on a skipped command, no debug prompt
458 * is shown by do_one_cmd(). This situation is indicated by debug_skipped, and
459 * debug_skipped_name is then set to the source name in the breakpoint case. If
460 * a skipped command decides itself that a debug prompt should be displayed, it
461 * can do so by calling dbg_check_skipped().
462 */
463static int debug_skipped;
464static char_u *debug_skipped_name;
465
466/*
467 * Go to debug mode when a breakpoint was encountered or "ex_nesting_level" is
468 * at or below the break level. But only when the line is actually
469 * executed. Return TRUE and set breakpoint_name for skipped commands that
470 * decide to execute something themselves.
471 * Called from do_one_cmd() before executing a command.
472 */
473 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +0100474dbg_check_breakpoint(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000475{
476 char_u *p;
477
478 debug_skipped = FALSE;
479 if (debug_breakpoint_name != NULL)
480 {
481 if (!eap->skip)
482 {
483 /* replace K_SNR with "<SNR>" */
484 if (debug_breakpoint_name[0] == K_SPECIAL
485 && debug_breakpoint_name[1] == KS_EXTRA
486 && debug_breakpoint_name[2] == (int)KE_SNR)
487 p = (char_u *)"<SNR>";
488 else
489 p = (char_u *)"";
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100490 smsg(_("Breakpoint in \"%s%s\" line %ld"),
Bram Moolenaar555b2802005-05-19 21:08:39 +0000491 p,
Bram Moolenaar071d4272004-06-13 20:20:40 +0000492 debug_breakpoint_name + (*p == NUL ? 0 : 3),
493 (long)debug_breakpoint_lnum);
494 debug_breakpoint_name = NULL;
495 do_debug(eap->cmd);
496 }
497 else
498 {
499 debug_skipped = TRUE;
500 debug_skipped_name = debug_breakpoint_name;
501 debug_breakpoint_name = NULL;
502 }
503 }
504 else if (ex_nesting_level <= debug_break_level)
505 {
506 if (!eap->skip)
507 do_debug(eap->cmd);
508 else
509 {
510 debug_skipped = TRUE;
511 debug_skipped_name = NULL;
512 }
513 }
514}
515
516/*
517 * Go to debug mode if skipped by dbg_check_breakpoint() because eap->skip was
518 * set. Return TRUE when the debug mode is entered this time.
519 */
520 int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +0100521dbg_check_skipped(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000522{
523 int prev_got_int;
524
525 if (debug_skipped)
526 {
527 /*
528 * Save the value of got_int and reset it. We don't want a previous
529 * interruption cause flushing the input buffer.
530 */
531 prev_got_int = got_int;
532 got_int = FALSE;
533 debug_breakpoint_name = debug_skipped_name;
534 /* eap->skip is TRUE */
535 eap->skip = FALSE;
536 (void)dbg_check_breakpoint(eap);
537 eap->skip = TRUE;
538 got_int |= prev_got_int;
539 return TRUE;
540 }
541 return FALSE;
542}
543
544/*
545 * The list of breakpoints: dbg_breakp.
546 * This is a grow-array of structs.
547 */
548struct debuggy
549{
550 int dbg_nr; /* breakpoint number */
Bram Moolenaarc6f9f732018-02-11 19:06:26 +0100551 int dbg_type; /* DBG_FUNC, DBG_FILE or DBG_EXPR */
552 char_u *dbg_name; /* function, expression or file name */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000553 regprog_T *dbg_prog; /* regexp program */
554 linenr_T dbg_lnum; /* line number in function or file */
Bram Moolenaar05159a02005-02-26 23:04:13 +0000555 int dbg_forceit; /* ! used */
Bram Moolenaarc6f9f732018-02-11 19:06:26 +0100556#ifdef FEAT_EVAL
557 typval_T *dbg_val; /* last result of watchexpression */
558#endif
559 int dbg_level; /* stored nested level for expr */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000560};
561
562static garray_T dbg_breakp = {0, 0, sizeof(struct debuggy), 4, NULL};
Bram Moolenaar05159a02005-02-26 23:04:13 +0000563#define BREAKP(idx) (((struct debuggy *)dbg_breakp.ga_data)[idx])
564#define DEBUGGY(gap, idx) (((struct debuggy *)gap->ga_data)[idx])
Bram Moolenaar071d4272004-06-13 20:20:40 +0000565static int last_breakp = 0; /* nr of last defined breakpoint */
566
Bram Moolenaar05159a02005-02-26 23:04:13 +0000567#ifdef FEAT_PROFILE
568/* Profiling uses file and func names similar to breakpoints. */
569static garray_T prof_ga = {0, 0, sizeof(struct debuggy), 4, NULL};
570#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000571#define DBG_FUNC 1
572#define DBG_FILE 2
Bram Moolenaarc6f9f732018-02-11 19:06:26 +0100573#define DBG_EXPR 3
Bram Moolenaar071d4272004-06-13 20:20:40 +0000574
Bram Moolenaarf28dbce2016-01-29 22:03:47 +0100575static linenr_T debuggy_find(int file,char_u *fname, linenr_T after, garray_T *gap, int *fp);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000576
577/*
Bram Moolenaar05159a02005-02-26 23:04:13 +0000578 * Parse the arguments of ":profile", ":breakadd" or ":breakdel" and put them
579 * in the entry just after the last one in dbg_breakp. Note that "dbg_name"
580 * is allocated.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000581 * Returns FAIL for failure.
582 */
583 static int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +0100584dbg_parsearg(
585 char_u *arg,
586 garray_T *gap) /* either &dbg_breakp or &prof_ga */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000587{
588 char_u *p = arg;
589 char_u *q;
590 struct debuggy *bp;
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000591 int here = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000592
Bram Moolenaar05159a02005-02-26 23:04:13 +0000593 if (ga_grow(gap, 1) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000594 return FAIL;
Bram Moolenaar05159a02005-02-26 23:04:13 +0000595 bp = &DEBUGGY(gap, gap->ga_len);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000596
597 /* Find "func" or "file". */
598 if (STRNCMP(p, "func", 4) == 0)
599 bp->dbg_type = DBG_FUNC;
600 else if (STRNCMP(p, "file", 4) == 0)
601 bp->dbg_type = DBG_FILE;
Bram Moolenaar05159a02005-02-26 23:04:13 +0000602 else if (
603#ifdef FEAT_PROFILE
604 gap != &prof_ga &&
605#endif
606 STRNCMP(p, "here", 4) == 0)
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000607 {
608 if (curbuf->b_ffname == NULL)
609 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100610 emsg(_(e_noname));
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000611 return FAIL;
612 }
613 bp->dbg_type = DBG_FILE;
614 here = TRUE;
615 }
Bram Moolenaarc6f9f732018-02-11 19:06:26 +0100616 else if (
617#ifdef FEAT_PROFILE
618 gap != &prof_ga &&
619#endif
620 STRNCMP(p, "expr", 4) == 0)
621 bp->dbg_type = DBG_EXPR;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000622 else
623 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100624 semsg(_(e_invarg2), p);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000625 return FAIL;
626 }
627 p = skipwhite(p + 4);
628
629 /* Find optional line number. */
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000630 if (here)
631 bp->dbg_lnum = curwin->w_cursor.lnum;
Bram Moolenaar05159a02005-02-26 23:04:13 +0000632 else if (
633#ifdef FEAT_PROFILE
634 gap != &prof_ga &&
635#endif
636 VIM_ISDIGIT(*p))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000637 {
638 bp->dbg_lnum = getdigits(&p);
639 p = skipwhite(p);
640 }
641 else
642 bp->dbg_lnum = 0;
643
644 /* Find the function or file name. Don't accept a function name with (). */
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000645 if ((!here && *p == NUL)
646 || (here && *p != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000647 || (bp->dbg_type == DBG_FUNC && strstr((char *)p, "()") != NULL))
648 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100649 semsg(_(e_invarg2), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000650 return FAIL;
651 }
652
653 if (bp->dbg_type == DBG_FUNC)
654 bp->dbg_name = vim_strsave(p);
Bram Moolenaarf4b8e572004-06-24 15:53:16 +0000655 else if (here)
656 bp->dbg_name = vim_strsave(curbuf->b_ffname);
Bram Moolenaarc6f9f732018-02-11 19:06:26 +0100657 else if (bp->dbg_type == DBG_EXPR)
658 {
659 bp->dbg_name = vim_strsave(p);
660 if (bp->dbg_name != NULL)
661 bp->dbg_val = eval_expr(bp->dbg_name, NULL);
662 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000663 else
664 {
665 /* Expand the file name in the same way as do_source(). This means
666 * doing it twice, so that $DIR/file gets expanded when $DIR is
667 * "~/dir". */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000668 q = expand_env_save(p);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000669 if (q == NULL)
670 return FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000671 p = expand_env_save(q);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000672 vim_free(q);
673 if (p == NULL)
674 return FAIL;
Bram Moolenaar843ee412004-06-30 16:16:41 +0000675 if (*p != '*')
676 {
677 bp->dbg_name = fix_fname(p);
678 vim_free(p);
679 }
680 else
681 bp->dbg_name = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000682 }
683
684 if (bp->dbg_name == NULL)
685 return FAIL;
686 return OK;
687}
688
689/*
Bram Moolenaare0be1672018-07-08 16:50:37 +0200690 * ":breakadd". Also used for ":profile".
Bram Moolenaar071d4272004-06-13 20:20:40 +0000691 */
692 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +0100693ex_breakadd(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000694{
695 struct debuggy *bp;
696 char_u *pat;
Bram Moolenaar05159a02005-02-26 23:04:13 +0000697 garray_T *gap;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000698
Bram Moolenaar05159a02005-02-26 23:04:13 +0000699 gap = &dbg_breakp;
700#ifdef FEAT_PROFILE
701 if (eap->cmdidx == CMD_profile)
702 gap = &prof_ga;
703#endif
704
705 if (dbg_parsearg(eap->arg, gap) == OK)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000706 {
Bram Moolenaar05159a02005-02-26 23:04:13 +0000707 bp = &DEBUGGY(gap, gap->ga_len);
708 bp->dbg_forceit = eap->forceit;
709
Bram Moolenaarc6f9f732018-02-11 19:06:26 +0100710 if (bp->dbg_type != DBG_EXPR)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000711 {
Bram Moolenaarc6f9f732018-02-11 19:06:26 +0100712 pat = file_pat_to_reg_pat(bp->dbg_name, NULL, NULL, FALSE);
713 if (pat != NULL)
714 {
715 bp->dbg_prog = vim_regcomp(pat, RE_MAGIC + RE_STRING);
716 vim_free(pat);
717 }
718 if (pat == NULL || bp->dbg_prog == NULL)
719 vim_free(bp->dbg_name);
720 else
721 {
722 if (bp->dbg_lnum == 0) /* default line number is 1 */
723 bp->dbg_lnum = 1;
724#ifdef FEAT_PROFILE
725 if (eap->cmdidx != CMD_profile)
726#endif
727 {
728 DEBUGGY(gap, gap->ga_len).dbg_nr = ++last_breakp;
729 ++debug_tick;
730 }
731 ++gap->ga_len;
732 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000733 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000734 else
735 {
Bram Moolenaarc6f9f732018-02-11 19:06:26 +0100736 /* DBG_EXPR */
737 DEBUGGY(gap, gap->ga_len++).dbg_nr = ++last_breakp;
738 ++debug_tick;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000739 }
740 }
741}
742
743/*
744 * ":debuggreedy".
745 */
746 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +0100747ex_debuggreedy(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000748{
749 if (eap->addr_count == 0 || eap->line2 != 0)
750 debug_greedy = TRUE;
751 else
752 debug_greedy = FALSE;
753}
754
755/*
Bram Moolenaard9fba312005-06-26 22:34:35 +0000756 * ":breakdel" and ":profdel".
Bram Moolenaar071d4272004-06-13 20:20:40 +0000757 */
758 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +0100759ex_breakdel(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000760{
761 struct debuggy *bp, *bpi;
762 int nr;
763 int todel = -1;
Bram Moolenaarf461c8e2005-06-25 23:04:51 +0000764 int del_all = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000765 int i;
766 linenr_T best_lnum = 0;
Bram Moolenaard9fba312005-06-26 22:34:35 +0000767 garray_T *gap;
768
769 gap = &dbg_breakp;
Bram Moolenaard9fba312005-06-26 22:34:35 +0000770 if (eap->cmdidx == CMD_profdel)
Bram Moolenaar38bdbd62012-06-20 15:48:57 +0200771 {
772#ifdef FEAT_PROFILE
Bram Moolenaard9fba312005-06-26 22:34:35 +0000773 gap = &prof_ga;
Bram Moolenaar38bdbd62012-06-20 15:48:57 +0200774#else
775 ex_ni(eap);
776 return;
Bram Moolenaard9fba312005-06-26 22:34:35 +0000777#endif
Bram Moolenaar38bdbd62012-06-20 15:48:57 +0200778 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000779
780 if (vim_isdigit(*eap->arg))
781 {
782 /* ":breakdel {nr}" */
783 nr = atol((char *)eap->arg);
Bram Moolenaard9fba312005-06-26 22:34:35 +0000784 for (i = 0; i < gap->ga_len; ++i)
785 if (DEBUGGY(gap, i).dbg_nr == nr)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000786 {
787 todel = i;
788 break;
789 }
790 }
Bram Moolenaarf461c8e2005-06-25 23:04:51 +0000791 else if (*eap->arg == '*')
792 {
793 todel = 0;
794 del_all = TRUE;
795 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000796 else
797 {
Bram Moolenaarc6f9f732018-02-11 19:06:26 +0100798 /* ":breakdel {func|file|expr} [lnum] {name}" */
Bram Moolenaard9fba312005-06-26 22:34:35 +0000799 if (dbg_parsearg(eap->arg, gap) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000800 return;
Bram Moolenaard9fba312005-06-26 22:34:35 +0000801 bp = &DEBUGGY(gap, gap->ga_len);
802 for (i = 0; i < gap->ga_len; ++i)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000803 {
Bram Moolenaard9fba312005-06-26 22:34:35 +0000804 bpi = &DEBUGGY(gap, i);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000805 if (bp->dbg_type == bpi->dbg_type
806 && STRCMP(bp->dbg_name, bpi->dbg_name) == 0
807 && (bp->dbg_lnum == bpi->dbg_lnum
808 || (bp->dbg_lnum == 0
809 && (best_lnum == 0
810 || bpi->dbg_lnum < best_lnum))))
811 {
812 todel = i;
813 best_lnum = bpi->dbg_lnum;
814 }
815 }
816 vim_free(bp->dbg_name);
817 }
818
819 if (todel < 0)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100820 semsg(_("E161: Breakpoint not found: %s"), eap->arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000821 else
Bram Moolenaard9fba312005-06-26 22:34:35 +0000822 {
823 while (gap->ga_len > 0)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +0000824 {
Bram Moolenaard9fba312005-06-26 22:34:35 +0000825 vim_free(DEBUGGY(gap, todel).dbg_name);
Bram Moolenaarc6f9f732018-02-11 19:06:26 +0100826#ifdef FEAT_EVAL
827 if (DEBUGGY(gap, todel).dbg_type == DBG_EXPR
828 && DEBUGGY(gap, todel).dbg_val != NULL)
829 free_tv(DEBUGGY(gap, todel).dbg_val);
830#endif
Bram Moolenaar473de612013-06-08 18:19:48 +0200831 vim_regfree(DEBUGGY(gap, todel).dbg_prog);
Bram Moolenaard9fba312005-06-26 22:34:35 +0000832 --gap->ga_len;
833 if (todel < gap->ga_len)
834 mch_memmove(&DEBUGGY(gap, todel), &DEBUGGY(gap, todel + 1),
835 (gap->ga_len - todel) * sizeof(struct debuggy));
836#ifdef FEAT_PROFILE
837 if (eap->cmdidx == CMD_breakdel)
838#endif
839 ++debug_tick;
Bram Moolenaarf461c8e2005-06-25 23:04:51 +0000840 if (!del_all)
841 break;
842 }
Bram Moolenaard9fba312005-06-26 22:34:35 +0000843
844 /* If all breakpoints were removed clear the array. */
845 if (gap->ga_len == 0)
846 ga_clear(gap);
847 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000848}
849
850/*
851 * ":breaklist".
852 */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000853 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +0100854ex_breaklist(exarg_T *eap UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000855{
856 struct debuggy *bp;
857 int i;
858
859 if (dbg_breakp.ga_len == 0)
Bram Moolenaar32526b32019-01-19 17:43:09 +0100860 msg(_("No breakpoints defined"));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000861 else
862 for (i = 0; i < dbg_breakp.ga_len; ++i)
863 {
864 bp = &BREAKP(i);
Bram Moolenaard58ea072011-06-26 04:25:30 +0200865 if (bp->dbg_type == DBG_FILE)
866 home_replace(NULL, bp->dbg_name, NameBuff, MAXPATHL, TRUE);
Bram Moolenaarc6f9f732018-02-11 19:06:26 +0100867 if (bp->dbg_type != DBG_EXPR)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100868 smsg(_("%3d %s %s line %ld"),
Bram Moolenaar071d4272004-06-13 20:20:40 +0000869 bp->dbg_nr,
870 bp->dbg_type == DBG_FUNC ? "func" : "file",
Bram Moolenaard58ea072011-06-26 04:25:30 +0200871 bp->dbg_type == DBG_FUNC ? bp->dbg_name : NameBuff,
Bram Moolenaar071d4272004-06-13 20:20:40 +0000872 (long)bp->dbg_lnum);
Bram Moolenaarc6f9f732018-02-11 19:06:26 +0100873 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +0100874 smsg(_("%3d expr %s"),
Bram Moolenaarc6f9f732018-02-11 19:06:26 +0100875 bp->dbg_nr, bp->dbg_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000876 }
877}
878
879/*
880 * Find a breakpoint for a function or sourced file.
881 * Returns line number at which to break; zero when no matching breakpoint.
882 */
883 linenr_T
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +0100884dbg_find_breakpoint(
885 int file, /* TRUE for a file, FALSE for a function */
886 char_u *fname, /* file or function name */
887 linenr_T after) /* after this line number */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000888{
Bram Moolenaar05159a02005-02-26 23:04:13 +0000889 return debuggy_find(file, fname, after, &dbg_breakp, NULL);
890}
891
892#if defined(FEAT_PROFILE) || defined(PROTO)
893/*
894 * Return TRUE if profiling is on for a function or sourced file.
895 */
896 int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +0100897has_profiling(
898 int file, /* TRUE for a file, FALSE for a function */
899 char_u *fname, /* file or function name */
900 int *fp) /* return: forceit */
Bram Moolenaar05159a02005-02-26 23:04:13 +0000901{
902 return (debuggy_find(file, fname, (linenr_T)0, &prof_ga, fp)
903 != (linenr_T)0);
904}
905#endif
906
907/*
908 * Common code for dbg_find_breakpoint() and has_profiling().
909 */
910 static linenr_T
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +0100911debuggy_find(
912 int file, /* TRUE for a file, FALSE for a function */
913 char_u *fname, /* file or function name */
914 linenr_T after, /* after this line number */
915 garray_T *gap, /* either &dbg_breakp or &prof_ga */
916 int *fp) /* if not NULL: return forceit */
Bram Moolenaar05159a02005-02-26 23:04:13 +0000917{
Bram Moolenaar071d4272004-06-13 20:20:40 +0000918 struct debuggy *bp;
919 int i;
920 linenr_T lnum = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000921 char_u *name = fname;
922 int prev_got_int;
923
Bram Moolenaar05159a02005-02-26 23:04:13 +0000924 /* Return quickly when there are no breakpoints. */
925 if (gap->ga_len == 0)
926 return (linenr_T)0;
927
Bram Moolenaar071d4272004-06-13 20:20:40 +0000928 /* Replace K_SNR in function name with "<SNR>". */
929 if (!file && fname[0] == K_SPECIAL)
930 {
931 name = alloc((unsigned)STRLEN(fname) + 3);
932 if (name == NULL)
933 name = fname;
934 else
935 {
936 STRCPY(name, "<SNR>");
937 STRCPY(name + 5, fname + 3);
938 }
939 }
940
Bram Moolenaar05159a02005-02-26 23:04:13 +0000941 for (i = 0; i < gap->ga_len; ++i)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000942 {
Bram Moolenaar05159a02005-02-26 23:04:13 +0000943 /* Skip entries that are not useful or are for a line that is beyond
944 * an already found breakpoint. */
945 bp = &DEBUGGY(gap, i);
Bram Moolenaarc6f9f732018-02-11 19:06:26 +0100946 if (((bp->dbg_type == DBG_FILE) == file &&
947 bp->dbg_type != DBG_EXPR && (
Bram Moolenaar05159a02005-02-26 23:04:13 +0000948#ifdef FEAT_PROFILE
949 gap == &prof_ga ||
950#endif
951 (bp->dbg_lnum > after && (lnum == 0 || bp->dbg_lnum < lnum)))))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000952 {
Bram Moolenaar071d4272004-06-13 20:20:40 +0000953 /*
Bram Moolenaar05159a02005-02-26 23:04:13 +0000954 * Save the value of got_int and reset it. We don't want a
955 * previous interruption cancel matching, only hitting CTRL-C
956 * while matching should abort it.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000957 */
958 prev_got_int = got_int;
959 got_int = FALSE;
Bram Moolenaardffa5b82014-11-19 16:38:07 +0100960 if (vim_regexec_prog(&bp->dbg_prog, FALSE, name, (colnr_T)0))
Bram Moolenaar05159a02005-02-26 23:04:13 +0000961 {
Bram Moolenaar071d4272004-06-13 20:20:40 +0000962 lnum = bp->dbg_lnum;
Bram Moolenaar05159a02005-02-26 23:04:13 +0000963 if (fp != NULL)
964 *fp = bp->dbg_forceit;
965 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000966 got_int |= prev_got_int;
967 }
Bram Moolenaarc6f9f732018-02-11 19:06:26 +0100968#ifdef FEAT_EVAL
969 else if (bp->dbg_type == DBG_EXPR)
970 {
971 typval_T *tv;
972 int line = FALSE;
973
974 prev_got_int = got_int;
975 got_int = FALSE;
976
977 tv = eval_expr(bp->dbg_name, NULL);
978 if (tv != NULL)
979 {
980 if (bp->dbg_val == NULL)
981 {
982 debug_oldval = typval_tostring(NULL);
983 bp->dbg_val = tv;
984 debug_newval = typval_tostring(bp->dbg_val);
985 line = TRUE;
986 }
987 else
988 {
Bram Moolenaar31988702018-02-13 12:57:42 +0100989 if (typval_compare(tv, bp->dbg_val, TYPE_EQUAL,
990 TRUE, FALSE) == OK
991 && tv->vval.v_number == FALSE)
Bram Moolenaarc6f9f732018-02-11 19:06:26 +0100992 {
Bram Moolenaar31988702018-02-13 12:57:42 +0100993 typval_T *v;
Bram Moolenaarc6f9f732018-02-11 19:06:26 +0100994
Bram Moolenaar31988702018-02-13 12:57:42 +0100995 line = TRUE;
996 debug_oldval = typval_tostring(bp->dbg_val);
997 /* Need to evaluate again, typval_compare() overwrites
998 * "tv". */
999 v = eval_expr(bp->dbg_name, NULL);
1000 debug_newval = typval_tostring(v);
1001 free_tv(bp->dbg_val);
1002 bp->dbg_val = v;
Bram Moolenaarc6f9f732018-02-11 19:06:26 +01001003 }
1004 free_tv(tv);
1005 }
1006 }
1007 else if (bp->dbg_val != NULL)
1008 {
1009 debug_oldval = typval_tostring(bp->dbg_val);
1010 debug_newval = typval_tostring(NULL);
1011 free_tv(bp->dbg_val);
1012 bp->dbg_val = NULL;
1013 line = TRUE;
1014 }
1015
1016 if (line)
1017 {
1018 lnum = after > 0 ? after : 1;
1019 break;
1020 }
1021
1022 got_int |= prev_got_int;
1023 }
1024#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001025 }
1026 if (name != fname)
1027 vim_free(name);
1028
1029 return lnum;
1030}
1031
1032/*
1033 * Called when a breakpoint was encountered.
1034 */
1035 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001036dbg_breakpoint(char_u *name, linenr_T lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001037{
1038 /* We need to check if this line is actually executed in do_one_cmd() */
1039 debug_breakpoint_name = name;
1040 debug_breakpoint_lnum = lnum;
1041}
Bram Moolenaar05159a02005-02-26 23:04:13 +00001042
1043
Bram Moolenaar433f7c82006-03-21 21:29:36 +00001044# if defined(FEAT_PROFILE) || defined(FEAT_RELTIME) || defined(PROTO)
Bram Moolenaar05159a02005-02-26 23:04:13 +00001045/*
1046 * Store the current time in "tm".
1047 */
1048 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001049profile_start(proftime_T *tm)
Bram Moolenaar05159a02005-02-26 23:04:13 +00001050{
Bram Moolenaar8cd06ca2005-02-28 22:44:58 +00001051# ifdef WIN3264
1052 QueryPerformanceCounter(tm);
1053# else
Bram Moolenaar05159a02005-02-26 23:04:13 +00001054 gettimeofday(tm, NULL);
Bram Moolenaar8cd06ca2005-02-28 22:44:58 +00001055# endif
Bram Moolenaar05159a02005-02-26 23:04:13 +00001056}
1057
1058/*
1059 * Compute the elapsed time from "tm" till now and store in "tm".
1060 */
1061 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001062profile_end(proftime_T *tm)
Bram Moolenaar05159a02005-02-26 23:04:13 +00001063{
1064 proftime_T now;
1065
Bram Moolenaar8cd06ca2005-02-28 22:44:58 +00001066# ifdef WIN3264
1067 QueryPerformanceCounter(&now);
1068 tm->QuadPart = now.QuadPart - tm->QuadPart;
1069# else
Bram Moolenaar05159a02005-02-26 23:04:13 +00001070 gettimeofday(&now, NULL);
1071 tm->tv_usec = now.tv_usec - tm->tv_usec;
1072 tm->tv_sec = now.tv_sec - tm->tv_sec;
1073 if (tm->tv_usec < 0)
1074 {
1075 tm->tv_usec += 1000000;
1076 --tm->tv_sec;
1077 }
Bram Moolenaar8cd06ca2005-02-28 22:44:58 +00001078# endif
Bram Moolenaar05159a02005-02-26 23:04:13 +00001079}
1080
1081/*
1082 * Subtract the time "tm2" from "tm".
1083 */
1084 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001085profile_sub(proftime_T *tm, proftime_T *tm2)
Bram Moolenaar05159a02005-02-26 23:04:13 +00001086{
Bram Moolenaar8cd06ca2005-02-28 22:44:58 +00001087# ifdef WIN3264
1088 tm->QuadPart -= tm2->QuadPart;
1089# else
Bram Moolenaar05159a02005-02-26 23:04:13 +00001090 tm->tv_usec -= tm2->tv_usec;
1091 tm->tv_sec -= tm2->tv_sec;
1092 if (tm->tv_usec < 0)
1093 {
1094 tm->tv_usec += 1000000;
1095 --tm->tv_sec;
1096 }
Bram Moolenaar8cd06ca2005-02-28 22:44:58 +00001097# endif
Bram Moolenaar05159a02005-02-26 23:04:13 +00001098}
1099
1100/*
Bram Moolenaar433f7c82006-03-21 21:29:36 +00001101 * Return a string that represents the time in "tm".
1102 * Uses a static buffer!
1103 */
1104 char *
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001105profile_msg(proftime_T *tm)
Bram Moolenaar433f7c82006-03-21 21:29:36 +00001106{
1107 static char buf[50];
1108
1109# ifdef WIN3264
1110 LARGE_INTEGER fr;
1111
1112 QueryPerformanceFrequency(&fr);
1113 sprintf(buf, "%10.6lf", (double)tm->QuadPart / (double)fr.QuadPart);
1114# else
1115 sprintf(buf, "%3ld.%06ld", (long)tm->tv_sec, (long)tm->tv_usec);
Bram Moolenaar76929292008-01-06 19:07:36 +00001116# endif
Bram Moolenaar433f7c82006-03-21 21:29:36 +00001117 return buf;
1118}
1119
Bram Moolenaar79c2c882016-02-07 21:19:28 +01001120# if defined(FEAT_FLOAT) || defined(PROTO)
1121/*
1122 * Return a float that represents the time in "tm".
1123 */
1124 float_T
1125profile_float(proftime_T *tm)
1126{
1127# ifdef WIN3264
1128 LARGE_INTEGER fr;
1129
1130 QueryPerformanceFrequency(&fr);
1131 return (float_T)tm->QuadPart / (float_T)fr.QuadPart;
1132# else
1133 return (float_T)tm->tv_sec + (float_T)tm->tv_usec / 1000000.0;
1134# endif
1135}
1136# endif
1137
Bram Moolenaar433f7c82006-03-21 21:29:36 +00001138/*
Bram Moolenaar76929292008-01-06 19:07:36 +00001139 * Put the time "msec" past now in "tm".
Bram Moolenaar433f7c82006-03-21 21:29:36 +00001140 */
Bram Moolenaar76929292008-01-06 19:07:36 +00001141 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001142profile_setlimit(long msec, proftime_T *tm)
Bram Moolenaar76929292008-01-06 19:07:36 +00001143{
1144 if (msec <= 0) /* no limit */
1145 profile_zero(tm);
1146 else
1147 {
1148# ifdef WIN3264
1149 LARGE_INTEGER fr;
1150
1151 QueryPerformanceCounter(tm);
1152 QueryPerformanceFrequency(&fr);
Bram Moolenaarb3c70982008-01-18 10:40:55 +00001153 tm->QuadPart += (LONGLONG)((double)msec / 1000.0 * (double)fr.QuadPart);
Bram Moolenaar76929292008-01-06 19:07:36 +00001154# else
1155 long usec;
1156
1157 gettimeofday(tm, NULL);
1158 usec = (long)tm->tv_usec + (long)msec * 1000;
1159 tm->tv_usec = usec % 1000000L;
1160 tm->tv_sec += usec / 1000000L;
1161# endif
1162 }
1163}
1164
1165/*
1166 * Return TRUE if the current time is past "tm".
1167 */
1168 int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001169profile_passed_limit(proftime_T *tm)
Bram Moolenaar76929292008-01-06 19:07:36 +00001170{
1171 proftime_T now;
1172
1173# ifdef WIN3264
1174 if (tm->QuadPart == 0) /* timer was not set */
1175 return FALSE;
1176 QueryPerformanceCounter(&now);
1177 return (now.QuadPart > tm->QuadPart);
1178# else
1179 if (tm->tv_sec == 0) /* timer was not set */
1180 return FALSE;
1181 gettimeofday(&now, NULL);
1182 return (now.tv_sec > tm->tv_sec
1183 || (now.tv_sec == tm->tv_sec && now.tv_usec > tm->tv_usec));
1184# endif
1185}
Bram Moolenaar433f7c82006-03-21 21:29:36 +00001186
1187/*
1188 * Set the time in "tm" to zero.
1189 */
1190 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001191profile_zero(proftime_T *tm)
Bram Moolenaar433f7c82006-03-21 21:29:36 +00001192{
1193# ifdef WIN3264
1194 tm->QuadPart = 0;
1195# else
1196 tm->tv_usec = 0;
1197 tm->tv_sec = 0;
1198# endif
1199}
1200
Bram Moolenaar76929292008-01-06 19:07:36 +00001201# endif /* FEAT_PROFILE || FEAT_RELTIME */
1202
Bram Moolenaar975b5272016-03-15 23:10:59 +01001203# if defined(FEAT_TIMERS) || defined(PROTO)
1204static timer_T *first_timer = NULL;
Bram Moolenaar75537a92016-09-05 22:45:28 +02001205static long last_timer_id = 0;
Bram Moolenaar975b5272016-03-15 23:10:59 +01001206
Bram Moolenaar56bc8e22018-05-10 18:05:56 +02001207 long
Bram Moolenaar51b0f372017-11-18 18:52:04 +01001208proftime_time_left(proftime_T *due, proftime_T *now)
Bram Moolenaara8e93d62017-09-18 21:50:47 +02001209{
1210# ifdef WIN3264
1211 LARGE_INTEGER fr;
1212
Bram Moolenaar51b0f372017-11-18 18:52:04 +01001213 if (now->QuadPart > due->QuadPart)
Bram Moolenaara8e93d62017-09-18 21:50:47 +02001214 return 0;
1215 QueryPerformanceFrequency(&fr);
Bram Moolenaar51b0f372017-11-18 18:52:04 +01001216 return (long)(((double)(due->QuadPart - now->QuadPart)
Bram Moolenaara8e93d62017-09-18 21:50:47 +02001217 / (double)fr.QuadPart) * 1000);
1218# else
Bram Moolenaar51b0f372017-11-18 18:52:04 +01001219 if (now->tv_sec > due->tv_sec)
Bram Moolenaara8e93d62017-09-18 21:50:47 +02001220 return 0;
Bram Moolenaar51b0f372017-11-18 18:52:04 +01001221 return (due->tv_sec - now->tv_sec) * 1000
1222 + (due->tv_usec - now->tv_usec) / 1000;
Bram Moolenaara8e93d62017-09-18 21:50:47 +02001223# endif
1224}
Bram Moolenaar417ccd72016-09-01 21:26:20 +02001225
Bram Moolenaar975b5272016-03-15 23:10:59 +01001226/*
1227 * Insert a timer in the list of timers.
1228 */
1229 static void
1230insert_timer(timer_T *timer)
1231{
1232 timer->tr_next = first_timer;
1233 timer->tr_prev = NULL;
1234 if (first_timer != NULL)
1235 first_timer->tr_prev = timer;
1236 first_timer = timer;
Bram Moolenaar4231da42016-06-02 14:30:04 +02001237 did_add_timer = TRUE;
Bram Moolenaar975b5272016-03-15 23:10:59 +01001238}
1239
1240/*
1241 * Take a timer out of the list of timers.
1242 */
1243 static void
1244remove_timer(timer_T *timer)
1245{
1246 if (timer->tr_prev == NULL)
1247 first_timer = timer->tr_next;
1248 else
1249 timer->tr_prev->tr_next = timer->tr_next;
1250 if (timer->tr_next != NULL)
1251 timer->tr_next->tr_prev = timer->tr_prev;
1252}
1253
1254 static void
1255free_timer(timer_T *timer)
1256{
Bram Moolenaar75537a92016-09-05 22:45:28 +02001257 free_callback(timer->tr_callback, timer->tr_partial);
1258 vim_free(timer);
Bram Moolenaar975b5272016-03-15 23:10:59 +01001259}
1260
1261/*
1262 * Create a timer and return it. NULL if out of memory.
1263 * Caller should set the callback.
1264 */
1265 timer_T *
1266create_timer(long msec, int repeat)
1267{
1268 timer_T *timer = (timer_T *)alloc_clear(sizeof(timer_T));
Bram Moolenaaree39ef02016-09-10 19:17:42 +02001269 long prev_id = last_timer_id;
Bram Moolenaar975b5272016-03-15 23:10:59 +01001270
1271 if (timer == NULL)
1272 return NULL;
Bram Moolenaaree39ef02016-09-10 19:17:42 +02001273 if (++last_timer_id <= prev_id)
Bram Moolenaar75537a92016-09-05 22:45:28 +02001274 /* Overflow! Might cause duplicates... */
1275 last_timer_id = 0;
1276 timer->tr_id = last_timer_id;
Bram Moolenaar975b5272016-03-15 23:10:59 +01001277 insert_timer(timer);
1278 if (repeat != 0)
Bram Moolenaar975b5272016-03-15 23:10:59 +01001279 timer->tr_repeat = repeat - 1;
Bram Moolenaar8e97bd72016-08-06 22:05:07 +02001280 timer->tr_interval = msec;
Bram Moolenaar975b5272016-03-15 23:10:59 +01001281
1282 profile_setlimit(msec, &timer->tr_due);
1283 return timer;
1284}
1285
1286/*
1287 * Invoke the callback of "timer".
1288 */
1289 static void
1290timer_callback(timer_T *timer)
1291{
1292 typval_T rettv;
1293 int dummy;
1294 typval_T argv[2];
1295
1296 argv[0].v_type = VAR_NUMBER;
Bram Moolenaar75537a92016-09-05 22:45:28 +02001297 argv[0].vval.v_number = (varnumber_T)timer->tr_id;
Bram Moolenaar975b5272016-03-15 23:10:59 +01001298 argv[1].v_type = VAR_UNKNOWN;
1299
1300 call_func(timer->tr_callback, (int)STRLEN(timer->tr_callback),
Bram Moolenaardf48fb42016-07-22 21:50:18 +02001301 &rettv, 1, argv, NULL, 0L, 0L, &dummy, TRUE,
Bram Moolenaar975b5272016-03-15 23:10:59 +01001302 timer->tr_partial, NULL);
1303 clear_tv(&rettv);
1304}
1305
1306/*
1307 * Call timers that are due.
1308 * Return the time in msec until the next timer is due.
Bram Moolenaarc4f83382017-07-07 14:50:44 +02001309 * Returns -1 if there are no pending timers.
Bram Moolenaar975b5272016-03-15 23:10:59 +01001310 */
1311 long
Bram Moolenaarcf089462016-06-12 21:18:43 +02001312check_due_timer(void)
Bram Moolenaar975b5272016-03-15 23:10:59 +01001313{
1314 timer_T *timer;
Bram Moolenaar75537a92016-09-05 22:45:28 +02001315 timer_T *timer_next;
Bram Moolenaar975b5272016-03-15 23:10:59 +01001316 long this_due;
Bram Moolenaar597385a2016-03-16 23:24:43 +01001317 long next_due = -1;
Bram Moolenaar975b5272016-03-15 23:10:59 +01001318 proftime_T now;
1319 int did_one = FALSE;
Bram Moolenaar02e177d2017-08-26 23:43:28 +02001320 int need_update_screen = FALSE;
Bram Moolenaar75537a92016-09-05 22:45:28 +02001321 long current_id = last_timer_id;
Bram Moolenaar975b5272016-03-15 23:10:59 +01001322
Bram Moolenaarc577d812017-07-08 22:37:34 +02001323 /* Don't run any timers while exiting or dealing with an error. */
1324 if (exiting || aborting())
Bram Moolenaarc4f83382017-07-07 14:50:44 +02001325 return next_due;
1326
Bram Moolenaar75537a92016-09-05 22:45:28 +02001327 profile_start(&now);
1328 for (timer = first_timer; timer != NULL && !got_int; timer = timer_next)
Bram Moolenaar975b5272016-03-15 23:10:59 +01001329 {
Bram Moolenaar75537a92016-09-05 22:45:28 +02001330 timer_next = timer->tr_next;
Bram Moolenaar417ccd72016-09-01 21:26:20 +02001331
Bram Moolenaar75537a92016-09-05 22:45:28 +02001332 if (timer->tr_id == -1 || timer->tr_firing || timer->tr_paused)
1333 continue;
Bram Moolenaar51b0f372017-11-18 18:52:04 +01001334 this_due = proftime_time_left(&timer->tr_due, &now);
Bram Moolenaar75537a92016-09-05 22:45:28 +02001335 if (this_due <= 1)
1336 {
Bram Moolenaarb0f42ba2018-05-12 15:38:26 +02001337 /* Save and restore a lot of flags, because the timer fires while
1338 * waiting for a character, which might be halfway a command. */
Bram Moolenaar1e8e1452017-06-24 16:03:06 +02001339 int save_timer_busy = timer_busy;
1340 int save_vgetc_busy = vgetc_busy;
Bram Moolenaare723c422017-09-06 23:40:10 +02001341 int save_did_emsg = did_emsg;
1342 int save_called_emsg = called_emsg;
Bram Moolenaar02e177d2017-08-26 23:43:28 +02001343 int save_must_redraw = must_redraw;
Bram Moolenaare723c422017-09-06 23:40:10 +02001344 int save_trylevel = trylevel;
1345 int save_did_throw = did_throw;
Bram Moolenaarf5291f32017-09-14 22:55:37 +02001346 int save_ex_pressedreturn = get_pressedreturn();
Bram Moolenaare723c422017-09-06 23:40:10 +02001347 except_T *save_current_exception = current_exception;
Bram Moolenaarb0f42ba2018-05-12 15:38:26 +02001348 vimvars_save_T vvsave;
Bram Moolenaar1e8e1452017-06-24 16:03:06 +02001349
Bram Moolenaare723c422017-09-06 23:40:10 +02001350 /* Create a scope for running the timer callback, ignoring most of
1351 * the current scope, such as being inside a try/catch. */
Bram Moolenaar1e8e1452017-06-24 16:03:06 +02001352 timer_busy = timer_busy > 0 || vgetc_busy > 0;
1353 vgetc_busy = 0;
Bram Moolenaarc577d812017-07-08 22:37:34 +02001354 called_emsg = FALSE;
Bram Moolenaare723c422017-09-06 23:40:10 +02001355 did_emsg = FALSE;
1356 did_uncaught_emsg = FALSE;
Bram Moolenaar02e177d2017-08-26 23:43:28 +02001357 must_redraw = 0;
Bram Moolenaare723c422017-09-06 23:40:10 +02001358 trylevel = 0;
1359 did_throw = FALSE;
1360 current_exception = NULL;
Bram Moolenaarb0f42ba2018-05-12 15:38:26 +02001361 save_vimvars(&vvsave);
Bram Moolenaare723c422017-09-06 23:40:10 +02001362
Bram Moolenaar75537a92016-09-05 22:45:28 +02001363 timer->tr_firing = TRUE;
1364 timer_callback(timer);
1365 timer->tr_firing = FALSE;
Bram Moolenaare723c422017-09-06 23:40:10 +02001366
Bram Moolenaar75537a92016-09-05 22:45:28 +02001367 timer_next = timer->tr_next;
1368 did_one = TRUE;
Bram Moolenaar1e8e1452017-06-24 16:03:06 +02001369 timer_busy = save_timer_busy;
1370 vgetc_busy = save_vgetc_busy;
Bram Moolenaare723c422017-09-06 23:40:10 +02001371 if (did_uncaught_emsg)
Bram Moolenaarc577d812017-07-08 22:37:34 +02001372 ++timer->tr_emsg_count;
Bram Moolenaare723c422017-09-06 23:40:10 +02001373 did_emsg = save_did_emsg;
1374 called_emsg = save_called_emsg;
1375 trylevel = save_trylevel;
1376 did_throw = save_did_throw;
1377 current_exception = save_current_exception;
Bram Moolenaarb0f42ba2018-05-12 15:38:26 +02001378 restore_vimvars(&vvsave);
Bram Moolenaar02e177d2017-08-26 23:43:28 +02001379 if (must_redraw != 0)
1380 need_update_screen = TRUE;
1381 must_redraw = must_redraw > save_must_redraw
1382 ? must_redraw : save_must_redraw;
Bram Moolenaarf5291f32017-09-14 22:55:37 +02001383 set_pressedreturn(save_ex_pressedreturn);
Bram Moolenaar75537a92016-09-05 22:45:28 +02001384
1385 /* Only fire the timer again if it repeats and stop_timer() wasn't
1386 * called while inside the callback (tr_id == -1). */
Bram Moolenaarc577d812017-07-08 22:37:34 +02001387 if (timer->tr_repeat != 0 && timer->tr_id != -1
1388 && timer->tr_emsg_count < 3)
Bram Moolenaar75537a92016-09-05 22:45:28 +02001389 {
1390 profile_setlimit(timer->tr_interval, &timer->tr_due);
Bram Moolenaar51b0f372017-11-18 18:52:04 +01001391 this_due = proftime_time_left(&timer->tr_due, &now);
Bram Moolenaar75537a92016-09-05 22:45:28 +02001392 if (this_due < 1)
1393 this_due = 1;
1394 if (timer->tr_repeat > 0)
1395 --timer->tr_repeat;
Bram Moolenaar975b5272016-03-15 23:10:59 +01001396 }
Bram Moolenaar75537a92016-09-05 22:45:28 +02001397 else
1398 {
1399 this_due = -1;
1400 remove_timer(timer);
1401 free_timer(timer);
1402 }
Bram Moolenaar975b5272016-03-15 23:10:59 +01001403 }
Bram Moolenaar75537a92016-09-05 22:45:28 +02001404 if (this_due > 0 && (next_due == -1 || next_due > this_due))
1405 next_due = this_due;
Bram Moolenaar975b5272016-03-15 23:10:59 +01001406 }
1407
1408 if (did_one)
Bram Moolenaar02e177d2017-08-26 23:43:28 +02001409 redraw_after_callback(need_update_screen);
Bram Moolenaar975b5272016-03-15 23:10:59 +01001410
Bram Moolenaarc3719bd2017-11-18 22:13:31 +01001411#ifdef FEAT_BEVAL_TERM
Bram Moolenaar51b0f372017-11-18 18:52:04 +01001412 if (bevalexpr_due_set)
1413 {
1414 this_due = proftime_time_left(&bevalexpr_due, &now);
1415 if (this_due <= 1)
1416 {
1417 bevalexpr_due_set = FALSE;
Bram Moolenaar51b0f372017-11-18 18:52:04 +01001418 if (balloonEval == NULL)
1419 {
Bram Moolenaarca4b6132018-06-28 12:05:11 +02001420 balloonEval = (BalloonEval *)alloc_clear(sizeof(BalloonEval));
Bram Moolenaar51b0f372017-11-18 18:52:04 +01001421 balloonEvalForTerm = TRUE;
1422 }
1423 if (balloonEval != NULL)
1424 general_beval_cb(balloonEval, 0);
1425 }
Bram Moolenaar1c17ffa2018-04-24 15:19:04 +02001426 else if (next_due == -1 || next_due > this_due)
Bram Moolenaar51b0f372017-11-18 18:52:04 +01001427 next_due = this_due;
1428 }
1429#endif
Bram Moolenaar56bc8e22018-05-10 18:05:56 +02001430#ifdef FEAT_TERMINAL
1431 /* Some terminal windows may need their buffer updated. */
1432 next_due = term_check_timers(next_due, &now);
1433#endif
Bram Moolenaar51b0f372017-11-18 18:52:04 +01001434
Bram Moolenaar75537a92016-09-05 22:45:28 +02001435 return current_id != last_timer_id ? 1 : next_due;
Bram Moolenaar975b5272016-03-15 23:10:59 +01001436}
1437
1438/*
1439 * Find a timer by ID. Returns NULL if not found;
1440 */
1441 timer_T *
Bram Moolenaar75537a92016-09-05 22:45:28 +02001442find_timer(long id)
Bram Moolenaar975b5272016-03-15 23:10:59 +01001443{
1444 timer_T *timer;
1445
Bram Moolenaar75537a92016-09-05 22:45:28 +02001446 if (id >= 0)
1447 {
1448 for (timer = first_timer; timer != NULL; timer = timer->tr_next)
1449 if (timer->tr_id == id)
1450 return timer;
1451 }
1452 return NULL;
Bram Moolenaar975b5272016-03-15 23:10:59 +01001453}
1454
1455
1456/*
1457 * Stop a timer and delete it.
1458 */
1459 void
1460stop_timer(timer_T *timer)
1461{
Bram Moolenaar75537a92016-09-05 22:45:28 +02001462 if (timer->tr_firing)
1463 /* Free the timer after the callback returns. */
1464 timer->tr_id = -1;
1465 else
1466 {
1467 remove_timer(timer);
1468 free_timer(timer);
1469 }
Bram Moolenaar975b5272016-03-15 23:10:59 +01001470}
Bram Moolenaare3188e22016-05-31 21:13:04 +02001471
Bram Moolenaar8e97bd72016-08-06 22:05:07 +02001472 void
Bram Moolenaarb73598e2016-08-07 18:22:53 +02001473stop_all_timers(void)
1474{
Bram Moolenaar75537a92016-09-05 22:45:28 +02001475 timer_T *timer;
1476 timer_T *timer_next;
1477
1478 for (timer = first_timer; timer != NULL; timer = timer_next)
1479 {
1480 timer_next = timer->tr_next;
1481 stop_timer(timer);
1482 }
Bram Moolenaarb73598e2016-08-07 18:22:53 +02001483}
1484
1485 void
Bram Moolenaar8e97bd72016-08-06 22:05:07 +02001486add_timer_info(typval_T *rettv, timer_T *timer)
1487{
1488 list_T *list = rettv->vval.v_list;
1489 dict_T *dict = dict_alloc();
1490 dictitem_T *di;
1491 long remaining;
1492 proftime_T now;
1493
1494 if (dict == NULL)
1495 return;
1496 list_append_dict(list, dict);
1497
Bram Moolenaare0be1672018-07-08 16:50:37 +02001498 dict_add_number(dict, "id", timer->tr_id);
1499 dict_add_number(dict, "time", (long)timer->tr_interval);
Bram Moolenaar8e97bd72016-08-06 22:05:07 +02001500
1501 profile_start(&now);
Bram Moolenaar51b0f372017-11-18 18:52:04 +01001502 remaining = proftime_time_left(&timer->tr_due, &now);
Bram Moolenaare0be1672018-07-08 16:50:37 +02001503 dict_add_number(dict, "remaining", (long)remaining);
Bram Moolenaar8e97bd72016-08-06 22:05:07 +02001504
Bram Moolenaare0be1672018-07-08 16:50:37 +02001505 dict_add_number(dict, "repeat",
1506 (long)(timer->tr_repeat < 0 ? -1 : timer->tr_repeat + 1));
1507 dict_add_number(dict, "paused", (long)(timer->tr_paused));
Bram Moolenaar8e97bd72016-08-06 22:05:07 +02001508
1509 di = dictitem_alloc((char_u *)"callback");
1510 if (di != NULL)
1511 {
1512 if (dict_add(dict, di) == FAIL)
1513 vim_free(di);
1514 else if (timer->tr_partial != NULL)
1515 {
1516 di->di_tv.v_type = VAR_PARTIAL;
1517 di->di_tv.vval.v_partial = timer->tr_partial;
1518 ++timer->tr_partial->pt_refcount;
1519 }
1520 else
1521 {
1522 di->di_tv.v_type = VAR_FUNC;
1523 di->di_tv.vval.v_string = vim_strsave(timer->tr_callback);
1524 }
Bram Moolenaar8e97bd72016-08-06 22:05:07 +02001525 }
1526}
1527
1528 void
1529add_timer_info_all(typval_T *rettv)
1530{
1531 timer_T *timer;
1532
1533 for (timer = first_timer; timer != NULL; timer = timer->tr_next)
Bram Moolenaar75537a92016-09-05 22:45:28 +02001534 if (timer->tr_id != -1)
1535 add_timer_info(rettv, timer);
Bram Moolenaar8e97bd72016-08-06 22:05:07 +02001536}
1537
Bram Moolenaare3188e22016-05-31 21:13:04 +02001538/*
1539 * Mark references in partials of timers.
1540 */
1541 int
1542set_ref_in_timer(int copyID)
1543{
1544 int abort = FALSE;
1545 timer_T *timer;
1546 typval_T tv;
1547
1548 for (timer = first_timer; timer != NULL; timer = timer->tr_next)
1549 {
Bram Moolenaar1e96d9b2016-07-29 22:15:09 +02001550 if (timer->tr_partial != NULL)
1551 {
1552 tv.v_type = VAR_PARTIAL;
1553 tv.vval.v_partial = timer->tr_partial;
1554 }
1555 else
1556 {
1557 tv.v_type = VAR_FUNC;
1558 tv.vval.v_string = timer->tr_callback;
1559 }
Bram Moolenaare3188e22016-05-31 21:13:04 +02001560 abort = abort || set_ref_in_item(&tv, copyID, NULL, NULL);
1561 }
1562 return abort;
1563}
Bram Moolenaar623e2632016-07-30 22:47:56 +02001564
1565# if defined(EXITFREE) || defined(PROTO)
1566 void
1567timer_free_all()
1568{
1569 timer_T *timer;
1570
1571 while (first_timer != NULL)
1572 {
1573 timer = first_timer;
1574 remove_timer(timer);
1575 free_timer(timer);
1576 }
1577}
1578# endif
Bram Moolenaar975b5272016-03-15 23:10:59 +01001579# endif
1580
Bram Moolenaar113e1072019-01-20 15:30:40 +01001581#if defined(FEAT_SYN_HL) && defined(FEAT_RELTIME) && defined(FEAT_FLOAT) && defined(FEAT_PROFILE)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02001582# if defined(HAVE_MATH_H)
1583# include <math.h>
1584# endif
1585
1586/*
1587 * Divide the time "tm" by "count" and store in "tm2".
1588 */
1589 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001590profile_divide(proftime_T *tm, int count, proftime_T *tm2)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02001591{
1592 if (count == 0)
1593 profile_zero(tm2);
1594 else
1595 {
1596# ifdef WIN3264
1597 tm2->QuadPart = tm->QuadPart / count;
1598# else
1599 double usec = (tm->tv_sec * 1000000.0 + tm->tv_usec) / count;
1600
1601 tm2->tv_sec = floor(usec / 1000000.0);
Bram Moolenaara2e14fc2013-06-10 20:10:44 +02001602 tm2->tv_usec = vim_round(usec - (tm2->tv_sec * 1000000.0));
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02001603# endif
1604 }
1605}
1606#endif
1607
Bram Moolenaar76929292008-01-06 19:07:36 +00001608# if defined(FEAT_PROFILE) || defined(PROTO)
1609/*
1610 * Functions for profiling.
1611 */
Bram Moolenaarf28dbce2016-01-29 22:03:47 +01001612static void script_dump_profile(FILE *fd);
Bram Moolenaar76929292008-01-06 19:07:36 +00001613static proftime_T prof_wait_time;
1614
Bram Moolenaar433f7c82006-03-21 21:29:36 +00001615/*
Bram Moolenaar05159a02005-02-26 23:04:13 +00001616 * Add the time "tm2" to "tm".
1617 */
1618 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001619profile_add(proftime_T *tm, proftime_T *tm2)
Bram Moolenaar05159a02005-02-26 23:04:13 +00001620{
Bram Moolenaar8cd06ca2005-02-28 22:44:58 +00001621# ifdef WIN3264
1622 tm->QuadPart += tm2->QuadPart;
1623# else
Bram Moolenaar05159a02005-02-26 23:04:13 +00001624 tm->tv_usec += tm2->tv_usec;
1625 tm->tv_sec += tm2->tv_sec;
1626 if (tm->tv_usec >= 1000000)
1627 {
1628 tm->tv_usec -= 1000000;
1629 ++tm->tv_sec;
1630 }
Bram Moolenaar8cd06ca2005-02-28 22:44:58 +00001631# endif
Bram Moolenaar05159a02005-02-26 23:04:13 +00001632}
1633
1634/*
Bram Moolenaar1056d982006-03-09 22:37:52 +00001635 * Add the "self" time from the total time and the children's time.
1636 */
1637 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001638profile_self(proftime_T *self, proftime_T *total, proftime_T *children)
Bram Moolenaar1056d982006-03-09 22:37:52 +00001639{
1640 /* Check that the result won't be negative. Can happen with recursive
1641 * calls. */
1642#ifdef WIN3264
1643 if (total->QuadPart <= children->QuadPart)
1644 return;
1645#else
1646 if (total->tv_sec < children->tv_sec
1647 || (total->tv_sec == children->tv_sec
1648 && total->tv_usec <= children->tv_usec))
1649 return;
1650#endif
1651 profile_add(self, total);
1652 profile_sub(self, children);
1653}
1654
1655/*
Bram Moolenaar05159a02005-02-26 23:04:13 +00001656 * Get the current waittime.
1657 */
1658 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001659profile_get_wait(proftime_T *tm)
Bram Moolenaar05159a02005-02-26 23:04:13 +00001660{
1661 *tm = prof_wait_time;
1662}
1663
1664/*
1665 * Subtract the passed waittime since "tm" from "tma".
1666 */
1667 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001668profile_sub_wait(proftime_T *tm, proftime_T *tma)
Bram Moolenaar05159a02005-02-26 23:04:13 +00001669{
1670 proftime_T tm3 = prof_wait_time;
1671
1672 profile_sub(&tm3, tm);
1673 profile_sub(tma, &tm3);
1674}
1675
1676/*
1677 * Return TRUE if "tm1" and "tm2" are equal.
1678 */
1679 int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001680profile_equal(proftime_T *tm1, proftime_T *tm2)
Bram Moolenaar05159a02005-02-26 23:04:13 +00001681{
Bram Moolenaar8cd06ca2005-02-28 22:44:58 +00001682# ifdef WIN3264
1683 return (tm1->QuadPart == tm2->QuadPart);
1684# else
Bram Moolenaar05159a02005-02-26 23:04:13 +00001685 return (tm1->tv_usec == tm2->tv_usec && tm1->tv_sec == tm2->tv_sec);
Bram Moolenaar8cd06ca2005-02-28 22:44:58 +00001686# endif
1687}
1688
1689/*
1690 * Return <0, 0 or >0 if "tm1" < "tm2", "tm1" == "tm2" or "tm1" > "tm2"
1691 */
1692 int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001693profile_cmp(const proftime_T *tm1, const proftime_T *tm2)
Bram Moolenaar8cd06ca2005-02-28 22:44:58 +00001694{
1695# ifdef WIN3264
1696 return (int)(tm2->QuadPart - tm1->QuadPart);
1697# else
1698 if (tm1->tv_sec == tm2->tv_sec)
1699 return tm2->tv_usec - tm1->tv_usec;
1700 return tm2->tv_sec - tm1->tv_sec;
1701# endif
Bram Moolenaar05159a02005-02-26 23:04:13 +00001702}
1703
Bram Moolenaar05159a02005-02-26 23:04:13 +00001704static char_u *profile_fname = NULL;
Bram Moolenaar9b2200a2006-03-20 21:55:45 +00001705static proftime_T pause_time;
Bram Moolenaar05159a02005-02-26 23:04:13 +00001706
1707/*
1708 * ":profile cmd args"
1709 */
1710 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001711ex_profile(exarg_T *eap)
Bram Moolenaar05159a02005-02-26 23:04:13 +00001712{
1713 char_u *e;
1714 int len;
1715
1716 e = skiptowhite(eap->arg);
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00001717 len = (int)(e - eap->arg);
Bram Moolenaar05159a02005-02-26 23:04:13 +00001718 e = skipwhite(e);
1719
1720 if (len == 5 && STRNCMP(eap->arg, "start", 5) == 0 && *e != NUL)
1721 {
1722 vim_free(profile_fname);
Bram Moolenaard94682f2015-04-13 15:37:56 +02001723 profile_fname = expand_env_save_opt(e, TRUE);
Bram Moolenaar9b2200a2006-03-20 21:55:45 +00001724 do_profiling = PROF_YES;
Bram Moolenaar05159a02005-02-26 23:04:13 +00001725 profile_zero(&prof_wait_time);
1726 set_vim_var_nr(VV_PROFILING, 1L);
1727 }
Bram Moolenaar9b2200a2006-03-20 21:55:45 +00001728 else if (do_profiling == PROF_NONE)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001729 emsg(_("E750: First use \":profile start {fname}\""));
Bram Moolenaar9b2200a2006-03-20 21:55:45 +00001730 else if (STRCMP(eap->arg, "pause") == 0)
1731 {
1732 if (do_profiling == PROF_YES)
1733 profile_start(&pause_time);
1734 do_profiling = PROF_PAUSED;
1735 }
1736 else if (STRCMP(eap->arg, "continue") == 0)
1737 {
1738 if (do_profiling == PROF_PAUSED)
1739 {
1740 profile_end(&pause_time);
1741 profile_add(&prof_wait_time, &pause_time);
1742 }
1743 do_profiling = PROF_YES;
1744 }
Bram Moolenaar05159a02005-02-26 23:04:13 +00001745 else
1746 {
1747 /* The rest is similar to ":breakadd". */
1748 ex_breakadd(eap);
1749 }
1750}
1751
Bram Moolenaarf86f26c2010-02-03 15:14:22 +01001752/* Command line expansion for :profile. */
1753static enum
1754{
1755 PEXP_SUBCMD, /* expand :profile sub-commands */
Bram Moolenaar49789dc2011-02-25 14:46:09 +01001756 PEXP_FUNC /* expand :profile func {funcname} */
Bram Moolenaarf86f26c2010-02-03 15:14:22 +01001757} pexpand_what;
1758
1759static char *pexpand_cmds[] = {
1760 "start",
1761#define PROFCMD_START 0
1762 "pause",
1763#define PROFCMD_PAUSE 1
1764 "continue",
1765#define PROFCMD_CONTINUE 2
1766 "func",
1767#define PROFCMD_FUNC 3
1768 "file",
1769#define PROFCMD_FILE 4
1770 NULL
1771#define PROFCMD_LAST 5
1772};
1773
1774/*
1775 * Function given to ExpandGeneric() to obtain the profile command
1776 * specific expansion.
1777 */
1778 char_u *
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001779get_profile_name(expand_T *xp UNUSED, int idx)
Bram Moolenaarf86f26c2010-02-03 15:14:22 +01001780{
1781 switch (pexpand_what)
1782 {
1783 case PEXP_SUBCMD:
1784 return (char_u *)pexpand_cmds[idx];
1785 /* case PEXP_FUNC: TODO */
1786 default:
1787 return NULL;
1788 }
1789}
1790
1791/*
1792 * Handle command line completion for :profile command.
1793 */
1794 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001795set_context_in_profile_cmd(expand_T *xp, char_u *arg)
Bram Moolenaarf86f26c2010-02-03 15:14:22 +01001796{
1797 char_u *end_subcmd;
Bram Moolenaarf86f26c2010-02-03 15:14:22 +01001798
1799 /* Default: expand subcommands. */
1800 xp->xp_context = EXPAND_PROFILE;
1801 pexpand_what = PEXP_SUBCMD;
1802 xp->xp_pattern = arg;
1803
1804 end_subcmd = skiptowhite(arg);
1805 if (*end_subcmd == NUL)
1806 return;
1807
Bram Moolenaar8b9c05f2010-03-02 17:54:33 +01001808 if (end_subcmd - arg == 5 && STRNCMP(arg, "start", 5) == 0)
Bram Moolenaarf86f26c2010-02-03 15:14:22 +01001809 {
1810 xp->xp_context = EXPAND_FILES;
1811 xp->xp_pattern = skipwhite(end_subcmd);
1812 return;
1813 }
1814
1815 /* TODO: expand function names after "func" */
1816 xp->xp_context = EXPAND_NOTHING;
1817}
1818
Bram Moolenaar05159a02005-02-26 23:04:13 +00001819/*
1820 * Dump the profiling info.
1821 */
1822 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001823profile_dump(void)
Bram Moolenaar05159a02005-02-26 23:04:13 +00001824{
1825 FILE *fd;
1826
1827 if (profile_fname != NULL)
1828 {
Bram Moolenaarbfd8fc02005-09-20 23:22:24 +00001829 fd = mch_fopen((char *)profile_fname, "w");
Bram Moolenaar05159a02005-02-26 23:04:13 +00001830 if (fd == NULL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001831 semsg(_(e_notopen), profile_fname);
Bram Moolenaar05159a02005-02-26 23:04:13 +00001832 else
1833 {
Bram Moolenaar05159a02005-02-26 23:04:13 +00001834 script_dump_profile(fd);
Bram Moolenaar8cd06ca2005-02-28 22:44:58 +00001835 func_dump_profile(fd);
Bram Moolenaar05159a02005-02-26 23:04:13 +00001836 fclose(fd);
1837 }
1838 }
1839}
1840
1841/*
1842 * Start profiling script "fp".
1843 */
1844 static void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001845script_do_profile(scriptitem_T *si)
Bram Moolenaar05159a02005-02-26 23:04:13 +00001846{
1847 si->sn_pr_count = 0;
1848 profile_zero(&si->sn_pr_total);
1849 profile_zero(&si->sn_pr_self);
1850
1851 ga_init2(&si->sn_prl_ga, sizeof(sn_prl_T), 100);
1852 si->sn_prl_idx = -1;
1853 si->sn_prof_on = TRUE;
1854 si->sn_pr_nest = 0;
1855}
1856
1857/*
Bram Moolenaar67435d92017-10-19 21:04:37 +02001858 * Save time when starting to invoke another script or function.
Bram Moolenaar05159a02005-02-26 23:04:13 +00001859 */
1860 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001861script_prof_save(
1862 proftime_T *tm) /* place to store wait time */
Bram Moolenaar05159a02005-02-26 23:04:13 +00001863{
1864 scriptitem_T *si;
1865
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02001866 if (current_sctx.sc_sid > 0 && current_sctx.sc_sid <= script_items.ga_len)
Bram Moolenaar05159a02005-02-26 23:04:13 +00001867 {
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02001868 si = &SCRIPT_ITEM(current_sctx.sc_sid);
Bram Moolenaar05159a02005-02-26 23:04:13 +00001869 if (si->sn_prof_on && si->sn_pr_nest++ == 0)
1870 profile_start(&si->sn_pr_child);
1871 }
1872 profile_get_wait(tm);
1873}
1874
1875/*
1876 * Count time spent in children after invoking another script or function.
1877 */
1878 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001879script_prof_restore(proftime_T *tm)
Bram Moolenaar05159a02005-02-26 23:04:13 +00001880{
1881 scriptitem_T *si;
1882
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02001883 if (current_sctx.sc_sid > 0 && current_sctx.sc_sid <= script_items.ga_len)
Bram Moolenaar05159a02005-02-26 23:04:13 +00001884 {
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02001885 si = &SCRIPT_ITEM(current_sctx.sc_sid);
Bram Moolenaar05159a02005-02-26 23:04:13 +00001886 if (si->sn_prof_on && --si->sn_pr_nest == 0)
1887 {
1888 profile_end(&si->sn_pr_child);
1889 profile_sub_wait(tm, &si->sn_pr_child); /* don't count wait time */
1890 profile_add(&si->sn_pr_children, &si->sn_pr_child);
1891 profile_add(&si->sn_prl_children, &si->sn_pr_child);
1892 }
1893 }
1894}
1895
1896static proftime_T inchar_time;
1897
1898/*
1899 * Called when starting to wait for the user to type a character.
1900 */
1901 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001902prof_inchar_enter(void)
Bram Moolenaar05159a02005-02-26 23:04:13 +00001903{
1904 profile_start(&inchar_time);
1905}
1906
1907/*
1908 * Called when finished waiting for the user to type a character.
1909 */
1910 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001911prof_inchar_exit(void)
Bram Moolenaar05159a02005-02-26 23:04:13 +00001912{
1913 profile_end(&inchar_time);
1914 profile_add(&prof_wait_time, &inchar_time);
1915}
1916
1917/*
1918 * Dump the profiling results for all scripts in file "fd".
1919 */
1920 static void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01001921script_dump_profile(FILE *fd)
Bram Moolenaar05159a02005-02-26 23:04:13 +00001922{
1923 int id;
1924 scriptitem_T *si;
1925 int i;
1926 FILE *sfd;
1927 sn_prl_T *pp;
1928
1929 for (id = 1; id <= script_items.ga_len; ++id)
1930 {
1931 si = &SCRIPT_ITEM(id);
1932 if (si->sn_prof_on)
1933 {
1934 fprintf(fd, "SCRIPT %s\n", si->sn_name);
1935 if (si->sn_pr_count == 1)
1936 fprintf(fd, "Sourced 1 time\n");
1937 else
1938 fprintf(fd, "Sourced %d times\n", si->sn_pr_count);
1939 fprintf(fd, "Total time: %s\n", profile_msg(&si->sn_pr_total));
1940 fprintf(fd, " Self time: %s\n", profile_msg(&si->sn_pr_self));
1941 fprintf(fd, "\n");
1942 fprintf(fd, "count total (s) self (s)\n");
1943
Bram Moolenaarbfd8fc02005-09-20 23:22:24 +00001944 sfd = mch_fopen((char *)si->sn_name, "r");
Bram Moolenaar05159a02005-02-26 23:04:13 +00001945 if (sfd == NULL)
1946 fprintf(fd, "Cannot open file!\n");
1947 else
1948 {
Bram Moolenaar67435d92017-10-19 21:04:37 +02001949 /* Keep going till the end of file, so that trailing
1950 * continuation lines are listed. */
1951 for (i = 0; ; ++i)
Bram Moolenaar05159a02005-02-26 23:04:13 +00001952 {
1953 if (vim_fgets(IObuff, IOSIZE, sfd))
1954 break;
Bram Moolenaarac112f02017-12-05 16:46:28 +01001955 /* When a line has been truncated, append NL, taking care
1956 * of multi-byte characters . */
1957 if (IObuff[IOSIZE - 2] != NUL && IObuff[IOSIZE - 2] != NL)
1958 {
1959 int n = IOSIZE - 2;
Bram Moolenaar13505972019-01-24 15:04:48 +01001960
Bram Moolenaarac112f02017-12-05 16:46:28 +01001961 if (enc_utf8)
1962 {
1963 /* Move to the first byte of this char.
1964 * utf_head_off() doesn't work, because it checks
1965 * for a truncated character. */
1966 while (n > 0 && (IObuff[n] & 0xc0) == 0x80)
1967 --n;
1968 }
1969 else if (has_mbyte)
1970 n -= mb_head_off(IObuff, IObuff + n);
Bram Moolenaarac112f02017-12-05 16:46:28 +01001971 IObuff[n] = NL;
1972 IObuff[n + 1] = NUL;
1973 }
Bram Moolenaar67435d92017-10-19 21:04:37 +02001974 if (i < si->sn_prl_ga.ga_len
1975 && (pp = &PRL_ITEM(si, i))->snp_count > 0)
Bram Moolenaar05159a02005-02-26 23:04:13 +00001976 {
1977 fprintf(fd, "%5d ", pp->snp_count);
1978 if (profile_equal(&pp->sn_prl_total, &pp->sn_prl_self))
1979 fprintf(fd, " ");
1980 else
1981 fprintf(fd, "%s ", profile_msg(&pp->sn_prl_total));
1982 fprintf(fd, "%s ", profile_msg(&pp->sn_prl_self));
1983 }
1984 else
1985 fprintf(fd, " ");
1986 fprintf(fd, "%s", IObuff);
1987 }
1988 fclose(sfd);
1989 }
1990 fprintf(fd, "\n");
1991 }
1992 }
1993}
1994
1995/*
1996 * Return TRUE when a function defined in the current script should be
1997 * profiled.
1998 */
1999 int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002000prof_def_func(void)
Bram Moolenaar05159a02005-02-26 23:04:13 +00002001{
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02002002 if (current_sctx.sc_sid > 0)
2003 return SCRIPT_ITEM(current_sctx.sc_sid).sn_pr_force;
Bram Moolenaar53180ce2005-07-05 21:48:14 +00002004 return FALSE;
Bram Moolenaar05159a02005-02-26 23:04:13 +00002005}
2006
2007# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002008#endif
2009
2010/*
2011 * If 'autowrite' option set, try to write the file.
2012 * Careful: autocommands may make "buf" invalid!
2013 *
2014 * return FAIL for failure, OK otherwise
2015 */
2016 int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002017autowrite(buf_T *buf, int forceit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002018{
Bram Moolenaar373154b2007-02-13 05:19:30 +00002019 int r;
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02002020 bufref_T bufref;
Bram Moolenaar373154b2007-02-13 05:19:30 +00002021
Bram Moolenaar071d4272004-06-13 20:20:40 +00002022 if (!(p_aw || p_awa) || !p_write
2023#ifdef FEAT_QUICKFIX
Bram Moolenaar373154b2007-02-13 05:19:30 +00002024 /* never autowrite a "nofile" or "nowrite" buffer */
2025 || bt_dontwrite(buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002026#endif
Bram Moolenaar373154b2007-02-13 05:19:30 +00002027 || (!forceit && buf->b_p_ro) || buf->b_ffname == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002028 return FAIL;
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02002029 set_bufref(&bufref, buf);
Bram Moolenaar373154b2007-02-13 05:19:30 +00002030 r = buf_write_all(buf, forceit);
2031
2032 /* Writing may succeed but the buffer still changed, e.g., when there is a
2033 * conversion error. We do want to return FAIL then. */
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02002034 if (bufref_valid(&bufref) && bufIsChanged(buf))
Bram Moolenaar373154b2007-02-13 05:19:30 +00002035 r = FAIL;
2036 return r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002037}
2038
2039/*
Bram Moolenaar8c9e7b02018-08-30 13:07:17 +02002040 * Flush all buffers, except the ones that are readonly or are never written.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002041 */
2042 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002043autowrite_all(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002044{
2045 buf_T *buf;
2046
2047 if (!(p_aw || p_awa) || !p_write)
2048 return;
Bram Moolenaar29323592016-07-24 22:04:11 +02002049 FOR_ALL_BUFFERS(buf)
Bram Moolenaar8c9e7b02018-08-30 13:07:17 +02002050 if (bufIsChanged(buf) && !buf->b_p_ro && !bt_dontwrite(buf))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002051 {
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02002052 bufref_T bufref;
2053
2054 set_bufref(&bufref, buf);
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01002055
Bram Moolenaar071d4272004-06-13 20:20:40 +00002056 (void)buf_write_all(buf, FALSE);
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01002057
Bram Moolenaar071d4272004-06-13 20:20:40 +00002058 /* an autocommand may have deleted the buffer */
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02002059 if (!bufref_valid(&bufref))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002060 buf = firstbuf;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002061 }
2062}
2063
2064/*
Bram Moolenaar45d3b142013-11-09 03:31:51 +01002065 * Return TRUE if buffer was changed and cannot be abandoned.
2066 * For flags use the CCGD_ values.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002067 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002068 int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002069check_changed(buf_T *buf, int flags)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002070{
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02002071 int forceit = (flags & CCGD_FORCEIT);
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02002072 bufref_T bufref;
2073
2074 set_bufref(&bufref, buf);
Bram Moolenaar45d3b142013-11-09 03:31:51 +01002075
Bram Moolenaar071d4272004-06-13 20:20:40 +00002076 if ( !forceit
2077 && bufIsChanged(buf)
Bram Moolenaar45d3b142013-11-09 03:31:51 +01002078 && ((flags & CCGD_MULTWIN) || buf->b_nwindows <= 1)
2079 && (!(flags & CCGD_AW) || autowrite(buf, forceit) == FAIL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002080 {
2081#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
2082 if ((p_confirm || cmdmod.confirm) && p_write)
2083 {
2084 buf_T *buf2;
2085 int count = 0;
2086
Bram Moolenaar45d3b142013-11-09 03:31:51 +01002087 if (flags & CCGD_ALLBUF)
Bram Moolenaar29323592016-07-24 22:04:11 +02002088 FOR_ALL_BUFFERS(buf2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002089 if (bufIsChanged(buf2)
2090 && (buf2->b_ffname != NULL
2091# ifdef FEAT_BROWSE
2092 || cmdmod.browse
2093# endif
2094 ))
2095 ++count;
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02002096 if (!bufref_valid(&bufref))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002097 /* Autocommand deleted buffer, oops! It's not changed now. */
2098 return FALSE;
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01002099
Bram Moolenaar071d4272004-06-13 20:20:40 +00002100 dialog_changed(buf, count > 1);
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01002101
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02002102 if (!bufref_valid(&bufref))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002103 /* Autocommand deleted buffer, oops! It's not changed now. */
2104 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002105 return bufIsChanged(buf);
2106 }
2107#endif
Bram Moolenaar45d3b142013-11-09 03:31:51 +01002108 if (flags & CCGD_EXCMD)
Bram Moolenaarf5be7cd2017-08-17 16:55:13 +02002109 no_write_message();
Bram Moolenaar45d3b142013-11-09 03:31:51 +01002110 else
Bram Moolenaar7a760922018-02-19 23:10:02 +01002111 no_write_message_nobang(curbuf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002112 return TRUE;
2113 }
2114 return FALSE;
2115}
2116
2117#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG) || defined(PROTO)
2118
2119#if defined(FEAT_BROWSE) || defined(PROTO)
2120/*
2121 * When wanting to write a file without a file name, ask the user for a name.
2122 */
2123 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002124browse_save_fname(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002125{
2126 if (buf->b_fname == NULL)
2127 {
2128 char_u *fname;
2129
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00002130 fname = do_browse(BROWSE_SAVE, (char_u *)_("Save As"),
2131 NULL, NULL, NULL, NULL, buf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002132 if (fname != NULL)
2133 {
2134 if (setfname(buf, fname, NULL, TRUE) == OK)
2135 buf->b_flags |= BF_NOTEDITED;
2136 vim_free(fname);
2137 }
2138 }
2139}
2140#endif
2141
2142/*
Bram Moolenaar9b486ca2011-05-19 18:26:40 +02002143 * Ask the user what to do when abandoning a changed buffer.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002144 * Must check 'write' option first!
2145 */
2146 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002147dialog_changed(
2148 buf_T *buf,
2149 int checkall) /* may abandon all changed buffers */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002150{
Bram Moolenaard9462e32011-04-11 21:35:11 +02002151 char_u buff[DIALOG_MSG_SIZE];
Bram Moolenaar071d4272004-06-13 20:20:40 +00002152 int ret;
2153 buf_T *buf2;
Bram Moolenaar8218f602012-04-25 17:32:18 +02002154 exarg_T ea;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002155
Bram Moolenaar3f9a1ff2017-08-21 22:06:02 +02002156 dialog_msg(buff, _("Save changes to \"%s\"?"), buf->b_fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002157 if (checkall)
2158 ret = vim_dialog_yesnoallcancel(VIM_QUESTION, NULL, buff, 1);
2159 else
2160 ret = vim_dialog_yesnocancel(VIM_QUESTION, NULL, buff, 1);
2161
Bram Moolenaar8218f602012-04-25 17:32:18 +02002162 /* Init ea pseudo-structure, this is needed for the check_overwrite()
2163 * function. */
2164 ea.append = ea.forceit = FALSE;
2165
Bram Moolenaar071d4272004-06-13 20:20:40 +00002166 if (ret == VIM_YES)
2167 {
2168#ifdef FEAT_BROWSE
2169 /* May get file name, when there is none */
2170 browse_save_fname(buf);
2171#endif
Bram Moolenaar8218f602012-04-25 17:32:18 +02002172 if (buf->b_fname != NULL && check_overwrite(&ea, buf,
2173 buf->b_fname, buf->b_ffname, FALSE) == OK)
2174 /* didn't hit Cancel */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002175 (void)buf_write_all(buf, FALSE);
2176 }
2177 else if (ret == VIM_NO)
2178 {
2179 unchanged(buf, TRUE);
2180 }
2181 else if (ret == VIM_ALL)
2182 {
2183 /*
2184 * Write all modified files that can be written.
2185 * Skip readonly buffers, these need to be confirmed
2186 * individually.
2187 */
Bram Moolenaar29323592016-07-24 22:04:11 +02002188 FOR_ALL_BUFFERS(buf2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002189 {
2190 if (bufIsChanged(buf2)
2191 && (buf2->b_ffname != NULL
2192#ifdef FEAT_BROWSE
2193 || cmdmod.browse
2194#endif
2195 )
2196 && !buf2->b_p_ro)
2197 {
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02002198 bufref_T bufref;
2199
2200 set_bufref(&bufref, buf2);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002201#ifdef FEAT_BROWSE
2202 /* May get file name, when there is none */
2203 browse_save_fname(buf2);
2204#endif
Bram Moolenaar8218f602012-04-25 17:32:18 +02002205 if (buf2->b_fname != NULL && check_overwrite(&ea, buf2,
2206 buf2->b_fname, buf2->b_ffname, FALSE) == OK)
2207 /* didn't hit Cancel */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002208 (void)buf_write_all(buf2, FALSE);
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01002209
Bram Moolenaar071d4272004-06-13 20:20:40 +00002210 /* an autocommand may have deleted the buffer */
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02002211 if (!bufref_valid(&bufref))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002212 buf2 = firstbuf;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002213 }
2214 }
2215 }
2216 else if (ret == VIM_DISCARDALL)
2217 {
2218 /*
2219 * mark all buffers as unchanged
2220 */
Bram Moolenaar29323592016-07-24 22:04:11 +02002221 FOR_ALL_BUFFERS(buf2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002222 unchanged(buf2, TRUE);
2223 }
2224}
2225#endif
2226
2227/*
2228 * Return TRUE if the buffer "buf" can be abandoned, either by making it
2229 * hidden, autowriting it or unloading it.
2230 */
2231 int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002232can_abandon(buf_T *buf, int forceit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002233{
Bram Moolenaareb44a682017-08-03 22:44:55 +02002234 return ( buf_hide(buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002235 || !bufIsChanged(buf)
2236 || buf->b_nwindows > 1
2237 || autowrite(buf, forceit) == OK
2238 || forceit);
2239}
2240
Bram Moolenaar970a1b82012-03-23 18:39:18 +01002241/*
2242 * Add a buffer number to "bufnrs", unless it's already there.
2243 */
2244 static void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002245add_bufnum(int *bufnrs, int *bufnump, int nr)
Bram Moolenaar970a1b82012-03-23 18:39:18 +01002246{
2247 int i;
2248
2249 for (i = 0; i < *bufnump; ++i)
2250 if (bufnrs[i] == nr)
2251 return;
2252 bufnrs[*bufnump] = nr;
2253 *bufnump = *bufnump + 1;
2254}
2255
Bram Moolenaar071d4272004-06-13 20:20:40 +00002256/*
2257 * Return TRUE if any buffer was changed and cannot be abandoned.
2258 * That changed buffer becomes the current buffer.
Bram Moolenaar25cdd9c2018-03-10 20:28:12 +01002259 * When "unload" is TRUE the current buffer is unloaded instead of making it
Bram Moolenaar027387f2016-01-02 22:25:52 +01002260 * hidden. This is used for ":q!".
Bram Moolenaar071d4272004-06-13 20:20:40 +00002261 */
2262 int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002263check_changed_any(
2264 int hidden, /* Only check hidden buffers */
2265 int unload)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002266{
Bram Moolenaar970a1b82012-03-23 18:39:18 +01002267 int ret = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002268 buf_T *buf;
2269 int save;
Bram Moolenaar970a1b82012-03-23 18:39:18 +01002270 int i;
2271 int bufnum = 0;
2272 int bufcount = 0;
2273 int *bufnrs;
Bram Moolenaar970a1b82012-03-23 18:39:18 +01002274 tabpage_T *tp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002275 win_T *wp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002276
Bram Moolenaar25cdd9c2018-03-10 20:28:12 +01002277 /* Make a list of all buffers, with the most important ones first. */
Bram Moolenaar29323592016-07-24 22:04:11 +02002278 FOR_ALL_BUFFERS(buf)
Bram Moolenaar970a1b82012-03-23 18:39:18 +01002279 ++bufcount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002280
Bram Moolenaar970a1b82012-03-23 18:39:18 +01002281 if (bufcount == 0)
2282 return FALSE;
2283
2284 bufnrs = (int *)alloc(sizeof(int) * bufcount);
2285 if (bufnrs == NULL)
2286 return FALSE;
2287
2288 /* curbuf */
2289 bufnrs[bufnum++] = curbuf->b_fnum;
Bram Moolenaar25cdd9c2018-03-10 20:28:12 +01002290
2291 /* buffers in current tab */
Bram Moolenaar970a1b82012-03-23 18:39:18 +01002292 FOR_ALL_WINDOWS(wp)
2293 if (wp->w_buffer != curbuf)
2294 add_bufnum(bufnrs, &bufnum, wp->w_buffer->b_fnum);
2295
Bram Moolenaar25cdd9c2018-03-10 20:28:12 +01002296 /* buffers in other tabs */
Bram Moolenaar29323592016-07-24 22:04:11 +02002297 FOR_ALL_TABPAGES(tp)
Bram Moolenaar970a1b82012-03-23 18:39:18 +01002298 if (tp != curtab)
2299 for (wp = tp->tp_firstwin; wp != NULL; wp = wp->w_next)
2300 add_bufnum(bufnrs, &bufnum, wp->w_buffer->b_fnum);
Bram Moolenaar25cdd9c2018-03-10 20:28:12 +01002301
2302 /* any other buffer */
Bram Moolenaar29323592016-07-24 22:04:11 +02002303 FOR_ALL_BUFFERS(buf)
Bram Moolenaar970a1b82012-03-23 18:39:18 +01002304 add_bufnum(bufnrs, &bufnum, buf->b_fnum);
2305
2306 for (i = 0; i < bufnum; ++i)
2307 {
2308 buf = buflist_findnr(bufnrs[i]);
2309 if (buf == NULL)
2310 continue;
2311 if ((!hidden || buf->b_nwindows == 0) && bufIsChanged(buf))
2312 {
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02002313 bufref_T bufref;
2314
2315 set_bufref(&bufref, buf);
Bram Moolenaar25cdd9c2018-03-10 20:28:12 +01002316#ifdef FEAT_TERMINAL
2317 if (term_job_running(buf->b_term))
2318 {
2319 if (term_try_stop_job(buf) == FAIL)
2320 break;
2321 }
2322 else
2323#endif
Bram Moolenaar970a1b82012-03-23 18:39:18 +01002324 /* Try auto-writing the buffer. If this fails but the buffer no
2325 * longer exists it's not changed, that's OK. */
Bram Moolenaar45d3b142013-11-09 03:31:51 +01002326 if (check_changed(buf, (p_awa ? CCGD_AW : 0)
2327 | CCGD_MULTWIN
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02002328 | CCGD_ALLBUF) && bufref_valid(&bufref))
Bram Moolenaar970a1b82012-03-23 18:39:18 +01002329 break; /* didn't save - still changes */
2330 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002331 }
2332
Bram Moolenaar970a1b82012-03-23 18:39:18 +01002333 if (i >= bufnum)
2334 goto theend;
2335
Bram Moolenaar25cdd9c2018-03-10 20:28:12 +01002336 /* Get here if "buf" cannot be abandoned. */
Bram Moolenaar970a1b82012-03-23 18:39:18 +01002337 ret = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002338 exiting = FALSE;
2339#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
2340 /*
2341 * When ":confirm" used, don't give an error message.
2342 */
2343 if (!(p_confirm || cmdmod.confirm))
2344#endif
2345 {
2346 /* There must be a wait_return for this message, do_buffer()
2347 * may cause a redraw. But wait_return() is a no-op when vgetc()
2348 * is busy (Quit used from window menu), then make sure we don't
2349 * cause a scroll up. */
Bram Moolenaar61660ea2006-04-07 21:40:07 +00002350 if (vgetc_busy > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002351 {
2352 msg_row = cmdline_row;
2353 msg_col = 0;
2354 msg_didout = FALSE;
2355 }
Bram Moolenaareb44a682017-08-03 22:44:55 +02002356 if (
2357#ifdef FEAT_TERMINAL
2358 term_job_running(buf->b_term)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01002359 ? semsg(_("E947: Job still running in buffer \"%s\""),
Bram Moolenaareb44a682017-08-03 22:44:55 +02002360 buf->b_fname)
2361 :
2362#endif
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01002363 semsg(_("E162: No write since last change for buffer \"%s\""),
Bram Moolenaare1704ba2012-10-03 18:25:00 +02002364 buf_spname(buf) != NULL ? buf_spname(buf) : buf->b_fname))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002365 {
2366 save = no_wait_return;
2367 no_wait_return = FALSE;
2368 wait_return(FALSE);
2369 no_wait_return = save;
2370 }
2371 }
2372
Bram Moolenaar071d4272004-06-13 20:20:40 +00002373 /* Try to find a window that contains the buffer. */
2374 if (buf != curbuf)
Bram Moolenaar970a1b82012-03-23 18:39:18 +01002375 FOR_ALL_TAB_WINDOWS(tp, wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002376 if (wp->w_buffer == buf)
2377 {
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02002378 bufref_T bufref;
2379
2380 set_bufref(&bufref, buf);
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01002381
Bram Moolenaar970a1b82012-03-23 18:39:18 +01002382 goto_tabpage_win(tp, wp);
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01002383
Bram Moolenaar071d4272004-06-13 20:20:40 +00002384 /* Paranoia: did autocms wipe out the buffer with changes? */
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02002385 if (!bufref_valid(&bufref))
Bram Moolenaar970a1b82012-03-23 18:39:18 +01002386 goto theend;
Bram Moolenaar970a1b82012-03-23 18:39:18 +01002387 goto buf_found;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002388 }
Bram Moolenaar970a1b82012-03-23 18:39:18 +01002389buf_found:
Bram Moolenaar071d4272004-06-13 20:20:40 +00002390
2391 /* Open the changed buffer in the current window. */
2392 if (buf != curbuf)
Bram Moolenaar027387f2016-01-02 22:25:52 +01002393 set_curbuf(buf, unload ? DOBUF_UNLOAD : DOBUF_GOTO);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002394
Bram Moolenaar970a1b82012-03-23 18:39:18 +01002395theend:
2396 vim_free(bufnrs);
2397 return ret;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002398}
2399
2400/*
2401 * return FAIL if there is no file name, OK if there is one
2402 * give error message for FAIL
2403 */
2404 int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002405check_fname(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002406{
2407 if (curbuf->b_ffname == NULL)
2408 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01002409 emsg(_(e_noname));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002410 return FAIL;
2411 }
2412 return OK;
2413}
2414
2415/*
2416 * flush the contents of a buffer, unless it has no file name
2417 *
2418 * return FAIL for failure, OK otherwise
2419 */
2420 int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002421buf_write_all(buf_T *buf, int forceit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002422{
2423 int retval;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002424 buf_T *old_curbuf = curbuf;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002425
2426 retval = (buf_write(buf, buf->b_ffname, buf->b_fname,
2427 (linenr_T)1, buf->b_ml.ml_line_count, NULL,
2428 FALSE, forceit, TRUE, FALSE));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002429 if (curbuf != old_curbuf)
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00002430 {
Bram Moolenaar8820b482017-03-16 17:23:31 +01002431 msg_source(HL_ATTR(HLF_W));
Bram Moolenaar32526b32019-01-19 17:43:09 +01002432 msg(_("Warning: Entered other buffer unexpectedly (check autocommands)"));
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00002433 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002434 return retval;
2435}
2436
2437/*
2438 * Code to handle the argument list.
2439 */
2440
Bram Moolenaar32bbd002018-08-31 23:06:22 +02002441static int do_arglist(char_u *str, int what, int after, int will_edit);
Bram Moolenaarf28dbce2016-01-29 22:03:47 +01002442static void alist_check_arg_idx(void);
Bram Moolenaar32bbd002018-08-31 23:06:22 +02002443static void alist_add_list(int count, char_u **files, int after, int will_edit);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002444#define AL_SET 1
2445#define AL_ADD 2
2446#define AL_DEL 3
2447
Bram Moolenaar071d4272004-06-13 20:20:40 +00002448/*
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002449 * Isolate one argument, taking backticks.
2450 * Changes the argument in-place, puts a NUL after it. Backticks remain.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002451 * Return a pointer to the start of the next argument.
2452 */
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002453 static char_u *
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002454do_one_arg(char_u *str)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002455{
2456 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002457 int inbacktick;
2458
Bram Moolenaar071d4272004-06-13 20:20:40 +00002459 inbacktick = FALSE;
2460 for (p = str; *str; ++str)
2461 {
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002462 /* When the backslash is used for escaping the special meaning of a
2463 * character we need to keep it until wildcard expansion. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002464 if (rem_backslash(str))
2465 {
2466 *p++ = *str++;
2467 *p++ = *str;
2468 }
2469 else
2470 {
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002471 /* An item ends at a space not in backticks */
2472 if (!inbacktick && vim_isspace(*str))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002473 break;
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002474 if (*str == '`')
Bram Moolenaar071d4272004-06-13 20:20:40 +00002475 inbacktick ^= TRUE;
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002476 *p++ = *str;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002477 }
2478 }
2479 str = skipwhite(str);
2480 *p = NUL;
2481
2482 return str;
2483}
2484
Bram Moolenaar86b68352004-12-27 21:59:20 +00002485/*
2486 * Separate the arguments in "str" and return a list of pointers in the
2487 * growarray "gap".
2488 */
Bram Moolenaar398ee732017-08-03 14:29:14 +02002489 static int
2490get_arglist(garray_T *gap, char_u *str, int escaped)
Bram Moolenaar86b68352004-12-27 21:59:20 +00002491{
2492 ga_init2(gap, (int)sizeof(char_u *), 20);
2493 while (*str != NUL)
2494 {
2495 if (ga_grow(gap, 1) == FAIL)
2496 {
2497 ga_clear(gap);
2498 return FAIL;
2499 }
2500 ((char_u **)gap->ga_data)[gap->ga_len++] = str;
2501
Bram Moolenaar398ee732017-08-03 14:29:14 +02002502 /* If str is escaped, don't handle backslashes or spaces */
2503 if (!escaped)
2504 return OK;
2505
Bram Moolenaar86b68352004-12-27 21:59:20 +00002506 /* Isolate one argument, change it in-place, put a NUL after it. */
2507 str = do_one_arg(str);
2508 }
2509 return OK;
2510}
2511
Bram Moolenaar7df351e2006-01-23 22:30:28 +00002512#if defined(FEAT_QUICKFIX) || defined(FEAT_SYN_HL) || defined(PROTO)
Bram Moolenaar13fcaaf2005-04-15 21:13:42 +00002513/*
2514 * Parse a list of arguments (file names), expand them and return in
Bram Moolenaar8f5c6f02012-06-29 12:57:06 +02002515 * "fnames[fcountp]". When "wig" is TRUE, removes files matching 'wildignore'.
Bram Moolenaar13fcaaf2005-04-15 21:13:42 +00002516 * Return FAIL or OK.
2517 */
2518 int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002519get_arglist_exp(
2520 char_u *str,
2521 int *fcountp,
2522 char_u ***fnamesp,
2523 int wig)
Bram Moolenaar13fcaaf2005-04-15 21:13:42 +00002524{
2525 garray_T ga;
2526 int i;
2527
Bram Moolenaar398ee732017-08-03 14:29:14 +02002528 if (get_arglist(&ga, str, TRUE) == FAIL)
Bram Moolenaar13fcaaf2005-04-15 21:13:42 +00002529 return FAIL;
Bram Moolenaar8f5c6f02012-06-29 12:57:06 +02002530 if (wig == TRUE)
2531 i = expand_wildcards(ga.ga_len, (char_u **)ga.ga_data,
2532 fcountp, fnamesp, EW_FILE|EW_NOTFOUND);
2533 else
2534 i = gen_expand_wildcards(ga.ga_len, (char_u **)ga.ga_data,
2535 fcountp, fnamesp, EW_FILE|EW_NOTFOUND);
2536
Bram Moolenaar13fcaaf2005-04-15 21:13:42 +00002537 ga_clear(&ga);
2538 return i;
2539}
2540#endif
2541
Bram Moolenaar071d4272004-06-13 20:20:40 +00002542/*
2543 * Redefine the argument list.
2544 */
2545 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002546set_arglist(char_u *str)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002547{
Bram Moolenaar32bbd002018-08-31 23:06:22 +02002548 do_arglist(str, AL_SET, 0, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002549}
Bram Moolenaar071d4272004-06-13 20:20:40 +00002550
2551/*
2552 * "what" == AL_SET: Redefine the argument list to 'str'.
2553 * "what" == AL_ADD: add files in 'str' to the argument list after "after".
2554 * "what" == AL_DEL: remove files in 'str' from the argument list.
2555 *
2556 * Return FAIL for failure, OK otherwise.
2557 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002558 static int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002559do_arglist(
2560 char_u *str,
Bram Moolenaarf1d25012016-03-03 12:22:53 +01002561 int what,
Bram Moolenaar32bbd002018-08-31 23:06:22 +02002562 int after UNUSED, // 0 means before first one
2563 int will_edit) // will edit added argument
Bram Moolenaar071d4272004-06-13 20:20:40 +00002564{
2565 garray_T new_ga;
2566 int exp_count;
2567 char_u **exp_files;
2568 int i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002569 char_u *p;
2570 int match;
Bram Moolenaar398ee732017-08-03 14:29:14 +02002571 int arg_escaped = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002572
2573 /*
Bram Moolenaar2faa29f2016-01-23 23:02:34 +01002574 * Set default argument for ":argadd" command.
2575 */
2576 if (what == AL_ADD && *str == NUL)
2577 {
2578 if (curbuf->b_ffname == NULL)
2579 return FAIL;
2580 str = curbuf->b_fname;
Bram Moolenaar398ee732017-08-03 14:29:14 +02002581 arg_escaped = FALSE;
Bram Moolenaar2faa29f2016-01-23 23:02:34 +01002582 }
2583
2584 /*
Bram Moolenaar071d4272004-06-13 20:20:40 +00002585 * Collect all file name arguments in "new_ga".
2586 */
Bram Moolenaar398ee732017-08-03 14:29:14 +02002587 if (get_arglist(&new_ga, str, arg_escaped) == FAIL)
Bram Moolenaar86b68352004-12-27 21:59:20 +00002588 return FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002589
Bram Moolenaar071d4272004-06-13 20:20:40 +00002590 if (what == AL_DEL)
2591 {
2592 regmatch_T regmatch;
2593 int didone;
2594
2595 /*
2596 * Delete the items: use each item as a regexp and find a match in the
2597 * argument list.
2598 */
Bram Moolenaar71afbfe2013-03-19 16:49:16 +01002599 regmatch.rm_ic = p_fic; /* ignore case when 'fileignorecase' is set */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002600 for (i = 0; i < new_ga.ga_len && !got_int; ++i)
2601 {
2602 p = ((char_u **)new_ga.ga_data)[i];
2603 p = file_pat_to_reg_pat(p, NULL, NULL, FALSE);
2604 if (p == NULL)
2605 break;
2606 regmatch.regprog = vim_regcomp(p, p_magic ? RE_MAGIC : 0);
2607 if (regmatch.regprog == NULL)
2608 {
2609 vim_free(p);
2610 break;
2611 }
2612
2613 didone = FALSE;
2614 for (match = 0; match < ARGCOUNT; ++match)
2615 if (vim_regexec(&regmatch, alist_name(&ARGLIST[match]),
2616 (colnr_T)0))
2617 {
2618 didone = TRUE;
2619 vim_free(ARGLIST[match].ae_fname);
2620 mch_memmove(ARGLIST + match, ARGLIST + match + 1,
2621 (ARGCOUNT - match - 1) * sizeof(aentry_T));
2622 --ALIST(curwin)->al_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002623 if (curwin->w_arg_idx > match)
2624 --curwin->w_arg_idx;
2625 --match;
2626 }
2627
Bram Moolenaar473de612013-06-08 18:19:48 +02002628 vim_regfree(regmatch.regprog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002629 vim_free(p);
2630 if (!didone)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01002631 semsg(_(e_nomatch2), ((char_u **)new_ga.ga_data)[i]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002632 }
2633 ga_clear(&new_ga);
2634 }
2635 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00002636 {
2637 i = expand_wildcards(new_ga.ga_len, (char_u **)new_ga.ga_data,
2638 &exp_count, &exp_files, EW_DIR|EW_FILE|EW_ADDSLASH|EW_NOTFOUND);
2639 ga_clear(&new_ga);
Bram Moolenaar2db5c3b2016-01-16 22:49:34 +01002640 if (i == FAIL || exp_count == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002641 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01002642 emsg(_(e_nomatch));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002643 return FAIL;
2644 }
2645
Bram Moolenaar071d4272004-06-13 20:20:40 +00002646 if (what == AL_ADD)
2647 {
Bram Moolenaar32bbd002018-08-31 23:06:22 +02002648 alist_add_list(exp_count, exp_files, after, will_edit);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002649 vim_free(exp_files);
2650 }
2651 else /* what == AL_SET */
Bram Moolenaar32bbd002018-08-31 23:06:22 +02002652 alist_set(ALIST(curwin), exp_count, exp_files, will_edit, NULL, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002653 }
2654
2655 alist_check_arg_idx();
2656
2657 return OK;
2658}
2659
2660/*
2661 * Check the validity of the arg_idx for each other window.
2662 */
2663 static void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002664alist_check_arg_idx(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002665{
Bram Moolenaar071d4272004-06-13 20:20:40 +00002666 win_T *win;
Bram Moolenaarf740b292006-02-16 22:11:02 +00002667 tabpage_T *tp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002668
Bram Moolenaarf740b292006-02-16 22:11:02 +00002669 FOR_ALL_TAB_WINDOWS(tp, win)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002670 if (win->w_alist == curwin->w_alist)
2671 check_arg_idx(win);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002672}
2673
2674/*
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +01002675 * Return TRUE if window "win" is editing the file at the current argument
Bram Moolenaard4755bb2004-09-02 19:12:26 +00002676 * index.
2677 */
2678 static int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002679editing_arg_idx(win_T *win)
Bram Moolenaard4755bb2004-09-02 19:12:26 +00002680{
2681 return !(win->w_arg_idx >= WARGCOUNT(win)
2682 || (win->w_buffer->b_fnum
2683 != WARGLIST(win)[win->w_arg_idx].ae_fnum
2684 && (win->w_buffer->b_ffname == NULL
2685 || !(fullpathcmp(
2686 alist_name(&WARGLIST(win)[win->w_arg_idx]),
2687 win->w_buffer->b_ffname, TRUE) & FPC_SAME))));
2688}
2689
2690/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00002691 * Check if window "win" is editing the w_arg_idx file in its argument list.
2692 */
2693 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002694check_arg_idx(win_T *win)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002695{
Bram Moolenaard4755bb2004-09-02 19:12:26 +00002696 if (WARGCOUNT(win) > 1 && !editing_arg_idx(win))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002697 {
2698 /* We are not editing the current entry in the argument list.
2699 * Set "arg_had_last" if we are editing the last one. */
2700 win->w_arg_idx_invalid = TRUE;
2701 if (win->w_arg_idx != WARGCOUNT(win) - 1
2702 && arg_had_last == FALSE
Bram Moolenaar071d4272004-06-13 20:20:40 +00002703 && ALIST(win) == &global_alist
Bram Moolenaar071d4272004-06-13 20:20:40 +00002704 && GARGCOUNT > 0
2705 && win->w_arg_idx < GARGCOUNT
2706 && (win->w_buffer->b_fnum == GARGLIST[GARGCOUNT - 1].ae_fnum
2707 || (win->w_buffer->b_ffname != NULL
2708 && (fullpathcmp(alist_name(&GARGLIST[GARGCOUNT - 1]),
2709 win->w_buffer->b_ffname, TRUE) & FPC_SAME))))
2710 arg_had_last = TRUE;
2711 }
2712 else
2713 {
2714 /* We are editing the current entry in the argument list.
2715 * Set "arg_had_last" if it's also the last one */
2716 win->w_arg_idx_invalid = FALSE;
2717 if (win->w_arg_idx == WARGCOUNT(win) - 1
Bram Moolenaar4033c552017-09-16 20:54:51 +02002718 && win->w_alist == &global_alist)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002719 arg_had_last = TRUE;
2720 }
2721}
2722
2723/*
2724 * ":args", ":argslocal" and ":argsglobal".
2725 */
2726 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002727ex_args(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002728{
2729 int i;
2730
2731 if (eap->cmdidx != CMD_args)
2732 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00002733 alist_unlink(ALIST(curwin));
2734 if (eap->cmdidx == CMD_argglobal)
2735 ALIST(curwin) = &global_alist;
2736 else /* eap->cmdidx == CMD_arglocal */
2737 alist_new();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002738 }
2739
Bram Moolenaar2ac372c2018-12-28 19:06:47 +01002740 if (*eap->arg != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002741 {
2742 /*
2743 * ":args file ..": define new argument list, handle like ":next"
2744 * Also for ":argslocal file .." and ":argsglobal file ..".
2745 */
2746 ex_next(eap);
2747 }
Bram Moolenaar0c72fe42018-03-29 16:04:08 +02002748 else if (eap->cmdidx == CMD_args)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002749 {
2750 /*
2751 * ":args": list arguments.
2752 */
2753 if (ARGCOUNT > 0)
2754 {
Bram Moolenaar405dadb2018-04-20 22:48:58 +02002755 char_u **items = (char_u **)alloc(sizeof(char_u *) * ARGCOUNT);
Bram Moolenaar5d69da42018-04-20 22:01:41 +02002756
2757 if (items != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002758 {
Bram Moolenaar5d69da42018-04-20 22:01:41 +02002759 /* Overwrite the command, for a short list there is no
2760 * scrolling required and no wait_return(). */
2761 gotocmdline(TRUE);
2762
2763 for (i = 0; i < ARGCOUNT; ++i)
Bram Moolenaar405dadb2018-04-20 22:48:58 +02002764 items[i] = alist_name(&ARGLIST[i]);
Bram Moolenaar5d69da42018-04-20 22:01:41 +02002765 list_in_columns(items, ARGCOUNT, curwin->w_arg_idx);
2766 vim_free(items);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002767 }
2768 }
2769 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002770 else if (eap->cmdidx == CMD_arglocal)
2771 {
2772 garray_T *gap = &curwin->w_alist->al_ga;
2773
2774 /*
2775 * ":argslocal": make a local copy of the global argument list.
2776 */
2777 if (ga_grow(gap, GARGCOUNT) == OK)
2778 for (i = 0; i < GARGCOUNT; ++i)
2779 if (GARGLIST[i].ae_fname != NULL)
2780 {
2781 AARGLIST(curwin->w_alist)[gap->ga_len].ae_fname =
2782 vim_strsave(GARGLIST[i].ae_fname);
2783 AARGLIST(curwin->w_alist)[gap->ga_len].ae_fnum =
2784 GARGLIST[i].ae_fnum;
2785 ++gap->ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002786 }
2787 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002788}
2789
2790/*
2791 * ":previous", ":sprevious", ":Next" and ":sNext".
2792 */
2793 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002794ex_previous(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002795{
2796 /* If past the last one already, go to the last one. */
2797 if (curwin->w_arg_idx - (int)eap->line2 >= ARGCOUNT)
2798 do_argfile(eap, ARGCOUNT - 1);
2799 else
2800 do_argfile(eap, curwin->w_arg_idx - (int)eap->line2);
2801}
2802
2803/*
2804 * ":rewind", ":first", ":sfirst" and ":srewind".
2805 */
2806 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002807ex_rewind(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002808{
2809 do_argfile(eap, 0);
2810}
2811
2812/*
2813 * ":last" and ":slast".
2814 */
2815 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002816ex_last(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002817{
2818 do_argfile(eap, ARGCOUNT - 1);
2819}
2820
2821/*
2822 * ":argument" and ":sargument".
2823 */
2824 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002825ex_argument(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002826{
2827 int i;
2828
2829 if (eap->addr_count > 0)
2830 i = eap->line2 - 1;
2831 else
2832 i = curwin->w_arg_idx;
2833 do_argfile(eap, i);
2834}
2835
2836/*
2837 * Edit file "argn" of the argument lists.
2838 */
2839 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002840do_argfile(exarg_T *eap, int argn)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002841{
2842 int other;
2843 char_u *p;
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00002844 int old_arg_idx = curwin->w_arg_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002845
2846 if (argn < 0 || argn >= ARGCOUNT)
2847 {
2848 if (ARGCOUNT <= 1)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01002849 emsg(_("E163: There is only one file to edit"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002850 else if (argn < 0)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01002851 emsg(_("E164: Cannot go before first file"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002852 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01002853 emsg(_("E165: Cannot go beyond last file"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002854 }
2855 else
2856 {
2857 setpcmark();
2858#ifdef FEAT_GUI
2859 need_mouse_correct = TRUE;
2860#endif
2861
Bram Moolenaardf1bdc92006-02-23 21:32:16 +00002862 /* split window or create new tab page first */
2863 if (*eap->cmd == 's' || cmdmod.tab != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002864 {
2865 if (win_split(0, 0) == FAIL)
2866 return;
Bram Moolenaar3368ea22010-09-21 16:56:35 +02002867 RESET_BINDING(curwin);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002868 }
2869 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00002870 {
2871 /*
2872 * if 'hidden' set, only check for changed file when re-editing
2873 * the same buffer
2874 */
2875 other = TRUE;
Bram Moolenaareb44a682017-08-03 22:44:55 +02002876 if (buf_hide(curbuf))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002877 {
2878 p = fix_fname(alist_name(&ARGLIST[argn]));
2879 other = otherfile(p);
2880 vim_free(p);
2881 }
Bram Moolenaareb44a682017-08-03 22:44:55 +02002882 if ((!buf_hide(curbuf) || !other)
Bram Moolenaar45d3b142013-11-09 03:31:51 +01002883 && check_changed(curbuf, CCGD_AW
2884 | (other ? 0 : CCGD_MULTWIN)
2885 | (eap->forceit ? CCGD_FORCEIT : 0)
2886 | CCGD_EXCMD))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002887 return;
2888 }
2889
2890 curwin->w_arg_idx = argn;
Bram Moolenaar4033c552017-09-16 20:54:51 +02002891 if (argn == ARGCOUNT - 1 && curwin->w_alist == &global_alist)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002892 arg_had_last = TRUE;
2893
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00002894 /* Edit the file; always use the last known line number.
2895 * When it fails (e.g. Abort for already edited file) restore the
2896 * argument index. */
2897 if (do_ecmd(0, alist_name(&ARGLIST[curwin->w_arg_idx]), NULL,
Bram Moolenaar071d4272004-06-13 20:20:40 +00002898 eap, ECMD_LAST,
Bram Moolenaareb44a682017-08-03 22:44:55 +02002899 (buf_hide(curwin->w_buffer) ? ECMD_HIDE : 0)
Bram Moolenaar701f7af2008-11-15 13:12:07 +00002900 + (eap->forceit ? ECMD_FORCEIT : 0), curwin) == FAIL)
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00002901 curwin->w_arg_idx = old_arg_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002902 /* like Vi: set the mark where the cursor is in the file. */
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00002903 else if (eap->cmdidx != CMD_argdo)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002904 setmark('\'');
2905 }
2906}
2907
2908/*
2909 * ":next", and commands that behave like it.
2910 */
2911 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002912ex_next(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002913{
2914 int i;
2915
2916 /*
2917 * check for changed buffer now, if this fails the argument list is not
2918 * redefined.
2919 */
Bram Moolenaareb44a682017-08-03 22:44:55 +02002920 if ( buf_hide(curbuf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002921 || eap->cmdidx == CMD_snext
Bram Moolenaar45d3b142013-11-09 03:31:51 +01002922 || !check_changed(curbuf, CCGD_AW
2923 | (eap->forceit ? CCGD_FORCEIT : 0)
2924 | CCGD_EXCMD))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002925 {
2926 if (*eap->arg != NUL) /* redefine file list */
2927 {
Bram Moolenaar32bbd002018-08-31 23:06:22 +02002928 if (do_arglist(eap->arg, AL_SET, 0, TRUE) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002929 return;
2930 i = 0;
2931 }
2932 else
2933 i = curwin->w_arg_idx + (int)eap->line2;
2934 do_argfile(eap, i);
2935 }
2936}
2937
Bram Moolenaar071d4272004-06-13 20:20:40 +00002938/*
2939 * ":argedit"
2940 */
2941 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002942ex_argedit(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002943{
Bram Moolenaar90305c62017-07-16 15:31:17 +02002944 int i = eap->addr_count ? (int)eap->line2 : curwin->w_arg_idx + 1;
Bram Moolenaar46a53df2018-04-24 21:58:51 +02002945 // Whether curbuf will be reused, curbuf->b_ffname will be set.
2946 int curbuf_is_reusable = curbuf_reusable();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002947
Bram Moolenaar32bbd002018-08-31 23:06:22 +02002948 if (do_arglist(eap->arg, AL_ADD, i, TRUE) == FAIL)
Bram Moolenaar90305c62017-07-16 15:31:17 +02002949 return;
2950#ifdef FEAT_TITLE
2951 maketitle();
2952#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002953
Bram Moolenaar46a53df2018-04-24 21:58:51 +02002954 if (curwin->w_arg_idx == 0
2955 && (curbuf->b_ml.ml_flags & ML_EMPTY)
2956 && (curbuf->b_ffname == NULL || curbuf_is_reusable))
Bram Moolenaar90305c62017-07-16 15:31:17 +02002957 i = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002958 /* Edit the argument. */
Bram Moolenaar90305c62017-07-16 15:31:17 +02002959 if (i < ARGCOUNT)
2960 do_argfile(eap, i);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002961}
2962
2963/*
2964 * ":argadd"
2965 */
2966 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002967ex_argadd(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002968{
2969 do_arglist(eap->arg, AL_ADD,
Bram Moolenaar32bbd002018-08-31 23:06:22 +02002970 eap->addr_count > 0 ? (int)eap->line2 : curwin->w_arg_idx + 1,
2971 FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002972#ifdef FEAT_TITLE
2973 maketitle();
2974#endif
2975}
2976
2977/*
2978 * ":argdelete"
2979 */
2980 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002981ex_argdelete(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002982{
2983 int i;
2984 int n;
2985
2986 if (eap->addr_count > 0)
2987 {
2988 /* ":1,4argdel": Delete all arguments in the range. */
2989 if (eap->line2 > ARGCOUNT)
2990 eap->line2 = ARGCOUNT;
2991 n = eap->line2 - eap->line1 + 1;
Bram Moolenaar69a92fb2017-03-09 15:58:30 +01002992 if (*eap->arg != NUL)
2993 /* Can't have both a range and an argument. */
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01002994 emsg(_(e_invarg));
Bram Moolenaar69a92fb2017-03-09 15:58:30 +01002995 else if (n <= 0)
2996 {
2997 /* Don't give an error for ":%argdel" if the list is empty. */
2998 if (eap->line1 != 1 || eap->line2 != 0)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01002999 emsg(_(e_invrange));
Bram Moolenaar69a92fb2017-03-09 15:58:30 +01003000 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003001 else
3002 {
3003 for (i = eap->line1; i <= eap->line2; ++i)
3004 vim_free(ARGLIST[i - 1].ae_fname);
3005 mch_memmove(ARGLIST + eap->line1 - 1, ARGLIST + eap->line2,
3006 (size_t)((ARGCOUNT - eap->line2) * sizeof(aentry_T)));
3007 ALIST(curwin)->al_ga.ga_len -= n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003008 if (curwin->w_arg_idx >= eap->line2)
3009 curwin->w_arg_idx -= n;
3010 else if (curwin->w_arg_idx > eap->line1)
3011 curwin->w_arg_idx = eap->line1;
Bram Moolenaar72defda2016-01-17 18:04:33 +01003012 if (ARGCOUNT == 0)
3013 curwin->w_arg_idx = 0;
3014 else if (curwin->w_arg_idx >= ARGCOUNT)
3015 curwin->w_arg_idx = ARGCOUNT - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003016 }
3017 }
3018 else if (*eap->arg == NUL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003019 emsg(_(e_argreq));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003020 else
Bram Moolenaar32bbd002018-08-31 23:06:22 +02003021 do_arglist(eap->arg, AL_DEL, 0, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003022#ifdef FEAT_TITLE
3023 maketitle();
3024#endif
3025}
3026
3027/*
Bram Moolenaaraa23b372015-09-08 18:46:31 +02003028 * ":argdo", ":windo", ":bufdo", ":tabdo", ":cdo", ":ldo", ":cfdo" and ":lfdo"
Bram Moolenaar071d4272004-06-13 20:20:40 +00003029 */
3030 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01003031ex_listdo(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003032{
3033 int i;
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003034 win_T *wp;
3035 tabpage_T *tp;
Bram Moolenaare25bb902015-02-27 20:33:37 +01003036 buf_T *buf = curbuf;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003037 int next_fnum = 0;
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01003038#if defined(FEAT_SYN_HL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003039 char_u *save_ei = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003040#endif
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00003041 char_u *p_shm_save;
Bram Moolenaaraa23b372015-09-08 18:46:31 +02003042#ifdef FEAT_QUICKFIX
Bram Moolenaared84b762015-09-09 22:35:29 +02003043 int qf_size = 0;
Bram Moolenaaraa23b372015-09-08 18:46:31 +02003044 int qf_idx;
3045#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003046
Bram Moolenaar0106e3d2016-02-23 18:55:43 +01003047#ifndef FEAT_QUICKFIX
3048 if (eap->cmdidx == CMD_cdo || eap->cmdidx == CMD_ldo ||
3049 eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo)
3050 {
3051 ex_ni(eap);
3052 return;
3053 }
3054#endif
3055
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01003056#if defined(FEAT_SYN_HL)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00003057 if (eap->cmdidx != CMD_windo && eap->cmdidx != CMD_tabdo)
Bram Moolenaardcaf10e2005-01-21 11:55:25 +00003058 /* Don't do syntax HL autocommands. Skipping the syntax file is a
3059 * great speed improvement. */
3060 save_ei = au_event_disable(",Syntax");
Bram Moolenaar071d4272004-06-13 20:20:40 +00003061#endif
Bram Moolenaar6b1ee342014-08-06 18:17:11 +02003062#ifdef FEAT_CLIPBOARD
3063 start_global_changes();
3064#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003065
3066 if (eap->cmdidx == CMD_windo
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003067 || eap->cmdidx == CMD_tabdo
Bram Moolenaareb44a682017-08-03 22:44:55 +02003068 || buf_hide(curbuf)
Bram Moolenaar45d3b142013-11-09 03:31:51 +01003069 || !check_changed(curbuf, CCGD_AW
3070 | (eap->forceit ? CCGD_FORCEIT : 0)
3071 | CCGD_EXCMD))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003072 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00003073 i = 0;
Bram Moolenaara162bc52015-01-07 16:54:21 +01003074 /* start at the eap->line1 argument/window/buffer */
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003075 wp = firstwin;
3076 tp = first_tabpage;
Bram Moolenaara162bc52015-01-07 16:54:21 +01003077 switch (eap->cmdidx)
3078 {
Bram Moolenaara162bc52015-01-07 16:54:21 +01003079 case CMD_windo:
3080 for ( ; wp != NULL && i + 1 < eap->line1; wp = wp->w_next)
3081 i++;
3082 break;
3083 case CMD_tabdo:
3084 for( ; tp != NULL && i + 1 < eap->line1; tp = tp->tp_next)
3085 i++;
3086 break;
Bram Moolenaara162bc52015-01-07 16:54:21 +01003087 case CMD_argdo:
3088 i = eap->line1 - 1;
3089 break;
Bram Moolenaara162bc52015-01-07 16:54:21 +01003090 default:
3091 break;
3092 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003093 /* set pcmark now */
3094 if (eap->cmdidx == CMD_bufdo)
Bram Moolenaaraa23b372015-09-08 18:46:31 +02003095 {
Bram Moolenaare25bb902015-02-27 20:33:37 +01003096 /* Advance to the first listed buffer after "eap->line1". */
Bram Moolenaaraa23b372015-09-08 18:46:31 +02003097 for (buf = firstbuf; buf != NULL && (buf->b_fnum < eap->line1
Bram Moolenaare25bb902015-02-27 20:33:37 +01003098 || !buf->b_p_bl); buf = buf->b_next)
3099 if (buf->b_fnum > eap->line2)
3100 {
3101 buf = NULL;
3102 break;
3103 }
Bram Moolenaaraa23b372015-09-08 18:46:31 +02003104 if (buf != NULL)
Bram Moolenaare25bb902015-02-27 20:33:37 +01003105 goto_buffer(eap, DOBUF_FIRST, FORWARD, buf->b_fnum);
Bram Moolenaaraa23b372015-09-08 18:46:31 +02003106 }
3107#ifdef FEAT_QUICKFIX
3108 else if (eap->cmdidx == CMD_cdo || eap->cmdidx == CMD_ldo
3109 || eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo)
3110 {
3111 qf_size = qf_get_size(eap);
3112 if (qf_size <= 0 || eap->line1 > qf_size)
3113 buf = NULL;
3114 else
3115 {
3116 ex_cc(eap);
3117
3118 buf = curbuf;
3119 i = eap->line1 - 1;
3120 if (eap->addr_count <= 0)
3121 /* default is all the quickfix/location list entries */
3122 eap->line2 = qf_size;
3123 }
3124 }
3125#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003126 else
3127 setpcmark();
3128 listcmd_busy = TRUE; /* avoids setting pcmark below */
3129
Bram Moolenaare25bb902015-02-27 20:33:37 +01003130 while (!got_int && buf != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003131 {
3132 if (eap->cmdidx == CMD_argdo)
3133 {
3134 /* go to argument "i" */
3135 if (i == ARGCOUNT)
3136 break;
3137 /* Don't call do_argfile() when already there, it will try
3138 * reloading the file. */
Bram Moolenaard4755bb2004-09-02 19:12:26 +00003139 if (curwin->w_arg_idx != i || !editing_arg_idx(curwin))
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00003140 {
3141 /* Clear 'shm' to avoid that the file message overwrites
3142 * any output from the command. */
3143 p_shm_save = vim_strsave(p_shm);
3144 set_option_value((char_u *)"shm", 0L, (char_u *)"", 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003145 do_argfile(eap, i);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00003146 set_option_value((char_u *)"shm", 0L, p_shm_save, 0);
3147 vim_free(p_shm_save);
3148 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003149 if (curwin->w_arg_idx != i)
3150 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003151 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003152 else if (eap->cmdidx == CMD_windo)
3153 {
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003154 /* go to window "wp" */
3155 if (!win_valid(wp))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003156 break;
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003157 win_goto(wp);
Bram Moolenaar41423242007-05-03 20:11:13 +00003158 if (curwin != wp)
3159 break; /* something must be wrong */
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003160 wp = curwin->w_next;
3161 }
3162 else if (eap->cmdidx == CMD_tabdo)
3163 {
3164 /* go to window "tp" */
3165 if (!valid_tabpage(tp))
3166 break;
Bram Moolenaar49e649f2013-05-06 04:50:35 +02003167 goto_tabpage_tp(tp, TRUE, TRUE);
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003168 tp = tp->tp_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003169 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003170 else if (eap->cmdidx == CMD_bufdo)
3171 {
3172 /* Remember the number of the next listed buffer, in case
3173 * ":bwipe" is used or autocommands do something strange. */
3174 next_fnum = -1;
3175 for (buf = curbuf->b_next; buf != NULL; buf = buf->b_next)
3176 if (buf->b_p_bl)
3177 {
3178 next_fnum = buf->b_fnum;
3179 break;
3180 }
3181 }
3182
Bram Moolenaara162bc52015-01-07 16:54:21 +01003183 ++i;
3184
Bram Moolenaar071d4272004-06-13 20:20:40 +00003185 /* execute the command */
3186 do_cmdline(eap->arg, eap->getline, eap->cookie,
3187 DOCMD_VERBOSE + DOCMD_NOWAIT);
3188
3189 if (eap->cmdidx == CMD_bufdo)
3190 {
3191 /* Done? */
Bram Moolenaara162bc52015-01-07 16:54:21 +01003192 if (next_fnum < 0 || next_fnum > eap->line2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003193 break;
3194 /* Check if the buffer still exists. */
Bram Moolenaar29323592016-07-24 22:04:11 +02003195 FOR_ALL_BUFFERS(buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003196 if (buf->b_fnum == next_fnum)
3197 break;
3198 if (buf == NULL)
3199 break;
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00003200
3201 /* Go to the next buffer. Clear 'shm' to avoid that the file
3202 * message overwrites any output from the command. */
3203 p_shm_save = vim_strsave(p_shm);
3204 set_option_value((char_u *)"shm", 0L, (char_u *)"", 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003205 goto_buffer(eap, DOBUF_FIRST, FORWARD, next_fnum);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00003206 set_option_value((char_u *)"shm", 0L, p_shm_save, 0);
3207 vim_free(p_shm_save);
3208
Bram Moolenaaraa23b372015-09-08 18:46:31 +02003209 /* If autocommands took us elsewhere, quit here. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003210 if (curbuf->b_fnum != next_fnum)
3211 break;
3212 }
3213
Bram Moolenaaraa23b372015-09-08 18:46:31 +02003214#ifdef FEAT_QUICKFIX
3215 if (eap->cmdidx == CMD_cdo || eap->cmdidx == CMD_ldo
3216 || eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo)
3217 {
3218 if (i >= qf_size || i >= eap->line2)
3219 break;
3220
3221 qf_idx = qf_get_cur_idx(eap);
3222
3223 ex_cnext(eap);
3224
3225 /* If jumping to the next quickfix entry fails, quit here */
3226 if (qf_get_cur_idx(eap) == qf_idx)
3227 break;
3228 }
3229#endif
3230
Bram Moolenaar071d4272004-06-13 20:20:40 +00003231 if (eap->cmdidx == CMD_windo)
3232 {
3233 validate_cursor(); /* cursor may have moved */
Bram Moolenaar8a3bb562018-03-04 20:14:14 +01003234
Bram Moolenaar071d4272004-06-13 20:20:40 +00003235 /* required when 'scrollbind' has been set */
3236 if (curwin->w_p_scb)
3237 do_check_scrollbind(TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003238 }
Bram Moolenaara162bc52015-01-07 16:54:21 +01003239
Bram Moolenaara162bc52015-01-07 16:54:21 +01003240 if (eap->cmdidx == CMD_windo || eap->cmdidx == CMD_tabdo)
3241 if (i+1 > eap->line2)
3242 break;
Bram Moolenaara162bc52015-01-07 16:54:21 +01003243 if (eap->cmdidx == CMD_argdo && i >= eap->line2)
3244 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003245 }
3246 listcmd_busy = FALSE;
3247 }
3248
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01003249#if defined(FEAT_SYN_HL)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00003250 if (save_ei != NULL)
3251 {
3252 au_event_restore(save_ei);
3253 apply_autocmds(EVENT_SYNTAX, curbuf->b_p_syn,
3254 curbuf->b_fname, TRUE, curbuf);
3255 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003256#endif
Bram Moolenaar6b1ee342014-08-06 18:17:11 +02003257#ifdef FEAT_CLIPBOARD
3258 end_global_changes();
3259#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003260}
3261
3262/*
3263 * Add files[count] to the arglist of the current window after arg "after".
3264 * The file names in files[count] must have been allocated and are taken over.
3265 * Files[] itself is not taken over.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003266 */
Bram Moolenaar32bbd002018-08-31 23:06:22 +02003267 static void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01003268alist_add_list(
3269 int count,
3270 char_u **files,
Bram Moolenaar32bbd002018-08-31 23:06:22 +02003271 int after, // where to add: 0 = before first one
3272 int will_edit) // will edit adding argument
Bram Moolenaar071d4272004-06-13 20:20:40 +00003273{
3274 int i;
Bram Moolenaara24f0a52016-01-17 19:39:00 +01003275 int old_argcount = ARGCOUNT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003276
3277 if (ga_grow(&ALIST(curwin)->al_ga, count) == OK)
3278 {
3279 if (after < 0)
3280 after = 0;
3281 if (after > ARGCOUNT)
3282 after = ARGCOUNT;
3283 if (after < ARGCOUNT)
3284 mch_memmove(&(ARGLIST[after + count]), &(ARGLIST[after]),
3285 (ARGCOUNT - after) * sizeof(aentry_T));
3286 for (i = 0; i < count; ++i)
3287 {
Bram Moolenaar32bbd002018-08-31 23:06:22 +02003288 int flags = BLN_LISTED | (will_edit ? BLN_CURBUF : 0);
3289
Bram Moolenaar071d4272004-06-13 20:20:40 +00003290 ARGLIST[after + i].ae_fname = files[i];
Bram Moolenaar32bbd002018-08-31 23:06:22 +02003291 ARGLIST[after + i].ae_fnum = buflist_add(files[i], flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003292 }
3293 ALIST(curwin)->al_ga.ga_len += count;
Bram Moolenaara24f0a52016-01-17 19:39:00 +01003294 if (old_argcount > 0 && curwin->w_arg_idx >= after)
3295 curwin->w_arg_idx += count;
Bram Moolenaar32bbd002018-08-31 23:06:22 +02003296 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003297 }
3298
3299 for (i = 0; i < count; ++i)
3300 vim_free(files[i]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003301}
3302
Bram Moolenaarcd43eff2018-03-29 15:55:38 +02003303#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
3304/*
3305 * Function given to ExpandGeneric() to obtain the possible arguments of the
3306 * argedit and argdelete commands.
3307 */
3308 char_u *
3309get_arglist_name(expand_T *xp UNUSED, int idx)
3310{
3311 if (idx >= ARGCOUNT)
3312 return NULL;
3313
3314 return alist_name(&ARGLIST[idx]);
3315}
3316#endif
3317
Bram Moolenaar0c72fe42018-03-29 16:04:08 +02003318
Bram Moolenaar071d4272004-06-13 20:20:40 +00003319#ifdef FEAT_EVAL
3320/*
3321 * ":compiler[!] {name}"
3322 */
3323 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01003324ex_compiler(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003325{
3326 char_u *buf;
3327 char_u *old_cur_comp = NULL;
3328 char_u *p;
3329
3330 if (*eap->arg == NUL)
3331 {
3332 /* List all compiler scripts. */
3333 do_cmdline_cmd((char_u *)"echo globpath(&rtp, 'compiler/*.vim')");
3334 /* ) keep the indenter happy... */
3335 }
3336 else
3337 {
3338 buf = alloc((unsigned)(STRLEN(eap->arg) + 14));
3339 if (buf != NULL)
3340 {
3341 if (eap->forceit)
3342 {
3343 /* ":compiler! {name}" sets global options */
3344 do_cmdline_cmd((char_u *)
3345 "command -nargs=* CompilerSet set <args>");
3346 }
3347 else
3348 {
3349 /* ":compiler! {name}" sets local options.
3350 * To remain backwards compatible "current_compiler" is always
3351 * used. A user's compiler plugin may set it, the distributed
3352 * plugin will then skip the settings. Afterwards set
Bram Moolenaar3d63e3f2010-01-19 16:13:50 +01003353 * "b:current_compiler" and restore "current_compiler".
3354 * Explicitly prepend "g:" to make it work in a function. */
3355 old_cur_comp = get_var_value((char_u *)"g:current_compiler");
Bram Moolenaar071d4272004-06-13 20:20:40 +00003356 if (old_cur_comp != NULL)
3357 old_cur_comp = vim_strsave(old_cur_comp);
3358 do_cmdline_cmd((char_u *)
3359 "command -nargs=* CompilerSet setlocal <args>");
3360 }
Bram Moolenaar3d63e3f2010-01-19 16:13:50 +01003361 do_unlet((char_u *)"g:current_compiler", TRUE);
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003362 do_unlet((char_u *)"b:current_compiler", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003363
3364 sprintf((char *)buf, "compiler/%s.vim", eap->arg);
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01003365 if (source_runtime(buf, DIP_ALL) == FAIL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003366 semsg(_("E666: compiler not supported: %s"), eap->arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003367 vim_free(buf);
3368
3369 do_cmdline_cmd((char_u *)":delcommand CompilerSet");
3370
3371 /* Set "b:current_compiler" from "current_compiler". */
Bram Moolenaar3d63e3f2010-01-19 16:13:50 +01003372 p = get_var_value((char_u *)"g:current_compiler");
Bram Moolenaar071d4272004-06-13 20:20:40 +00003373 if (p != NULL)
3374 set_internal_string_var((char_u *)"b:current_compiler", p);
3375
3376 /* Restore "current_compiler" for ":compiler {name}". */
3377 if (!eap->forceit)
3378 {
3379 if (old_cur_comp != NULL)
3380 {
Bram Moolenaar3d63e3f2010-01-19 16:13:50 +01003381 set_internal_string_var((char_u *)"g:current_compiler",
Bram Moolenaar071d4272004-06-13 20:20:40 +00003382 old_cur_comp);
3383 vim_free(old_cur_comp);
3384 }
3385 else
Bram Moolenaar3d63e3f2010-01-19 16:13:50 +01003386 do_unlet((char_u *)"g:current_compiler", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003387 }
3388 }
3389 }
3390}
3391#endif
3392
3393/*
Bram Moolenaar8dcf2592016-03-12 22:47:14 +01003394 * ":runtime [what] {name}"
Bram Moolenaar071d4272004-06-13 20:20:40 +00003395 */
3396 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01003397ex_runtime(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003398{
Bram Moolenaar8dcf2592016-03-12 22:47:14 +01003399 char_u *arg = eap->arg;
3400 char_u *p = skiptowhite(arg);
3401 int len = (int)(p - arg);
3402 int flags = eap->forceit ? DIP_ALL : 0;
3403
3404 if (STRNCMP(arg, "START", len) == 0)
3405 {
3406 flags += DIP_START + DIP_NORTP;
3407 arg = skipwhite(arg + len);
3408 }
3409 else if (STRNCMP(arg, "OPT", len) == 0)
3410 {
3411 flags += DIP_OPT + DIP_NORTP;
3412 arg = skipwhite(arg + len);
3413 }
3414 else if (STRNCMP(arg, "PACK", len) == 0)
3415 {
3416 flags += DIP_START + DIP_OPT + DIP_NORTP;
3417 arg = skipwhite(arg + len);
3418 }
3419 else if (STRNCMP(arg, "ALL", len) == 0)
3420 {
3421 flags += DIP_START + DIP_OPT;
3422 arg = skipwhite(arg + len);
3423 }
3424
3425 source_runtime(arg, flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003426}
3427
Bram Moolenaar071d4272004-06-13 20:20:40 +00003428 static void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01003429source_callback(char_u *fname, void *cookie UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003430{
Bram Moolenaar910f66f2006-04-05 20:41:53 +00003431 (void)do_source(fname, FALSE, DOSO_NONE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003432}
3433
3434/*
Bram Moolenaarbe82c252016-03-06 14:44:08 +01003435 * Find the file "name" in all directories in "path" and invoke
3436 * "callback(fname, cookie)".
3437 * "name" can contain wildcards.
3438 * When "flags" has DIP_ALL: source all files, otherwise only the first one.
3439 * When "flags" has DIP_DIR: find directories instead of files.
3440 * When "flags" has DIP_ERR: give an error message if there is no match.
3441 *
3442 * return FAIL when no file could be sourced, OK otherwise.
3443 */
Bram Moolenaar6bef5302016-03-12 21:28:26 +01003444 int
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01003445do_in_path(
3446 char_u *path,
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01003447 char_u *name,
Bram Moolenaar91715872016-03-03 17:13:03 +01003448 int flags,
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01003449 void (*callback)(char_u *fname, void *ck),
3450 void *cookie)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003451{
3452 char_u *rtp;
3453 char_u *np;
3454 char_u *buf;
3455 char_u *rtp_copy;
3456 char_u *tail;
3457 int num_files;
3458 char_u **files;
3459 int i;
3460 int did_one = FALSE;
3461#ifdef AMIGA
3462 struct Process *proc = (struct Process *)FindTask(0L);
3463 APTR save_winptr = proc->pr_WindowPtr;
3464
3465 /* Avoid a requester here for a volume that doesn't exist. */
3466 proc->pr_WindowPtr = (APTR)-1L;
3467#endif
3468
3469 /* Make a copy of 'runtimepath'. Invoking the callback may change the
3470 * value. */
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01003471 rtp_copy = vim_strsave(path);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003472 buf = alloc(MAXPATHL);
3473 if (buf != NULL && rtp_copy != NULL)
3474 {
Bram Moolenaarc09a6d62013-06-10 21:27:29 +02003475 if (p_verbose > 1 && name != NULL)
Bram Moolenaar54ee7752005-05-31 22:22:17 +00003476 {
3477 verbose_enter();
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003478 smsg(_("Searching for \"%s\" in \"%s\""),
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01003479 (char *)name, (char *)path);
Bram Moolenaar54ee7752005-05-31 22:22:17 +00003480 verbose_leave();
3481 }
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00003482
Bram Moolenaar071d4272004-06-13 20:20:40 +00003483 /* Loop over all entries in 'runtimepath'. */
3484 rtp = rtp_copy;
Bram Moolenaar91715872016-03-03 17:13:03 +01003485 while (*rtp != NUL && ((flags & DIP_ALL) || !did_one))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003486 {
Bram Moolenaar66459b72016-08-06 19:01:55 +02003487 size_t buflen;
3488
Bram Moolenaar071d4272004-06-13 20:20:40 +00003489 /* Copy the path from 'runtimepath' to buf[]. */
3490 copy_option_part(&rtp, buf, MAXPATHL, ",");
Bram Moolenaar66459b72016-08-06 19:01:55 +02003491 buflen = STRLEN(buf);
3492
3493 /* Skip after or non-after directories. */
3494 if (flags & (DIP_NOAFTER | DIP_AFTER))
3495 {
3496 int is_after = buflen >= 5
3497 && STRCMP(buf + buflen - 5, "after") == 0;
3498
3499 if ((is_after && (flags & DIP_NOAFTER))
3500 || (!is_after && (flags & DIP_AFTER)))
3501 continue;
3502 }
3503
Bram Moolenaarc09a6d62013-06-10 21:27:29 +02003504 if (name == NULL)
3505 {
3506 (*callback)(buf, (void *) &cookie);
3507 if (!did_one)
3508 did_one = (cookie == NULL);
3509 }
Bram Moolenaar66459b72016-08-06 19:01:55 +02003510 else if (buflen + STRLEN(name) + 2 < MAXPATHL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003511 {
3512 add_pathsep(buf);
3513 tail = buf + STRLEN(buf);
3514
3515 /* Loop over all patterns in "name" */
3516 np = name;
Bram Moolenaar91715872016-03-03 17:13:03 +01003517 while (*np != NUL && ((flags & DIP_ALL) || !did_one))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003518 {
3519 /* Append the pattern from "name" to buf[]. */
3520 copy_option_part(&np, tail, (int)(MAXPATHL - (tail - buf)),
3521 "\t ");
3522
3523 if (p_verbose > 2)
Bram Moolenaar54ee7752005-05-31 22:22:17 +00003524 {
3525 verbose_enter();
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003526 smsg(_("Searching for \"%s\""), buf);
Bram Moolenaar54ee7752005-05-31 22:22:17 +00003527 verbose_leave();
3528 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003529
3530 /* Expand wildcards, invoke the callback for each match. */
3531 if (gen_expand_wildcards(1, &buf, &num_files, &files,
Bram Moolenaar91715872016-03-03 17:13:03 +01003532 (flags & DIP_DIR) ? EW_DIR : EW_FILE) == OK)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003533 {
3534 for (i = 0; i < num_files; ++i)
3535 {
Bram Moolenaar13fcaaf2005-04-15 21:13:42 +00003536 (*callback)(files[i], cookie);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003537 did_one = TRUE;
Bram Moolenaar91715872016-03-03 17:13:03 +01003538 if (!(flags & DIP_ALL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003539 break;
3540 }
3541 FreeWild(num_files, files);
3542 }
3543 }
3544 }
3545 }
3546 }
3547 vim_free(buf);
3548 vim_free(rtp_copy);
Bram Moolenaarbe82c252016-03-06 14:44:08 +01003549 if (!did_one && name != NULL)
Bram Moolenaar54ee7752005-05-31 22:22:17 +00003550 {
Bram Moolenaarbe82c252016-03-06 14:44:08 +01003551 char *basepath = path == p_rtp ? "runtimepath" : "packpath";
3552
3553 if (flags & DIP_ERR)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003554 semsg(_(e_dirnotf), basepath, name);
Bram Moolenaarbe82c252016-03-06 14:44:08 +01003555 else if (p_verbose > 0)
3556 {
3557 verbose_enter();
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01003558 smsg(_("not found in '%s': \"%s\""), basepath, name);
Bram Moolenaarbe82c252016-03-06 14:44:08 +01003559 verbose_leave();
3560 }
Bram Moolenaar54ee7752005-05-31 22:22:17 +00003561 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003562
3563#ifdef AMIGA
3564 proc->pr_WindowPtr = save_winptr;
3565#endif
3566
3567 return did_one ? OK : FAIL;
3568}
3569
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01003570/*
Bram Moolenaar07ecfa62017-06-27 14:43:55 +02003571 * Find "name" in "path". When found, invoke the callback function for
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01003572 * it: callback(fname, "cookie")
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01003573 * When "flags" has DIP_ALL repeat for all matches, otherwise only the first
3574 * one is used.
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01003575 * Returns OK when at least one match found, FAIL otherwise.
3576 *
Bram Moolenaar07ecfa62017-06-27 14:43:55 +02003577 * If "name" is NULL calls callback for each entry in "path". Cookie is
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01003578 * passed by reference in this case, setting it to NULL indicates that callback
3579 * has done its job.
3580 */
Bram Moolenaar07ecfa62017-06-27 14:43:55 +02003581 static int
3582do_in_path_and_pp(
3583 char_u *path,
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01003584 char_u *name,
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01003585 int flags,
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01003586 void (*callback)(char_u *fname, void *ck),
3587 void *cookie)
3588{
Bram Moolenaar8dcf2592016-03-12 22:47:14 +01003589 int done = FAIL;
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01003590 char_u *s;
3591 int len;
3592 char *start_dir = "pack/*/start/*/%s";
3593 char *opt_dir = "pack/*/opt/*/%s";
3594
Bram Moolenaar8dcf2592016-03-12 22:47:14 +01003595 if ((flags & DIP_NORTP) == 0)
Bram Moolenaar07ecfa62017-06-27 14:43:55 +02003596 done = do_in_path(path, name, flags, callback, cookie);
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01003597
Bram Moolenaar8dcf2592016-03-12 22:47:14 +01003598 if ((done == FAIL || (flags & DIP_ALL)) && (flags & DIP_START))
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01003599 {
Bram Moolenaar1c8b4ed2016-03-17 21:51:03 +01003600 len = (int)(STRLEN(start_dir) + STRLEN(name));
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01003601 s = alloc(len);
3602 if (s == NULL)
3603 return FAIL;
3604 vim_snprintf((char *)s, len, start_dir, name);
3605 done = do_in_path(p_pp, s, flags, callback, cookie);
3606 vim_free(s);
3607 }
3608
Bram Moolenaar8dcf2592016-03-12 22:47:14 +01003609 if ((done == FAIL || (flags & DIP_ALL)) && (flags & DIP_OPT))
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01003610 {
Bram Moolenaar1c8b4ed2016-03-17 21:51:03 +01003611 len = (int)(STRLEN(opt_dir) + STRLEN(name));
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01003612 s = alloc(len);
3613 if (s == NULL)
3614 return FAIL;
3615 vim_snprintf((char *)s, len, opt_dir, name);
3616 done = do_in_path(p_pp, s, flags, callback, cookie);
3617 vim_free(s);
3618 }
3619
3620 return done;
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01003621}
3622
Bram Moolenaar1bdd4262016-03-03 14:23:10 +01003623/*
Bram Moolenaar07ecfa62017-06-27 14:43:55 +02003624 * Just like do_in_path_and_pp(), using 'runtimepath' for "path".
3625 */
3626 int
3627do_in_runtimepath(
3628 char_u *name,
3629 int flags,
3630 void (*callback)(char_u *fname, void *ck),
3631 void *cookie)
3632{
3633 return do_in_path_and_pp(p_rtp, name, flags, callback, cookie);
3634}
3635
3636/*
3637 * Source the file "name" from all directories in 'runtimepath'.
3638 * "name" can contain wildcards.
3639 * When "flags" has DIP_ALL: source all files, otherwise only the first one.
3640 *
3641 * return FAIL when no file could be sourced, OK otherwise.
3642 */
3643 int
3644source_runtime(char_u *name, int flags)
3645{
3646 return source_in_path(p_rtp, name, flags);
3647}
3648
3649/*
3650 * Just like source_runtime(), but use "path" instead of 'runtimepath'.
3651 */
3652 int
3653source_in_path(char_u *path, char_u *name, int flags)
3654{
3655 return do_in_path_and_pp(path, name, flags, source_callback, NULL);
3656}
3657
3658
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01003659#if defined(FEAT_EVAL) || defined(PROTO)
3660
Bram Moolenaar07ecfa62017-06-27 14:43:55 +02003661/*
Bram Moolenaarf3654822016-03-04 22:12:23 +01003662 * Expand wildcards in "pat" and invoke do_source() for each match.
Bram Moolenaar1bdd4262016-03-03 14:23:10 +01003663 */
3664 static void
Bram Moolenaarf3654822016-03-04 22:12:23 +01003665source_all_matches(char_u *pat)
Bram Moolenaar1bdd4262016-03-03 14:23:10 +01003666{
Bram Moolenaarf3654822016-03-04 22:12:23 +01003667 int num_files;
3668 char_u **files;
3669 int i;
Bram Moolenaar1bdd4262016-03-03 14:23:10 +01003670
Bram Moolenaarf3654822016-03-04 22:12:23 +01003671 if (gen_expand_wildcards(1, &pat, &num_files, &files, EW_FILE) == OK)
Bram Moolenaar1bdd4262016-03-03 14:23:10 +01003672 {
Bram Moolenaarf3654822016-03-04 22:12:23 +01003673 for (i = 0; i < num_files; ++i)
3674 (void)do_source(files[i], FALSE, DOSO_NONE);
3675 FreeWild(num_files, files);
Bram Moolenaar1bdd4262016-03-03 14:23:10 +01003676 }
Bram Moolenaar1bdd4262016-03-03 14:23:10 +01003677}
3678
Bram Moolenaar2374faa2018-02-04 17:47:42 +01003679/*
3680 * Add the package directory to 'runtimepath'.
3681 */
3682 static int
3683add_pack_dir_to_rtp(char_u *fname)
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01003684{
Bram Moolenaarf3654822016-03-04 22:12:23 +01003685 char_u *p4, *p3, *p2, *p1, *p;
Bram Moolenaar99396d42018-09-08 18:21:16 +02003686 char_u *entry;
3687 char_u *insp = NULL;
Bram Moolenaar91715872016-03-03 17:13:03 +01003688 int c;
3689 char_u *new_rtp;
3690 int keep;
Bram Moolenaarb0550662016-05-31 21:37:36 +02003691 size_t oldlen;
3692 size_t addlen;
Bram Moolenaar99396d42018-09-08 18:21:16 +02003693 size_t new_rtp_len;
Bram Moolenaar2374faa2018-02-04 17:47:42 +01003694 char_u *afterdir = NULL;
Bram Moolenaarb0550662016-05-31 21:37:36 +02003695 size_t afterlen = 0;
Bram Moolenaar99396d42018-09-08 18:21:16 +02003696 char_u *after_insp = NULL;
Bram Moolenaar2374faa2018-02-04 17:47:42 +01003697 char_u *ffname = NULL;
Bram Moolenaarfef524b2016-07-02 22:07:22 +02003698 size_t fname_len;
Bram Moolenaar2f9e5752017-02-05 16:07:54 +01003699 char_u *buf = NULL;
3700 char_u *rtp_ffname;
3701 int match;
Bram Moolenaar2374faa2018-02-04 17:47:42 +01003702 int retval = FAIL;
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01003703
Bram Moolenaar2374faa2018-02-04 17:47:42 +01003704 p4 = p3 = p2 = p1 = get_past_head(fname);
3705 for (p = p1; *p; MB_PTR_ADV(p))
3706 if (vim_ispathsep_nocolon(*p))
3707 {
3708 p4 = p3; p3 = p2; p2 = p1; p1 = p;
3709 }
3710
3711 /* now we have:
3712 * rtp/pack/name/start/name
3713 * p4 p3 p2 p1
3714 *
3715 * find the part up to "pack" in 'runtimepath' */
3716 c = *++p4; /* append pathsep in order to expand symlink */
3717 *p4 = NUL;
3718 ffname = fix_fname(fname);
3719 *p4 = c;
Bram Moolenaar91715872016-03-03 17:13:03 +01003720 if (ffname == NULL)
Bram Moolenaar2374faa2018-02-04 17:47:42 +01003721 return FAIL;
3722
Bram Moolenaar99396d42018-09-08 18:21:16 +02003723 // Find "ffname" in "p_rtp", ignoring '/' vs '\' differences.
3724 // Also stop at the first "after" directory.
Bram Moolenaar2374faa2018-02-04 17:47:42 +01003725 fname_len = STRLEN(ffname);
Bram Moolenaar2374faa2018-02-04 17:47:42 +01003726 buf = alloc(MAXPATHL);
3727 if (buf == NULL)
3728 goto theend;
Bram Moolenaar99396d42018-09-08 18:21:16 +02003729 for (entry = p_rtp; *entry != NUL; )
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01003730 {
Bram Moolenaar99396d42018-09-08 18:21:16 +02003731 char_u *cur_entry = entry;
3732
3733 copy_option_part(&entry, buf, MAXPATHL, ",");
3734 if (insp == NULL)
3735 {
3736 add_pathsep(buf);
3737 rtp_ffname = fix_fname(buf);
3738 if (rtp_ffname == NULL)
3739 goto theend;
3740 match = vim_fnamencmp(rtp_ffname, ffname, fname_len) == 0;
3741 vim_free(rtp_ffname);
3742 if (match)
3743 // Insert "ffname" after this entry (and comma).
3744 insp = entry;
3745 }
3746
3747 if ((p = (char_u *)strstr((char *)buf, "after")) != NULL
3748 && p > buf
3749 && vim_ispathsep(p[-1])
3750 && (vim_ispathsep(p[5]) || p[5] == NUL || p[5] == ','))
3751 {
3752 if (insp == NULL)
3753 // Did not find "ffname" before the first "after" directory,
3754 // insert it before this entry.
3755 insp = cur_entry;
3756 after_insp = cur_entry;
Bram Moolenaar2374faa2018-02-04 17:47:42 +01003757 break;
Bram Moolenaar99396d42018-09-08 18:21:16 +02003758 }
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01003759 }
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01003760
Bram Moolenaar99396d42018-09-08 18:21:16 +02003761 if (insp == NULL)
3762 // Both "fname" and "after" not found, append at the end.
Bram Moolenaar2374faa2018-02-04 17:47:42 +01003763 insp = p_rtp + STRLEN(p_rtp);
Bram Moolenaar2374faa2018-02-04 17:47:42 +01003764
Bram Moolenaar99396d42018-09-08 18:21:16 +02003765 // check if rtp/pack/name/start/name/after exists
Bram Moolenaar2374faa2018-02-04 17:47:42 +01003766 afterdir = concat_fnames(fname, (char_u *)"after", TRUE);
3767 if (afterdir != NULL && mch_isdir(afterdir))
Bram Moolenaar99396d42018-09-08 18:21:16 +02003768 afterlen = STRLEN(afterdir) + 1; // add one for comma
Bram Moolenaar2374faa2018-02-04 17:47:42 +01003769
3770 oldlen = STRLEN(p_rtp);
Bram Moolenaar99396d42018-09-08 18:21:16 +02003771 addlen = STRLEN(fname) + 1; // add one for comma
3772 new_rtp = alloc((int)(oldlen + addlen + afterlen + 1)); // add one for NUL
Bram Moolenaar2374faa2018-02-04 17:47:42 +01003773 if (new_rtp == NULL)
3774 goto theend;
Bram Moolenaar99396d42018-09-08 18:21:16 +02003775
3776 // We now have 'rtp' parts: {keep}{keep_after}{rest}.
3777 // Create new_rtp, first: {keep},{fname}
Bram Moolenaar2374faa2018-02-04 17:47:42 +01003778 keep = (int)(insp - p_rtp);
3779 mch_memmove(new_rtp, p_rtp, keep);
Bram Moolenaar99396d42018-09-08 18:21:16 +02003780 new_rtp_len = keep;
3781 if (*insp == NUL)
3782 new_rtp[new_rtp_len++] = ','; // add comma before
3783 mch_memmove(new_rtp + new_rtp_len, fname, addlen - 1);
3784 new_rtp_len += addlen - 1;
3785 if (*insp != NUL)
3786 new_rtp[new_rtp_len++] = ','; // add comma after
3787
3788 if (afterlen > 0 && after_insp != NULL)
Bram Moolenaarf3654822016-03-04 22:12:23 +01003789 {
Bram Moolenaar99396d42018-09-08 18:21:16 +02003790 int keep_after = (int)(after_insp - p_rtp);
3791
3792 // Add to new_rtp: {keep},{fname}{keep_after},{afterdir}
3793 mch_memmove(new_rtp + new_rtp_len, p_rtp + keep,
3794 keep_after - keep);
3795 new_rtp_len += keep_after - keep;
3796 mch_memmove(new_rtp + new_rtp_len, afterdir, afterlen - 1);
3797 new_rtp_len += afterlen - 1;
3798 new_rtp[new_rtp_len++] = ',';
3799 keep = keep_after;
3800 }
3801
3802 if (p_rtp[keep] != NUL)
3803 // Append rest: {keep},{fname}{keep_after},{afterdir}{rest}
3804 mch_memmove(new_rtp + new_rtp_len, p_rtp + keep, oldlen - keep + 1);
3805 else
3806 new_rtp[new_rtp_len] = NUL;
3807
3808 if (afterlen > 0 && after_insp == NULL)
3809 {
3810 // Append afterdir when "after" was not found:
3811 // {keep},{fname}{rest},{afterdir}
Bram Moolenaar2374faa2018-02-04 17:47:42 +01003812 STRCAT(new_rtp, ",");
3813 STRCAT(new_rtp, afterdir);
Bram Moolenaarf3654822016-03-04 22:12:23 +01003814 }
Bram Moolenaar99396d42018-09-08 18:21:16 +02003815
Bram Moolenaar2374faa2018-02-04 17:47:42 +01003816 set_option_value((char_u *)"rtp", 0L, new_rtp, 0);
3817 vim_free(new_rtp);
3818 retval = OK;
Bram Moolenaarf3654822016-03-04 22:12:23 +01003819
3820theend:
Bram Moolenaar2f9e5752017-02-05 16:07:54 +01003821 vim_free(buf);
Bram Moolenaarf3654822016-03-04 22:12:23 +01003822 vim_free(ffname);
Bram Moolenaar2374faa2018-02-04 17:47:42 +01003823 vim_free(afterdir);
3824 return retval;
3825}
3826
3827/*
3828 * Load scripts in "plugin" and "ftdetect" directories of the package.
3829 */
3830 static int
3831load_pack_plugin(char_u *fname)
3832{
3833 static char *plugpat = "%s/plugin/**/*.vim";
3834 static char *ftpat = "%s/ftdetect/*.vim";
3835 int len;
3836 char_u *ffname = fix_fname(fname);
3837 char_u *pat = NULL;
3838 int retval = FAIL;
3839
3840 if (ffname == NULL)
3841 return FAIL;
3842 len = (int)STRLEN(ffname) + (int)STRLEN(ftpat);
3843 pat = alloc(len);
3844 if (pat == NULL)
3845 goto theend;
3846 vim_snprintf((char *)pat, len, plugpat, ffname);
3847 source_all_matches(pat);
3848
Bram Moolenaar2374faa2018-02-04 17:47:42 +01003849 {
3850 char_u *cmd = vim_strsave((char_u *)"g:did_load_filetypes");
3851
3852 /* If runtime/filetype.vim wasn't loaded yet, the scripts will be
3853 * found when it loads. */
3854 if (cmd != NULL && eval_to_number(cmd) > 0)
3855 {
3856 do_cmdline_cmd((char_u *)"augroup filetypedetect");
3857 vim_snprintf((char *)pat, len, ftpat, ffname);
3858 source_all_matches(pat);
3859 do_cmdline_cmd((char_u *)"augroup END");
3860 }
3861 vim_free(cmd);
3862 }
Bram Moolenaar2374faa2018-02-04 17:47:42 +01003863 vim_free(pat);
3864 retval = OK;
3865
3866theend:
3867 vim_free(ffname);
3868 return retval;
3869}
3870
3871/* used for "cookie" of add_pack_plugin() */
3872static int APP_ADD_DIR;
3873static int APP_LOAD;
3874static int APP_BOTH;
3875
3876 static void
3877add_pack_plugin(char_u *fname, void *cookie)
3878{
Bram Moolenaarf98a39c2018-04-18 22:18:23 +02003879 if (cookie != &APP_LOAD)
3880 {
3881 char_u *buf = alloc(MAXPATHL);
3882 char_u *p;
3883 int found = FALSE;
3884
3885 if (buf == NULL)
Bram Moolenaar2374faa2018-02-04 17:47:42 +01003886 return;
Bram Moolenaarf98a39c2018-04-18 22:18:23 +02003887 p = p_rtp;
3888 while (*p != NUL)
3889 {
3890 copy_option_part(&p, buf, MAXPATHL, ",");
3891 if (pathcmp((char *)buf, (char *)fname, -1) == 0)
3892 {
3893 found = TRUE;
3894 break;
3895 }
3896 }
3897 vim_free(buf);
3898 if (!found)
3899 /* directory is not yet in 'runtimepath', add it */
3900 if (add_pack_dir_to_rtp(fname) == FAIL)
3901 return;
3902 }
Bram Moolenaar2374faa2018-02-04 17:47:42 +01003903
3904 if (cookie != &APP_ADD_DIR)
3905 load_pack_plugin(fname);
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01003906}
3907
Bram Moolenaarce876aa2017-06-04 17:47:42 +02003908/*
3909 * Add all packages in the "start" directory to 'runtimepath'.
3910 */
3911 void
3912add_pack_start_dirs(void)
3913{
3914 do_in_path(p_pp, (char_u *)"pack/*/start/*", DIP_ALL + DIP_DIR,
3915 add_pack_plugin, &APP_ADD_DIR);
3916}
3917
3918/*
3919 * Load plugins from all packages in the "start" directory.
3920 */
3921 void
3922load_start_packages(void)
3923{
3924 did_source_packages = TRUE;
3925 do_in_path(p_pp, (char_u *)"pack/*/start/*", DIP_ALL + DIP_DIR,
3926 add_pack_plugin, &APP_LOAD);
3927}
Bram Moolenaar2d8f56a2016-03-12 20:34:27 +01003928
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01003929/*
Bram Moolenaar2d8f56a2016-03-12 20:34:27 +01003930 * ":packloadall"
Bram Moolenaarf3654822016-03-04 22:12:23 +01003931 * Find plugins in the package directories and source them.
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01003932 */
3933 void
Bram Moolenaar2d8f56a2016-03-12 20:34:27 +01003934ex_packloadall(exarg_T *eap)
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01003935{
Bram Moolenaarce876aa2017-06-04 17:47:42 +02003936 if (!did_source_packages || eap->forceit)
Bram Moolenaar2d8f56a2016-03-12 20:34:27 +01003937 {
Bram Moolenaar49b27322016-04-05 21:13:00 +02003938 /* First do a round to add all directories to 'runtimepath', then load
3939 * the plugins. This allows for plugins to use an autoload directory
3940 * of another plugin. */
Bram Moolenaarce876aa2017-06-04 17:47:42 +02003941 add_pack_start_dirs();
3942 load_start_packages();
Bram Moolenaar2d8f56a2016-03-12 20:34:27 +01003943 }
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01003944}
3945
3946/*
Bram Moolenaarf3654822016-03-04 22:12:23 +01003947 * ":packadd[!] {name}"
Bram Moolenaar91715872016-03-03 17:13:03 +01003948 */
3949 void
3950ex_packadd(exarg_T *eap)
3951{
Bram Moolenaar9e1d3992017-12-17 14:26:46 +01003952 static char *plugpat = "pack/*/%s/%s";
Bram Moolenaar91715872016-03-03 17:13:03 +01003953 int len;
3954 char *pat;
Bram Moolenaar9e1d3992017-12-17 14:26:46 +01003955 int round;
3956 int res = OK;
Bram Moolenaar91715872016-03-03 17:13:03 +01003957
Bram Moolenaar9e1d3992017-12-17 14:26:46 +01003958 /* Round 1: use "start", round 2: use "opt". */
3959 for (round = 1; round <= 2; ++round)
3960 {
3961 /* Only look under "start" when loading packages wasn't done yet. */
3962 if (round == 1 && did_source_packages)
3963 continue;
3964
3965 len = (int)STRLEN(plugpat) + (int)STRLEN(eap->arg) + 5;
3966 pat = (char *)alloc(len);
3967 if (pat == NULL)
3968 return;
3969 vim_snprintf(pat, len, plugpat, round == 1 ? "start" : "opt", eap->arg);
3970 /* The first round don't give a "not found" error, in the second round
3971 * only when nothing was found in the first round. */
3972 res = do_in_path(p_pp, (char_u *)pat,
3973 DIP_ALL + DIP_DIR + (round == 2 && res == FAIL ? DIP_ERR : 0),
3974 add_pack_plugin, eap->forceit ? &APP_ADD_DIR : &APP_BOTH);
3975 vim_free(pat);
3976 }
Bram Moolenaar91715872016-03-03 17:13:03 +01003977}
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01003978#endif
Bram Moolenaar91715872016-03-03 17:13:03 +01003979
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01003980#if defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003981/*
3982 * ":options"
3983 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003984 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01003985ex_options(
3986 exarg_T *eap UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003987{
Bram Moolenaarab6c8582017-08-11 17:15:09 +02003988 vim_setenv((char_u *)"OPTWIN_CMD", (char_u *)(cmdmod.tab ? "tab" : ""));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003989 cmd_source((char_u *)SYS_OPTWIN_FILE, NULL);
3990}
3991#endif
3992
Bram Moolenaarf42dd3c2017-01-28 16:06:38 +01003993#if defined(FEAT_PYTHON3) || defined(FEAT_PYTHON) || defined(PROTO)
3994
3995# if (defined(FEAT_PYTHON) && defined(FEAT_PYTHON3)) || defined(PROTO)
3996/*
3997 * Detect Python 3 or 2, and initialize 'pyxversion'.
3998 */
3999 void
4000init_pyxversion(void)
4001{
4002 if (p_pyx == 0)
4003 {
4004 if (python3_enabled(FALSE))
4005 p_pyx = 3;
4006 else if (python_enabled(FALSE))
4007 p_pyx = 2;
4008 }
4009}
4010# endif
4011
4012/*
4013 * Does a file contain one of the following strings at the beginning of any
4014 * line?
4015 * "#!(any string)python2" => returns 2
4016 * "#!(any string)python3" => returns 3
4017 * "# requires python 2.x" => returns 2
4018 * "# requires python 3.x" => returns 3
4019 * otherwise return 0.
4020 */
4021 static int
4022requires_py_version(char_u *filename)
4023{
4024 FILE *file;
4025 int requires_py_version = 0;
4026 int i, lines;
4027
4028 lines = (int)p_mls;
4029 if (lines < 0)
4030 lines = 5;
4031
4032 file = mch_fopen((char *)filename, "r");
4033 if (file != NULL)
4034 {
4035 for (i = 0; i < lines; i++)
4036 {
4037 if (vim_fgets(IObuff, IOSIZE, file))
4038 break;
4039 if (i == 0 && IObuff[0] == '#' && IObuff[1] == '!')
4040 {
4041 /* Check shebang. */
4042 if (strstr((char *)IObuff + 2, "python2") != NULL)
4043 {
4044 requires_py_version = 2;
4045 break;
4046 }
4047 if (strstr((char *)IObuff + 2, "python3") != NULL)
4048 {
4049 requires_py_version = 3;
4050 break;
4051 }
4052 }
4053 IObuff[21] = '\0';
4054 if (STRCMP("# requires python 2.x", IObuff) == 0)
4055 {
4056 requires_py_version = 2;
4057 break;
4058 }
4059 if (STRCMP("# requires python 3.x", IObuff) == 0)
4060 {
4061 requires_py_version = 3;
4062 break;
4063 }
4064 }
4065 fclose(file);
4066 }
4067 return requires_py_version;
4068}
4069
4070
4071/*
4072 * Source a python file using the requested python version.
4073 */
4074 static void
4075source_pyx_file(exarg_T *eap, char_u *fname)
4076{
4077 exarg_T ex;
4078 int v = requires_py_version(fname);
4079
4080# if defined(FEAT_PYTHON) && defined(FEAT_PYTHON3)
4081 init_pyxversion();
4082# endif
4083 if (v == 0)
4084 {
4085# if defined(FEAT_PYTHON) && defined(FEAT_PYTHON3)
4086 /* user didn't choose a preference, 'pyx' is used */
4087 v = p_pyx;
4088# elif defined(FEAT_PYTHON)
4089 v = 2;
4090# elif defined(FEAT_PYTHON3)
4091 v = 3;
4092# endif
4093 }
4094
4095 /*
4096 * now source, if required python version is not supported show
4097 * unobtrusive message.
4098 */
4099 if (eap == NULL)
4100 vim_memset(&ex, 0, sizeof(ex));
4101 else
4102 ex = *eap;
4103 ex.arg = fname;
4104 ex.cmd = (char_u *)(v == 2 ? "pyfile" : "pyfile3");
4105
4106 if (v == 2)
4107 {
4108# ifdef FEAT_PYTHON
4109 ex_pyfile(&ex);
4110# else
4111 vim_snprintf((char *)IObuff, IOSIZE,
4112 _("W20: Required python version 2.x not supported, ignoring file: %s"),
4113 fname);
Bram Moolenaar32526b32019-01-19 17:43:09 +01004114 msg((char *)IObuff);
Bram Moolenaarf42dd3c2017-01-28 16:06:38 +01004115# endif
4116 return;
4117 }
4118 else
4119 {
4120# ifdef FEAT_PYTHON3
4121 ex_py3file(&ex);
4122# else
4123 vim_snprintf((char *)IObuff, IOSIZE,
4124 _("W21: Required python version 3.x not supported, ignoring file: %s"),
4125 fname);
Bram Moolenaar32526b32019-01-19 17:43:09 +01004126 msg((char *)IObuff);
Bram Moolenaarf42dd3c2017-01-28 16:06:38 +01004127# endif
4128 return;
4129 }
4130}
4131
4132/*
4133 * ":pyxfile {fname}"
4134 */
4135 void
4136ex_pyxfile(exarg_T *eap)
4137{
4138 source_pyx_file(eap, eap->arg);
4139}
4140
4141/*
4142 * ":pyx"
4143 */
4144 void
4145ex_pyx(exarg_T *eap)
4146{
4147# if defined(FEAT_PYTHON) && defined(FEAT_PYTHON3)
4148 init_pyxversion();
4149 if (p_pyx == 2)
4150 ex_python(eap);
4151 else
4152 ex_py3(eap);
4153# elif defined(FEAT_PYTHON)
4154 ex_python(eap);
4155# elif defined(FEAT_PYTHON3)
4156 ex_py3(eap);
4157# endif
4158}
4159
4160/*
4161 * ":pyxdo"
4162 */
4163 void
4164ex_pyxdo(exarg_T *eap)
4165{
4166# if defined(FEAT_PYTHON) && defined(FEAT_PYTHON3)
4167 init_pyxversion();
4168 if (p_pyx == 2)
4169 ex_pydo(eap);
4170 else
4171 ex_py3do(eap);
4172# elif defined(FEAT_PYTHON)
4173 ex_pydo(eap);
4174# elif defined(FEAT_PYTHON3)
4175 ex_py3do(eap);
4176# endif
4177}
4178
4179#endif
4180
Bram Moolenaar071d4272004-06-13 20:20:40 +00004181/*
4182 * ":source {fname}"
4183 */
4184 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01004185ex_source(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004186{
4187#ifdef FEAT_BROWSE
4188 if (cmdmod.browse)
4189 {
4190 char_u *fname = NULL;
4191
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00004192 fname = do_browse(0, (char_u *)_("Source Vim script"), eap->arg,
Bram Moolenaarc36651b2018-04-29 12:22:56 +02004193 NULL, NULL,
4194 (char_u *)_(BROWSE_FILTER_MACROS), NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004195 if (fname != NULL)
4196 {
4197 cmd_source(fname, eap);
4198 vim_free(fname);
4199 }
4200 }
4201 else
4202#endif
4203 cmd_source(eap->arg, eap);
4204}
4205
4206 static void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01004207cmd_source(char_u *fname, exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004208{
4209 if (*fname == NUL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004210 emsg(_(e_argreq));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004211
Bram Moolenaar071d4272004-06-13 20:20:40 +00004212 else if (eap != NULL && eap->forceit)
Bram Moolenaar84a05ac2013-05-06 04:24:17 +02004213 /* ":source!": read Normal mode commands
Bram Moolenaar1f35bf92006-03-07 22:38:47 +00004214 * Need to execute the commands directly. This is required at least
4215 * for:
Bram Moolenaar071d4272004-06-13 20:20:40 +00004216 * - ":g" command busy
4217 * - after ":argdo", ":windo" or ":bufdo"
4218 * - another command follows
4219 * - inside a loop
4220 */
4221 openscript(fname, global_busy || listcmd_busy || eap->nextcmd != NULL
4222#ifdef FEAT_EVAL
4223 || eap->cstack->cs_idx >= 0
4224#endif
4225 );
4226
4227 /* ":source" read ex commands */
Bram Moolenaar910f66f2006-04-05 20:41:53 +00004228 else if (do_source(fname, FALSE, DOSO_NONE) == FAIL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004229 semsg(_(e_notopen), fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004230}
4231
4232/*
4233 * ":source" and associated commands.
4234 */
4235/*
4236 * Structure used to store info for each sourced file.
4237 * It is shared between do_source() and getsourceline().
4238 * This is required, because it needs to be handed to do_cmdline() and
4239 * sourcing can be done recursively.
4240 */
4241struct source_cookie
4242{
4243 FILE *fp; /* opened file for sourcing */
4244 char_u *nextline; /* if not NULL: line that was read ahead */
4245 int finished; /* ":finish" used */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004246#if defined(USE_CRNL) || defined(USE_CR)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004247 int fileformat; /* EOL_UNKNOWN, EOL_UNIX or EOL_DOS */
4248 int error; /* TRUE if LF found after CR-LF */
4249#endif
4250#ifdef FEAT_EVAL
4251 linenr_T breakpoint; /* next line with breakpoint or zero */
4252 char_u *fname; /* name of sourced file */
4253 int dbg_tick; /* debug_tick when breakpoint was set */
4254 int level; /* top nesting level of sourced file */
4255#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004256 vimconv_T conv; /* type of conversion */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004257};
4258
4259#ifdef FEAT_EVAL
4260/*
4261 * Return the address holding the next breakpoint line for a source cookie.
4262 */
4263 linenr_T *
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01004264source_breakpoint(void *cookie)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004265{
4266 return &((struct source_cookie *)cookie)->breakpoint;
4267}
4268
4269/*
4270 * Return the address holding the debug tick for a source cookie.
4271 */
4272 int *
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01004273source_dbg_tick(void *cookie)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004274{
4275 return &((struct source_cookie *)cookie)->dbg_tick;
4276}
4277
4278/*
4279 * Return the nesting level for a source cookie.
4280 */
4281 int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01004282source_level(void *cookie)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004283{
4284 return ((struct source_cookie *)cookie)->level;
4285}
4286#endif
4287
Bram Moolenaarf28dbce2016-01-29 22:03:47 +01004288static char_u *get_one_sourceline(struct source_cookie *sp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004289
Bram Moolenaar6b29b0e2010-01-19 16:22:03 +01004290#if (defined(WIN32) && defined(FEAT_CSCOPE)) || defined(HAVE_FD_CLOEXEC)
4291# define USE_FOPEN_NOINH
Bram Moolenaar071d4272004-06-13 20:20:40 +00004292/*
4293 * Special function to open a file without handle inheritance.
Bram Moolenaar6b29b0e2010-01-19 16:22:03 +01004294 * When possible the handle is closed on exec().
Bram Moolenaar071d4272004-06-13 20:20:40 +00004295 */
4296 static FILE *
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01004297fopen_noinh_readbin(char *filename)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004298{
Bram Moolenaar6b29b0e2010-01-19 16:22:03 +01004299# ifdef WIN32
Bram Moolenaar8d8ef0b2010-01-20 21:41:47 +01004300 int fd_tmp = mch_open(filename, O_RDONLY | O_BINARY | O_NOINHERIT, 0);
4301# else
4302 int fd_tmp = mch_open(filename, O_RDONLY, 0);
Bram Moolenaar6b29b0e2010-01-19 16:22:03 +01004303# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004304
4305 if (fd_tmp == -1)
4306 return NULL;
Bram Moolenaar6b29b0e2010-01-19 16:22:03 +01004307
4308# ifdef HAVE_FD_CLOEXEC
4309 {
4310 int fdflags = fcntl(fd_tmp, F_GETFD);
4311 if (fdflags >= 0 && (fdflags & FD_CLOEXEC) == 0)
Bram Moolenaarcde88542015-08-11 19:14:00 +02004312 (void)fcntl(fd_tmp, F_SETFD, fdflags | FD_CLOEXEC);
Bram Moolenaar6b29b0e2010-01-19 16:22:03 +01004313 }
4314# endif
4315
Bram Moolenaar071d4272004-06-13 20:20:40 +00004316 return fdopen(fd_tmp, READBIN);
4317}
4318#endif
4319
4320
4321/*
4322 * do_source: Read the file "fname" and execute its lines as EX commands.
4323 *
4324 * This function may be called recursively!
4325 *
4326 * return FAIL if file could not be opened, OK otherwise
4327 */
4328 int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01004329do_source(
4330 char_u *fname,
4331 int check_other, /* check for .vimrc and _vimrc */
4332 int is_vimrc) /* DOSO_ value */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004333{
4334 struct source_cookie cookie;
4335 char_u *save_sourcing_name;
4336 linenr_T save_sourcing_lnum;
4337 char_u *p;
4338 char_u *fname_exp;
Bram Moolenaar73881402009-02-04 16:50:47 +00004339 char_u *firstline = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004340 int retval = FAIL;
4341#ifdef FEAT_EVAL
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02004342 sctx_T save_current_sctx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004343 static scid_T last_current_SID = 0;
Bram Moolenaarded5f1b2018-11-10 17:33:29 +01004344 static int last_current_SID_seq = 0;
Bram Moolenaar27e80c82018-10-14 21:41:01 +02004345 funccal_entry_T funccalp_entry;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004346 int save_debug_break_level = debug_break_level;
Bram Moolenaar05159a02005-02-26 23:04:13 +00004347 scriptitem_T *si = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004348# ifdef UNIX
Bram Moolenaar8767f522016-07-01 17:17:39 +02004349 stat_T st;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004350 int stat_ok;
4351# endif
4352#endif
4353#ifdef STARTUPTIME
4354 struct timeval tv_rel;
4355 struct timeval tv_start;
4356#endif
Bram Moolenaar05159a02005-02-26 23:04:13 +00004357#ifdef FEAT_PROFILE
4358 proftime_T wait_start;
4359#endif
Bram Moolenaar2b618522019-01-12 13:26:03 +01004360 int trigger_source_post = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004361
Bram Moolenaar071d4272004-06-13 20:20:40 +00004362 p = expand_env_save(fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004363 if (p == NULL)
4364 return retval;
4365 fname_exp = fix_fname(p);
4366 vim_free(p);
4367 if (fname_exp == NULL)
4368 return retval;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004369 if (mch_isdir(fname_exp))
4370 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004371 smsg(_("Cannot source a directory: \"%s\""), fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004372 goto theend;
4373 }
4374
Bram Moolenaar8dd1aa52007-01-16 20:33:19 +00004375 /* Apply SourceCmd autocommands, they should get the file and source it. */
4376 if (has_autocmd(EVENT_SOURCECMD, fname_exp, NULL)
4377 && apply_autocmds(EVENT_SOURCECMD, fname_exp, fname_exp,
4378 FALSE, curbuf))
Bram Moolenaarf33943e2008-01-15 21:17:30 +00004379 {
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01004380#ifdef FEAT_EVAL
Bram Moolenaarf33943e2008-01-15 21:17:30 +00004381 retval = aborting() ? FAIL : OK;
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01004382#else
Bram Moolenaarf33943e2008-01-15 21:17:30 +00004383 retval = OK;
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01004384#endif
Bram Moolenaar2b618522019-01-12 13:26:03 +01004385 if (retval == OK)
4386 // Apply SourcePost autocommands.
4387 apply_autocmds(EVENT_SOURCEPOST, fname_exp, fname_exp,
4388 FALSE, curbuf);
Bram Moolenaarf33943e2008-01-15 21:17:30 +00004389 goto theend;
4390 }
Bram Moolenaar8dd1aa52007-01-16 20:33:19 +00004391
4392 /* Apply SourcePre autocommands, they may get the file. */
Bram Moolenaar1f35bf92006-03-07 22:38:47 +00004393 apply_autocmds(EVENT_SOURCEPRE, fname_exp, fname_exp, FALSE, curbuf);
Bram Moolenaar1f35bf92006-03-07 22:38:47 +00004394
Bram Moolenaar6b29b0e2010-01-19 16:22:03 +01004395#ifdef USE_FOPEN_NOINH
Bram Moolenaar071d4272004-06-13 20:20:40 +00004396 cookie.fp = fopen_noinh_readbin((char *)fname_exp);
4397#else
4398 cookie.fp = mch_fopen((char *)fname_exp, READBIN);
4399#endif
4400 if (cookie.fp == NULL && check_other)
4401 {
4402 /*
4403 * Try again, replacing file name ".vimrc" by "_vimrc" or vice versa,
4404 * and ".exrc" by "_exrc" or vice versa.
4405 */
4406 p = gettail(fname_exp);
4407 if ((*p == '.' || *p == '_')
4408 && (STRICMP(p + 1, "vimrc") == 0
4409 || STRICMP(p + 1, "gvimrc") == 0
4410 || STRICMP(p + 1, "exrc") == 0))
4411 {
4412 if (*p == '_')
4413 *p = '.';
4414 else
4415 *p = '_';
Bram Moolenaar6b29b0e2010-01-19 16:22:03 +01004416#ifdef USE_FOPEN_NOINH
Bram Moolenaar071d4272004-06-13 20:20:40 +00004417 cookie.fp = fopen_noinh_readbin((char *)fname_exp);
4418#else
4419 cookie.fp = mch_fopen((char *)fname_exp, READBIN);
4420#endif
4421 }
4422 }
4423
4424 if (cookie.fp == NULL)
4425 {
4426 if (p_verbose > 0)
4427 {
Bram Moolenaar54ee7752005-05-31 22:22:17 +00004428 verbose_enter();
Bram Moolenaar071d4272004-06-13 20:20:40 +00004429 if (sourcing_name == NULL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004430 smsg(_("could not source \"%s\""), fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004431 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004432 smsg(_("line %ld: could not source \"%s\""),
Bram Moolenaar555b2802005-05-19 21:08:39 +00004433 sourcing_lnum, fname);
Bram Moolenaar54ee7752005-05-31 22:22:17 +00004434 verbose_leave();
Bram Moolenaar071d4272004-06-13 20:20:40 +00004435 }
4436 goto theend;
4437 }
4438
4439 /*
4440 * The file exists.
4441 * - In verbose mode, give a message.
4442 * - For a vimrc file, may want to set 'compatible', call vimrc_found().
4443 */
4444 if (p_verbose > 1)
4445 {
Bram Moolenaar54ee7752005-05-31 22:22:17 +00004446 verbose_enter();
Bram Moolenaar071d4272004-06-13 20:20:40 +00004447 if (sourcing_name == NULL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004448 smsg(_("sourcing \"%s\""), fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004449 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004450 smsg(_("line %ld: sourcing \"%s\""),
Bram Moolenaar555b2802005-05-19 21:08:39 +00004451 sourcing_lnum, fname);
Bram Moolenaar54ee7752005-05-31 22:22:17 +00004452 verbose_leave();
Bram Moolenaar071d4272004-06-13 20:20:40 +00004453 }
Bram Moolenaar910f66f2006-04-05 20:41:53 +00004454 if (is_vimrc == DOSO_VIMRC)
4455 vimrc_found(fname_exp, (char_u *)"MYVIMRC");
4456 else if (is_vimrc == DOSO_GVIMRC)
4457 vimrc_found(fname_exp, (char_u *)"MYGVIMRC");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004458
4459#ifdef USE_CRNL
4460 /* If no automatic file format: Set default to CR-NL. */
4461 if (*p_ffs == NUL)
4462 cookie.fileformat = EOL_DOS;
4463 else
4464 cookie.fileformat = EOL_UNKNOWN;
4465 cookie.error = FALSE;
4466#endif
4467
4468#ifdef USE_CR
4469 /* If no automatic file format: Set default to CR. */
4470 if (*p_ffs == NUL)
4471 cookie.fileformat = EOL_MAC;
4472 else
4473 cookie.fileformat = EOL_UNKNOWN;
4474 cookie.error = FALSE;
4475#endif
4476
4477 cookie.nextline = NULL;
4478 cookie.finished = FALSE;
4479
4480#ifdef FEAT_EVAL
4481 /*
4482 * Check if this script has a breakpoint.
4483 */
4484 cookie.breakpoint = dbg_find_breakpoint(TRUE, fname_exp, (linenr_T)0);
4485 cookie.fname = fname_exp;
4486 cookie.dbg_tick = debug_tick;
4487
4488 cookie.level = ex_nesting_level;
4489#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004490
4491 /*
4492 * Keep the sourcing name/lnum, for recursive calls.
4493 */
4494 save_sourcing_name = sourcing_name;
4495 sourcing_name = fname_exp;
4496 save_sourcing_lnum = sourcing_lnum;
4497 sourcing_lnum = 0;
4498
4499#ifdef STARTUPTIME
Bram Moolenaarc4e41982010-01-19 16:31:47 +01004500 if (time_fd != NULL)
4501 time_push(&tv_rel, &tv_start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004502#endif
4503
4504#ifdef FEAT_EVAL
Bram Moolenaar05159a02005-02-26 23:04:13 +00004505# ifdef FEAT_PROFILE
Bram Moolenaar9b2200a2006-03-20 21:55:45 +00004506 if (do_profiling == PROF_YES)
Bram Moolenaar05159a02005-02-26 23:04:13 +00004507 prof_child_enter(&wait_start); /* entering a child now */
4508# endif
4509
4510 /* Don't use local function variables, if called from a function.
4511 * Also starts profiling timer for nested script. */
Bram Moolenaar27e80c82018-10-14 21:41:01 +02004512 save_funccal(&funccalp_entry);
Bram Moolenaar05159a02005-02-26 23:04:13 +00004513
Bram Moolenaarded5f1b2018-11-10 17:33:29 +01004514 // Check if this script was sourced before to finds its SID.
4515 // If it's new, generate a new SID.
4516 // Always use a new sequence number.
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02004517 save_current_sctx = current_sctx;
Bram Moolenaarded5f1b2018-11-10 17:33:29 +01004518 current_sctx.sc_seq = ++last_current_SID_seq;
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02004519 current_sctx.sc_lnum = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004520# ifdef UNIX
4521 stat_ok = (mch_stat((char *)fname_exp, &st) >= 0);
4522# endif
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02004523 for (current_sctx.sc_sid = script_items.ga_len; current_sctx.sc_sid > 0;
4524 --current_sctx.sc_sid)
Bram Moolenaar05159a02005-02-26 23:04:13 +00004525 {
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02004526 si = &SCRIPT_ITEM(current_sctx.sc_sid);
Bram Moolenaar05159a02005-02-26 23:04:13 +00004527 if (si->sn_name != NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00004528 && (
4529# ifdef UNIX
Bram Moolenaar7c626922005-02-07 22:01:03 +00004530 /* Compare dev/ino when possible, it catches symbolic
4531 * links. Also compare file names, the inode may change
4532 * when the file was edited. */
Bram Moolenaarbf0c4522009-05-16 19:16:33 +00004533 ((stat_ok && si->sn_dev_valid)
Bram Moolenaar05159a02005-02-26 23:04:13 +00004534 && (si->sn_dev == st.st_dev
4535 && si->sn_ino == st.st_ino)) ||
Bram Moolenaar071d4272004-06-13 20:20:40 +00004536# endif
Bram Moolenaar05159a02005-02-26 23:04:13 +00004537 fnamecmp(si->sn_name, fname_exp) == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004538 break;
Bram Moolenaar05159a02005-02-26 23:04:13 +00004539 }
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02004540 if (current_sctx.sc_sid == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004541 {
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02004542 current_sctx.sc_sid = ++last_current_SID;
4543 if (ga_grow(&script_items,
4544 (int)(current_sctx.sc_sid - script_items.ga_len)) == FAIL)
Bram Moolenaar05159a02005-02-26 23:04:13 +00004545 goto almosttheend;
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02004546 while (script_items.ga_len < current_sctx.sc_sid)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004547 {
Bram Moolenaar05159a02005-02-26 23:04:13 +00004548 ++script_items.ga_len;
4549 SCRIPT_ITEM(script_items.ga_len).sn_name = NULL;
4550# ifdef FEAT_PROFILE
4551 SCRIPT_ITEM(script_items.ga_len).sn_prof_on = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004552# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004553 }
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02004554 si = &SCRIPT_ITEM(current_sctx.sc_sid);
Bram Moolenaar05159a02005-02-26 23:04:13 +00004555 si->sn_name = fname_exp;
Bram Moolenaarea56e162019-01-12 15:15:38 +01004556 fname_exp = vim_strsave(si->sn_name); // used for autocmd
Bram Moolenaar05159a02005-02-26 23:04:13 +00004557# ifdef UNIX
4558 if (stat_ok)
4559 {
Bram Moolenaarbf0c4522009-05-16 19:16:33 +00004560 si->sn_dev_valid = TRUE;
Bram Moolenaar05159a02005-02-26 23:04:13 +00004561 si->sn_dev = st.st_dev;
4562 si->sn_ino = st.st_ino;
4563 }
4564 else
Bram Moolenaarbf0c4522009-05-16 19:16:33 +00004565 si->sn_dev_valid = FALSE;
Bram Moolenaar05159a02005-02-26 23:04:13 +00004566# endif
4567
Bram Moolenaar071d4272004-06-13 20:20:40 +00004568 /* Allocate the local script variables to use for this script. */
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02004569 new_script_vars(current_sctx.sc_sid);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004570 }
4571
Bram Moolenaar05159a02005-02-26 23:04:13 +00004572# ifdef FEAT_PROFILE
Bram Moolenaar9b2200a2006-03-20 21:55:45 +00004573 if (do_profiling == PROF_YES)
Bram Moolenaar05159a02005-02-26 23:04:13 +00004574 {
4575 int forceit;
4576
4577 /* Check if we do profiling for this script. */
4578 if (!si->sn_prof_on && has_profiling(TRUE, si->sn_name, &forceit))
4579 {
4580 script_do_profile(si);
4581 si->sn_pr_force = forceit;
4582 }
4583 if (si->sn_prof_on)
4584 {
4585 ++si->sn_pr_count;
4586 profile_start(&si->sn_pr_start);
4587 profile_zero(&si->sn_pr_children);
4588 }
4589 }
4590# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004591#endif
4592
Bram Moolenaar67435d92017-10-19 21:04:37 +02004593 cookie.conv.vc_type = CONV_NONE; /* no conversion */
4594
4595 /* Read the first line so we can check for a UTF-8 BOM. */
4596 firstline = getsourceline(0, (void *)&cookie, 0);
4597 if (firstline != NULL && STRLEN(firstline) >= 3 && firstline[0] == 0xef
4598 && firstline[1] == 0xbb && firstline[2] == 0xbf)
4599 {
4600 /* Found BOM; setup conversion, skip over BOM and recode the line. */
4601 convert_setup(&cookie.conv, (char_u *)"utf-8", p_enc);
4602 p = string_convert(&cookie.conv, firstline + 3, NULL);
4603 if (p == NULL)
4604 p = vim_strsave(firstline + 3);
4605 if (p != NULL)
4606 {
4607 vim_free(firstline);
4608 firstline = p;
4609 }
4610 }
Bram Moolenaar67435d92017-10-19 21:04:37 +02004611
Bram Moolenaar071d4272004-06-13 20:20:40 +00004612 /*
4613 * Call do_cmdline, which will call getsourceline() to get the lines.
4614 */
Bram Moolenaar73881402009-02-04 16:50:47 +00004615 do_cmdline(firstline, getsourceline, (void *)&cookie,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004616 DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_REPEAT);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004617 retval = OK;
Bram Moolenaar05159a02005-02-26 23:04:13 +00004618
4619#ifdef FEAT_PROFILE
Bram Moolenaar9b2200a2006-03-20 21:55:45 +00004620 if (do_profiling == PROF_YES)
Bram Moolenaar05159a02005-02-26 23:04:13 +00004621 {
4622 /* Get "si" again, "script_items" may have been reallocated. */
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02004623 si = &SCRIPT_ITEM(current_sctx.sc_sid);
Bram Moolenaar05159a02005-02-26 23:04:13 +00004624 if (si->sn_prof_on)
4625 {
4626 profile_end(&si->sn_pr_start);
4627 profile_sub_wait(&wait_start, &si->sn_pr_start);
4628 profile_add(&si->sn_pr_total, &si->sn_pr_start);
Bram Moolenaar1056d982006-03-09 22:37:52 +00004629 profile_self(&si->sn_pr_self, &si->sn_pr_start,
4630 &si->sn_pr_children);
Bram Moolenaar05159a02005-02-26 23:04:13 +00004631 }
4632 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004633#endif
4634
4635 if (got_int)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004636 emsg(_(e_interr));
Bram Moolenaar071d4272004-06-13 20:20:40 +00004637 sourcing_name = save_sourcing_name;
4638 sourcing_lnum = save_sourcing_lnum;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004639 if (p_verbose > 1)
4640 {
Bram Moolenaar54ee7752005-05-31 22:22:17 +00004641 verbose_enter();
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004642 smsg(_("finished sourcing %s"), fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004643 if (sourcing_name != NULL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004644 smsg(_("continuing in %s"), sourcing_name);
Bram Moolenaar54ee7752005-05-31 22:22:17 +00004645 verbose_leave();
Bram Moolenaar071d4272004-06-13 20:20:40 +00004646 }
4647#ifdef STARTUPTIME
Bram Moolenaarc4e41982010-01-19 16:31:47 +01004648 if (time_fd != NULL)
4649 {
4650 vim_snprintf((char *)IObuff, IOSIZE, "sourcing %s", fname);
4651 time_msg((char *)IObuff, &tv_start);
4652 time_pop(&tv_rel);
4653 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004654#endif
4655
Bram Moolenaar2b618522019-01-12 13:26:03 +01004656 if (!got_int)
4657 trigger_source_post = TRUE;
4658
Bram Moolenaar071d4272004-06-13 20:20:40 +00004659#ifdef FEAT_EVAL
4660 /*
4661 * After a "finish" in debug mode, need to break at first command of next
4662 * sourced file.
4663 */
4664 if (save_debug_break_level > ex_nesting_level
4665 && debug_break_level == ex_nesting_level)
4666 ++debug_break_level;
4667#endif
4668
Bram Moolenaar05159a02005-02-26 23:04:13 +00004669#ifdef FEAT_EVAL
4670almosttheend:
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02004671 current_sctx = save_current_sctx;
Bram Moolenaar27e80c82018-10-14 21:41:01 +02004672 restore_funccal();
Bram Moolenaar05159a02005-02-26 23:04:13 +00004673# ifdef FEAT_PROFILE
Bram Moolenaar9b2200a2006-03-20 21:55:45 +00004674 if (do_profiling == PROF_YES)
Bram Moolenaar05159a02005-02-26 23:04:13 +00004675 prof_child_exit(&wait_start); /* leaving a child now */
4676# endif
4677#endif
4678 fclose(cookie.fp);
4679 vim_free(cookie.nextline);
Bram Moolenaar73881402009-02-04 16:50:47 +00004680 vim_free(firstline);
Bram Moolenaar05159a02005-02-26 23:04:13 +00004681 convert_setup(&cookie.conv, NULL, NULL);
Bram Moolenaar05159a02005-02-26 23:04:13 +00004682
Bram Moolenaar2b618522019-01-12 13:26:03 +01004683 if (trigger_source_post)
Bram Moolenaarea56e162019-01-12 15:15:38 +01004684 apply_autocmds(EVENT_SOURCEPOST, fname_exp, fname_exp, FALSE, curbuf);
Bram Moolenaar2b618522019-01-12 13:26:03 +01004685
Bram Moolenaar071d4272004-06-13 20:20:40 +00004686theend:
4687 vim_free(fname_exp);
4688 return retval;
4689}
4690
4691#if defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00004692
Bram Moolenaar071d4272004-06-13 20:20:40 +00004693/*
4694 * ":scriptnames"
4695 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004696 void
Bram Moolenaar07dc18f2018-11-30 22:48:32 +01004697ex_scriptnames(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004698{
4699 int i;
4700
Bram Moolenaar07dc18f2018-11-30 22:48:32 +01004701 if (eap->addr_count > 0)
4702 {
4703 // :script {scriptId}: edit the script
4704 if (eap->line2 < 1 || eap->line2 > script_items.ga_len)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004705 emsg(_(e_invarg));
Bram Moolenaar07dc18f2018-11-30 22:48:32 +01004706 else
4707 {
4708 eap->arg = SCRIPT_ITEM(eap->line2).sn_name;
4709 do_exedit(eap, NULL);
4710 }
4711 return;
4712 }
4713
Bram Moolenaar05159a02005-02-26 23:04:13 +00004714 for (i = 1; i <= script_items.ga_len && !got_int; ++i)
4715 if (SCRIPT_ITEM(i).sn_name != NULL)
Bram Moolenaard58ea072011-06-26 04:25:30 +02004716 {
4717 home_replace(NULL, SCRIPT_ITEM(i).sn_name,
4718 NameBuff, MAXPATHL, TRUE);
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01004719 smsg("%3d: %s", i, NameBuff);
Bram Moolenaar970a1b82012-03-23 18:39:18 +01004720 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004721}
4722
4723# if defined(BACKSLASH_IN_FILENAME) || defined(PROTO)
4724/*
4725 * Fix slashes in the list of script names for 'shellslash'.
4726 */
4727 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01004728scriptnames_slash_adjust(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004729{
4730 int i;
4731
Bram Moolenaar05159a02005-02-26 23:04:13 +00004732 for (i = 1; i <= script_items.ga_len; ++i)
4733 if (SCRIPT_ITEM(i).sn_name != NULL)
4734 slash_adjust(SCRIPT_ITEM(i).sn_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004735}
4736# endif
4737
4738/*
4739 * Get a pointer to a script name. Used for ":verbose set".
4740 */
4741 char_u *
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01004742get_scriptname(scid_T id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004743{
4744 if (id == SID_MODELINE)
Bram Moolenaard1f56e62006-02-22 21:25:37 +00004745 return (char_u *)_("modeline");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004746 if (id == SID_CMDARG)
Bram Moolenaard1f56e62006-02-22 21:25:37 +00004747 return (char_u *)_("--cmd argument");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004748 if (id == SID_CARG)
Bram Moolenaard1f56e62006-02-22 21:25:37 +00004749 return (char_u *)_("-c argument");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004750 if (id == SID_ENV)
Bram Moolenaard1f56e62006-02-22 21:25:37 +00004751 return (char_u *)_("environment variable");
4752 if (id == SID_ERROR)
4753 return (char_u *)_("error handler");
Bram Moolenaar05159a02005-02-26 23:04:13 +00004754 return SCRIPT_ITEM(id).sn_name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004755}
Bram Moolenaar05159a02005-02-26 23:04:13 +00004756
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00004757# if defined(EXITFREE) || defined(PROTO)
4758 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01004759free_scriptnames(void)
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00004760{
4761 int i;
4762
4763 for (i = script_items.ga_len; i > 0; --i)
4764 vim_free(SCRIPT_ITEM(i).sn_name);
4765 ga_clear(&script_items);
4766}
4767# endif
4768
Bram Moolenaar071d4272004-06-13 20:20:40 +00004769#endif
4770
4771#if defined(USE_CR) || defined(PROTO)
4772
4773# if defined(__MSL__) && (__MSL__ >= 22)
4774/*
4775 * Newer version of the Metrowerks library handle DOS and UNIX files
4776 * without help.
4777 * Test with earlier versions, MSL 2.2 is the library supplied with
4778 * Codewarrior Pro 2.
4779 */
4780 char *
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01004781fgets_cr(char *s, int n, FILE *stream)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004782{
4783 return fgets(s, n, stream);
4784}
4785# else
4786/*
4787 * Version of fgets() which also works for lines ending in a <CR> only
4788 * (Macintosh format).
4789 * For older versions of the Metrowerks library.
4790 * At least CodeWarrior 9 needed this code.
4791 */
4792 char *
Bram Moolenaard14e00e2016-01-31 17:30:51 +01004793fgets_cr(char *s, int n, FILE *stream)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004794{
4795 int c = 0;
4796 int char_read = 0;
4797
4798 while (!feof(stream) && c != '\r' && c != '\n' && char_read < n - 1)
4799 {
4800 c = fgetc(stream);
4801 s[char_read++] = c;
4802 /* If the file is in DOS format, we need to skip a NL after a CR. I
4803 * thought it was the other way around, but this appears to work... */
4804 if (c == '\n')
4805 {
4806 c = fgetc(stream);
4807 if (c != '\r')
4808 ungetc(c, stream);
4809 }
4810 }
4811
4812 s[char_read] = 0;
4813 if (char_read == 0)
4814 return NULL;
4815
4816 if (feof(stream) && char_read == 1)
4817 return NULL;
4818
4819 return s;
4820}
4821# endif
4822#endif
4823
4824/*
4825 * Get one full line from a sourced file.
4826 * Called by do_cmdline() when it's called from do_source().
4827 *
4828 * Return a pointer to the line in allocated memory.
4829 * Return NULL for end-of-file or some error.
4830 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004831 char_u *
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01004832getsourceline(int c UNUSED, void *cookie, int indent UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004833{
4834 struct source_cookie *sp = (struct source_cookie *)cookie;
4835 char_u *line;
Bram Moolenaarc047b9a2012-02-11 20:40:55 +01004836 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004837
4838#ifdef FEAT_EVAL
4839 /* If breakpoints have been added/deleted need to check for it. */
4840 if (sp->dbg_tick < debug_tick)
4841 {
4842 sp->breakpoint = dbg_find_breakpoint(TRUE, sp->fname, sourcing_lnum);
4843 sp->dbg_tick = debug_tick;
4844 }
Bram Moolenaar05159a02005-02-26 23:04:13 +00004845# ifdef FEAT_PROFILE
Bram Moolenaar9b2200a2006-03-20 21:55:45 +00004846 if (do_profiling == PROF_YES)
Bram Moolenaar05159a02005-02-26 23:04:13 +00004847 script_line_end();
4848# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004849#endif
4850 /*
4851 * Get current line. If there is a read-ahead line, use it, otherwise get
4852 * one now.
4853 */
4854 if (sp->finished)
4855 line = NULL;
4856 else if (sp->nextline == NULL)
4857 line = get_one_sourceline(sp);
4858 else
4859 {
4860 line = sp->nextline;
4861 sp->nextline = NULL;
4862 ++sourcing_lnum;
4863 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00004864#ifdef FEAT_PROFILE
Bram Moolenaar9b2200a2006-03-20 21:55:45 +00004865 if (line != NULL && do_profiling == PROF_YES)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00004866 script_line_start();
4867#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004868
4869 /* Only concatenate lines starting with a \ when 'cpoptions' doesn't
4870 * contain the 'C' flag. */
4871 if (line != NULL && (vim_strchr(p_cpo, CPO_CONCAT) == NULL))
4872 {
4873 /* compensate for the one line read-ahead */
4874 --sourcing_lnum;
Bram Moolenaarb3a6bbc2012-02-05 23:10:30 +01004875
Bram Moolenaar67f8ab82018-09-11 22:37:29 +02004876 // Get the next line and concatenate it when it starts with a
4877 // backslash. We always need to read the next line, keep it in
4878 // sp->nextline.
4879 /* Also check for a comment in between continuation lines: "\ */
Bram Moolenaarb3a6bbc2012-02-05 23:10:30 +01004880 sp->nextline = get_one_sourceline(sp);
Bram Moolenaar67f8ab82018-09-11 22:37:29 +02004881 if (sp->nextline != NULL
4882 && (*(p = skipwhite(sp->nextline)) == '\\'
4883 || (p[0] == '"' && p[1] == '\\' && p[2] == ' ')))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004884 {
Bram Moolenaarb3a6bbc2012-02-05 23:10:30 +01004885 garray_T ga;
4886
Bram Moolenaarb549a732012-02-22 18:29:33 +01004887 ga_init2(&ga, (int)sizeof(char_u), 400);
Bram Moolenaarb3a6bbc2012-02-05 23:10:30 +01004888 ga_concat(&ga, line);
Bram Moolenaar67f8ab82018-09-11 22:37:29 +02004889 if (*p == '\\')
4890 ga_concat(&ga, p + 1);
Bram Moolenaarb3a6bbc2012-02-05 23:10:30 +01004891 for (;;)
4892 {
4893 vim_free(sp->nextline);
4894 sp->nextline = get_one_sourceline(sp);
4895 if (sp->nextline == NULL)
4896 break;
4897 p = skipwhite(sp->nextline);
Bram Moolenaar67f8ab82018-09-11 22:37:29 +02004898 if (*p == '\\')
Bram Moolenaarb549a732012-02-22 18:29:33 +01004899 {
Bram Moolenaar67f8ab82018-09-11 22:37:29 +02004900 // Adjust the growsize to the current length to speed up
4901 // concatenating many lines.
4902 if (ga.ga_len > 400)
4903 {
4904 if (ga.ga_len > 8000)
4905 ga.ga_growsize = 8000;
4906 else
4907 ga.ga_growsize = ga.ga_len;
4908 }
4909 ga_concat(&ga, p + 1);
Bram Moolenaarb549a732012-02-22 18:29:33 +01004910 }
Bram Moolenaar67f8ab82018-09-11 22:37:29 +02004911 else if (p[0] != '"' || p[1] != '\\' || p[2] != ' ')
4912 break;
Bram Moolenaarb3a6bbc2012-02-05 23:10:30 +01004913 }
4914 ga_append(&ga, NUL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004915 vim_free(line);
Bram Moolenaarb3a6bbc2012-02-05 23:10:30 +01004916 line = ga.ga_data;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004917 }
4918 }
4919
Bram Moolenaar071d4272004-06-13 20:20:40 +00004920 if (line != NULL && sp->conv.vc_type != CONV_NONE)
4921 {
Bram Moolenaarc047b9a2012-02-11 20:40:55 +01004922 char_u *s;
4923
Bram Moolenaar071d4272004-06-13 20:20:40 +00004924 /* Convert the encoding of the script line. */
4925 s = string_convert(&sp->conv, line, NULL);
4926 if (s != NULL)
4927 {
4928 vim_free(line);
4929 line = s;
4930 }
4931 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004932
4933#ifdef FEAT_EVAL
4934 /* Did we encounter a breakpoint? */
4935 if (sp->breakpoint != 0 && sp->breakpoint <= sourcing_lnum)
4936 {
4937 dbg_breakpoint(sp->fname, sourcing_lnum);
4938 /* Find next breakpoint. */
4939 sp->breakpoint = dbg_find_breakpoint(TRUE, sp->fname, sourcing_lnum);
4940 sp->dbg_tick = debug_tick;
4941 }
4942#endif
4943
4944 return line;
4945}
4946
4947 static char_u *
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01004948get_one_sourceline(struct source_cookie *sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004949{
4950 garray_T ga;
4951 int len;
4952 int c;
4953 char_u *buf;
4954#ifdef USE_CRNL
4955 int has_cr; /* CR-LF found */
4956#endif
4957#ifdef USE_CR
4958 char_u *scan;
4959#endif
4960 int have_read = FALSE;
4961
4962 /* use a growarray to store the sourced line */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004963 ga_init2(&ga, 1, 250);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004964
4965 /*
4966 * Loop until there is a finished line (or end-of-file).
4967 */
4968 sourcing_lnum++;
4969 for (;;)
4970 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004971 /* make room to read at least 120 (more) characters */
4972 if (ga_grow(&ga, 120) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004973 break;
4974 buf = (char_u *)ga.ga_data;
4975
4976#ifdef USE_CR
4977 if (sp->fileformat == EOL_MAC)
4978 {
Bram Moolenaar86b68352004-12-27 21:59:20 +00004979 if (fgets_cr((char *)buf + ga.ga_len, ga.ga_maxlen - ga.ga_len,
4980 sp->fp) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004981 break;
4982 }
4983 else
4984#endif
Bram Moolenaar86b68352004-12-27 21:59:20 +00004985 if (fgets((char *)buf + ga.ga_len, ga.ga_maxlen - ga.ga_len,
4986 sp->fp) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004987 break;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004988 len = ga.ga_len + (int)STRLEN(buf + ga.ga_len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004989#ifdef USE_CRNL
4990 /* Ignore a trailing CTRL-Z, when in Dos mode. Only recognize the
4991 * CTRL-Z by its own, or after a NL. */
4992 if ( (len == 1 || (len >= 2 && buf[len - 2] == '\n'))
4993 && sp->fileformat == EOL_DOS
4994 && buf[len - 1] == Ctrl_Z)
4995 {
4996 buf[len - 1] = NUL;
4997 break;
4998 }
4999#endif
5000
5001#ifdef USE_CR
5002 /* If the read doesn't stop on a new line, and there's
5003 * some CR then we assume a Mac format */
5004 if (sp->fileformat == EOL_UNKNOWN)
5005 {
5006 if (buf[len - 1] != '\n' && vim_strchr(buf, '\r') != NULL)
5007 sp->fileformat = EOL_MAC;
5008 else
5009 sp->fileformat = EOL_UNIX;
5010 }
5011
5012 if (sp->fileformat == EOL_MAC)
5013 {
5014 scan = vim_strchr(buf, '\r');
5015
5016 if (scan != NULL)
5017 {
5018 *scan = '\n';
5019 if (*(scan + 1) != 0)
5020 {
5021 *(scan + 1) = 0;
5022 fseek(sp->fp, (long)(scan - buf - len + 1), SEEK_CUR);
5023 }
5024 }
5025 len = STRLEN(buf);
5026 }
5027#endif
5028
5029 have_read = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005030 ga.ga_len = len;
5031
5032 /* If the line was longer than the buffer, read more. */
Bram Moolenaar86b68352004-12-27 21:59:20 +00005033 if (ga.ga_maxlen - ga.ga_len == 1 && buf[len - 1] != '\n')
Bram Moolenaar071d4272004-06-13 20:20:40 +00005034 continue;
5035
5036 if (len >= 1 && buf[len - 1] == '\n') /* remove trailing NL */
5037 {
5038#ifdef USE_CRNL
5039 has_cr = (len >= 2 && buf[len - 2] == '\r');
5040 if (sp->fileformat == EOL_UNKNOWN)
5041 {
5042 if (has_cr)
5043 sp->fileformat = EOL_DOS;
5044 else
5045 sp->fileformat = EOL_UNIX;
5046 }
5047
5048 if (sp->fileformat == EOL_DOS)
5049 {
5050 if (has_cr) /* replace trailing CR */
5051 {
5052 buf[len - 2] = '\n';
5053 --len;
5054 --ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005055 }
5056 else /* lines like ":map xx yy^M" will have failed */
5057 {
5058 if (!sp->error)
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00005059 {
Bram Moolenaar8820b482017-03-16 17:23:31 +01005060 msg_source(HL_ATTR(HLF_W));
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005061 emsg(_("W15: Warning: Wrong line separator, ^M may be missing"));
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00005062 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005063 sp->error = TRUE;
5064 sp->fileformat = EOL_UNIX;
5065 }
5066 }
5067#endif
5068 /* The '\n' is escaped if there is an odd number of ^V's just
5069 * before it, first set "c" just before the 'V's and then check
5070 * len&c parities (is faster than ((len-c)%2 == 0)) -- Acevedo */
5071 for (c = len - 2; c >= 0 && buf[c] == Ctrl_V; c--)
5072 ;
5073 if ((len & 1) != (c & 1)) /* escaped NL, read more */
5074 {
5075 sourcing_lnum++;
5076 continue;
5077 }
5078
5079 buf[len - 1] = NUL; /* remove the NL */
5080 }
5081
5082 /*
5083 * Check for ^C here now and then, so recursive :so can be broken.
5084 */
5085 line_breakcheck();
5086 break;
5087 }
5088
5089 if (have_read)
5090 return (char_u *)ga.ga_data;
5091
5092 vim_free(ga.ga_data);
5093 return NULL;
5094}
5095
Bram Moolenaar05159a02005-02-26 23:04:13 +00005096#if defined(FEAT_PROFILE) || defined(PROTO)
5097/*
5098 * Called when starting to read a script line.
5099 * "sourcing_lnum" must be correct!
5100 * When skipping lines it may not actually be executed, but we won't find out
5101 * until later and we need to store the time now.
5102 */
5103 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01005104script_line_start(void)
Bram Moolenaar05159a02005-02-26 23:04:13 +00005105{
5106 scriptitem_T *si;
5107 sn_prl_T *pp;
5108
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02005109 if (current_sctx.sc_sid <= 0 || current_sctx.sc_sid > script_items.ga_len)
Bram Moolenaar05159a02005-02-26 23:04:13 +00005110 return;
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02005111 si = &SCRIPT_ITEM(current_sctx.sc_sid);
Bram Moolenaar05159a02005-02-26 23:04:13 +00005112 if (si->sn_prof_on && sourcing_lnum >= 1)
5113 {
Bram Moolenaar8c8de832008-06-24 22:58:06 +00005114 /* Grow the array before starting the timer, so that the time spent
Bram Moolenaar05159a02005-02-26 23:04:13 +00005115 * here isn't counted. */
Bram Moolenaar67435d92017-10-19 21:04:37 +02005116 (void)ga_grow(&si->sn_prl_ga,
5117 (int)(sourcing_lnum - si->sn_prl_ga.ga_len));
Bram Moolenaar05159a02005-02-26 23:04:13 +00005118 si->sn_prl_idx = sourcing_lnum - 1;
5119 while (si->sn_prl_ga.ga_len <= si->sn_prl_idx
5120 && si->sn_prl_ga.ga_len < si->sn_prl_ga.ga_maxlen)
5121 {
5122 /* Zero counters for a line that was not used before. */
5123 pp = &PRL_ITEM(si, si->sn_prl_ga.ga_len);
5124 pp->snp_count = 0;
5125 profile_zero(&pp->sn_prl_total);
5126 profile_zero(&pp->sn_prl_self);
5127 ++si->sn_prl_ga.ga_len;
5128 }
5129 si->sn_prl_execed = FALSE;
5130 profile_start(&si->sn_prl_start);
5131 profile_zero(&si->sn_prl_children);
5132 profile_get_wait(&si->sn_prl_wait);
5133 }
5134}
5135
5136/*
5137 * Called when actually executing a function line.
5138 */
5139 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01005140script_line_exec(void)
Bram Moolenaar05159a02005-02-26 23:04:13 +00005141{
5142 scriptitem_T *si;
5143
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02005144 if (current_sctx.sc_sid <= 0 || current_sctx.sc_sid > script_items.ga_len)
Bram Moolenaar05159a02005-02-26 23:04:13 +00005145 return;
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02005146 si = &SCRIPT_ITEM(current_sctx.sc_sid);
Bram Moolenaar05159a02005-02-26 23:04:13 +00005147 if (si->sn_prof_on && si->sn_prl_idx >= 0)
5148 si->sn_prl_execed = TRUE;
5149}
5150
5151/*
Bram Moolenaar67435d92017-10-19 21:04:37 +02005152 * Called when done with a script line.
Bram Moolenaar05159a02005-02-26 23:04:13 +00005153 */
5154 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01005155script_line_end(void)
Bram Moolenaar05159a02005-02-26 23:04:13 +00005156{
5157 scriptitem_T *si;
5158 sn_prl_T *pp;
5159
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02005160 if (current_sctx.sc_sid <= 0 || current_sctx.sc_sid > script_items.ga_len)
Bram Moolenaar05159a02005-02-26 23:04:13 +00005161 return;
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02005162 si = &SCRIPT_ITEM(current_sctx.sc_sid);
Bram Moolenaar05159a02005-02-26 23:04:13 +00005163 if (si->sn_prof_on && si->sn_prl_idx >= 0
5164 && si->sn_prl_idx < si->sn_prl_ga.ga_len)
5165 {
5166 if (si->sn_prl_execed)
5167 {
5168 pp = &PRL_ITEM(si, si->sn_prl_idx);
5169 ++pp->snp_count;
5170 profile_end(&si->sn_prl_start);
5171 profile_sub_wait(&si->sn_prl_wait, &si->sn_prl_start);
Bram Moolenaar05159a02005-02-26 23:04:13 +00005172 profile_add(&pp->sn_prl_total, &si->sn_prl_start);
Bram Moolenaar1056d982006-03-09 22:37:52 +00005173 profile_self(&pp->sn_prl_self, &si->sn_prl_start,
5174 &si->sn_prl_children);
Bram Moolenaar05159a02005-02-26 23:04:13 +00005175 }
5176 si->sn_prl_idx = -1;
5177 }
5178}
5179#endif
5180
Bram Moolenaar071d4272004-06-13 20:20:40 +00005181/*
5182 * ":scriptencoding": Set encoding conversion for a sourced script.
5183 * Without the multi-byte feature it's simply ignored.
5184 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005185 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01005186ex_scriptencoding(exarg_T *eap UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005187{
Bram Moolenaar071d4272004-06-13 20:20:40 +00005188 struct source_cookie *sp;
5189 char_u *name;
5190
5191 if (!getline_equal(eap->getline, eap->cookie, getsourceline))
5192 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005193 emsg(_("E167: :scriptencoding used outside of a sourced file"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00005194 return;
5195 }
5196
5197 if (*eap->arg != NUL)
5198 {
5199 name = enc_canonize(eap->arg);
5200 if (name == NULL) /* out of memory */
5201 return;
5202 }
5203 else
5204 name = eap->arg;
5205
5206 /* Setup for conversion from the specified encoding to 'encoding'. */
5207 sp = (struct source_cookie *)getline_cookie(eap->getline, eap->cookie);
5208 convert_setup(&sp->conv, name, p_enc);
5209
5210 if (name != eap->arg)
5211 vim_free(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005212}
5213
5214#if defined(FEAT_EVAL) || defined(PROTO)
5215/*
5216 * ":finish": Mark a sourced file as finished.
5217 */
5218 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01005219ex_finish(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005220{
5221 if (getline_equal(eap->getline, eap->cookie, getsourceline))
5222 do_finish(eap, FALSE);
5223 else
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005224 emsg(_("E168: :finish used outside of a sourced file"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00005225}
5226
5227/*
5228 * Mark a sourced file as finished. Possibly makes the ":finish" pending.
5229 * Also called for a pending finish at the ":endtry" or after returning from
5230 * an extra do_cmdline(). "reanimate" is used in the latter case.
5231 */
5232 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01005233do_finish(exarg_T *eap, int reanimate)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005234{
5235 int idx;
5236
5237 if (reanimate)
5238 ((struct source_cookie *)getline_cookie(eap->getline,
5239 eap->cookie))->finished = FALSE;
5240
5241 /*
5242 * Cleanup (and inactivate) conditionals, but stop when a try conditional
5243 * not in its finally clause (which then is to be executed next) is found.
5244 * In this case, make the ":finish" pending for execution at the ":endtry".
5245 * Otherwise, finish normally.
5246 */
5247 idx = cleanup_conditionals(eap->cstack, 0, TRUE);
5248 if (idx >= 0)
5249 {
5250 eap->cstack->cs_pending[idx] = CSTP_FINISH;
5251 report_make_pending(CSTP_FINISH, NULL);
5252 }
5253 else
5254 ((struct source_cookie *)getline_cookie(eap->getline,
5255 eap->cookie))->finished = TRUE;
5256}
5257
5258
5259/*
5260 * Return TRUE when a sourced file had the ":finish" command: Don't give error
5261 * message for missing ":endif".
5262 * Return FALSE when not sourcing a file.
5263 */
5264 int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01005265source_finished(
5266 char_u *(*fgetline)(int, void *, int),
5267 void *cookie)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005268{
Bram Moolenaar89d40322006-08-29 15:30:07 +00005269 return (getline_equal(fgetline, cookie, getsourceline)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005270 && ((struct source_cookie *)getline_cookie(
Bram Moolenaar89d40322006-08-29 15:30:07 +00005271 fgetline, cookie))->finished);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005272}
5273#endif
5274
Bram Moolenaar071d4272004-06-13 20:20:40 +00005275/*
5276 * ":checktime [buffer]"
5277 */
5278 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01005279ex_checktime(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005280{
5281 buf_T *buf;
5282 int save_no_check_timestamps = no_check_timestamps;
5283
5284 no_check_timestamps = 0;
5285 if (eap->addr_count == 0) /* default is all buffers */
5286 check_timestamps(FALSE);
5287 else
5288 {
5289 buf = buflist_findnr((int)eap->line2);
5290 if (buf != NULL) /* cannot happen? */
5291 (void)buf_check_timestamp(buf, FALSE);
5292 }
5293 no_check_timestamps = save_no_check_timestamps;
5294}
Bram Moolenaar071d4272004-06-13 20:20:40 +00005295
Bram Moolenaar071d4272004-06-13 20:20:40 +00005296#if (defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \
5297 && (defined(FEAT_EVAL) || defined(FEAT_MULTI_LANG))
Bram Moolenaar8765a4a2010-07-27 22:41:43 +02005298# define HAVE_GET_LOCALE_VAL
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01005299 static char_u *
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01005300get_locale_val(int what)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005301{
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01005302 char_u *loc;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005303
Bram Moolenaar48e330a2016-02-23 14:53:34 +01005304 /* Obtain the locale value from the libraries. */
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01005305 loc = (char_u *)setlocale(what, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005306
Bram Moolenaar61660ea2006-04-07 21:40:07 +00005307# ifdef WIN32
Bram Moolenaar071d4272004-06-13 20:20:40 +00005308 if (loc != NULL)
5309 {
5310 char_u *p;
5311
Bram Moolenaar61660ea2006-04-07 21:40:07 +00005312 /* setocale() returns something like "LC_COLLATE=<name>;LC_..." when
5313 * one of the values (e.g., LC_CTYPE) differs. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005314 p = vim_strchr(loc, '=');
5315 if (p != NULL)
5316 {
5317 loc = ++p;
5318 while (*p != NUL) /* remove trailing newline */
5319 {
Bram Moolenaar61660ea2006-04-07 21:40:07 +00005320 if (*p < ' ' || *p == ';')
Bram Moolenaar071d4272004-06-13 20:20:40 +00005321 {
5322 *p = NUL;
5323 break;
5324 }
5325 ++p;
5326 }
5327 }
5328 }
5329# endif
5330
5331 return loc;
5332}
5333#endif
5334
5335
5336#ifdef WIN32
5337/*
5338 * On MS-Windows locale names are strings like "German_Germany.1252", but
5339 * gettext expects "de". Try to translate one into another here for a few
5340 * supported languages.
5341 */
5342 static char_u *
5343gettext_lang(char_u *name)
5344{
5345 int i;
5346 static char *(mtable[]) = {
5347 "afrikaans", "af",
5348 "czech", "cs",
5349 "dutch", "nl",
5350 "german", "de",
5351 "english_united kingdom", "en_GB",
5352 "spanish", "es",
5353 "french", "fr",
5354 "italian", "it",
5355 "japanese", "ja",
5356 "korean", "ko",
5357 "norwegian", "no",
5358 "polish", "pl",
5359 "russian", "ru",
5360 "slovak", "sk",
5361 "swedish", "sv",
5362 "ukrainian", "uk",
5363 "chinese_china", "zh_CN",
5364 "chinese_taiwan", "zh_TW",
5365 NULL};
5366
5367 for (i = 0; mtable[i] != NULL; i += 2)
5368 if (STRNICMP(mtable[i], name, STRLEN(mtable[i])) == 0)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01005369 return (char_u *)mtable[i + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00005370 return name;
5371}
5372#endif
5373
5374#if defined(FEAT_MULTI_LANG) || defined(PROTO)
5375/*
Bram Moolenaar389ab712018-11-05 20:25:52 +01005376 * Return TRUE when "lang" starts with a valid language name.
5377 * Rejects NULL, empty string, "C", "C.UTF-8" and others.
5378 */
5379 static int
5380is_valid_mess_lang(char_u *lang)
5381{
5382 return lang != NULL && ASCII_ISALPHA(lang[0]) && ASCII_ISALPHA(lang[1]);
5383}
5384
5385/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00005386 * Obtain the current messages language. Used to set the default for
5387 * 'helplang'. May return NULL or an empty string.
5388 */
5389 char_u *
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01005390get_mess_lang(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005391{
5392 char_u *p;
5393
Bram Moolenaar8765a4a2010-07-27 22:41:43 +02005394# ifdef HAVE_GET_LOCALE_VAL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005395# if defined(LC_MESSAGES)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01005396 p = get_locale_val(LC_MESSAGES);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005397# else
5398 /* This is necessary for Win32, where LC_MESSAGES is not defined and $LANG
Bram Moolenaar61660ea2006-04-07 21:40:07 +00005399 * may be set to the LCID number. LC_COLLATE is the best guess, LC_TIME
5400 * and LC_MONETARY may be set differently for a Japanese working in the
5401 * US. */
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01005402 p = get_locale_val(LC_COLLATE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005403# endif
5404# else
5405 p = mch_getenv((char_u *)"LC_ALL");
Bram Moolenaar389ab712018-11-05 20:25:52 +01005406 if (!is_valid_mess_lang(p))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005407 {
5408 p = mch_getenv((char_u *)"LC_MESSAGES");
Bram Moolenaar389ab712018-11-05 20:25:52 +01005409 if (!is_valid_mess_lang(p))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005410 p = mch_getenv((char_u *)"LANG");
5411 }
5412# endif
5413# ifdef WIN32
5414 p = gettext_lang(p);
5415# endif
Bram Moolenaar389ab712018-11-05 20:25:52 +01005416 return is_valid_mess_lang(p) ? p : NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005417}
5418#endif
5419
Bram Moolenaardef9e822004-12-31 20:58:58 +00005420/* Complicated #if; matches with where get_mess_env() is used below. */
5421#if (defined(FEAT_EVAL) && !((defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \
5422 && defined(LC_MESSAGES))) \
5423 || ((defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \
Bram Moolenaardef9e822004-12-31 20:58:58 +00005424 && !defined(LC_MESSAGES))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005425/*
5426 * Get the language used for messages from the environment.
5427 */
5428 static char_u *
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01005429get_mess_env(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005430{
5431 char_u *p;
5432
5433 p = mch_getenv((char_u *)"LC_ALL");
5434 if (p == NULL || *p == NUL)
5435 {
5436 p = mch_getenv((char_u *)"LC_MESSAGES");
5437 if (p == NULL || *p == NUL)
5438 {
5439 p = mch_getenv((char_u *)"LANG");
5440 if (p != NULL && VIM_ISDIGIT(*p))
5441 p = NULL; /* ignore something like "1043" */
Bram Moolenaar8765a4a2010-07-27 22:41:43 +02005442# ifdef HAVE_GET_LOCALE_VAL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005443 if (p == NULL || *p == NUL)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01005444 p = get_locale_val(LC_CTYPE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005445# endif
5446 }
5447 }
5448 return p;
5449}
5450#endif
5451
5452#if defined(FEAT_EVAL) || defined(PROTO)
5453
5454/*
5455 * Set the "v:lang" variable according to the current locale setting.
5456 * Also do "v:lc_time"and "v:ctype".
5457 */
5458 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01005459set_lang_var(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005460{
5461 char_u *loc;
5462
Bram Moolenaar8765a4a2010-07-27 22:41:43 +02005463# ifdef HAVE_GET_LOCALE_VAL
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01005464 loc = get_locale_val(LC_CTYPE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005465# else
5466 /* setlocale() not supported: use the default value */
5467 loc = (char_u *)"C";
5468# endif
5469 set_vim_var_string(VV_CTYPE, loc, -1);
5470
5471 /* When LC_MESSAGES isn't defined use the value from $LC_MESSAGES, fall
5472 * back to LC_CTYPE if it's empty. */
Bram Moolenaar8765a4a2010-07-27 22:41:43 +02005473# if defined(HAVE_GET_LOCALE_VAL) && defined(LC_MESSAGES)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01005474 loc = get_locale_val(LC_MESSAGES);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005475# else
5476 loc = get_mess_env();
5477# endif
5478 set_vim_var_string(VV_LANG, loc, -1);
5479
Bram Moolenaar8765a4a2010-07-27 22:41:43 +02005480# ifdef HAVE_GET_LOCALE_VAL
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01005481 loc = get_locale_val(LC_TIME);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005482# endif
5483 set_vim_var_string(VV_LC_TIME, loc, -1);
5484}
5485#endif
5486
Bram Moolenaarfc3abf42019-01-24 15:54:21 +01005487#if defined(HAVE_LOCALE_H) || defined(X_LOCALE) \
Bram Moolenaar071d4272004-06-13 20:20:40 +00005488/*
5489 * ":language": Set the language (locale).
5490 */
5491 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01005492ex_language(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005493{
5494 char *loc;
5495 char_u *p;
5496 char_u *name;
5497 int what = LC_ALL;
5498 char *whatstr = "";
5499#ifdef LC_MESSAGES
5500# define VIM_LC_MESSAGES LC_MESSAGES
5501#else
5502# define VIM_LC_MESSAGES 6789
5503#endif
5504
5505 name = eap->arg;
5506
5507 /* Check for "messages {name}", "ctype {name}" or "time {name}" argument.
5508 * Allow abbreviation, but require at least 3 characters to avoid
5509 * confusion with a two letter language name "me" or "ct". */
5510 p = skiptowhite(eap->arg);
Bram Moolenaar1c465442017-03-12 20:10:05 +01005511 if ((*p == NUL || VIM_ISWHITE(*p)) && p - eap->arg >= 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005512 {
5513 if (STRNICMP(eap->arg, "messages", p - eap->arg) == 0)
5514 {
5515 what = VIM_LC_MESSAGES;
5516 name = skipwhite(p);
5517 whatstr = "messages ";
5518 }
5519 else if (STRNICMP(eap->arg, "ctype", p - eap->arg) == 0)
5520 {
5521 what = LC_CTYPE;
5522 name = skipwhite(p);
5523 whatstr = "ctype ";
5524 }
5525 else if (STRNICMP(eap->arg, "time", p - eap->arg) == 0)
5526 {
5527 what = LC_TIME;
5528 name = skipwhite(p);
5529 whatstr = "time ";
5530 }
5531 }
5532
5533 if (*name == NUL)
5534 {
5535#ifndef LC_MESSAGES
5536 if (what == VIM_LC_MESSAGES)
5537 p = get_mess_env();
5538 else
5539#endif
5540 p = (char_u *)setlocale(what, NULL);
5541 if (p == NULL || *p == NUL)
5542 p = (char_u *)"Unknown";
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005543 smsg(_("Current %slanguage: \"%s\""), whatstr, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005544 }
5545 else
5546 {
5547#ifndef LC_MESSAGES
5548 if (what == VIM_LC_MESSAGES)
5549 loc = "";
5550 else
5551#endif
Bram Moolenaar8c8de832008-06-24 22:58:06 +00005552 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00005553 loc = setlocale(what, (char *)name);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00005554#if defined(FEAT_FLOAT) && defined(LC_NUMERIC)
5555 /* Make sure strtod() uses a decimal point, not a comma. */
5556 setlocale(LC_NUMERIC, "C");
5557#endif
5558 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005559 if (loc == NULL)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005560 semsg(_("E197: Cannot set language to \"%s\""), name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005561 else
5562 {
5563#ifdef HAVE_NL_MSG_CAT_CNTR
5564 /* Need to do this for GNU gettext, otherwise cached translations
5565 * will be used again. */
5566 extern int _nl_msg_cat_cntr;
5567
5568 ++_nl_msg_cat_cntr;
5569#endif
Bram Moolenaarb8017e72007-05-10 18:59:07 +00005570 /* Reset $LC_ALL, otherwise it would overrule everything. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005571 vim_setenv((char_u *)"LC_ALL", (char_u *)"");
5572
5573 if (what != LC_TIME)
5574 {
5575 /* Tell gettext() what to translate to. It apparently doesn't
5576 * use the currently effective locale. Also do this when
5577 * FEAT_GETTEXT isn't defined, so that shell commands use this
5578 * value. */
5579 if (what == LC_ALL)
Bram Moolenaar482aaeb2005-09-29 18:26:07 +00005580 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00005581 vim_setenv((char_u *)"LANG", name);
Bram Moolenaare3a0b532013-06-28 20:36:30 +02005582
5583 /* Clear $LANGUAGE because GNU gettext uses it. */
5584 vim_setenv((char_u *)"LANGUAGE", (char_u *)"");
Bram Moolenaar482aaeb2005-09-29 18:26:07 +00005585# ifdef WIN32
5586 /* Apparently MS-Windows printf() may cause a crash when
5587 * we give it 8-bit text while it's expecting text in the
5588 * current locale. This call avoids that. */
5589 setlocale(LC_CTYPE, "C");
5590# endif
5591 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005592 if (what != LC_CTYPE)
5593 {
5594 char_u *mname;
5595#ifdef WIN32
5596 mname = gettext_lang(name);
5597#else
5598 mname = name;
5599#endif
5600 vim_setenv((char_u *)"LC_MESSAGES", mname);
5601#ifdef FEAT_MULTI_LANG
5602 set_helplang_default(mname);
5603#endif
5604 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005605 }
5606
5607# ifdef FEAT_EVAL
5608 /* Set v:lang, v:lc_time and v:ctype to the final result. */
5609 set_lang_var();
5610# endif
Bram Moolenaar6cc00c72011-10-20 21:41:09 +02005611# ifdef FEAT_TITLE
5612 maketitle();
5613# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005614 }
5615 }
5616}
5617
5618# if defined(FEAT_CMDL_COMPL) || defined(PROTO)
Bram Moolenaar9b486ca2011-05-19 18:26:40 +02005619
5620static char_u **locales = NULL; /* Array of all available locales */
Bram Moolenaarb8f7bd62017-01-12 20:28:25 +01005621
5622# ifndef WIN32
Bram Moolenaar9b486ca2011-05-19 18:26:40 +02005623static int did_init_locales = FALSE;
5624
Bram Moolenaar9b486ca2011-05-19 18:26:40 +02005625/* Return an array of strings for all available locales + NULL for the
5626 * last element. Return NULL in case of error. */
5627 static char_u **
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01005628find_locales(void)
Bram Moolenaar9b486ca2011-05-19 18:26:40 +02005629{
5630 garray_T locales_ga;
5631 char_u *loc;
5632
5633 /* Find all available locales by running command "locale -a". If this
5634 * doesn't work we won't have completion. */
5635 char_u *locale_a = get_cmd_output((char_u *)"locale -a",
Bram Moolenaar39c29ed2014-04-05 19:44:40 +02005636 NULL, SHELL_SILENT, NULL);
Bram Moolenaar9b486ca2011-05-19 18:26:40 +02005637 if (locale_a == NULL)
5638 return NULL;
5639 ga_init2(&locales_ga, sizeof(char_u *), 20);
5640
5641 /* Transform locale_a string where each locale is separated by "\n"
5642 * into an array of locale strings. */
5643 loc = (char_u *)strtok((char *)locale_a, "\n");
5644
5645 while (loc != NULL)
5646 {
5647 if (ga_grow(&locales_ga, 1) == FAIL)
5648 break;
5649 loc = vim_strsave(loc);
5650 if (loc == NULL)
5651 break;
5652
5653 ((char_u **)locales_ga.ga_data)[locales_ga.ga_len++] = loc;
5654 loc = (char_u *)strtok(NULL, "\n");
5655 }
5656 vim_free(locale_a);
5657 if (ga_grow(&locales_ga, 1) == FAIL)
5658 {
5659 ga_clear(&locales_ga);
5660 return NULL;
5661 }
5662 ((char_u **)locales_ga.ga_data)[locales_ga.ga_len] = NULL;
5663 return (char_u **)locales_ga.ga_data;
5664}
Bram Moolenaarb8f7bd62017-01-12 20:28:25 +01005665# endif
5666
5667/*
5668 * Lazy initialization of all available locales.
5669 */
5670 static void
5671init_locales(void)
5672{
5673# ifndef WIN32
5674 if (!did_init_locales)
5675 {
5676 did_init_locales = TRUE;
5677 locales = find_locales();
5678 }
5679# endif
5680}
Bram Moolenaar9b486ca2011-05-19 18:26:40 +02005681
5682# if defined(EXITFREE) || defined(PROTO)
5683 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01005684free_locales(void)
Bram Moolenaar9b486ca2011-05-19 18:26:40 +02005685{
5686 int i;
5687 if (locales != NULL)
5688 {
5689 for (i = 0; locales[i] != NULL; i++)
5690 vim_free(locales[i]);
Bram Moolenaard23a8232018-02-10 18:45:26 +01005691 VIM_CLEAR(locales);
Bram Moolenaar9b486ca2011-05-19 18:26:40 +02005692 }
5693}
5694# endif
5695
Bram Moolenaar071d4272004-06-13 20:20:40 +00005696/*
5697 * Function given to ExpandGeneric() to obtain the possible arguments of the
5698 * ":language" command.
5699 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005700 char_u *
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01005701get_lang_arg(expand_T *xp UNUSED, int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005702{
5703 if (idx == 0)
5704 return (char_u *)"messages";
5705 if (idx == 1)
5706 return (char_u *)"ctype";
5707 if (idx == 2)
5708 return (char_u *)"time";
Bram Moolenaar9b486ca2011-05-19 18:26:40 +02005709
5710 init_locales();
5711 if (locales == NULL)
5712 return NULL;
5713 return locales[idx - 3];
5714}
5715
5716/*
5717 * Function given to ExpandGeneric() to obtain the available locales.
5718 */
5719 char_u *
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01005720get_locales(expand_T *xp UNUSED, int idx)
Bram Moolenaar9b486ca2011-05-19 18:26:40 +02005721{
5722 init_locales();
5723 if (locales == NULL)
5724 return NULL;
5725 return locales[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00005726}
5727# endif
5728
5729#endif