blob: d6b6f5bf89a36654748f4c63449fc5f7686cebcc [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)
146 MSG(_("Entering Debug mode. Type \"cont\" to continue."));
Bram Moolenaarc6f9f732018-02-11 19:06:26 +0100147 if (debug_oldval != NULL)
148 {
149 smsg((char_u *)_("Oldval = \"%s\""), debug_oldval);
150 vim_free(debug_oldval);
151 debug_oldval = NULL;
152 }
153 if (debug_newval != NULL)
154 {
155 smsg((char_u *)_("Newval = \"%s\""), debug_newval);
156 vim_free(debug_newval);
157 debug_newval = NULL;
158 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000159 if (sourcing_name != NULL)
160 msg(sourcing_name);
161 if (sourcing_lnum != 0)
Bram Moolenaar555b2802005-05-19 21:08:39 +0000162 smsg((char_u *)_("line %ld: %s"), (long)sourcing_lnum, cmd);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000163 else
Bram Moolenaar555b2802005-05-19 21:08:39 +0000164 smsg((char_u *)_("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;
393 MSG(_("frame is zero"));
394 }
395 else
396 {
397 int max = get_maxbacktrace_level();
398
399 if (debug_backtrace_level > max)
400 {
401 debug_backtrace_level = max;
402 smsg((char_u *)_("frame at highest level: %d"), max);
403 }
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)
424 smsg((char_u *)"->%d %s", max - i, cur);
425 else
426 smsg((char_u *)" %d %s", max - i, cur);
427 ++i;
428 if (next == NULL)
429 break;
430 *next = '.';
431 cur = next + 2;
432 }
433 }
434 if (sourcing_lnum != 0)
435 smsg((char_u *)_("line %ld: %s"), (long)sourcing_lnum, cmd);
436 else
437 smsg((char_u *)_("cmd: %s"), cmd);
438}
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 Moolenaar555b2802005-05-19 21:08:39 +0000490 smsg((char_u *)_("Breakpoint in \"%s%s\" line %ld"),
491 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 {
610 EMSG(_(e_noname));
611 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 {
624 EMSG2(_(e_invarg2), p);
625 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 {
649 EMSG2(_(e_invarg2), arg);
650 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)
820 EMSG2(_("E161: Breakpoint not found: %s"), eap->arg);
821 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)
860 MSG(_("No breakpoints defined"));
861 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)
868 smsg((char_u *)_("%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
874 smsg((char_u *)_("%3d expr %s"),
875 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 Moolenaar8a7f5a22013-06-06 14:01:46 +02001581#if defined(FEAT_SYN_HL) && defined(FEAT_RELTIME) && defined(FEAT_FLOAT)
1582# 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 Moolenaar54c1b492010-02-24 14:01:28 +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)
1831 EMSG2(_(e_notopen), profile_fname);
1832 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;
1960# ifdef FEAT_MBYTE
1961 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);
1971# endif
1972 IObuff[n] = NL;
1973 IObuff[n + 1] = NUL;
1974 }
Bram Moolenaar67435d92017-10-19 21:04:37 +02001975 if (i < si->sn_prl_ga.ga_len
1976 && (pp = &PRL_ITEM(si, i))->snp_count > 0)
Bram Moolenaar05159a02005-02-26 23:04:13 +00001977 {
1978 fprintf(fd, "%5d ", pp->snp_count);
1979 if (profile_equal(&pp->sn_prl_total, &pp->sn_prl_self))
1980 fprintf(fd, " ");
1981 else
1982 fprintf(fd, "%s ", profile_msg(&pp->sn_prl_total));
1983 fprintf(fd, "%s ", profile_msg(&pp->sn_prl_self));
1984 }
1985 else
1986 fprintf(fd, " ");
1987 fprintf(fd, "%s", IObuff);
1988 }
1989 fclose(sfd);
1990 }
1991 fprintf(fd, "\n");
1992 }
1993 }
1994}
1995
1996/*
1997 * Return TRUE when a function defined in the current script should be
1998 * profiled.
1999 */
2000 int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002001prof_def_func(void)
Bram Moolenaar05159a02005-02-26 23:04:13 +00002002{
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02002003 if (current_sctx.sc_sid > 0)
2004 return SCRIPT_ITEM(current_sctx.sc_sid).sn_pr_force;
Bram Moolenaar53180ce2005-07-05 21:48:14 +00002005 return FALSE;
Bram Moolenaar05159a02005-02-26 23:04:13 +00002006}
2007
2008# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002009#endif
2010
2011/*
2012 * If 'autowrite' option set, try to write the file.
2013 * Careful: autocommands may make "buf" invalid!
2014 *
2015 * return FAIL for failure, OK otherwise
2016 */
2017 int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002018autowrite(buf_T *buf, int forceit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002019{
Bram Moolenaar373154b2007-02-13 05:19:30 +00002020 int r;
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02002021 bufref_T bufref;
Bram Moolenaar373154b2007-02-13 05:19:30 +00002022
Bram Moolenaar071d4272004-06-13 20:20:40 +00002023 if (!(p_aw || p_awa) || !p_write
2024#ifdef FEAT_QUICKFIX
Bram Moolenaar373154b2007-02-13 05:19:30 +00002025 /* never autowrite a "nofile" or "nowrite" buffer */
2026 || bt_dontwrite(buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002027#endif
Bram Moolenaar373154b2007-02-13 05:19:30 +00002028 || (!forceit && buf->b_p_ro) || buf->b_ffname == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002029 return FAIL;
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02002030 set_bufref(&bufref, buf);
Bram Moolenaar373154b2007-02-13 05:19:30 +00002031 r = buf_write_all(buf, forceit);
2032
2033 /* Writing may succeed but the buffer still changed, e.g., when there is a
2034 * conversion error. We do want to return FAIL then. */
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02002035 if (bufref_valid(&bufref) && bufIsChanged(buf))
Bram Moolenaar373154b2007-02-13 05:19:30 +00002036 r = FAIL;
2037 return r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002038}
2039
2040/*
Bram Moolenaar8c9e7b02018-08-30 13:07:17 +02002041 * Flush all buffers, except the ones that are readonly or are never written.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002042 */
2043 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002044autowrite_all(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002045{
2046 buf_T *buf;
2047
2048 if (!(p_aw || p_awa) || !p_write)
2049 return;
Bram Moolenaar29323592016-07-24 22:04:11 +02002050 FOR_ALL_BUFFERS(buf)
Bram Moolenaar8c9e7b02018-08-30 13:07:17 +02002051 if (bufIsChanged(buf) && !buf->b_p_ro && !bt_dontwrite(buf))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002052 {
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02002053 bufref_T bufref;
2054
2055 set_bufref(&bufref, buf);
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01002056
Bram Moolenaar071d4272004-06-13 20:20:40 +00002057 (void)buf_write_all(buf, FALSE);
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01002058
Bram Moolenaar071d4272004-06-13 20:20:40 +00002059 /* an autocommand may have deleted the buffer */
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02002060 if (!bufref_valid(&bufref))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002061 buf = firstbuf;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002062 }
2063}
2064
2065/*
Bram Moolenaar45d3b142013-11-09 03:31:51 +01002066 * Return TRUE if buffer was changed and cannot be abandoned.
2067 * For flags use the CCGD_ values.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002068 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002069 int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002070check_changed(buf_T *buf, int flags)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002071{
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02002072 int forceit = (flags & CCGD_FORCEIT);
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02002073 bufref_T bufref;
2074
2075 set_bufref(&bufref, buf);
Bram Moolenaar45d3b142013-11-09 03:31:51 +01002076
Bram Moolenaar071d4272004-06-13 20:20:40 +00002077 if ( !forceit
2078 && bufIsChanged(buf)
Bram Moolenaar45d3b142013-11-09 03:31:51 +01002079 && ((flags & CCGD_MULTWIN) || buf->b_nwindows <= 1)
2080 && (!(flags & CCGD_AW) || autowrite(buf, forceit) == FAIL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002081 {
2082#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
2083 if ((p_confirm || cmdmod.confirm) && p_write)
2084 {
2085 buf_T *buf2;
2086 int count = 0;
2087
Bram Moolenaar45d3b142013-11-09 03:31:51 +01002088 if (flags & CCGD_ALLBUF)
Bram Moolenaar29323592016-07-24 22:04:11 +02002089 FOR_ALL_BUFFERS(buf2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002090 if (bufIsChanged(buf2)
2091 && (buf2->b_ffname != NULL
2092# ifdef FEAT_BROWSE
2093 || cmdmod.browse
2094# endif
2095 ))
2096 ++count;
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02002097 if (!bufref_valid(&bufref))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002098 /* Autocommand deleted buffer, oops! It's not changed now. */
2099 return FALSE;
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01002100
Bram Moolenaar071d4272004-06-13 20:20:40 +00002101 dialog_changed(buf, count > 1);
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01002102
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02002103 if (!bufref_valid(&bufref))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002104 /* Autocommand deleted buffer, oops! It's not changed now. */
2105 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002106 return bufIsChanged(buf);
2107 }
2108#endif
Bram Moolenaar45d3b142013-11-09 03:31:51 +01002109 if (flags & CCGD_EXCMD)
Bram Moolenaarf5be7cd2017-08-17 16:55:13 +02002110 no_write_message();
Bram Moolenaar45d3b142013-11-09 03:31:51 +01002111 else
Bram Moolenaar7a760922018-02-19 23:10:02 +01002112 no_write_message_nobang(curbuf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002113 return TRUE;
2114 }
2115 return FALSE;
2116}
2117
2118#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG) || defined(PROTO)
2119
2120#if defined(FEAT_BROWSE) || defined(PROTO)
2121/*
2122 * When wanting to write a file without a file name, ask the user for a name.
2123 */
2124 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002125browse_save_fname(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002126{
2127 if (buf->b_fname == NULL)
2128 {
2129 char_u *fname;
2130
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00002131 fname = do_browse(BROWSE_SAVE, (char_u *)_("Save As"),
2132 NULL, NULL, NULL, NULL, buf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002133 if (fname != NULL)
2134 {
2135 if (setfname(buf, fname, NULL, TRUE) == OK)
2136 buf->b_flags |= BF_NOTEDITED;
2137 vim_free(fname);
2138 }
2139 }
2140}
2141#endif
2142
2143/*
Bram Moolenaar9b486ca2011-05-19 18:26:40 +02002144 * Ask the user what to do when abandoning a changed buffer.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002145 * Must check 'write' option first!
2146 */
2147 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002148dialog_changed(
2149 buf_T *buf,
2150 int checkall) /* may abandon all changed buffers */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002151{
Bram Moolenaard9462e32011-04-11 21:35:11 +02002152 char_u buff[DIALOG_MSG_SIZE];
Bram Moolenaar071d4272004-06-13 20:20:40 +00002153 int ret;
2154 buf_T *buf2;
Bram Moolenaar8218f602012-04-25 17:32:18 +02002155 exarg_T ea;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002156
Bram Moolenaar3f9a1ff2017-08-21 22:06:02 +02002157 dialog_msg(buff, _("Save changes to \"%s\"?"), buf->b_fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002158 if (checkall)
2159 ret = vim_dialog_yesnoallcancel(VIM_QUESTION, NULL, buff, 1);
2160 else
2161 ret = vim_dialog_yesnocancel(VIM_QUESTION, NULL, buff, 1);
2162
Bram Moolenaar8218f602012-04-25 17:32:18 +02002163 /* Init ea pseudo-structure, this is needed for the check_overwrite()
2164 * function. */
2165 ea.append = ea.forceit = FALSE;
2166
Bram Moolenaar071d4272004-06-13 20:20:40 +00002167 if (ret == VIM_YES)
2168 {
2169#ifdef FEAT_BROWSE
2170 /* May get file name, when there is none */
2171 browse_save_fname(buf);
2172#endif
Bram Moolenaar8218f602012-04-25 17:32:18 +02002173 if (buf->b_fname != NULL && check_overwrite(&ea, buf,
2174 buf->b_fname, buf->b_ffname, FALSE) == OK)
2175 /* didn't hit Cancel */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002176 (void)buf_write_all(buf, FALSE);
2177 }
2178 else if (ret == VIM_NO)
2179 {
2180 unchanged(buf, TRUE);
2181 }
2182 else if (ret == VIM_ALL)
2183 {
2184 /*
2185 * Write all modified files that can be written.
2186 * Skip readonly buffers, these need to be confirmed
2187 * individually.
2188 */
Bram Moolenaar29323592016-07-24 22:04:11 +02002189 FOR_ALL_BUFFERS(buf2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002190 {
2191 if (bufIsChanged(buf2)
2192 && (buf2->b_ffname != NULL
2193#ifdef FEAT_BROWSE
2194 || cmdmod.browse
2195#endif
2196 )
2197 && !buf2->b_p_ro)
2198 {
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02002199 bufref_T bufref;
2200
2201 set_bufref(&bufref, buf2);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002202#ifdef FEAT_BROWSE
2203 /* May get file name, when there is none */
2204 browse_save_fname(buf2);
2205#endif
Bram Moolenaar8218f602012-04-25 17:32:18 +02002206 if (buf2->b_fname != NULL && check_overwrite(&ea, buf2,
2207 buf2->b_fname, buf2->b_ffname, FALSE) == OK)
2208 /* didn't hit Cancel */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002209 (void)buf_write_all(buf2, FALSE);
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01002210
Bram Moolenaar071d4272004-06-13 20:20:40 +00002211 /* an autocommand may have deleted the buffer */
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02002212 if (!bufref_valid(&bufref))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002213 buf2 = firstbuf;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002214 }
2215 }
2216 }
2217 else if (ret == VIM_DISCARDALL)
2218 {
2219 /*
2220 * mark all buffers as unchanged
2221 */
Bram Moolenaar29323592016-07-24 22:04:11 +02002222 FOR_ALL_BUFFERS(buf2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002223 unchanged(buf2, TRUE);
2224 }
2225}
2226#endif
2227
2228/*
2229 * Return TRUE if the buffer "buf" can be abandoned, either by making it
2230 * hidden, autowriting it or unloading it.
2231 */
2232 int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002233can_abandon(buf_T *buf, int forceit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002234{
Bram Moolenaareb44a682017-08-03 22:44:55 +02002235 return ( buf_hide(buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002236 || !bufIsChanged(buf)
2237 || buf->b_nwindows > 1
2238 || autowrite(buf, forceit) == OK
2239 || forceit);
2240}
2241
Bram Moolenaar970a1b82012-03-23 18:39:18 +01002242/*
2243 * Add a buffer number to "bufnrs", unless it's already there.
2244 */
2245 static void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002246add_bufnum(int *bufnrs, int *bufnump, int nr)
Bram Moolenaar970a1b82012-03-23 18:39:18 +01002247{
2248 int i;
2249
2250 for (i = 0; i < *bufnump; ++i)
2251 if (bufnrs[i] == nr)
2252 return;
2253 bufnrs[*bufnump] = nr;
2254 *bufnump = *bufnump + 1;
2255}
2256
Bram Moolenaar071d4272004-06-13 20:20:40 +00002257/*
2258 * Return TRUE if any buffer was changed and cannot be abandoned.
2259 * That changed buffer becomes the current buffer.
Bram Moolenaar25cdd9c2018-03-10 20:28:12 +01002260 * When "unload" is TRUE the current buffer is unloaded instead of making it
Bram Moolenaar027387f2016-01-02 22:25:52 +01002261 * hidden. This is used for ":q!".
Bram Moolenaar071d4272004-06-13 20:20:40 +00002262 */
2263 int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002264check_changed_any(
2265 int hidden, /* Only check hidden buffers */
2266 int unload)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002267{
Bram Moolenaar970a1b82012-03-23 18:39:18 +01002268 int ret = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002269 buf_T *buf;
2270 int save;
Bram Moolenaar970a1b82012-03-23 18:39:18 +01002271 int i;
2272 int bufnum = 0;
2273 int bufcount = 0;
2274 int *bufnrs;
Bram Moolenaar970a1b82012-03-23 18:39:18 +01002275 tabpage_T *tp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002276 win_T *wp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002277
Bram Moolenaar25cdd9c2018-03-10 20:28:12 +01002278 /* Make a list of all buffers, with the most important ones first. */
Bram Moolenaar29323592016-07-24 22:04:11 +02002279 FOR_ALL_BUFFERS(buf)
Bram Moolenaar970a1b82012-03-23 18:39:18 +01002280 ++bufcount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002281
Bram Moolenaar970a1b82012-03-23 18:39:18 +01002282 if (bufcount == 0)
2283 return FALSE;
2284
2285 bufnrs = (int *)alloc(sizeof(int) * bufcount);
2286 if (bufnrs == NULL)
2287 return FALSE;
2288
2289 /* curbuf */
2290 bufnrs[bufnum++] = curbuf->b_fnum;
Bram Moolenaar25cdd9c2018-03-10 20:28:12 +01002291
2292 /* buffers in current tab */
Bram Moolenaar970a1b82012-03-23 18:39:18 +01002293 FOR_ALL_WINDOWS(wp)
2294 if (wp->w_buffer != curbuf)
2295 add_bufnum(bufnrs, &bufnum, wp->w_buffer->b_fnum);
2296
Bram Moolenaar25cdd9c2018-03-10 20:28:12 +01002297 /* buffers in other tabs */
Bram Moolenaar29323592016-07-24 22:04:11 +02002298 FOR_ALL_TABPAGES(tp)
Bram Moolenaar970a1b82012-03-23 18:39:18 +01002299 if (tp != curtab)
2300 for (wp = tp->tp_firstwin; wp != NULL; wp = wp->w_next)
2301 add_bufnum(bufnrs, &bufnum, wp->w_buffer->b_fnum);
Bram Moolenaar25cdd9c2018-03-10 20:28:12 +01002302
2303 /* any other buffer */
Bram Moolenaar29323592016-07-24 22:04:11 +02002304 FOR_ALL_BUFFERS(buf)
Bram Moolenaar970a1b82012-03-23 18:39:18 +01002305 add_bufnum(bufnrs, &bufnum, buf->b_fnum);
2306
2307 for (i = 0; i < bufnum; ++i)
2308 {
2309 buf = buflist_findnr(bufnrs[i]);
2310 if (buf == NULL)
2311 continue;
2312 if ((!hidden || buf->b_nwindows == 0) && bufIsChanged(buf))
2313 {
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02002314 bufref_T bufref;
2315
2316 set_bufref(&bufref, buf);
Bram Moolenaar25cdd9c2018-03-10 20:28:12 +01002317#ifdef FEAT_TERMINAL
2318 if (term_job_running(buf->b_term))
2319 {
2320 if (term_try_stop_job(buf) == FAIL)
2321 break;
2322 }
2323 else
2324#endif
Bram Moolenaar970a1b82012-03-23 18:39:18 +01002325 /* Try auto-writing the buffer. If this fails but the buffer no
2326 * longer exists it's not changed, that's OK. */
Bram Moolenaar45d3b142013-11-09 03:31:51 +01002327 if (check_changed(buf, (p_awa ? CCGD_AW : 0)
2328 | CCGD_MULTWIN
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02002329 | CCGD_ALLBUF) && bufref_valid(&bufref))
Bram Moolenaar970a1b82012-03-23 18:39:18 +01002330 break; /* didn't save - still changes */
2331 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002332 }
2333
Bram Moolenaar970a1b82012-03-23 18:39:18 +01002334 if (i >= bufnum)
2335 goto theend;
2336
Bram Moolenaar25cdd9c2018-03-10 20:28:12 +01002337 /* Get here if "buf" cannot be abandoned. */
Bram Moolenaar970a1b82012-03-23 18:39:18 +01002338 ret = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002339 exiting = FALSE;
2340#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
2341 /*
2342 * When ":confirm" used, don't give an error message.
2343 */
2344 if (!(p_confirm || cmdmod.confirm))
2345#endif
2346 {
2347 /* There must be a wait_return for this message, do_buffer()
2348 * may cause a redraw. But wait_return() is a no-op when vgetc()
2349 * is busy (Quit used from window menu), then make sure we don't
2350 * cause a scroll up. */
Bram Moolenaar61660ea2006-04-07 21:40:07 +00002351 if (vgetc_busy > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002352 {
2353 msg_row = cmdline_row;
2354 msg_col = 0;
2355 msg_didout = FALSE;
2356 }
Bram Moolenaareb44a682017-08-03 22:44:55 +02002357 if (
2358#ifdef FEAT_TERMINAL
2359 term_job_running(buf->b_term)
2360 ? EMSG2(_("E947: Job still running in buffer \"%s\""),
2361 buf->b_fname)
2362 :
2363#endif
2364 EMSG2(_("E162: No write since last change for buffer \"%s\""),
Bram Moolenaare1704ba2012-10-03 18:25:00 +02002365 buf_spname(buf) != NULL ? buf_spname(buf) : buf->b_fname))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002366 {
2367 save = no_wait_return;
2368 no_wait_return = FALSE;
2369 wait_return(FALSE);
2370 no_wait_return = save;
2371 }
2372 }
2373
Bram Moolenaar071d4272004-06-13 20:20:40 +00002374 /* Try to find a window that contains the buffer. */
2375 if (buf != curbuf)
Bram Moolenaar970a1b82012-03-23 18:39:18 +01002376 FOR_ALL_TAB_WINDOWS(tp, wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002377 if (wp->w_buffer == buf)
2378 {
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02002379 bufref_T bufref;
2380
2381 set_bufref(&bufref, buf);
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01002382
Bram Moolenaar970a1b82012-03-23 18:39:18 +01002383 goto_tabpage_win(tp, wp);
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01002384
Bram Moolenaar071d4272004-06-13 20:20:40 +00002385 /* Paranoia: did autocms wipe out the buffer with changes? */
Bram Moolenaar7c0a2f32016-07-10 22:11:16 +02002386 if (!bufref_valid(&bufref))
Bram Moolenaar970a1b82012-03-23 18:39:18 +01002387 goto theend;
Bram Moolenaar970a1b82012-03-23 18:39:18 +01002388 goto buf_found;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002389 }
Bram Moolenaar970a1b82012-03-23 18:39:18 +01002390buf_found:
Bram Moolenaar071d4272004-06-13 20:20:40 +00002391
2392 /* Open the changed buffer in the current window. */
2393 if (buf != curbuf)
Bram Moolenaar027387f2016-01-02 22:25:52 +01002394 set_curbuf(buf, unload ? DOBUF_UNLOAD : DOBUF_GOTO);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002395
Bram Moolenaar970a1b82012-03-23 18:39:18 +01002396theend:
2397 vim_free(bufnrs);
2398 return ret;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002399}
2400
2401/*
2402 * return FAIL if there is no file name, OK if there is one
2403 * give error message for FAIL
2404 */
2405 int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002406check_fname(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002407{
2408 if (curbuf->b_ffname == NULL)
2409 {
2410 EMSG(_(e_noname));
2411 return FAIL;
2412 }
2413 return OK;
2414}
2415
2416/*
2417 * flush the contents of a buffer, unless it has no file name
2418 *
2419 * return FAIL for failure, OK otherwise
2420 */
2421 int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002422buf_write_all(buf_T *buf, int forceit)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002423{
2424 int retval;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002425 buf_T *old_curbuf = curbuf;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002426
2427 retval = (buf_write(buf, buf->b_ffname, buf->b_fname,
2428 (linenr_T)1, buf->b_ml.ml_line_count, NULL,
2429 FALSE, forceit, TRUE, FALSE));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002430 if (curbuf != old_curbuf)
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00002431 {
Bram Moolenaar8820b482017-03-16 17:23:31 +01002432 msg_source(HL_ATTR(HLF_W));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002433 MSG(_("Warning: Entered other buffer unexpectedly (check autocommands)"));
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00002434 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002435 return retval;
2436}
2437
2438/*
2439 * Code to handle the argument list.
2440 */
2441
Bram Moolenaar32bbd002018-08-31 23:06:22 +02002442static int do_arglist(char_u *str, int what, int after, int will_edit);
Bram Moolenaarf28dbce2016-01-29 22:03:47 +01002443static void alist_check_arg_idx(void);
Bram Moolenaar32bbd002018-08-31 23:06:22 +02002444static void alist_add_list(int count, char_u **files, int after, int will_edit);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002445#define AL_SET 1
2446#define AL_ADD 2
2447#define AL_DEL 3
2448
Bram Moolenaar071d4272004-06-13 20:20:40 +00002449/*
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002450 * Isolate one argument, taking backticks.
2451 * Changes the argument in-place, puts a NUL after it. Backticks remain.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002452 * Return a pointer to the start of the next argument.
2453 */
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002454 static char_u *
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002455do_one_arg(char_u *str)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002456{
2457 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002458 int inbacktick;
2459
Bram Moolenaar071d4272004-06-13 20:20:40 +00002460 inbacktick = FALSE;
2461 for (p = str; *str; ++str)
2462 {
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002463 /* When the backslash is used for escaping the special meaning of a
2464 * character we need to keep it until wildcard expansion. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002465 if (rem_backslash(str))
2466 {
2467 *p++ = *str++;
2468 *p++ = *str;
2469 }
2470 else
2471 {
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002472 /* An item ends at a space not in backticks */
2473 if (!inbacktick && vim_isspace(*str))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002474 break;
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002475 if (*str == '`')
Bram Moolenaar071d4272004-06-13 20:20:40 +00002476 inbacktick ^= TRUE;
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00002477 *p++ = *str;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002478 }
2479 }
2480 str = skipwhite(str);
2481 *p = NUL;
2482
2483 return str;
2484}
2485
Bram Moolenaar86b68352004-12-27 21:59:20 +00002486/*
2487 * Separate the arguments in "str" and return a list of pointers in the
2488 * growarray "gap".
2489 */
Bram Moolenaar398ee732017-08-03 14:29:14 +02002490 static int
2491get_arglist(garray_T *gap, char_u *str, int escaped)
Bram Moolenaar86b68352004-12-27 21:59:20 +00002492{
2493 ga_init2(gap, (int)sizeof(char_u *), 20);
2494 while (*str != NUL)
2495 {
2496 if (ga_grow(gap, 1) == FAIL)
2497 {
2498 ga_clear(gap);
2499 return FAIL;
2500 }
2501 ((char_u **)gap->ga_data)[gap->ga_len++] = str;
2502
Bram Moolenaar398ee732017-08-03 14:29:14 +02002503 /* If str is escaped, don't handle backslashes or spaces */
2504 if (!escaped)
2505 return OK;
2506
Bram Moolenaar86b68352004-12-27 21:59:20 +00002507 /* Isolate one argument, change it in-place, put a NUL after it. */
2508 str = do_one_arg(str);
2509 }
2510 return OK;
2511}
2512
Bram Moolenaar7df351e2006-01-23 22:30:28 +00002513#if defined(FEAT_QUICKFIX) || defined(FEAT_SYN_HL) || defined(PROTO)
Bram Moolenaar13fcaaf2005-04-15 21:13:42 +00002514/*
2515 * Parse a list of arguments (file names), expand them and return in
Bram Moolenaar8f5c6f02012-06-29 12:57:06 +02002516 * "fnames[fcountp]". When "wig" is TRUE, removes files matching 'wildignore'.
Bram Moolenaar13fcaaf2005-04-15 21:13:42 +00002517 * Return FAIL or OK.
2518 */
2519 int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002520get_arglist_exp(
2521 char_u *str,
2522 int *fcountp,
2523 char_u ***fnamesp,
2524 int wig)
Bram Moolenaar13fcaaf2005-04-15 21:13:42 +00002525{
2526 garray_T ga;
2527 int i;
2528
Bram Moolenaar398ee732017-08-03 14:29:14 +02002529 if (get_arglist(&ga, str, TRUE) == FAIL)
Bram Moolenaar13fcaaf2005-04-15 21:13:42 +00002530 return FAIL;
Bram Moolenaar8f5c6f02012-06-29 12:57:06 +02002531 if (wig == TRUE)
2532 i = expand_wildcards(ga.ga_len, (char_u **)ga.ga_data,
2533 fcountp, fnamesp, EW_FILE|EW_NOTFOUND);
2534 else
2535 i = gen_expand_wildcards(ga.ga_len, (char_u **)ga.ga_data,
2536 fcountp, fnamesp, EW_FILE|EW_NOTFOUND);
2537
Bram Moolenaar13fcaaf2005-04-15 21:13:42 +00002538 ga_clear(&ga);
2539 return i;
2540}
2541#endif
2542
Bram Moolenaar071d4272004-06-13 20:20:40 +00002543/*
2544 * Redefine the argument list.
2545 */
2546 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002547set_arglist(char_u *str)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002548{
Bram Moolenaar32bbd002018-08-31 23:06:22 +02002549 do_arglist(str, AL_SET, 0, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002550}
Bram Moolenaar071d4272004-06-13 20:20:40 +00002551
2552/*
2553 * "what" == AL_SET: Redefine the argument list to 'str'.
2554 * "what" == AL_ADD: add files in 'str' to the argument list after "after".
2555 * "what" == AL_DEL: remove files in 'str' from the argument list.
2556 *
2557 * Return FAIL for failure, OK otherwise.
2558 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002559 static int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002560do_arglist(
2561 char_u *str,
Bram Moolenaarf1d25012016-03-03 12:22:53 +01002562 int what,
Bram Moolenaar32bbd002018-08-31 23:06:22 +02002563 int after UNUSED, // 0 means before first one
2564 int will_edit) // will edit added argument
Bram Moolenaar071d4272004-06-13 20:20:40 +00002565{
2566 garray_T new_ga;
2567 int exp_count;
2568 char_u **exp_files;
2569 int i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002570 char_u *p;
2571 int match;
Bram Moolenaar398ee732017-08-03 14:29:14 +02002572 int arg_escaped = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002573
2574 /*
Bram Moolenaar2faa29f2016-01-23 23:02:34 +01002575 * Set default argument for ":argadd" command.
2576 */
2577 if (what == AL_ADD && *str == NUL)
2578 {
2579 if (curbuf->b_ffname == NULL)
2580 return FAIL;
2581 str = curbuf->b_fname;
Bram Moolenaar398ee732017-08-03 14:29:14 +02002582 arg_escaped = FALSE;
Bram Moolenaar2faa29f2016-01-23 23:02:34 +01002583 }
2584
2585 /*
Bram Moolenaar071d4272004-06-13 20:20:40 +00002586 * Collect all file name arguments in "new_ga".
2587 */
Bram Moolenaar398ee732017-08-03 14:29:14 +02002588 if (get_arglist(&new_ga, str, arg_escaped) == FAIL)
Bram Moolenaar86b68352004-12-27 21:59:20 +00002589 return FAIL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002590
Bram Moolenaar071d4272004-06-13 20:20:40 +00002591 if (what == AL_DEL)
2592 {
2593 regmatch_T regmatch;
2594 int didone;
2595
2596 /*
2597 * Delete the items: use each item as a regexp and find a match in the
2598 * argument list.
2599 */
Bram Moolenaar71afbfe2013-03-19 16:49:16 +01002600 regmatch.rm_ic = p_fic; /* ignore case when 'fileignorecase' is set */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002601 for (i = 0; i < new_ga.ga_len && !got_int; ++i)
2602 {
2603 p = ((char_u **)new_ga.ga_data)[i];
2604 p = file_pat_to_reg_pat(p, NULL, NULL, FALSE);
2605 if (p == NULL)
2606 break;
2607 regmatch.regprog = vim_regcomp(p, p_magic ? RE_MAGIC : 0);
2608 if (regmatch.regprog == NULL)
2609 {
2610 vim_free(p);
2611 break;
2612 }
2613
2614 didone = FALSE;
2615 for (match = 0; match < ARGCOUNT; ++match)
2616 if (vim_regexec(&regmatch, alist_name(&ARGLIST[match]),
2617 (colnr_T)0))
2618 {
2619 didone = TRUE;
2620 vim_free(ARGLIST[match].ae_fname);
2621 mch_memmove(ARGLIST + match, ARGLIST + match + 1,
2622 (ARGCOUNT - match - 1) * sizeof(aentry_T));
2623 --ALIST(curwin)->al_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002624 if (curwin->w_arg_idx > match)
2625 --curwin->w_arg_idx;
2626 --match;
2627 }
2628
Bram Moolenaar473de612013-06-08 18:19:48 +02002629 vim_regfree(regmatch.regprog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002630 vim_free(p);
2631 if (!didone)
2632 EMSG2(_(e_nomatch2), ((char_u **)new_ga.ga_data)[i]);
2633 }
2634 ga_clear(&new_ga);
2635 }
2636 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00002637 {
2638 i = expand_wildcards(new_ga.ga_len, (char_u **)new_ga.ga_data,
2639 &exp_count, &exp_files, EW_DIR|EW_FILE|EW_ADDSLASH|EW_NOTFOUND);
2640 ga_clear(&new_ga);
Bram Moolenaar2db5c3b2016-01-16 22:49:34 +01002641 if (i == FAIL || exp_count == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002642 {
2643 EMSG(_(e_nomatch));
2644 return FAIL;
2645 }
2646
Bram Moolenaar071d4272004-06-13 20:20:40 +00002647 if (what == AL_ADD)
2648 {
Bram Moolenaar32bbd002018-08-31 23:06:22 +02002649 alist_add_list(exp_count, exp_files, after, will_edit);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002650 vim_free(exp_files);
2651 }
2652 else /* what == AL_SET */
Bram Moolenaar32bbd002018-08-31 23:06:22 +02002653 alist_set(ALIST(curwin), exp_count, exp_files, will_edit, NULL, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002654 }
2655
2656 alist_check_arg_idx();
2657
2658 return OK;
2659}
2660
2661/*
2662 * Check the validity of the arg_idx for each other window.
2663 */
2664 static void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002665alist_check_arg_idx(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002666{
Bram Moolenaar071d4272004-06-13 20:20:40 +00002667 win_T *win;
Bram Moolenaarf740b292006-02-16 22:11:02 +00002668 tabpage_T *tp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002669
Bram Moolenaarf740b292006-02-16 22:11:02 +00002670 FOR_ALL_TAB_WINDOWS(tp, win)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002671 if (win->w_alist == curwin->w_alist)
2672 check_arg_idx(win);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002673}
2674
2675/*
Bram Moolenaarb8ff1fb2012-02-04 21:59:01 +01002676 * Return TRUE if window "win" is editing the file at the current argument
Bram Moolenaard4755bb2004-09-02 19:12:26 +00002677 * index.
2678 */
2679 static int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002680editing_arg_idx(win_T *win)
Bram Moolenaard4755bb2004-09-02 19:12:26 +00002681{
2682 return !(win->w_arg_idx >= WARGCOUNT(win)
2683 || (win->w_buffer->b_fnum
2684 != WARGLIST(win)[win->w_arg_idx].ae_fnum
2685 && (win->w_buffer->b_ffname == NULL
2686 || !(fullpathcmp(
2687 alist_name(&WARGLIST(win)[win->w_arg_idx]),
2688 win->w_buffer->b_ffname, TRUE) & FPC_SAME))));
2689}
2690
2691/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00002692 * Check if window "win" is editing the w_arg_idx file in its argument list.
2693 */
2694 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002695check_arg_idx(win_T *win)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002696{
Bram Moolenaard4755bb2004-09-02 19:12:26 +00002697 if (WARGCOUNT(win) > 1 && !editing_arg_idx(win))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002698 {
2699 /* We are not editing the current entry in the argument list.
2700 * Set "arg_had_last" if we are editing the last one. */
2701 win->w_arg_idx_invalid = TRUE;
2702 if (win->w_arg_idx != WARGCOUNT(win) - 1
2703 && arg_had_last == FALSE
Bram Moolenaar071d4272004-06-13 20:20:40 +00002704 && ALIST(win) == &global_alist
Bram Moolenaar071d4272004-06-13 20:20:40 +00002705 && GARGCOUNT > 0
2706 && win->w_arg_idx < GARGCOUNT
2707 && (win->w_buffer->b_fnum == GARGLIST[GARGCOUNT - 1].ae_fnum
2708 || (win->w_buffer->b_ffname != NULL
2709 && (fullpathcmp(alist_name(&GARGLIST[GARGCOUNT - 1]),
2710 win->w_buffer->b_ffname, TRUE) & FPC_SAME))))
2711 arg_had_last = TRUE;
2712 }
2713 else
2714 {
2715 /* We are editing the current entry in the argument list.
2716 * Set "arg_had_last" if it's also the last one */
2717 win->w_arg_idx_invalid = FALSE;
2718 if (win->w_arg_idx == WARGCOUNT(win) - 1
Bram Moolenaar4033c552017-09-16 20:54:51 +02002719 && win->w_alist == &global_alist)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002720 arg_had_last = TRUE;
2721 }
2722}
2723
2724/*
2725 * ":args", ":argslocal" and ":argsglobal".
2726 */
2727 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002728ex_args(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002729{
2730 int i;
2731
2732 if (eap->cmdidx != CMD_args)
2733 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00002734 alist_unlink(ALIST(curwin));
2735 if (eap->cmdidx == CMD_argglobal)
2736 ALIST(curwin) = &global_alist;
2737 else /* eap->cmdidx == CMD_arglocal */
2738 alist_new();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002739 }
2740
2741 if (!ends_excmd(*eap->arg))
2742 {
2743 /*
2744 * ":args file ..": define new argument list, handle like ":next"
2745 * Also for ":argslocal file .." and ":argsglobal file ..".
2746 */
2747 ex_next(eap);
2748 }
Bram Moolenaar0c72fe42018-03-29 16:04:08 +02002749 else if (eap->cmdidx == CMD_args)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002750 {
2751 /*
2752 * ":args": list arguments.
2753 */
2754 if (ARGCOUNT > 0)
2755 {
Bram Moolenaar405dadb2018-04-20 22:48:58 +02002756 char_u **items = (char_u **)alloc(sizeof(char_u *) * ARGCOUNT);
Bram Moolenaar5d69da42018-04-20 22:01:41 +02002757
2758 if (items != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002759 {
Bram Moolenaar5d69da42018-04-20 22:01:41 +02002760 /* Overwrite the command, for a short list there is no
2761 * scrolling required and no wait_return(). */
2762 gotocmdline(TRUE);
2763
2764 for (i = 0; i < ARGCOUNT; ++i)
Bram Moolenaar405dadb2018-04-20 22:48:58 +02002765 items[i] = alist_name(&ARGLIST[i]);
Bram Moolenaar5d69da42018-04-20 22:01:41 +02002766 list_in_columns(items, ARGCOUNT, curwin->w_arg_idx);
2767 vim_free(items);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002768 }
2769 }
2770 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002771 else if (eap->cmdidx == CMD_arglocal)
2772 {
2773 garray_T *gap = &curwin->w_alist->al_ga;
2774
2775 /*
2776 * ":argslocal": make a local copy of the global argument list.
2777 */
2778 if (ga_grow(gap, GARGCOUNT) == OK)
2779 for (i = 0; i < GARGCOUNT; ++i)
2780 if (GARGLIST[i].ae_fname != NULL)
2781 {
2782 AARGLIST(curwin->w_alist)[gap->ga_len].ae_fname =
2783 vim_strsave(GARGLIST[i].ae_fname);
2784 AARGLIST(curwin->w_alist)[gap->ga_len].ae_fnum =
2785 GARGLIST[i].ae_fnum;
2786 ++gap->ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002787 }
2788 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002789}
2790
2791/*
2792 * ":previous", ":sprevious", ":Next" and ":sNext".
2793 */
2794 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002795ex_previous(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002796{
2797 /* If past the last one already, go to the last one. */
2798 if (curwin->w_arg_idx - (int)eap->line2 >= ARGCOUNT)
2799 do_argfile(eap, ARGCOUNT - 1);
2800 else
2801 do_argfile(eap, curwin->w_arg_idx - (int)eap->line2);
2802}
2803
2804/*
2805 * ":rewind", ":first", ":sfirst" and ":srewind".
2806 */
2807 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002808ex_rewind(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002809{
2810 do_argfile(eap, 0);
2811}
2812
2813/*
2814 * ":last" and ":slast".
2815 */
2816 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002817ex_last(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002818{
2819 do_argfile(eap, ARGCOUNT - 1);
2820}
2821
2822/*
2823 * ":argument" and ":sargument".
2824 */
2825 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002826ex_argument(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002827{
2828 int i;
2829
2830 if (eap->addr_count > 0)
2831 i = eap->line2 - 1;
2832 else
2833 i = curwin->w_arg_idx;
2834 do_argfile(eap, i);
2835}
2836
2837/*
2838 * Edit file "argn" of the argument lists.
2839 */
2840 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002841do_argfile(exarg_T *eap, int argn)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002842{
2843 int other;
2844 char_u *p;
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00002845 int old_arg_idx = curwin->w_arg_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002846
2847 if (argn < 0 || argn >= ARGCOUNT)
2848 {
2849 if (ARGCOUNT <= 1)
2850 EMSG(_("E163: There is only one file to edit"));
2851 else if (argn < 0)
2852 EMSG(_("E164: Cannot go before first file"));
2853 else
2854 EMSG(_("E165: Cannot go beyond last file"));
2855 }
2856 else
2857 {
2858 setpcmark();
2859#ifdef FEAT_GUI
2860 need_mouse_correct = TRUE;
2861#endif
2862
Bram Moolenaardf1bdc92006-02-23 21:32:16 +00002863 /* split window or create new tab page first */
2864 if (*eap->cmd == 's' || cmdmod.tab != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002865 {
2866 if (win_split(0, 0) == FAIL)
2867 return;
Bram Moolenaar3368ea22010-09-21 16:56:35 +02002868 RESET_BINDING(curwin);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002869 }
2870 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00002871 {
2872 /*
2873 * if 'hidden' set, only check for changed file when re-editing
2874 * the same buffer
2875 */
2876 other = TRUE;
Bram Moolenaareb44a682017-08-03 22:44:55 +02002877 if (buf_hide(curbuf))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002878 {
2879 p = fix_fname(alist_name(&ARGLIST[argn]));
2880 other = otherfile(p);
2881 vim_free(p);
2882 }
Bram Moolenaareb44a682017-08-03 22:44:55 +02002883 if ((!buf_hide(curbuf) || !other)
Bram Moolenaar45d3b142013-11-09 03:31:51 +01002884 && check_changed(curbuf, CCGD_AW
2885 | (other ? 0 : CCGD_MULTWIN)
2886 | (eap->forceit ? CCGD_FORCEIT : 0)
2887 | CCGD_EXCMD))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002888 return;
2889 }
2890
2891 curwin->w_arg_idx = argn;
Bram Moolenaar4033c552017-09-16 20:54:51 +02002892 if (argn == ARGCOUNT - 1 && curwin->w_alist == &global_alist)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002893 arg_had_last = TRUE;
2894
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00002895 /* Edit the file; always use the last known line number.
2896 * When it fails (e.g. Abort for already edited file) restore the
2897 * argument index. */
2898 if (do_ecmd(0, alist_name(&ARGLIST[curwin->w_arg_idx]), NULL,
Bram Moolenaar071d4272004-06-13 20:20:40 +00002899 eap, ECMD_LAST,
Bram Moolenaareb44a682017-08-03 22:44:55 +02002900 (buf_hide(curwin->w_buffer) ? ECMD_HIDE : 0)
Bram Moolenaar701f7af2008-11-15 13:12:07 +00002901 + (eap->forceit ? ECMD_FORCEIT : 0), curwin) == FAIL)
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00002902 curwin->w_arg_idx = old_arg_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002903 /* like Vi: set the mark where the cursor is in the file. */
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00002904 else if (eap->cmdidx != CMD_argdo)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002905 setmark('\'');
2906 }
2907}
2908
2909/*
2910 * ":next", and commands that behave like it.
2911 */
2912 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002913ex_next(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002914{
2915 int i;
2916
2917 /*
2918 * check for changed buffer now, if this fails the argument list is not
2919 * redefined.
2920 */
Bram Moolenaareb44a682017-08-03 22:44:55 +02002921 if ( buf_hide(curbuf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002922 || eap->cmdidx == CMD_snext
Bram Moolenaar45d3b142013-11-09 03:31:51 +01002923 || !check_changed(curbuf, CCGD_AW
2924 | (eap->forceit ? CCGD_FORCEIT : 0)
2925 | CCGD_EXCMD))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002926 {
2927 if (*eap->arg != NUL) /* redefine file list */
2928 {
Bram Moolenaar32bbd002018-08-31 23:06:22 +02002929 if (do_arglist(eap->arg, AL_SET, 0, TRUE) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002930 return;
2931 i = 0;
2932 }
2933 else
2934 i = curwin->w_arg_idx + (int)eap->line2;
2935 do_argfile(eap, i);
2936 }
2937}
2938
Bram Moolenaar071d4272004-06-13 20:20:40 +00002939/*
2940 * ":argedit"
2941 */
2942 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002943ex_argedit(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002944{
Bram Moolenaar90305c62017-07-16 15:31:17 +02002945 int i = eap->addr_count ? (int)eap->line2 : curwin->w_arg_idx + 1;
Bram Moolenaar46a53df2018-04-24 21:58:51 +02002946 // Whether curbuf will be reused, curbuf->b_ffname will be set.
2947 int curbuf_is_reusable = curbuf_reusable();
Bram Moolenaar071d4272004-06-13 20:20:40 +00002948
Bram Moolenaar32bbd002018-08-31 23:06:22 +02002949 if (do_arglist(eap->arg, AL_ADD, i, TRUE) == FAIL)
Bram Moolenaar90305c62017-07-16 15:31:17 +02002950 return;
2951#ifdef FEAT_TITLE
2952 maketitle();
2953#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002954
Bram Moolenaar46a53df2018-04-24 21:58:51 +02002955 if (curwin->w_arg_idx == 0
2956 && (curbuf->b_ml.ml_flags & ML_EMPTY)
2957 && (curbuf->b_ffname == NULL || curbuf_is_reusable))
Bram Moolenaar90305c62017-07-16 15:31:17 +02002958 i = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002959 /* Edit the argument. */
Bram Moolenaar90305c62017-07-16 15:31:17 +02002960 if (i < ARGCOUNT)
2961 do_argfile(eap, i);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002962}
2963
2964/*
2965 * ":argadd"
2966 */
2967 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002968ex_argadd(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002969{
2970 do_arglist(eap->arg, AL_ADD,
Bram Moolenaar32bbd002018-08-31 23:06:22 +02002971 eap->addr_count > 0 ? (int)eap->line2 : curwin->w_arg_idx + 1,
2972 FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002973#ifdef FEAT_TITLE
2974 maketitle();
2975#endif
2976}
2977
2978/*
2979 * ":argdelete"
2980 */
2981 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01002982ex_argdelete(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002983{
2984 int i;
2985 int n;
2986
2987 if (eap->addr_count > 0)
2988 {
2989 /* ":1,4argdel": Delete all arguments in the range. */
2990 if (eap->line2 > ARGCOUNT)
2991 eap->line2 = ARGCOUNT;
2992 n = eap->line2 - eap->line1 + 1;
Bram Moolenaar69a92fb2017-03-09 15:58:30 +01002993 if (*eap->arg != NUL)
2994 /* Can't have both a range and an argument. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002995 EMSG(_(e_invarg));
Bram Moolenaar69a92fb2017-03-09 15:58:30 +01002996 else if (n <= 0)
2997 {
2998 /* Don't give an error for ":%argdel" if the list is empty. */
2999 if (eap->line1 != 1 || eap->line2 != 0)
3000 EMSG(_(e_invrange));
3001 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003002 else
3003 {
3004 for (i = eap->line1; i <= eap->line2; ++i)
3005 vim_free(ARGLIST[i - 1].ae_fname);
3006 mch_memmove(ARGLIST + eap->line1 - 1, ARGLIST + eap->line2,
3007 (size_t)((ARGCOUNT - eap->line2) * sizeof(aentry_T)));
3008 ALIST(curwin)->al_ga.ga_len -= n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003009 if (curwin->w_arg_idx >= eap->line2)
3010 curwin->w_arg_idx -= n;
3011 else if (curwin->w_arg_idx > eap->line1)
3012 curwin->w_arg_idx = eap->line1;
Bram Moolenaar72defda2016-01-17 18:04:33 +01003013 if (ARGCOUNT == 0)
3014 curwin->w_arg_idx = 0;
3015 else if (curwin->w_arg_idx >= ARGCOUNT)
3016 curwin->w_arg_idx = ARGCOUNT - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003017 }
3018 }
3019 else if (*eap->arg == NUL)
3020 EMSG(_(e_argreq));
3021 else
Bram Moolenaar32bbd002018-08-31 23:06:22 +02003022 do_arglist(eap->arg, AL_DEL, 0, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003023#ifdef FEAT_TITLE
3024 maketitle();
3025#endif
3026}
3027
3028/*
Bram Moolenaaraa23b372015-09-08 18:46:31 +02003029 * ":argdo", ":windo", ":bufdo", ":tabdo", ":cdo", ":ldo", ":cfdo" and ":lfdo"
Bram Moolenaar071d4272004-06-13 20:20:40 +00003030 */
3031 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01003032ex_listdo(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003033{
3034 int i;
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003035 win_T *wp;
3036 tabpage_T *tp;
Bram Moolenaare25bb902015-02-27 20:33:37 +01003037 buf_T *buf = curbuf;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003038 int next_fnum = 0;
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01003039#if defined(FEAT_SYN_HL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003040 char_u *save_ei = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003041#endif
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00003042 char_u *p_shm_save;
Bram Moolenaaraa23b372015-09-08 18:46:31 +02003043#ifdef FEAT_QUICKFIX
Bram Moolenaared84b762015-09-09 22:35:29 +02003044 int qf_size = 0;
Bram Moolenaaraa23b372015-09-08 18:46:31 +02003045 int qf_idx;
3046#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003047
Bram Moolenaar0106e3d2016-02-23 18:55:43 +01003048#ifndef FEAT_QUICKFIX
3049 if (eap->cmdidx == CMD_cdo || eap->cmdidx == CMD_ldo ||
3050 eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo)
3051 {
3052 ex_ni(eap);
3053 return;
3054 }
3055#endif
3056
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01003057#if defined(FEAT_SYN_HL)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00003058 if (eap->cmdidx != CMD_windo && eap->cmdidx != CMD_tabdo)
Bram Moolenaardcaf10e2005-01-21 11:55:25 +00003059 /* Don't do syntax HL autocommands. Skipping the syntax file is a
3060 * great speed improvement. */
3061 save_ei = au_event_disable(",Syntax");
Bram Moolenaar071d4272004-06-13 20:20:40 +00003062#endif
Bram Moolenaar6b1ee342014-08-06 18:17:11 +02003063#ifdef FEAT_CLIPBOARD
3064 start_global_changes();
3065#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003066
3067 if (eap->cmdidx == CMD_windo
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003068 || eap->cmdidx == CMD_tabdo
Bram Moolenaareb44a682017-08-03 22:44:55 +02003069 || buf_hide(curbuf)
Bram Moolenaar45d3b142013-11-09 03:31:51 +01003070 || !check_changed(curbuf, CCGD_AW
3071 | (eap->forceit ? CCGD_FORCEIT : 0)
3072 | CCGD_EXCMD))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003073 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00003074 i = 0;
Bram Moolenaara162bc52015-01-07 16:54:21 +01003075 /* start at the eap->line1 argument/window/buffer */
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003076 wp = firstwin;
3077 tp = first_tabpage;
Bram Moolenaara162bc52015-01-07 16:54:21 +01003078 switch (eap->cmdidx)
3079 {
Bram Moolenaara162bc52015-01-07 16:54:21 +01003080 case CMD_windo:
3081 for ( ; wp != NULL && i + 1 < eap->line1; wp = wp->w_next)
3082 i++;
3083 break;
3084 case CMD_tabdo:
3085 for( ; tp != NULL && i + 1 < eap->line1; tp = tp->tp_next)
3086 i++;
3087 break;
Bram Moolenaara162bc52015-01-07 16:54:21 +01003088 case CMD_argdo:
3089 i = eap->line1 - 1;
3090 break;
Bram Moolenaara162bc52015-01-07 16:54:21 +01003091 default:
3092 break;
3093 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003094 /* set pcmark now */
3095 if (eap->cmdidx == CMD_bufdo)
Bram Moolenaaraa23b372015-09-08 18:46:31 +02003096 {
Bram Moolenaare25bb902015-02-27 20:33:37 +01003097 /* Advance to the first listed buffer after "eap->line1". */
Bram Moolenaaraa23b372015-09-08 18:46:31 +02003098 for (buf = firstbuf; buf != NULL && (buf->b_fnum < eap->line1
Bram Moolenaare25bb902015-02-27 20:33:37 +01003099 || !buf->b_p_bl); buf = buf->b_next)
3100 if (buf->b_fnum > eap->line2)
3101 {
3102 buf = NULL;
3103 break;
3104 }
Bram Moolenaaraa23b372015-09-08 18:46:31 +02003105 if (buf != NULL)
Bram Moolenaare25bb902015-02-27 20:33:37 +01003106 goto_buffer(eap, DOBUF_FIRST, FORWARD, buf->b_fnum);
Bram Moolenaaraa23b372015-09-08 18:46:31 +02003107 }
3108#ifdef FEAT_QUICKFIX
3109 else if (eap->cmdidx == CMD_cdo || eap->cmdidx == CMD_ldo
3110 || eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo)
3111 {
3112 qf_size = qf_get_size(eap);
3113 if (qf_size <= 0 || eap->line1 > qf_size)
3114 buf = NULL;
3115 else
3116 {
3117 ex_cc(eap);
3118
3119 buf = curbuf;
3120 i = eap->line1 - 1;
3121 if (eap->addr_count <= 0)
3122 /* default is all the quickfix/location list entries */
3123 eap->line2 = qf_size;
3124 }
3125 }
3126#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003127 else
3128 setpcmark();
3129 listcmd_busy = TRUE; /* avoids setting pcmark below */
3130
Bram Moolenaare25bb902015-02-27 20:33:37 +01003131 while (!got_int && buf != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003132 {
3133 if (eap->cmdidx == CMD_argdo)
3134 {
3135 /* go to argument "i" */
3136 if (i == ARGCOUNT)
3137 break;
3138 /* Don't call do_argfile() when already there, it will try
3139 * reloading the file. */
Bram Moolenaard4755bb2004-09-02 19:12:26 +00003140 if (curwin->w_arg_idx != i || !editing_arg_idx(curwin))
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00003141 {
3142 /* Clear 'shm' to avoid that the file message overwrites
3143 * any output from the command. */
3144 p_shm_save = vim_strsave(p_shm);
3145 set_option_value((char_u *)"shm", 0L, (char_u *)"", 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003146 do_argfile(eap, i);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00003147 set_option_value((char_u *)"shm", 0L, p_shm_save, 0);
3148 vim_free(p_shm_save);
3149 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003150 if (curwin->w_arg_idx != i)
3151 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003152 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003153 else if (eap->cmdidx == CMD_windo)
3154 {
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003155 /* go to window "wp" */
3156 if (!win_valid(wp))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003157 break;
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003158 win_goto(wp);
Bram Moolenaar41423242007-05-03 20:11:13 +00003159 if (curwin != wp)
3160 break; /* something must be wrong */
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003161 wp = curwin->w_next;
3162 }
3163 else if (eap->cmdidx == CMD_tabdo)
3164 {
3165 /* go to window "tp" */
3166 if (!valid_tabpage(tp))
3167 break;
Bram Moolenaar49e649f2013-05-06 04:50:35 +02003168 goto_tabpage_tp(tp, TRUE, TRUE);
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003169 tp = tp->tp_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003170 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003171 else if (eap->cmdidx == CMD_bufdo)
3172 {
3173 /* Remember the number of the next listed buffer, in case
3174 * ":bwipe" is used or autocommands do something strange. */
3175 next_fnum = -1;
3176 for (buf = curbuf->b_next; buf != NULL; buf = buf->b_next)
3177 if (buf->b_p_bl)
3178 {
3179 next_fnum = buf->b_fnum;
3180 break;
3181 }
3182 }
3183
Bram Moolenaara162bc52015-01-07 16:54:21 +01003184 ++i;
3185
Bram Moolenaar071d4272004-06-13 20:20:40 +00003186 /* execute the command */
3187 do_cmdline(eap->arg, eap->getline, eap->cookie,
3188 DOCMD_VERBOSE + DOCMD_NOWAIT);
3189
3190 if (eap->cmdidx == CMD_bufdo)
3191 {
3192 /* Done? */
Bram Moolenaara162bc52015-01-07 16:54:21 +01003193 if (next_fnum < 0 || next_fnum > eap->line2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003194 break;
3195 /* Check if the buffer still exists. */
Bram Moolenaar29323592016-07-24 22:04:11 +02003196 FOR_ALL_BUFFERS(buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003197 if (buf->b_fnum == next_fnum)
3198 break;
3199 if (buf == NULL)
3200 break;
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00003201
3202 /* Go to the next buffer. Clear 'shm' to avoid that the file
3203 * message overwrites any output from the command. */
3204 p_shm_save = vim_strsave(p_shm);
3205 set_option_value((char_u *)"shm", 0L, (char_u *)"", 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003206 goto_buffer(eap, DOBUF_FIRST, FORWARD, next_fnum);
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00003207 set_option_value((char_u *)"shm", 0L, p_shm_save, 0);
3208 vim_free(p_shm_save);
3209
Bram Moolenaaraa23b372015-09-08 18:46:31 +02003210 /* If autocommands took us elsewhere, quit here. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003211 if (curbuf->b_fnum != next_fnum)
3212 break;
3213 }
3214
Bram Moolenaaraa23b372015-09-08 18:46:31 +02003215#ifdef FEAT_QUICKFIX
3216 if (eap->cmdidx == CMD_cdo || eap->cmdidx == CMD_ldo
3217 || eap->cmdidx == CMD_cfdo || eap->cmdidx == CMD_lfdo)
3218 {
3219 if (i >= qf_size || i >= eap->line2)
3220 break;
3221
3222 qf_idx = qf_get_cur_idx(eap);
3223
3224 ex_cnext(eap);
3225
3226 /* If jumping to the next quickfix entry fails, quit here */
3227 if (qf_get_cur_idx(eap) == qf_idx)
3228 break;
3229 }
3230#endif
3231
Bram Moolenaar071d4272004-06-13 20:20:40 +00003232 if (eap->cmdidx == CMD_windo)
3233 {
3234 validate_cursor(); /* cursor may have moved */
Bram Moolenaar8a3bb562018-03-04 20:14:14 +01003235
Bram Moolenaar071d4272004-06-13 20:20:40 +00003236 /* required when 'scrollbind' has been set */
3237 if (curwin->w_p_scb)
3238 do_check_scrollbind(TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003239 }
Bram Moolenaara162bc52015-01-07 16:54:21 +01003240
Bram Moolenaara162bc52015-01-07 16:54:21 +01003241 if (eap->cmdidx == CMD_windo || eap->cmdidx == CMD_tabdo)
3242 if (i+1 > eap->line2)
3243 break;
Bram Moolenaara162bc52015-01-07 16:54:21 +01003244 if (eap->cmdidx == CMD_argdo && i >= eap->line2)
3245 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003246 }
3247 listcmd_busy = FALSE;
3248 }
3249
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01003250#if defined(FEAT_SYN_HL)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00003251 if (save_ei != NULL)
3252 {
3253 au_event_restore(save_ei);
3254 apply_autocmds(EVENT_SYNTAX, curbuf->b_p_syn,
3255 curbuf->b_fname, TRUE, curbuf);
3256 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003257#endif
Bram Moolenaar6b1ee342014-08-06 18:17:11 +02003258#ifdef FEAT_CLIPBOARD
3259 end_global_changes();
3260#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003261}
3262
3263/*
3264 * Add files[count] to the arglist of the current window after arg "after".
3265 * The file names in files[count] must have been allocated and are taken over.
3266 * Files[] itself is not taken over.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003267 */
Bram Moolenaar32bbd002018-08-31 23:06:22 +02003268 static void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01003269alist_add_list(
3270 int count,
3271 char_u **files,
Bram Moolenaar32bbd002018-08-31 23:06:22 +02003272 int after, // where to add: 0 = before first one
3273 int will_edit) // will edit adding argument
Bram Moolenaar071d4272004-06-13 20:20:40 +00003274{
3275 int i;
Bram Moolenaara24f0a52016-01-17 19:39:00 +01003276 int old_argcount = ARGCOUNT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003277
3278 if (ga_grow(&ALIST(curwin)->al_ga, count) == OK)
3279 {
3280 if (after < 0)
3281 after = 0;
3282 if (after > ARGCOUNT)
3283 after = ARGCOUNT;
3284 if (after < ARGCOUNT)
3285 mch_memmove(&(ARGLIST[after + count]), &(ARGLIST[after]),
3286 (ARGCOUNT - after) * sizeof(aentry_T));
3287 for (i = 0; i < count; ++i)
3288 {
Bram Moolenaar32bbd002018-08-31 23:06:22 +02003289 int flags = BLN_LISTED | (will_edit ? BLN_CURBUF : 0);
3290
Bram Moolenaar071d4272004-06-13 20:20:40 +00003291 ARGLIST[after + i].ae_fname = files[i];
Bram Moolenaar32bbd002018-08-31 23:06:22 +02003292 ARGLIST[after + i].ae_fnum = buflist_add(files[i], flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003293 }
3294 ALIST(curwin)->al_ga.ga_len += count;
Bram Moolenaara24f0a52016-01-17 19:39:00 +01003295 if (old_argcount > 0 && curwin->w_arg_idx >= after)
3296 curwin->w_arg_idx += count;
Bram Moolenaar32bbd002018-08-31 23:06:22 +02003297 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003298 }
3299
3300 for (i = 0; i < count; ++i)
3301 vim_free(files[i]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003302}
3303
Bram Moolenaarcd43eff2018-03-29 15:55:38 +02003304#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
3305/*
3306 * Function given to ExpandGeneric() to obtain the possible arguments of the
3307 * argedit and argdelete commands.
3308 */
3309 char_u *
3310get_arglist_name(expand_T *xp UNUSED, int idx)
3311{
3312 if (idx >= ARGCOUNT)
3313 return NULL;
3314
3315 return alist_name(&ARGLIST[idx]);
3316}
3317#endif
3318
Bram Moolenaar0c72fe42018-03-29 16:04:08 +02003319
Bram Moolenaar071d4272004-06-13 20:20:40 +00003320#ifdef FEAT_EVAL
3321/*
3322 * ":compiler[!] {name}"
3323 */
3324 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01003325ex_compiler(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003326{
3327 char_u *buf;
3328 char_u *old_cur_comp = NULL;
3329 char_u *p;
3330
3331 if (*eap->arg == NUL)
3332 {
3333 /* List all compiler scripts. */
3334 do_cmdline_cmd((char_u *)"echo globpath(&rtp, 'compiler/*.vim')");
3335 /* ) keep the indenter happy... */
3336 }
3337 else
3338 {
3339 buf = alloc((unsigned)(STRLEN(eap->arg) + 14));
3340 if (buf != NULL)
3341 {
3342 if (eap->forceit)
3343 {
3344 /* ":compiler! {name}" sets global options */
3345 do_cmdline_cmd((char_u *)
3346 "command -nargs=* CompilerSet set <args>");
3347 }
3348 else
3349 {
3350 /* ":compiler! {name}" sets local options.
3351 * To remain backwards compatible "current_compiler" is always
3352 * used. A user's compiler plugin may set it, the distributed
3353 * plugin will then skip the settings. Afterwards set
Bram Moolenaar3d63e3f2010-01-19 16:13:50 +01003354 * "b:current_compiler" and restore "current_compiler".
3355 * Explicitly prepend "g:" to make it work in a function. */
3356 old_cur_comp = get_var_value((char_u *)"g:current_compiler");
Bram Moolenaar071d4272004-06-13 20:20:40 +00003357 if (old_cur_comp != NULL)
3358 old_cur_comp = vim_strsave(old_cur_comp);
3359 do_cmdline_cmd((char_u *)
3360 "command -nargs=* CompilerSet setlocal <args>");
3361 }
Bram Moolenaar3d63e3f2010-01-19 16:13:50 +01003362 do_unlet((char_u *)"g:current_compiler", TRUE);
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003363 do_unlet((char_u *)"b:current_compiler", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003364
3365 sprintf((char *)buf, "compiler/%s.vim", eap->arg);
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01003366 if (source_runtime(buf, DIP_ALL) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003367 EMSG2(_("E666: compiler not supported: %s"), eap->arg);
3368 vim_free(buf);
3369
3370 do_cmdline_cmd((char_u *)":delcommand CompilerSet");
3371
3372 /* Set "b:current_compiler" from "current_compiler". */
Bram Moolenaar3d63e3f2010-01-19 16:13:50 +01003373 p = get_var_value((char_u *)"g:current_compiler");
Bram Moolenaar071d4272004-06-13 20:20:40 +00003374 if (p != NULL)
3375 set_internal_string_var((char_u *)"b:current_compiler", p);
3376
3377 /* Restore "current_compiler" for ":compiler {name}". */
3378 if (!eap->forceit)
3379 {
3380 if (old_cur_comp != NULL)
3381 {
Bram Moolenaar3d63e3f2010-01-19 16:13:50 +01003382 set_internal_string_var((char_u *)"g:current_compiler",
Bram Moolenaar071d4272004-06-13 20:20:40 +00003383 old_cur_comp);
3384 vim_free(old_cur_comp);
3385 }
3386 else
Bram Moolenaar3d63e3f2010-01-19 16:13:50 +01003387 do_unlet((char_u *)"g:current_compiler", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003388 }
3389 }
3390 }
3391}
3392#endif
3393
3394/*
Bram Moolenaar8dcf2592016-03-12 22:47:14 +01003395 * ":runtime [what] {name}"
Bram Moolenaar071d4272004-06-13 20:20:40 +00003396 */
3397 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01003398ex_runtime(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003399{
Bram Moolenaar8dcf2592016-03-12 22:47:14 +01003400 char_u *arg = eap->arg;
3401 char_u *p = skiptowhite(arg);
3402 int len = (int)(p - arg);
3403 int flags = eap->forceit ? DIP_ALL : 0;
3404
3405 if (STRNCMP(arg, "START", len) == 0)
3406 {
3407 flags += DIP_START + DIP_NORTP;
3408 arg = skipwhite(arg + len);
3409 }
3410 else if (STRNCMP(arg, "OPT", len) == 0)
3411 {
3412 flags += DIP_OPT + DIP_NORTP;
3413 arg = skipwhite(arg + len);
3414 }
3415 else if (STRNCMP(arg, "PACK", len) == 0)
3416 {
3417 flags += DIP_START + DIP_OPT + DIP_NORTP;
3418 arg = skipwhite(arg + len);
3419 }
3420 else if (STRNCMP(arg, "ALL", len) == 0)
3421 {
3422 flags += DIP_START + DIP_OPT;
3423 arg = skipwhite(arg + len);
3424 }
3425
3426 source_runtime(arg, flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003427}
3428
Bram Moolenaar071d4272004-06-13 20:20:40 +00003429 static void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01003430source_callback(char_u *fname, void *cookie UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003431{
Bram Moolenaar910f66f2006-04-05 20:41:53 +00003432 (void)do_source(fname, FALSE, DOSO_NONE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003433}
3434
3435/*
Bram Moolenaarbe82c252016-03-06 14:44:08 +01003436 * Find the file "name" in all directories in "path" and invoke
3437 * "callback(fname, cookie)".
3438 * "name" can contain wildcards.
3439 * When "flags" has DIP_ALL: source all files, otherwise only the first one.
3440 * When "flags" has DIP_DIR: find directories instead of files.
3441 * When "flags" has DIP_ERR: give an error message if there is no match.
3442 *
3443 * return FAIL when no file could be sourced, OK otherwise.
3444 */
Bram Moolenaar6bef5302016-03-12 21:28:26 +01003445 int
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01003446do_in_path(
3447 char_u *path,
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01003448 char_u *name,
Bram Moolenaar91715872016-03-03 17:13:03 +01003449 int flags,
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01003450 void (*callback)(char_u *fname, void *ck),
3451 void *cookie)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003452{
3453 char_u *rtp;
3454 char_u *np;
3455 char_u *buf;
3456 char_u *rtp_copy;
3457 char_u *tail;
3458 int num_files;
3459 char_u **files;
3460 int i;
3461 int did_one = FALSE;
3462#ifdef AMIGA
3463 struct Process *proc = (struct Process *)FindTask(0L);
3464 APTR save_winptr = proc->pr_WindowPtr;
3465
3466 /* Avoid a requester here for a volume that doesn't exist. */
3467 proc->pr_WindowPtr = (APTR)-1L;
3468#endif
3469
3470 /* Make a copy of 'runtimepath'. Invoking the callback may change the
3471 * value. */
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01003472 rtp_copy = vim_strsave(path);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003473 buf = alloc(MAXPATHL);
3474 if (buf != NULL && rtp_copy != NULL)
3475 {
Bram Moolenaarc09a6d62013-06-10 21:27:29 +02003476 if (p_verbose > 1 && name != NULL)
Bram Moolenaar54ee7752005-05-31 22:22:17 +00003477 {
3478 verbose_enter();
Bram Moolenaar555b2802005-05-19 21:08:39 +00003479 smsg((char_u *)_("Searching for \"%s\" in \"%s\""),
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01003480 (char *)name, (char *)path);
Bram Moolenaar54ee7752005-05-31 22:22:17 +00003481 verbose_leave();
3482 }
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00003483
Bram Moolenaar071d4272004-06-13 20:20:40 +00003484 /* Loop over all entries in 'runtimepath'. */
3485 rtp = rtp_copy;
Bram Moolenaar91715872016-03-03 17:13:03 +01003486 while (*rtp != NUL && ((flags & DIP_ALL) || !did_one))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003487 {
Bram Moolenaar66459b72016-08-06 19:01:55 +02003488 size_t buflen;
3489
Bram Moolenaar071d4272004-06-13 20:20:40 +00003490 /* Copy the path from 'runtimepath' to buf[]. */
3491 copy_option_part(&rtp, buf, MAXPATHL, ",");
Bram Moolenaar66459b72016-08-06 19:01:55 +02003492 buflen = STRLEN(buf);
3493
3494 /* Skip after or non-after directories. */
3495 if (flags & (DIP_NOAFTER | DIP_AFTER))
3496 {
3497 int is_after = buflen >= 5
3498 && STRCMP(buf + buflen - 5, "after") == 0;
3499
3500 if ((is_after && (flags & DIP_NOAFTER))
3501 || (!is_after && (flags & DIP_AFTER)))
3502 continue;
3503 }
3504
Bram Moolenaarc09a6d62013-06-10 21:27:29 +02003505 if (name == NULL)
3506 {
3507 (*callback)(buf, (void *) &cookie);
3508 if (!did_one)
3509 did_one = (cookie == NULL);
3510 }
Bram Moolenaar66459b72016-08-06 19:01:55 +02003511 else if (buflen + STRLEN(name) + 2 < MAXPATHL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003512 {
3513 add_pathsep(buf);
3514 tail = buf + STRLEN(buf);
3515
3516 /* Loop over all patterns in "name" */
3517 np = name;
Bram Moolenaar91715872016-03-03 17:13:03 +01003518 while (*np != NUL && ((flags & DIP_ALL) || !did_one))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003519 {
3520 /* Append the pattern from "name" to buf[]. */
3521 copy_option_part(&np, tail, (int)(MAXPATHL - (tail - buf)),
3522 "\t ");
3523
3524 if (p_verbose > 2)
Bram Moolenaar54ee7752005-05-31 22:22:17 +00003525 {
3526 verbose_enter();
Bram Moolenaar555b2802005-05-19 21:08:39 +00003527 smsg((char_u *)_("Searching for \"%s\""), buf);
Bram Moolenaar54ee7752005-05-31 22:22:17 +00003528 verbose_leave();
3529 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003530
3531 /* Expand wildcards, invoke the callback for each match. */
3532 if (gen_expand_wildcards(1, &buf, &num_files, &files,
Bram Moolenaar91715872016-03-03 17:13:03 +01003533 (flags & DIP_DIR) ? EW_DIR : EW_FILE) == OK)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003534 {
3535 for (i = 0; i < num_files; ++i)
3536 {
Bram Moolenaar13fcaaf2005-04-15 21:13:42 +00003537 (*callback)(files[i], cookie);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003538 did_one = TRUE;
Bram Moolenaar91715872016-03-03 17:13:03 +01003539 if (!(flags & DIP_ALL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003540 break;
3541 }
3542 FreeWild(num_files, files);
3543 }
3544 }
3545 }
3546 }
3547 }
3548 vim_free(buf);
3549 vim_free(rtp_copy);
Bram Moolenaarbe82c252016-03-06 14:44:08 +01003550 if (!did_one && name != NULL)
Bram Moolenaar54ee7752005-05-31 22:22:17 +00003551 {
Bram Moolenaarbe82c252016-03-06 14:44:08 +01003552 char *basepath = path == p_rtp ? "runtimepath" : "packpath";
3553
3554 if (flags & DIP_ERR)
3555 EMSG3(_(e_dirnotf), basepath, name);
3556 else if (p_verbose > 0)
3557 {
3558 verbose_enter();
3559 smsg((char_u *)_("not found in '%s': \"%s\""), basepath, name);
3560 verbose_leave();
3561 }
Bram Moolenaar54ee7752005-05-31 22:22:17 +00003562 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003563
3564#ifdef AMIGA
3565 proc->pr_WindowPtr = save_winptr;
3566#endif
3567
3568 return did_one ? OK : FAIL;
3569}
3570
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01003571/*
Bram Moolenaar07ecfa62017-06-27 14:43:55 +02003572 * Find "name" in "path". When found, invoke the callback function for
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01003573 * it: callback(fname, "cookie")
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01003574 * When "flags" has DIP_ALL repeat for all matches, otherwise only the first
3575 * one is used.
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01003576 * Returns OK when at least one match found, FAIL otherwise.
3577 *
Bram Moolenaar07ecfa62017-06-27 14:43:55 +02003578 * If "name" is NULL calls callback for each entry in "path". Cookie is
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01003579 * passed by reference in this case, setting it to NULL indicates that callback
3580 * has done its job.
3581 */
Bram Moolenaar07ecfa62017-06-27 14:43:55 +02003582 static int
3583do_in_path_and_pp(
3584 char_u *path,
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01003585 char_u *name,
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01003586 int flags,
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01003587 void (*callback)(char_u *fname, void *ck),
3588 void *cookie)
3589{
Bram Moolenaar8dcf2592016-03-12 22:47:14 +01003590 int done = FAIL;
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01003591 char_u *s;
3592 int len;
3593 char *start_dir = "pack/*/start/*/%s";
3594 char *opt_dir = "pack/*/opt/*/%s";
3595
Bram Moolenaar8dcf2592016-03-12 22:47:14 +01003596 if ((flags & DIP_NORTP) == 0)
Bram Moolenaar07ecfa62017-06-27 14:43:55 +02003597 done = do_in_path(path, name, flags, callback, cookie);
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01003598
Bram Moolenaar8dcf2592016-03-12 22:47:14 +01003599 if ((done == FAIL || (flags & DIP_ALL)) && (flags & DIP_START))
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01003600 {
Bram Moolenaar1c8b4ed2016-03-17 21:51:03 +01003601 len = (int)(STRLEN(start_dir) + STRLEN(name));
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01003602 s = alloc(len);
3603 if (s == NULL)
3604 return FAIL;
3605 vim_snprintf((char *)s, len, start_dir, name);
3606 done = do_in_path(p_pp, s, flags, callback, cookie);
3607 vim_free(s);
3608 }
3609
Bram Moolenaar8dcf2592016-03-12 22:47:14 +01003610 if ((done == FAIL || (flags & DIP_ALL)) && (flags & DIP_OPT))
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01003611 {
Bram Moolenaar1c8b4ed2016-03-17 21:51:03 +01003612 len = (int)(STRLEN(opt_dir) + STRLEN(name));
Bram Moolenaar7f8989d2016-03-12 22:11:39 +01003613 s = alloc(len);
3614 if (s == NULL)
3615 return FAIL;
3616 vim_snprintf((char *)s, len, opt_dir, name);
3617 done = do_in_path(p_pp, s, flags, callback, cookie);
3618 vim_free(s);
3619 }
3620
3621 return done;
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01003622}
3623
Bram Moolenaar1bdd4262016-03-03 14:23:10 +01003624/*
Bram Moolenaar07ecfa62017-06-27 14:43:55 +02003625 * Just like do_in_path_and_pp(), using 'runtimepath' for "path".
3626 */
3627 int
3628do_in_runtimepath(
3629 char_u *name,
3630 int flags,
3631 void (*callback)(char_u *fname, void *ck),
3632 void *cookie)
3633{
3634 return do_in_path_and_pp(p_rtp, name, flags, callback, cookie);
3635}
3636
3637/*
3638 * Source the file "name" from all directories in 'runtimepath'.
3639 * "name" can contain wildcards.
3640 * When "flags" has DIP_ALL: source all files, otherwise only the first one.
3641 *
3642 * return FAIL when no file could be sourced, OK otherwise.
3643 */
3644 int
3645source_runtime(char_u *name, int flags)
3646{
3647 return source_in_path(p_rtp, name, flags);
3648}
3649
3650/*
3651 * Just like source_runtime(), but use "path" instead of 'runtimepath'.
3652 */
3653 int
3654source_in_path(char_u *path, char_u *name, int flags)
3655{
3656 return do_in_path_and_pp(path, name, flags, source_callback, NULL);
3657}
3658
3659
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01003660#if defined(FEAT_EVAL) || defined(PROTO)
3661
Bram Moolenaar07ecfa62017-06-27 14:43:55 +02003662/*
Bram Moolenaarf3654822016-03-04 22:12:23 +01003663 * Expand wildcards in "pat" and invoke do_source() for each match.
Bram Moolenaar1bdd4262016-03-03 14:23:10 +01003664 */
3665 static void
Bram Moolenaarf3654822016-03-04 22:12:23 +01003666source_all_matches(char_u *pat)
Bram Moolenaar1bdd4262016-03-03 14:23:10 +01003667{
Bram Moolenaarf3654822016-03-04 22:12:23 +01003668 int num_files;
3669 char_u **files;
3670 int i;
Bram Moolenaar1bdd4262016-03-03 14:23:10 +01003671
Bram Moolenaarf3654822016-03-04 22:12:23 +01003672 if (gen_expand_wildcards(1, &pat, &num_files, &files, EW_FILE) == OK)
Bram Moolenaar1bdd4262016-03-03 14:23:10 +01003673 {
Bram Moolenaarf3654822016-03-04 22:12:23 +01003674 for (i = 0; i < num_files; ++i)
3675 (void)do_source(files[i], FALSE, DOSO_NONE);
3676 FreeWild(num_files, files);
Bram Moolenaar1bdd4262016-03-03 14:23:10 +01003677 }
Bram Moolenaar1bdd4262016-03-03 14:23:10 +01003678}
3679
Bram Moolenaar2374faa2018-02-04 17:47:42 +01003680/*
3681 * Add the package directory to 'runtimepath'.
3682 */
3683 static int
3684add_pack_dir_to_rtp(char_u *fname)
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01003685{
Bram Moolenaarf3654822016-03-04 22:12:23 +01003686 char_u *p4, *p3, *p2, *p1, *p;
Bram Moolenaar99396d42018-09-08 18:21:16 +02003687 char_u *entry;
3688 char_u *insp = NULL;
Bram Moolenaar91715872016-03-03 17:13:03 +01003689 int c;
3690 char_u *new_rtp;
3691 int keep;
Bram Moolenaarb0550662016-05-31 21:37:36 +02003692 size_t oldlen;
3693 size_t addlen;
Bram Moolenaar99396d42018-09-08 18:21:16 +02003694 size_t new_rtp_len;
Bram Moolenaar2374faa2018-02-04 17:47:42 +01003695 char_u *afterdir = NULL;
Bram Moolenaarb0550662016-05-31 21:37:36 +02003696 size_t afterlen = 0;
Bram Moolenaar99396d42018-09-08 18:21:16 +02003697 char_u *after_insp = NULL;
Bram Moolenaar2374faa2018-02-04 17:47:42 +01003698 char_u *ffname = NULL;
Bram Moolenaarfef524b2016-07-02 22:07:22 +02003699 size_t fname_len;
Bram Moolenaar2f9e5752017-02-05 16:07:54 +01003700 char_u *buf = NULL;
3701 char_u *rtp_ffname;
3702 int match;
Bram Moolenaar2374faa2018-02-04 17:47:42 +01003703 int retval = FAIL;
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01003704
Bram Moolenaar2374faa2018-02-04 17:47:42 +01003705 p4 = p3 = p2 = p1 = get_past_head(fname);
3706 for (p = p1; *p; MB_PTR_ADV(p))
3707 if (vim_ispathsep_nocolon(*p))
3708 {
3709 p4 = p3; p3 = p2; p2 = p1; p1 = p;
3710 }
3711
3712 /* now we have:
3713 * rtp/pack/name/start/name
3714 * p4 p3 p2 p1
3715 *
3716 * find the part up to "pack" in 'runtimepath' */
3717 c = *++p4; /* append pathsep in order to expand symlink */
3718 *p4 = NUL;
3719 ffname = fix_fname(fname);
3720 *p4 = c;
Bram Moolenaar91715872016-03-03 17:13:03 +01003721 if (ffname == NULL)
Bram Moolenaar2374faa2018-02-04 17:47:42 +01003722 return FAIL;
3723
Bram Moolenaar99396d42018-09-08 18:21:16 +02003724 // Find "ffname" in "p_rtp", ignoring '/' vs '\' differences.
3725 // Also stop at the first "after" directory.
Bram Moolenaar2374faa2018-02-04 17:47:42 +01003726 fname_len = STRLEN(ffname);
Bram Moolenaar2374faa2018-02-04 17:47:42 +01003727 buf = alloc(MAXPATHL);
3728 if (buf == NULL)
3729 goto theend;
Bram Moolenaar99396d42018-09-08 18:21:16 +02003730 for (entry = p_rtp; *entry != NUL; )
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01003731 {
Bram Moolenaar99396d42018-09-08 18:21:16 +02003732 char_u *cur_entry = entry;
3733
3734 copy_option_part(&entry, buf, MAXPATHL, ",");
3735 if (insp == NULL)
3736 {
3737 add_pathsep(buf);
3738 rtp_ffname = fix_fname(buf);
3739 if (rtp_ffname == NULL)
3740 goto theend;
3741 match = vim_fnamencmp(rtp_ffname, ffname, fname_len) == 0;
3742 vim_free(rtp_ffname);
3743 if (match)
3744 // Insert "ffname" after this entry (and comma).
3745 insp = entry;
3746 }
3747
3748 if ((p = (char_u *)strstr((char *)buf, "after")) != NULL
3749 && p > buf
3750 && vim_ispathsep(p[-1])
3751 && (vim_ispathsep(p[5]) || p[5] == NUL || p[5] == ','))
3752 {
3753 if (insp == NULL)
3754 // Did not find "ffname" before the first "after" directory,
3755 // insert it before this entry.
3756 insp = cur_entry;
3757 after_insp = cur_entry;
Bram Moolenaar2374faa2018-02-04 17:47:42 +01003758 break;
Bram Moolenaar99396d42018-09-08 18:21:16 +02003759 }
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01003760 }
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01003761
Bram Moolenaar99396d42018-09-08 18:21:16 +02003762 if (insp == NULL)
3763 // Both "fname" and "after" not found, append at the end.
Bram Moolenaar2374faa2018-02-04 17:47:42 +01003764 insp = p_rtp + STRLEN(p_rtp);
Bram Moolenaar2374faa2018-02-04 17:47:42 +01003765
Bram Moolenaar99396d42018-09-08 18:21:16 +02003766 // check if rtp/pack/name/start/name/after exists
Bram Moolenaar2374faa2018-02-04 17:47:42 +01003767 afterdir = concat_fnames(fname, (char_u *)"after", TRUE);
3768 if (afterdir != NULL && mch_isdir(afterdir))
Bram Moolenaar99396d42018-09-08 18:21:16 +02003769 afterlen = STRLEN(afterdir) + 1; // add one for comma
Bram Moolenaar2374faa2018-02-04 17:47:42 +01003770
3771 oldlen = STRLEN(p_rtp);
Bram Moolenaar99396d42018-09-08 18:21:16 +02003772 addlen = STRLEN(fname) + 1; // add one for comma
3773 new_rtp = alloc((int)(oldlen + addlen + afterlen + 1)); // add one for NUL
Bram Moolenaar2374faa2018-02-04 17:47:42 +01003774 if (new_rtp == NULL)
3775 goto theend;
Bram Moolenaar99396d42018-09-08 18:21:16 +02003776
3777 // We now have 'rtp' parts: {keep}{keep_after}{rest}.
3778 // Create new_rtp, first: {keep},{fname}
Bram Moolenaar2374faa2018-02-04 17:47:42 +01003779 keep = (int)(insp - p_rtp);
3780 mch_memmove(new_rtp, p_rtp, keep);
Bram Moolenaar99396d42018-09-08 18:21:16 +02003781 new_rtp_len = keep;
3782 if (*insp == NUL)
3783 new_rtp[new_rtp_len++] = ','; // add comma before
3784 mch_memmove(new_rtp + new_rtp_len, fname, addlen - 1);
3785 new_rtp_len += addlen - 1;
3786 if (*insp != NUL)
3787 new_rtp[new_rtp_len++] = ','; // add comma after
3788
3789 if (afterlen > 0 && after_insp != NULL)
Bram Moolenaarf3654822016-03-04 22:12:23 +01003790 {
Bram Moolenaar99396d42018-09-08 18:21:16 +02003791 int keep_after = (int)(after_insp - p_rtp);
3792
3793 // Add to new_rtp: {keep},{fname}{keep_after},{afterdir}
3794 mch_memmove(new_rtp + new_rtp_len, p_rtp + keep,
3795 keep_after - keep);
3796 new_rtp_len += keep_after - keep;
3797 mch_memmove(new_rtp + new_rtp_len, afterdir, afterlen - 1);
3798 new_rtp_len += afterlen - 1;
3799 new_rtp[new_rtp_len++] = ',';
3800 keep = keep_after;
3801 }
3802
3803 if (p_rtp[keep] != NUL)
3804 // Append rest: {keep},{fname}{keep_after},{afterdir}{rest}
3805 mch_memmove(new_rtp + new_rtp_len, p_rtp + keep, oldlen - keep + 1);
3806 else
3807 new_rtp[new_rtp_len] = NUL;
3808
3809 if (afterlen > 0 && after_insp == NULL)
3810 {
3811 // Append afterdir when "after" was not found:
3812 // {keep},{fname}{rest},{afterdir}
Bram Moolenaar2374faa2018-02-04 17:47:42 +01003813 STRCAT(new_rtp, ",");
3814 STRCAT(new_rtp, afterdir);
Bram Moolenaarf3654822016-03-04 22:12:23 +01003815 }
Bram Moolenaar99396d42018-09-08 18:21:16 +02003816
Bram Moolenaar2374faa2018-02-04 17:47:42 +01003817 set_option_value((char_u *)"rtp", 0L, new_rtp, 0);
3818 vim_free(new_rtp);
3819 retval = OK;
Bram Moolenaarf3654822016-03-04 22:12:23 +01003820
3821theend:
Bram Moolenaar2f9e5752017-02-05 16:07:54 +01003822 vim_free(buf);
Bram Moolenaarf3654822016-03-04 22:12:23 +01003823 vim_free(ffname);
Bram Moolenaar2374faa2018-02-04 17:47:42 +01003824 vim_free(afterdir);
3825 return retval;
3826}
3827
3828/*
3829 * Load scripts in "plugin" and "ftdetect" directories of the package.
3830 */
3831 static int
3832load_pack_plugin(char_u *fname)
3833{
3834 static char *plugpat = "%s/plugin/**/*.vim";
3835 static char *ftpat = "%s/ftdetect/*.vim";
3836 int len;
3837 char_u *ffname = fix_fname(fname);
3838 char_u *pat = NULL;
3839 int retval = FAIL;
3840
3841 if (ffname == NULL)
3842 return FAIL;
3843 len = (int)STRLEN(ffname) + (int)STRLEN(ftpat);
3844 pat = alloc(len);
3845 if (pat == NULL)
3846 goto theend;
3847 vim_snprintf((char *)pat, len, plugpat, ffname);
3848 source_all_matches(pat);
3849
Bram Moolenaar2374faa2018-02-04 17:47:42 +01003850 {
3851 char_u *cmd = vim_strsave((char_u *)"g:did_load_filetypes");
3852
3853 /* If runtime/filetype.vim wasn't loaded yet, the scripts will be
3854 * found when it loads. */
3855 if (cmd != NULL && eval_to_number(cmd) > 0)
3856 {
3857 do_cmdline_cmd((char_u *)"augroup filetypedetect");
3858 vim_snprintf((char *)pat, len, ftpat, ffname);
3859 source_all_matches(pat);
3860 do_cmdline_cmd((char_u *)"augroup END");
3861 }
3862 vim_free(cmd);
3863 }
Bram Moolenaar2374faa2018-02-04 17:47:42 +01003864 vim_free(pat);
3865 retval = OK;
3866
3867theend:
3868 vim_free(ffname);
3869 return retval;
3870}
3871
3872/* used for "cookie" of add_pack_plugin() */
3873static int APP_ADD_DIR;
3874static int APP_LOAD;
3875static int APP_BOTH;
3876
3877 static void
3878add_pack_plugin(char_u *fname, void *cookie)
3879{
Bram Moolenaarf98a39c2018-04-18 22:18:23 +02003880 if (cookie != &APP_LOAD)
3881 {
3882 char_u *buf = alloc(MAXPATHL);
3883 char_u *p;
3884 int found = FALSE;
3885
3886 if (buf == NULL)
Bram Moolenaar2374faa2018-02-04 17:47:42 +01003887 return;
Bram Moolenaarf98a39c2018-04-18 22:18:23 +02003888 p = p_rtp;
3889 while (*p != NUL)
3890 {
3891 copy_option_part(&p, buf, MAXPATHL, ",");
3892 if (pathcmp((char *)buf, (char *)fname, -1) == 0)
3893 {
3894 found = TRUE;
3895 break;
3896 }
3897 }
3898 vim_free(buf);
3899 if (!found)
3900 /* directory is not yet in 'runtimepath', add it */
3901 if (add_pack_dir_to_rtp(fname) == FAIL)
3902 return;
3903 }
Bram Moolenaar2374faa2018-02-04 17:47:42 +01003904
3905 if (cookie != &APP_ADD_DIR)
3906 load_pack_plugin(fname);
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01003907}
3908
Bram Moolenaarce876aa2017-06-04 17:47:42 +02003909/*
3910 * Add all packages in the "start" directory to 'runtimepath'.
3911 */
3912 void
3913add_pack_start_dirs(void)
3914{
3915 do_in_path(p_pp, (char_u *)"pack/*/start/*", DIP_ALL + DIP_DIR,
3916 add_pack_plugin, &APP_ADD_DIR);
3917}
3918
3919/*
3920 * Load plugins from all packages in the "start" directory.
3921 */
3922 void
3923load_start_packages(void)
3924{
3925 did_source_packages = TRUE;
3926 do_in_path(p_pp, (char_u *)"pack/*/start/*", DIP_ALL + DIP_DIR,
3927 add_pack_plugin, &APP_LOAD);
3928}
Bram Moolenaar2d8f56a2016-03-12 20:34:27 +01003929
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01003930/*
Bram Moolenaar2d8f56a2016-03-12 20:34:27 +01003931 * ":packloadall"
Bram Moolenaarf3654822016-03-04 22:12:23 +01003932 * Find plugins in the package directories and source them.
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01003933 */
3934 void
Bram Moolenaar2d8f56a2016-03-12 20:34:27 +01003935ex_packloadall(exarg_T *eap)
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01003936{
Bram Moolenaarce876aa2017-06-04 17:47:42 +02003937 if (!did_source_packages || eap->forceit)
Bram Moolenaar2d8f56a2016-03-12 20:34:27 +01003938 {
Bram Moolenaar49b27322016-04-05 21:13:00 +02003939 /* First do a round to add all directories to 'runtimepath', then load
3940 * the plugins. This allows for plugins to use an autoload directory
3941 * of another plugin. */
Bram Moolenaarce876aa2017-06-04 17:47:42 +02003942 add_pack_start_dirs();
3943 load_start_packages();
Bram Moolenaar2d8f56a2016-03-12 20:34:27 +01003944 }
Bram Moolenaarf6fee0e2016-02-21 23:02:49 +01003945}
3946
3947/*
Bram Moolenaarf3654822016-03-04 22:12:23 +01003948 * ":packadd[!] {name}"
Bram Moolenaar91715872016-03-03 17:13:03 +01003949 */
3950 void
3951ex_packadd(exarg_T *eap)
3952{
Bram Moolenaar9e1d3992017-12-17 14:26:46 +01003953 static char *plugpat = "pack/*/%s/%s";
Bram Moolenaar91715872016-03-03 17:13:03 +01003954 int len;
3955 char *pat;
Bram Moolenaar9e1d3992017-12-17 14:26:46 +01003956 int round;
3957 int res = OK;
Bram Moolenaar91715872016-03-03 17:13:03 +01003958
Bram Moolenaar9e1d3992017-12-17 14:26:46 +01003959 /* Round 1: use "start", round 2: use "opt". */
3960 for (round = 1; round <= 2; ++round)
3961 {
3962 /* Only look under "start" when loading packages wasn't done yet. */
3963 if (round == 1 && did_source_packages)
3964 continue;
3965
3966 len = (int)STRLEN(plugpat) + (int)STRLEN(eap->arg) + 5;
3967 pat = (char *)alloc(len);
3968 if (pat == NULL)
3969 return;
3970 vim_snprintf(pat, len, plugpat, round == 1 ? "start" : "opt", eap->arg);
3971 /* The first round don't give a "not found" error, in the second round
3972 * only when nothing was found in the first round. */
3973 res = do_in_path(p_pp, (char_u *)pat,
3974 DIP_ALL + DIP_DIR + (round == 2 && res == FAIL ? DIP_ERR : 0),
3975 add_pack_plugin, eap->forceit ? &APP_ADD_DIR : &APP_BOTH);
3976 vim_free(pat);
3977 }
Bram Moolenaar91715872016-03-03 17:13:03 +01003978}
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01003979#endif
Bram Moolenaar91715872016-03-03 17:13:03 +01003980
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01003981#if defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003982/*
3983 * ":options"
3984 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003985 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01003986ex_options(
3987 exarg_T *eap UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003988{
Bram Moolenaarab6c8582017-08-11 17:15:09 +02003989 vim_setenv((char_u *)"OPTWIN_CMD", (char_u *)(cmdmod.tab ? "tab" : ""));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003990 cmd_source((char_u *)SYS_OPTWIN_FILE, NULL);
3991}
3992#endif
3993
Bram Moolenaarf42dd3c2017-01-28 16:06:38 +01003994#if defined(FEAT_PYTHON3) || defined(FEAT_PYTHON) || defined(PROTO)
3995
3996# if (defined(FEAT_PYTHON) && defined(FEAT_PYTHON3)) || defined(PROTO)
3997/*
3998 * Detect Python 3 or 2, and initialize 'pyxversion'.
3999 */
4000 void
4001init_pyxversion(void)
4002{
4003 if (p_pyx == 0)
4004 {
4005 if (python3_enabled(FALSE))
4006 p_pyx = 3;
4007 else if (python_enabled(FALSE))
4008 p_pyx = 2;
4009 }
4010}
4011# endif
4012
4013/*
4014 * Does a file contain one of the following strings at the beginning of any
4015 * line?
4016 * "#!(any string)python2" => returns 2
4017 * "#!(any string)python3" => returns 3
4018 * "# requires python 2.x" => returns 2
4019 * "# requires python 3.x" => returns 3
4020 * otherwise return 0.
4021 */
4022 static int
4023requires_py_version(char_u *filename)
4024{
4025 FILE *file;
4026 int requires_py_version = 0;
4027 int i, lines;
4028
4029 lines = (int)p_mls;
4030 if (lines < 0)
4031 lines = 5;
4032
4033 file = mch_fopen((char *)filename, "r");
4034 if (file != NULL)
4035 {
4036 for (i = 0; i < lines; i++)
4037 {
4038 if (vim_fgets(IObuff, IOSIZE, file))
4039 break;
4040 if (i == 0 && IObuff[0] == '#' && IObuff[1] == '!')
4041 {
4042 /* Check shebang. */
4043 if (strstr((char *)IObuff + 2, "python2") != NULL)
4044 {
4045 requires_py_version = 2;
4046 break;
4047 }
4048 if (strstr((char *)IObuff + 2, "python3") != NULL)
4049 {
4050 requires_py_version = 3;
4051 break;
4052 }
4053 }
4054 IObuff[21] = '\0';
4055 if (STRCMP("# requires python 2.x", IObuff) == 0)
4056 {
4057 requires_py_version = 2;
4058 break;
4059 }
4060 if (STRCMP("# requires python 3.x", IObuff) == 0)
4061 {
4062 requires_py_version = 3;
4063 break;
4064 }
4065 }
4066 fclose(file);
4067 }
4068 return requires_py_version;
4069}
4070
4071
4072/*
4073 * Source a python file using the requested python version.
4074 */
4075 static void
4076source_pyx_file(exarg_T *eap, char_u *fname)
4077{
4078 exarg_T ex;
4079 int v = requires_py_version(fname);
4080
4081# if defined(FEAT_PYTHON) && defined(FEAT_PYTHON3)
4082 init_pyxversion();
4083# endif
4084 if (v == 0)
4085 {
4086# if defined(FEAT_PYTHON) && defined(FEAT_PYTHON3)
4087 /* user didn't choose a preference, 'pyx' is used */
4088 v = p_pyx;
4089# elif defined(FEAT_PYTHON)
4090 v = 2;
4091# elif defined(FEAT_PYTHON3)
4092 v = 3;
4093# endif
4094 }
4095
4096 /*
4097 * now source, if required python version is not supported show
4098 * unobtrusive message.
4099 */
4100 if (eap == NULL)
4101 vim_memset(&ex, 0, sizeof(ex));
4102 else
4103 ex = *eap;
4104 ex.arg = fname;
4105 ex.cmd = (char_u *)(v == 2 ? "pyfile" : "pyfile3");
4106
4107 if (v == 2)
4108 {
4109# ifdef FEAT_PYTHON
4110 ex_pyfile(&ex);
4111# else
4112 vim_snprintf((char *)IObuff, IOSIZE,
4113 _("W20: Required python version 2.x not supported, ignoring file: %s"),
4114 fname);
4115 MSG(IObuff);
4116# endif
4117 return;
4118 }
4119 else
4120 {
4121# ifdef FEAT_PYTHON3
4122 ex_py3file(&ex);
4123# else
4124 vim_snprintf((char *)IObuff, IOSIZE,
4125 _("W21: Required python version 3.x not supported, ignoring file: %s"),
4126 fname);
4127 MSG(IObuff);
4128# endif
4129 return;
4130 }
4131}
4132
4133/*
4134 * ":pyxfile {fname}"
4135 */
4136 void
4137ex_pyxfile(exarg_T *eap)
4138{
4139 source_pyx_file(eap, eap->arg);
4140}
4141
4142/*
4143 * ":pyx"
4144 */
4145 void
4146ex_pyx(exarg_T *eap)
4147{
4148# if defined(FEAT_PYTHON) && defined(FEAT_PYTHON3)
4149 init_pyxversion();
4150 if (p_pyx == 2)
4151 ex_python(eap);
4152 else
4153 ex_py3(eap);
4154# elif defined(FEAT_PYTHON)
4155 ex_python(eap);
4156# elif defined(FEAT_PYTHON3)
4157 ex_py3(eap);
4158# endif
4159}
4160
4161/*
4162 * ":pyxdo"
4163 */
4164 void
4165ex_pyxdo(exarg_T *eap)
4166{
4167# if defined(FEAT_PYTHON) && defined(FEAT_PYTHON3)
4168 init_pyxversion();
4169 if (p_pyx == 2)
4170 ex_pydo(eap);
4171 else
4172 ex_py3do(eap);
4173# elif defined(FEAT_PYTHON)
4174 ex_pydo(eap);
4175# elif defined(FEAT_PYTHON3)
4176 ex_py3do(eap);
4177# endif
4178}
4179
4180#endif
4181
Bram Moolenaar071d4272004-06-13 20:20:40 +00004182/*
4183 * ":source {fname}"
4184 */
4185 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01004186ex_source(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004187{
4188#ifdef FEAT_BROWSE
4189 if (cmdmod.browse)
4190 {
4191 char_u *fname = NULL;
4192
Bram Moolenaar7b0294c2004-10-11 10:16:09 +00004193 fname = do_browse(0, (char_u *)_("Source Vim script"), eap->arg,
Bram Moolenaarc36651b2018-04-29 12:22:56 +02004194 NULL, NULL,
4195 (char_u *)_(BROWSE_FILTER_MACROS), NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004196 if (fname != NULL)
4197 {
4198 cmd_source(fname, eap);
4199 vim_free(fname);
4200 }
4201 }
4202 else
4203#endif
4204 cmd_source(eap->arg, eap);
4205}
4206
4207 static void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01004208cmd_source(char_u *fname, exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004209{
4210 if (*fname == NUL)
4211 EMSG(_(e_argreq));
4212
Bram Moolenaar071d4272004-06-13 20:20:40 +00004213 else if (eap != NULL && eap->forceit)
Bram Moolenaar84a05ac2013-05-06 04:24:17 +02004214 /* ":source!": read Normal mode commands
Bram Moolenaar1f35bf92006-03-07 22:38:47 +00004215 * Need to execute the commands directly. This is required at least
4216 * for:
Bram Moolenaar071d4272004-06-13 20:20:40 +00004217 * - ":g" command busy
4218 * - after ":argdo", ":windo" or ":bufdo"
4219 * - another command follows
4220 * - inside a loop
4221 */
4222 openscript(fname, global_busy || listcmd_busy || eap->nextcmd != NULL
4223#ifdef FEAT_EVAL
4224 || eap->cstack->cs_idx >= 0
4225#endif
4226 );
4227
4228 /* ":source" read ex commands */
Bram Moolenaar910f66f2006-04-05 20:41:53 +00004229 else if (do_source(fname, FALSE, DOSO_NONE) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004230 EMSG2(_(e_notopen), fname);
4231}
4232
4233/*
4234 * ":source" and associated commands.
4235 */
4236/*
4237 * Structure used to store info for each sourced file.
4238 * It is shared between do_source() and getsourceline().
4239 * This is required, because it needs to be handed to do_cmdline() and
4240 * sourcing can be done recursively.
4241 */
4242struct source_cookie
4243{
4244 FILE *fp; /* opened file for sourcing */
4245 char_u *nextline; /* if not NULL: line that was read ahead */
4246 int finished; /* ":finish" used */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004247#if defined(USE_CRNL) || defined(USE_CR)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004248 int fileformat; /* EOL_UNKNOWN, EOL_UNIX or EOL_DOS */
4249 int error; /* TRUE if LF found after CR-LF */
4250#endif
4251#ifdef FEAT_EVAL
4252 linenr_T breakpoint; /* next line with breakpoint or zero */
4253 char_u *fname; /* name of sourced file */
4254 int dbg_tick; /* debug_tick when breakpoint was set */
4255 int level; /* top nesting level of sourced file */
4256#endif
4257#ifdef FEAT_MBYTE
4258 vimconv_T conv; /* type of conversion */
4259#endif
4260};
4261
4262#ifdef FEAT_EVAL
4263/*
4264 * Return the address holding the next breakpoint line for a source cookie.
4265 */
4266 linenr_T *
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01004267source_breakpoint(void *cookie)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004268{
4269 return &((struct source_cookie *)cookie)->breakpoint;
4270}
4271
4272/*
4273 * Return the address holding the debug tick for a source cookie.
4274 */
4275 int *
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01004276source_dbg_tick(void *cookie)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004277{
4278 return &((struct source_cookie *)cookie)->dbg_tick;
4279}
4280
4281/*
4282 * Return the nesting level for a source cookie.
4283 */
4284 int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01004285source_level(void *cookie)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004286{
4287 return ((struct source_cookie *)cookie)->level;
4288}
4289#endif
4290
Bram Moolenaarf28dbce2016-01-29 22:03:47 +01004291static char_u *get_one_sourceline(struct source_cookie *sp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004292
Bram Moolenaar6b29b0e2010-01-19 16:22:03 +01004293#if (defined(WIN32) && defined(FEAT_CSCOPE)) || defined(HAVE_FD_CLOEXEC)
4294# define USE_FOPEN_NOINH
Bram Moolenaar071d4272004-06-13 20:20:40 +00004295/*
4296 * Special function to open a file without handle inheritance.
Bram Moolenaar6b29b0e2010-01-19 16:22:03 +01004297 * When possible the handle is closed on exec().
Bram Moolenaar071d4272004-06-13 20:20:40 +00004298 */
4299 static FILE *
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01004300fopen_noinh_readbin(char *filename)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004301{
Bram Moolenaar6b29b0e2010-01-19 16:22:03 +01004302# ifdef WIN32
Bram Moolenaar8d8ef0b2010-01-20 21:41:47 +01004303 int fd_tmp = mch_open(filename, O_RDONLY | O_BINARY | O_NOINHERIT, 0);
4304# else
4305 int fd_tmp = mch_open(filename, O_RDONLY, 0);
Bram Moolenaar6b29b0e2010-01-19 16:22:03 +01004306# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004307
4308 if (fd_tmp == -1)
4309 return NULL;
Bram Moolenaar6b29b0e2010-01-19 16:22:03 +01004310
4311# ifdef HAVE_FD_CLOEXEC
4312 {
4313 int fdflags = fcntl(fd_tmp, F_GETFD);
4314 if (fdflags >= 0 && (fdflags & FD_CLOEXEC) == 0)
Bram Moolenaarcde88542015-08-11 19:14:00 +02004315 (void)fcntl(fd_tmp, F_SETFD, fdflags | FD_CLOEXEC);
Bram Moolenaar6b29b0e2010-01-19 16:22:03 +01004316 }
4317# endif
4318
Bram Moolenaar071d4272004-06-13 20:20:40 +00004319 return fdopen(fd_tmp, READBIN);
4320}
4321#endif
4322
4323
4324/*
4325 * do_source: Read the file "fname" and execute its lines as EX commands.
4326 *
4327 * This function may be called recursively!
4328 *
4329 * return FAIL if file could not be opened, OK otherwise
4330 */
4331 int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01004332do_source(
4333 char_u *fname,
4334 int check_other, /* check for .vimrc and _vimrc */
4335 int is_vimrc) /* DOSO_ value */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004336{
4337 struct source_cookie cookie;
4338 char_u *save_sourcing_name;
4339 linenr_T save_sourcing_lnum;
4340 char_u *p;
4341 char_u *fname_exp;
Bram Moolenaar73881402009-02-04 16:50:47 +00004342 char_u *firstline = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004343 int retval = FAIL;
4344#ifdef FEAT_EVAL
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02004345 sctx_T save_current_sctx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004346 static scid_T last_current_SID = 0;
Bram Moolenaarded5f1b2018-11-10 17:33:29 +01004347 static int last_current_SID_seq = 0;
Bram Moolenaar27e80c82018-10-14 21:41:01 +02004348 funccal_entry_T funccalp_entry;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004349 int save_debug_break_level = debug_break_level;
Bram Moolenaar05159a02005-02-26 23:04:13 +00004350 scriptitem_T *si = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004351# ifdef UNIX
Bram Moolenaar8767f522016-07-01 17:17:39 +02004352 stat_T st;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004353 int stat_ok;
4354# endif
4355#endif
4356#ifdef STARTUPTIME
4357 struct timeval tv_rel;
4358 struct timeval tv_start;
4359#endif
Bram Moolenaar05159a02005-02-26 23:04:13 +00004360#ifdef FEAT_PROFILE
4361 proftime_T wait_start;
4362#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004363
Bram Moolenaar071d4272004-06-13 20:20:40 +00004364 p = expand_env_save(fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004365 if (p == NULL)
4366 return retval;
4367 fname_exp = fix_fname(p);
4368 vim_free(p);
4369 if (fname_exp == NULL)
4370 return retval;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004371 if (mch_isdir(fname_exp))
4372 {
Bram Moolenaar555b2802005-05-19 21:08:39 +00004373 smsg((char_u *)_("Cannot source a directory: \"%s\""), fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004374 goto theend;
4375 }
4376
Bram Moolenaar8dd1aa52007-01-16 20:33:19 +00004377 /* Apply SourceCmd autocommands, they should get the file and source it. */
4378 if (has_autocmd(EVENT_SOURCECMD, fname_exp, NULL)
4379 && apply_autocmds(EVENT_SOURCECMD, fname_exp, fname_exp,
4380 FALSE, curbuf))
Bram Moolenaarf33943e2008-01-15 21:17:30 +00004381 {
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01004382#ifdef FEAT_EVAL
Bram Moolenaarf33943e2008-01-15 21:17:30 +00004383 retval = aborting() ? FAIL : OK;
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01004384#else
Bram Moolenaarf33943e2008-01-15 21:17:30 +00004385 retval = OK;
Bram Moolenaarf2bd8ef2018-03-04 18:08:14 +01004386#endif
Bram Moolenaarf33943e2008-01-15 21:17:30 +00004387 goto theend;
4388 }
Bram Moolenaar8dd1aa52007-01-16 20:33:19 +00004389
4390 /* Apply SourcePre autocommands, they may get the file. */
Bram Moolenaar1f35bf92006-03-07 22:38:47 +00004391 apply_autocmds(EVENT_SOURCEPRE, fname_exp, fname_exp, FALSE, curbuf);
Bram Moolenaar1f35bf92006-03-07 22:38:47 +00004392
Bram Moolenaar6b29b0e2010-01-19 16:22:03 +01004393#ifdef USE_FOPEN_NOINH
Bram Moolenaar071d4272004-06-13 20:20:40 +00004394 cookie.fp = fopen_noinh_readbin((char *)fname_exp);
4395#else
4396 cookie.fp = mch_fopen((char *)fname_exp, READBIN);
4397#endif
4398 if (cookie.fp == NULL && check_other)
4399 {
4400 /*
4401 * Try again, replacing file name ".vimrc" by "_vimrc" or vice versa,
4402 * and ".exrc" by "_exrc" or vice versa.
4403 */
4404 p = gettail(fname_exp);
4405 if ((*p == '.' || *p == '_')
4406 && (STRICMP(p + 1, "vimrc") == 0
4407 || STRICMP(p + 1, "gvimrc") == 0
4408 || STRICMP(p + 1, "exrc") == 0))
4409 {
4410 if (*p == '_')
4411 *p = '.';
4412 else
4413 *p = '_';
Bram Moolenaar6b29b0e2010-01-19 16:22:03 +01004414#ifdef USE_FOPEN_NOINH
Bram Moolenaar071d4272004-06-13 20:20:40 +00004415 cookie.fp = fopen_noinh_readbin((char *)fname_exp);
4416#else
4417 cookie.fp = mch_fopen((char *)fname_exp, READBIN);
4418#endif
4419 }
4420 }
4421
4422 if (cookie.fp == NULL)
4423 {
4424 if (p_verbose > 0)
4425 {
Bram Moolenaar54ee7752005-05-31 22:22:17 +00004426 verbose_enter();
Bram Moolenaar071d4272004-06-13 20:20:40 +00004427 if (sourcing_name == NULL)
Bram Moolenaar555b2802005-05-19 21:08:39 +00004428 smsg((char_u *)_("could not source \"%s\""), fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004429 else
4430 smsg((char_u *)_("line %ld: could not source \"%s\""),
Bram Moolenaar555b2802005-05-19 21:08:39 +00004431 sourcing_lnum, fname);
Bram Moolenaar54ee7752005-05-31 22:22:17 +00004432 verbose_leave();
Bram Moolenaar071d4272004-06-13 20:20:40 +00004433 }
4434 goto theend;
4435 }
4436
4437 /*
4438 * The file exists.
4439 * - In verbose mode, give a message.
4440 * - For a vimrc file, may want to set 'compatible', call vimrc_found().
4441 */
4442 if (p_verbose > 1)
4443 {
Bram Moolenaar54ee7752005-05-31 22:22:17 +00004444 verbose_enter();
Bram Moolenaar071d4272004-06-13 20:20:40 +00004445 if (sourcing_name == NULL)
Bram Moolenaar555b2802005-05-19 21:08:39 +00004446 smsg((char_u *)_("sourcing \"%s\""), fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004447 else
4448 smsg((char_u *)_("line %ld: sourcing \"%s\""),
Bram Moolenaar555b2802005-05-19 21:08:39 +00004449 sourcing_lnum, fname);
Bram Moolenaar54ee7752005-05-31 22:22:17 +00004450 verbose_leave();
Bram Moolenaar071d4272004-06-13 20:20:40 +00004451 }
Bram Moolenaar910f66f2006-04-05 20:41:53 +00004452 if (is_vimrc == DOSO_VIMRC)
4453 vimrc_found(fname_exp, (char_u *)"MYVIMRC");
4454 else if (is_vimrc == DOSO_GVIMRC)
4455 vimrc_found(fname_exp, (char_u *)"MYGVIMRC");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004456
4457#ifdef USE_CRNL
4458 /* If no automatic file format: Set default to CR-NL. */
4459 if (*p_ffs == NUL)
4460 cookie.fileformat = EOL_DOS;
4461 else
4462 cookie.fileformat = EOL_UNKNOWN;
4463 cookie.error = FALSE;
4464#endif
4465
4466#ifdef USE_CR
4467 /* If no automatic file format: Set default to CR. */
4468 if (*p_ffs == NUL)
4469 cookie.fileformat = EOL_MAC;
4470 else
4471 cookie.fileformat = EOL_UNKNOWN;
4472 cookie.error = FALSE;
4473#endif
4474
4475 cookie.nextline = NULL;
4476 cookie.finished = FALSE;
4477
4478#ifdef FEAT_EVAL
4479 /*
4480 * Check if this script has a breakpoint.
4481 */
4482 cookie.breakpoint = dbg_find_breakpoint(TRUE, fname_exp, (linenr_T)0);
4483 cookie.fname = fname_exp;
4484 cookie.dbg_tick = debug_tick;
4485
4486 cookie.level = ex_nesting_level;
4487#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004488
4489 /*
4490 * Keep the sourcing name/lnum, for recursive calls.
4491 */
4492 save_sourcing_name = sourcing_name;
4493 sourcing_name = fname_exp;
4494 save_sourcing_lnum = sourcing_lnum;
4495 sourcing_lnum = 0;
4496
4497#ifdef STARTUPTIME
Bram Moolenaarc4e41982010-01-19 16:31:47 +01004498 if (time_fd != NULL)
4499 time_push(&tv_rel, &tv_start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004500#endif
4501
4502#ifdef FEAT_EVAL
Bram Moolenaar05159a02005-02-26 23:04:13 +00004503# ifdef FEAT_PROFILE
Bram Moolenaar9b2200a2006-03-20 21:55:45 +00004504 if (do_profiling == PROF_YES)
Bram Moolenaar05159a02005-02-26 23:04:13 +00004505 prof_child_enter(&wait_start); /* entering a child now */
4506# endif
4507
4508 /* Don't use local function variables, if called from a function.
4509 * Also starts profiling timer for nested script. */
Bram Moolenaar27e80c82018-10-14 21:41:01 +02004510 save_funccal(&funccalp_entry);
Bram Moolenaar05159a02005-02-26 23:04:13 +00004511
Bram Moolenaarded5f1b2018-11-10 17:33:29 +01004512 // Check if this script was sourced before to finds its SID.
4513 // If it's new, generate a new SID.
4514 // Always use a new sequence number.
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02004515 save_current_sctx = current_sctx;
Bram Moolenaarded5f1b2018-11-10 17:33:29 +01004516 current_sctx.sc_seq = ++last_current_SID_seq;
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02004517 current_sctx.sc_lnum = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004518# ifdef UNIX
4519 stat_ok = (mch_stat((char *)fname_exp, &st) >= 0);
4520# endif
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02004521 for (current_sctx.sc_sid = script_items.ga_len; current_sctx.sc_sid > 0;
4522 --current_sctx.sc_sid)
Bram Moolenaar05159a02005-02-26 23:04:13 +00004523 {
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02004524 si = &SCRIPT_ITEM(current_sctx.sc_sid);
Bram Moolenaar05159a02005-02-26 23:04:13 +00004525 if (si->sn_name != NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00004526 && (
4527# ifdef UNIX
Bram Moolenaar7c626922005-02-07 22:01:03 +00004528 /* Compare dev/ino when possible, it catches symbolic
4529 * links. Also compare file names, the inode may change
4530 * when the file was edited. */
Bram Moolenaarbf0c4522009-05-16 19:16:33 +00004531 ((stat_ok && si->sn_dev_valid)
Bram Moolenaar05159a02005-02-26 23:04:13 +00004532 && (si->sn_dev == st.st_dev
4533 && si->sn_ino == st.st_ino)) ||
Bram Moolenaar071d4272004-06-13 20:20:40 +00004534# endif
Bram Moolenaar05159a02005-02-26 23:04:13 +00004535 fnamecmp(si->sn_name, fname_exp) == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004536 break;
Bram Moolenaar05159a02005-02-26 23:04:13 +00004537 }
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02004538 if (current_sctx.sc_sid == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004539 {
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02004540 current_sctx.sc_sid = ++last_current_SID;
4541 if (ga_grow(&script_items,
4542 (int)(current_sctx.sc_sid - script_items.ga_len)) == FAIL)
Bram Moolenaar05159a02005-02-26 23:04:13 +00004543 goto almosttheend;
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02004544 while (script_items.ga_len < current_sctx.sc_sid)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004545 {
Bram Moolenaar05159a02005-02-26 23:04:13 +00004546 ++script_items.ga_len;
4547 SCRIPT_ITEM(script_items.ga_len).sn_name = NULL;
4548# ifdef FEAT_PROFILE
4549 SCRIPT_ITEM(script_items.ga_len).sn_prof_on = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004550# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004551 }
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02004552 si = &SCRIPT_ITEM(current_sctx.sc_sid);
Bram Moolenaar05159a02005-02-26 23:04:13 +00004553 si->sn_name = fname_exp;
4554 fname_exp = NULL;
4555# ifdef UNIX
4556 if (stat_ok)
4557 {
Bram Moolenaarbf0c4522009-05-16 19:16:33 +00004558 si->sn_dev_valid = TRUE;
Bram Moolenaar05159a02005-02-26 23:04:13 +00004559 si->sn_dev = st.st_dev;
4560 si->sn_ino = st.st_ino;
4561 }
4562 else
Bram Moolenaarbf0c4522009-05-16 19:16:33 +00004563 si->sn_dev_valid = FALSE;
Bram Moolenaar05159a02005-02-26 23:04:13 +00004564# endif
4565
Bram Moolenaar071d4272004-06-13 20:20:40 +00004566 /* Allocate the local script variables to use for this script. */
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02004567 new_script_vars(current_sctx.sc_sid);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004568 }
4569
Bram Moolenaar05159a02005-02-26 23:04:13 +00004570# ifdef FEAT_PROFILE
Bram Moolenaar9b2200a2006-03-20 21:55:45 +00004571 if (do_profiling == PROF_YES)
Bram Moolenaar05159a02005-02-26 23:04:13 +00004572 {
4573 int forceit;
4574
4575 /* Check if we do profiling for this script. */
4576 if (!si->sn_prof_on && has_profiling(TRUE, si->sn_name, &forceit))
4577 {
4578 script_do_profile(si);
4579 si->sn_pr_force = forceit;
4580 }
4581 if (si->sn_prof_on)
4582 {
4583 ++si->sn_pr_count;
4584 profile_start(&si->sn_pr_start);
4585 profile_zero(&si->sn_pr_children);
4586 }
4587 }
4588# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004589#endif
4590
Bram Moolenaar67435d92017-10-19 21:04:37 +02004591#ifdef FEAT_MBYTE
4592 cookie.conv.vc_type = CONV_NONE; /* no conversion */
4593
4594 /* Read the first line so we can check for a UTF-8 BOM. */
4595 firstline = getsourceline(0, (void *)&cookie, 0);
4596 if (firstline != NULL && STRLEN(firstline) >= 3 && firstline[0] == 0xef
4597 && firstline[1] == 0xbb && firstline[2] == 0xbf)
4598 {
4599 /* Found BOM; setup conversion, skip over BOM and recode the line. */
4600 convert_setup(&cookie.conv, (char_u *)"utf-8", p_enc);
4601 p = string_convert(&cookie.conv, firstline + 3, NULL);
4602 if (p == NULL)
4603 p = vim_strsave(firstline + 3);
4604 if (p != NULL)
4605 {
4606 vim_free(firstline);
4607 firstline = p;
4608 }
4609 }
4610#endif
4611
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)
4636 EMSG(_(e_interr));
4637 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 Moolenaar555b2802005-05-19 21:08:39 +00004642 smsg((char_u *)_("finished sourcing %s"), fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004643 if (sourcing_name != NULL)
Bram Moolenaar555b2802005-05-19 21:08:39 +00004644 smsg((char_u *)_("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
4656#ifdef FEAT_EVAL
4657 /*
4658 * After a "finish" in debug mode, need to break at first command of next
4659 * sourced file.
4660 */
4661 if (save_debug_break_level > ex_nesting_level
4662 && debug_break_level == ex_nesting_level)
4663 ++debug_break_level;
4664#endif
4665
Bram Moolenaar05159a02005-02-26 23:04:13 +00004666#ifdef FEAT_EVAL
4667almosttheend:
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02004668 current_sctx = save_current_sctx;
Bram Moolenaar27e80c82018-10-14 21:41:01 +02004669 restore_funccal();
Bram Moolenaar05159a02005-02-26 23:04:13 +00004670# ifdef FEAT_PROFILE
Bram Moolenaar9b2200a2006-03-20 21:55:45 +00004671 if (do_profiling == PROF_YES)
Bram Moolenaar05159a02005-02-26 23:04:13 +00004672 prof_child_exit(&wait_start); /* leaving a child now */
4673# endif
4674#endif
4675 fclose(cookie.fp);
4676 vim_free(cookie.nextline);
Bram Moolenaar73881402009-02-04 16:50:47 +00004677 vim_free(firstline);
Bram Moolenaar05159a02005-02-26 23:04:13 +00004678#ifdef FEAT_MBYTE
4679 convert_setup(&cookie.conv, NULL, NULL);
4680#endif
4681
Bram Moolenaar071d4272004-06-13 20:20:40 +00004682theend:
4683 vim_free(fname_exp);
4684 return retval;
4685}
4686
4687#if defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00004688
Bram Moolenaar071d4272004-06-13 20:20:40 +00004689/*
4690 * ":scriptnames"
4691 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004692 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01004693ex_scriptnames(exarg_T *eap UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004694{
4695 int i;
4696
Bram Moolenaar05159a02005-02-26 23:04:13 +00004697 for (i = 1; i <= script_items.ga_len && !got_int; ++i)
4698 if (SCRIPT_ITEM(i).sn_name != NULL)
Bram Moolenaard58ea072011-06-26 04:25:30 +02004699 {
4700 home_replace(NULL, SCRIPT_ITEM(i).sn_name,
4701 NameBuff, MAXPATHL, TRUE);
4702 smsg((char_u *)"%3d: %s", i, NameBuff);
Bram Moolenaar970a1b82012-03-23 18:39:18 +01004703 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004704}
4705
4706# if defined(BACKSLASH_IN_FILENAME) || defined(PROTO)
4707/*
4708 * Fix slashes in the list of script names for 'shellslash'.
4709 */
4710 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01004711scriptnames_slash_adjust(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004712{
4713 int i;
4714
Bram Moolenaar05159a02005-02-26 23:04:13 +00004715 for (i = 1; i <= script_items.ga_len; ++i)
4716 if (SCRIPT_ITEM(i).sn_name != NULL)
4717 slash_adjust(SCRIPT_ITEM(i).sn_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004718}
4719# endif
4720
4721/*
4722 * Get a pointer to a script name. Used for ":verbose set".
4723 */
4724 char_u *
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01004725get_scriptname(scid_T id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004726{
4727 if (id == SID_MODELINE)
Bram Moolenaard1f56e62006-02-22 21:25:37 +00004728 return (char_u *)_("modeline");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004729 if (id == SID_CMDARG)
Bram Moolenaard1f56e62006-02-22 21:25:37 +00004730 return (char_u *)_("--cmd argument");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004731 if (id == SID_CARG)
Bram Moolenaard1f56e62006-02-22 21:25:37 +00004732 return (char_u *)_("-c argument");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004733 if (id == SID_ENV)
Bram Moolenaard1f56e62006-02-22 21:25:37 +00004734 return (char_u *)_("environment variable");
4735 if (id == SID_ERROR)
4736 return (char_u *)_("error handler");
Bram Moolenaar05159a02005-02-26 23:04:13 +00004737 return SCRIPT_ITEM(id).sn_name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004738}
Bram Moolenaar05159a02005-02-26 23:04:13 +00004739
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00004740# if defined(EXITFREE) || defined(PROTO)
4741 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01004742free_scriptnames(void)
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00004743{
4744 int i;
4745
4746 for (i = script_items.ga_len; i > 0; --i)
4747 vim_free(SCRIPT_ITEM(i).sn_name);
4748 ga_clear(&script_items);
4749}
4750# endif
4751
Bram Moolenaar071d4272004-06-13 20:20:40 +00004752#endif
4753
4754#if defined(USE_CR) || defined(PROTO)
4755
4756# if defined(__MSL__) && (__MSL__ >= 22)
4757/*
4758 * Newer version of the Metrowerks library handle DOS and UNIX files
4759 * without help.
4760 * Test with earlier versions, MSL 2.2 is the library supplied with
4761 * Codewarrior Pro 2.
4762 */
4763 char *
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01004764fgets_cr(char *s, int n, FILE *stream)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004765{
4766 return fgets(s, n, stream);
4767}
4768# else
4769/*
4770 * Version of fgets() which also works for lines ending in a <CR> only
4771 * (Macintosh format).
4772 * For older versions of the Metrowerks library.
4773 * At least CodeWarrior 9 needed this code.
4774 */
4775 char *
Bram Moolenaard14e00e2016-01-31 17:30:51 +01004776fgets_cr(char *s, int n, FILE *stream)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004777{
4778 int c = 0;
4779 int char_read = 0;
4780
4781 while (!feof(stream) && c != '\r' && c != '\n' && char_read < n - 1)
4782 {
4783 c = fgetc(stream);
4784 s[char_read++] = c;
4785 /* If the file is in DOS format, we need to skip a NL after a CR. I
4786 * thought it was the other way around, but this appears to work... */
4787 if (c == '\n')
4788 {
4789 c = fgetc(stream);
4790 if (c != '\r')
4791 ungetc(c, stream);
4792 }
4793 }
4794
4795 s[char_read] = 0;
4796 if (char_read == 0)
4797 return NULL;
4798
4799 if (feof(stream) && char_read == 1)
4800 return NULL;
4801
4802 return s;
4803}
4804# endif
4805#endif
4806
4807/*
4808 * Get one full line from a sourced file.
4809 * Called by do_cmdline() when it's called from do_source().
4810 *
4811 * Return a pointer to the line in allocated memory.
4812 * Return NULL for end-of-file or some error.
4813 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004814 char_u *
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01004815getsourceline(int c UNUSED, void *cookie, int indent UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004816{
4817 struct source_cookie *sp = (struct source_cookie *)cookie;
4818 char_u *line;
Bram Moolenaarc047b9a2012-02-11 20:40:55 +01004819 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004820
4821#ifdef FEAT_EVAL
4822 /* If breakpoints have been added/deleted need to check for it. */
4823 if (sp->dbg_tick < debug_tick)
4824 {
4825 sp->breakpoint = dbg_find_breakpoint(TRUE, sp->fname, sourcing_lnum);
4826 sp->dbg_tick = debug_tick;
4827 }
Bram Moolenaar05159a02005-02-26 23:04:13 +00004828# ifdef FEAT_PROFILE
Bram Moolenaar9b2200a2006-03-20 21:55:45 +00004829 if (do_profiling == PROF_YES)
Bram Moolenaar05159a02005-02-26 23:04:13 +00004830 script_line_end();
4831# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004832#endif
4833 /*
4834 * Get current line. If there is a read-ahead line, use it, otherwise get
4835 * one now.
4836 */
4837 if (sp->finished)
4838 line = NULL;
4839 else if (sp->nextline == NULL)
4840 line = get_one_sourceline(sp);
4841 else
4842 {
4843 line = sp->nextline;
4844 sp->nextline = NULL;
4845 ++sourcing_lnum;
4846 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00004847#ifdef FEAT_PROFILE
Bram Moolenaar9b2200a2006-03-20 21:55:45 +00004848 if (line != NULL && do_profiling == PROF_YES)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00004849 script_line_start();
4850#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004851
4852 /* Only concatenate lines starting with a \ when 'cpoptions' doesn't
4853 * contain the 'C' flag. */
4854 if (line != NULL && (vim_strchr(p_cpo, CPO_CONCAT) == NULL))
4855 {
4856 /* compensate for the one line read-ahead */
4857 --sourcing_lnum;
Bram Moolenaarb3a6bbc2012-02-05 23:10:30 +01004858
Bram Moolenaar67f8ab82018-09-11 22:37:29 +02004859 // Get the next line and concatenate it when it starts with a
4860 // backslash. We always need to read the next line, keep it in
4861 // sp->nextline.
4862 /* Also check for a comment in between continuation lines: "\ */
Bram Moolenaarb3a6bbc2012-02-05 23:10:30 +01004863 sp->nextline = get_one_sourceline(sp);
Bram Moolenaar67f8ab82018-09-11 22:37:29 +02004864 if (sp->nextline != NULL
4865 && (*(p = skipwhite(sp->nextline)) == '\\'
4866 || (p[0] == '"' && p[1] == '\\' && p[2] == ' ')))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004867 {
Bram Moolenaarb3a6bbc2012-02-05 23:10:30 +01004868 garray_T ga;
4869
Bram Moolenaarb549a732012-02-22 18:29:33 +01004870 ga_init2(&ga, (int)sizeof(char_u), 400);
Bram Moolenaarb3a6bbc2012-02-05 23:10:30 +01004871 ga_concat(&ga, line);
Bram Moolenaar67f8ab82018-09-11 22:37:29 +02004872 if (*p == '\\')
4873 ga_concat(&ga, p + 1);
Bram Moolenaarb3a6bbc2012-02-05 23:10:30 +01004874 for (;;)
4875 {
4876 vim_free(sp->nextline);
4877 sp->nextline = get_one_sourceline(sp);
4878 if (sp->nextline == NULL)
4879 break;
4880 p = skipwhite(sp->nextline);
Bram Moolenaar67f8ab82018-09-11 22:37:29 +02004881 if (*p == '\\')
Bram Moolenaarb549a732012-02-22 18:29:33 +01004882 {
Bram Moolenaar67f8ab82018-09-11 22:37:29 +02004883 // Adjust the growsize to the current length to speed up
4884 // concatenating many lines.
4885 if (ga.ga_len > 400)
4886 {
4887 if (ga.ga_len > 8000)
4888 ga.ga_growsize = 8000;
4889 else
4890 ga.ga_growsize = ga.ga_len;
4891 }
4892 ga_concat(&ga, p + 1);
Bram Moolenaarb549a732012-02-22 18:29:33 +01004893 }
Bram Moolenaar67f8ab82018-09-11 22:37:29 +02004894 else if (p[0] != '"' || p[1] != '\\' || p[2] != ' ')
4895 break;
Bram Moolenaarb3a6bbc2012-02-05 23:10:30 +01004896 }
4897 ga_append(&ga, NUL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004898 vim_free(line);
Bram Moolenaarb3a6bbc2012-02-05 23:10:30 +01004899 line = ga.ga_data;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004900 }
4901 }
4902
4903#ifdef FEAT_MBYTE
4904 if (line != NULL && sp->conv.vc_type != CONV_NONE)
4905 {
Bram Moolenaarc047b9a2012-02-11 20:40:55 +01004906 char_u *s;
4907
Bram Moolenaar071d4272004-06-13 20:20:40 +00004908 /* Convert the encoding of the script line. */
4909 s = string_convert(&sp->conv, line, NULL);
4910 if (s != NULL)
4911 {
4912 vim_free(line);
4913 line = s;
4914 }
4915 }
4916#endif
4917
4918#ifdef FEAT_EVAL
4919 /* Did we encounter a breakpoint? */
4920 if (sp->breakpoint != 0 && sp->breakpoint <= sourcing_lnum)
4921 {
4922 dbg_breakpoint(sp->fname, sourcing_lnum);
4923 /* Find next breakpoint. */
4924 sp->breakpoint = dbg_find_breakpoint(TRUE, sp->fname, sourcing_lnum);
4925 sp->dbg_tick = debug_tick;
4926 }
4927#endif
4928
4929 return line;
4930}
4931
4932 static char_u *
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01004933get_one_sourceline(struct source_cookie *sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004934{
4935 garray_T ga;
4936 int len;
4937 int c;
4938 char_u *buf;
4939#ifdef USE_CRNL
4940 int has_cr; /* CR-LF found */
4941#endif
4942#ifdef USE_CR
4943 char_u *scan;
4944#endif
4945 int have_read = FALSE;
4946
4947 /* use a growarray to store the sourced line */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004948 ga_init2(&ga, 1, 250);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004949
4950 /*
4951 * Loop until there is a finished line (or end-of-file).
4952 */
4953 sourcing_lnum++;
4954 for (;;)
4955 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004956 /* make room to read at least 120 (more) characters */
4957 if (ga_grow(&ga, 120) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004958 break;
4959 buf = (char_u *)ga.ga_data;
4960
4961#ifdef USE_CR
4962 if (sp->fileformat == EOL_MAC)
4963 {
Bram Moolenaar86b68352004-12-27 21:59:20 +00004964 if (fgets_cr((char *)buf + ga.ga_len, ga.ga_maxlen - ga.ga_len,
4965 sp->fp) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004966 break;
4967 }
4968 else
4969#endif
Bram Moolenaar86b68352004-12-27 21:59:20 +00004970 if (fgets((char *)buf + ga.ga_len, ga.ga_maxlen - ga.ga_len,
4971 sp->fp) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004972 break;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004973 len = ga.ga_len + (int)STRLEN(buf + ga.ga_len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004974#ifdef USE_CRNL
4975 /* Ignore a trailing CTRL-Z, when in Dos mode. Only recognize the
4976 * CTRL-Z by its own, or after a NL. */
4977 if ( (len == 1 || (len >= 2 && buf[len - 2] == '\n'))
4978 && sp->fileformat == EOL_DOS
4979 && buf[len - 1] == Ctrl_Z)
4980 {
4981 buf[len - 1] = NUL;
4982 break;
4983 }
4984#endif
4985
4986#ifdef USE_CR
4987 /* If the read doesn't stop on a new line, and there's
4988 * some CR then we assume a Mac format */
4989 if (sp->fileformat == EOL_UNKNOWN)
4990 {
4991 if (buf[len - 1] != '\n' && vim_strchr(buf, '\r') != NULL)
4992 sp->fileformat = EOL_MAC;
4993 else
4994 sp->fileformat = EOL_UNIX;
4995 }
4996
4997 if (sp->fileformat == EOL_MAC)
4998 {
4999 scan = vim_strchr(buf, '\r');
5000
5001 if (scan != NULL)
5002 {
5003 *scan = '\n';
5004 if (*(scan + 1) != 0)
5005 {
5006 *(scan + 1) = 0;
5007 fseek(sp->fp, (long)(scan - buf - len + 1), SEEK_CUR);
5008 }
5009 }
5010 len = STRLEN(buf);
5011 }
5012#endif
5013
5014 have_read = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005015 ga.ga_len = len;
5016
5017 /* If the line was longer than the buffer, read more. */
Bram Moolenaar86b68352004-12-27 21:59:20 +00005018 if (ga.ga_maxlen - ga.ga_len == 1 && buf[len - 1] != '\n')
Bram Moolenaar071d4272004-06-13 20:20:40 +00005019 continue;
5020
5021 if (len >= 1 && buf[len - 1] == '\n') /* remove trailing NL */
5022 {
5023#ifdef USE_CRNL
5024 has_cr = (len >= 2 && buf[len - 2] == '\r');
5025 if (sp->fileformat == EOL_UNKNOWN)
5026 {
5027 if (has_cr)
5028 sp->fileformat = EOL_DOS;
5029 else
5030 sp->fileformat = EOL_UNIX;
5031 }
5032
5033 if (sp->fileformat == EOL_DOS)
5034 {
5035 if (has_cr) /* replace trailing CR */
5036 {
5037 buf[len - 2] = '\n';
5038 --len;
5039 --ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005040 }
5041 else /* lines like ":map xx yy^M" will have failed */
5042 {
5043 if (!sp->error)
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00005044 {
Bram Moolenaar8820b482017-03-16 17:23:31 +01005045 msg_source(HL_ATTR(HLF_W));
Bram Moolenaar071d4272004-06-13 20:20:40 +00005046 EMSG(_("W15: Warning: Wrong line separator, ^M may be missing"));
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00005047 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005048 sp->error = TRUE;
5049 sp->fileformat = EOL_UNIX;
5050 }
5051 }
5052#endif
5053 /* The '\n' is escaped if there is an odd number of ^V's just
5054 * before it, first set "c" just before the 'V's and then check
5055 * len&c parities (is faster than ((len-c)%2 == 0)) -- Acevedo */
5056 for (c = len - 2; c >= 0 && buf[c] == Ctrl_V; c--)
5057 ;
5058 if ((len & 1) != (c & 1)) /* escaped NL, read more */
5059 {
5060 sourcing_lnum++;
5061 continue;
5062 }
5063
5064 buf[len - 1] = NUL; /* remove the NL */
5065 }
5066
5067 /*
5068 * Check for ^C here now and then, so recursive :so can be broken.
5069 */
5070 line_breakcheck();
5071 break;
5072 }
5073
5074 if (have_read)
5075 return (char_u *)ga.ga_data;
5076
5077 vim_free(ga.ga_data);
5078 return NULL;
5079}
5080
Bram Moolenaar05159a02005-02-26 23:04:13 +00005081#if defined(FEAT_PROFILE) || defined(PROTO)
5082/*
5083 * Called when starting to read a script line.
5084 * "sourcing_lnum" must be correct!
5085 * When skipping lines it may not actually be executed, but we won't find out
5086 * until later and we need to store the time now.
5087 */
5088 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01005089script_line_start(void)
Bram Moolenaar05159a02005-02-26 23:04:13 +00005090{
5091 scriptitem_T *si;
5092 sn_prl_T *pp;
5093
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02005094 if (current_sctx.sc_sid <= 0 || current_sctx.sc_sid > script_items.ga_len)
Bram Moolenaar05159a02005-02-26 23:04:13 +00005095 return;
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02005096 si = &SCRIPT_ITEM(current_sctx.sc_sid);
Bram Moolenaar05159a02005-02-26 23:04:13 +00005097 if (si->sn_prof_on && sourcing_lnum >= 1)
5098 {
Bram Moolenaar8c8de832008-06-24 22:58:06 +00005099 /* Grow the array before starting the timer, so that the time spent
Bram Moolenaar05159a02005-02-26 23:04:13 +00005100 * here isn't counted. */
Bram Moolenaar67435d92017-10-19 21:04:37 +02005101 (void)ga_grow(&si->sn_prl_ga,
5102 (int)(sourcing_lnum - si->sn_prl_ga.ga_len));
Bram Moolenaar05159a02005-02-26 23:04:13 +00005103 si->sn_prl_idx = sourcing_lnum - 1;
5104 while (si->sn_prl_ga.ga_len <= si->sn_prl_idx
5105 && si->sn_prl_ga.ga_len < si->sn_prl_ga.ga_maxlen)
5106 {
5107 /* Zero counters for a line that was not used before. */
5108 pp = &PRL_ITEM(si, si->sn_prl_ga.ga_len);
5109 pp->snp_count = 0;
5110 profile_zero(&pp->sn_prl_total);
5111 profile_zero(&pp->sn_prl_self);
5112 ++si->sn_prl_ga.ga_len;
5113 }
5114 si->sn_prl_execed = FALSE;
5115 profile_start(&si->sn_prl_start);
5116 profile_zero(&si->sn_prl_children);
5117 profile_get_wait(&si->sn_prl_wait);
5118 }
5119}
5120
5121/*
5122 * Called when actually executing a function line.
5123 */
5124 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01005125script_line_exec(void)
Bram Moolenaar05159a02005-02-26 23:04:13 +00005126{
5127 scriptitem_T *si;
5128
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02005129 if (current_sctx.sc_sid <= 0 || current_sctx.sc_sid > script_items.ga_len)
Bram Moolenaar05159a02005-02-26 23:04:13 +00005130 return;
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02005131 si = &SCRIPT_ITEM(current_sctx.sc_sid);
Bram Moolenaar05159a02005-02-26 23:04:13 +00005132 if (si->sn_prof_on && si->sn_prl_idx >= 0)
5133 si->sn_prl_execed = TRUE;
5134}
5135
5136/*
Bram Moolenaar67435d92017-10-19 21:04:37 +02005137 * Called when done with a script line.
Bram Moolenaar05159a02005-02-26 23:04:13 +00005138 */
5139 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01005140script_line_end(void)
Bram Moolenaar05159a02005-02-26 23:04:13 +00005141{
5142 scriptitem_T *si;
5143 sn_prl_T *pp;
5144
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02005145 if (current_sctx.sc_sid <= 0 || current_sctx.sc_sid > script_items.ga_len)
Bram Moolenaar05159a02005-02-26 23:04:13 +00005146 return;
Bram Moolenaarf29c1c62018-09-10 21:05:02 +02005147 si = &SCRIPT_ITEM(current_sctx.sc_sid);
Bram Moolenaar05159a02005-02-26 23:04:13 +00005148 if (si->sn_prof_on && si->sn_prl_idx >= 0
5149 && si->sn_prl_idx < si->sn_prl_ga.ga_len)
5150 {
5151 if (si->sn_prl_execed)
5152 {
5153 pp = &PRL_ITEM(si, si->sn_prl_idx);
5154 ++pp->snp_count;
5155 profile_end(&si->sn_prl_start);
5156 profile_sub_wait(&si->sn_prl_wait, &si->sn_prl_start);
Bram Moolenaar05159a02005-02-26 23:04:13 +00005157 profile_add(&pp->sn_prl_total, &si->sn_prl_start);
Bram Moolenaar1056d982006-03-09 22:37:52 +00005158 profile_self(&pp->sn_prl_self, &si->sn_prl_start,
5159 &si->sn_prl_children);
Bram Moolenaar05159a02005-02-26 23:04:13 +00005160 }
5161 si->sn_prl_idx = -1;
5162 }
5163}
5164#endif
5165
Bram Moolenaar071d4272004-06-13 20:20:40 +00005166/*
5167 * ":scriptencoding": Set encoding conversion for a sourced script.
5168 * Without the multi-byte feature it's simply ignored.
5169 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005170 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01005171ex_scriptencoding(exarg_T *eap UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005172{
5173#ifdef FEAT_MBYTE
5174 struct source_cookie *sp;
5175 char_u *name;
5176
5177 if (!getline_equal(eap->getline, eap->cookie, getsourceline))
5178 {
5179 EMSG(_("E167: :scriptencoding used outside of a sourced file"));
5180 return;
5181 }
5182
5183 if (*eap->arg != NUL)
5184 {
5185 name = enc_canonize(eap->arg);
5186 if (name == NULL) /* out of memory */
5187 return;
5188 }
5189 else
5190 name = eap->arg;
5191
5192 /* Setup for conversion from the specified encoding to 'encoding'. */
5193 sp = (struct source_cookie *)getline_cookie(eap->getline, eap->cookie);
5194 convert_setup(&sp->conv, name, p_enc);
5195
5196 if (name != eap->arg)
5197 vim_free(name);
5198#endif
5199}
5200
5201#if defined(FEAT_EVAL) || defined(PROTO)
5202/*
5203 * ":finish": Mark a sourced file as finished.
5204 */
5205 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01005206ex_finish(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005207{
5208 if (getline_equal(eap->getline, eap->cookie, getsourceline))
5209 do_finish(eap, FALSE);
5210 else
5211 EMSG(_("E168: :finish used outside of a sourced file"));
5212}
5213
5214/*
5215 * Mark a sourced file as finished. Possibly makes the ":finish" pending.
5216 * Also called for a pending finish at the ":endtry" or after returning from
5217 * an extra do_cmdline(). "reanimate" is used in the latter case.
5218 */
5219 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01005220do_finish(exarg_T *eap, int reanimate)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005221{
5222 int idx;
5223
5224 if (reanimate)
5225 ((struct source_cookie *)getline_cookie(eap->getline,
5226 eap->cookie))->finished = FALSE;
5227
5228 /*
5229 * Cleanup (and inactivate) conditionals, but stop when a try conditional
5230 * not in its finally clause (which then is to be executed next) is found.
5231 * In this case, make the ":finish" pending for execution at the ":endtry".
5232 * Otherwise, finish normally.
5233 */
5234 idx = cleanup_conditionals(eap->cstack, 0, TRUE);
5235 if (idx >= 0)
5236 {
5237 eap->cstack->cs_pending[idx] = CSTP_FINISH;
5238 report_make_pending(CSTP_FINISH, NULL);
5239 }
5240 else
5241 ((struct source_cookie *)getline_cookie(eap->getline,
5242 eap->cookie))->finished = TRUE;
5243}
5244
5245
5246/*
5247 * Return TRUE when a sourced file had the ":finish" command: Don't give error
5248 * message for missing ":endif".
5249 * Return FALSE when not sourcing a file.
5250 */
5251 int
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01005252source_finished(
5253 char_u *(*fgetline)(int, void *, int),
5254 void *cookie)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005255{
Bram Moolenaar89d40322006-08-29 15:30:07 +00005256 return (getline_equal(fgetline, cookie, getsourceline)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005257 && ((struct source_cookie *)getline_cookie(
Bram Moolenaar89d40322006-08-29 15:30:07 +00005258 fgetline, cookie))->finished);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005259}
5260#endif
5261
Bram Moolenaar071d4272004-06-13 20:20:40 +00005262/*
5263 * ":checktime [buffer]"
5264 */
5265 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01005266ex_checktime(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005267{
5268 buf_T *buf;
5269 int save_no_check_timestamps = no_check_timestamps;
5270
5271 no_check_timestamps = 0;
5272 if (eap->addr_count == 0) /* default is all buffers */
5273 check_timestamps(FALSE);
5274 else
5275 {
5276 buf = buflist_findnr((int)eap->line2);
5277 if (buf != NULL) /* cannot happen? */
5278 (void)buf_check_timestamp(buf, FALSE);
5279 }
5280 no_check_timestamps = save_no_check_timestamps;
5281}
Bram Moolenaar071d4272004-06-13 20:20:40 +00005282
Bram Moolenaar071d4272004-06-13 20:20:40 +00005283#if (defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \
5284 && (defined(FEAT_EVAL) || defined(FEAT_MULTI_LANG))
Bram Moolenaar8765a4a2010-07-27 22:41:43 +02005285# define HAVE_GET_LOCALE_VAL
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01005286 static char_u *
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01005287get_locale_val(int what)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005288{
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01005289 char_u *loc;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005290
Bram Moolenaar48e330a2016-02-23 14:53:34 +01005291 /* Obtain the locale value from the libraries. */
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01005292 loc = (char_u *)setlocale(what, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005293
Bram Moolenaar61660ea2006-04-07 21:40:07 +00005294# ifdef WIN32
Bram Moolenaar071d4272004-06-13 20:20:40 +00005295 if (loc != NULL)
5296 {
5297 char_u *p;
5298
Bram Moolenaar61660ea2006-04-07 21:40:07 +00005299 /* setocale() returns something like "LC_COLLATE=<name>;LC_..." when
5300 * one of the values (e.g., LC_CTYPE) differs. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005301 p = vim_strchr(loc, '=');
5302 if (p != NULL)
5303 {
5304 loc = ++p;
5305 while (*p != NUL) /* remove trailing newline */
5306 {
Bram Moolenaar61660ea2006-04-07 21:40:07 +00005307 if (*p < ' ' || *p == ';')
Bram Moolenaar071d4272004-06-13 20:20:40 +00005308 {
5309 *p = NUL;
5310 break;
5311 }
5312 ++p;
5313 }
5314 }
5315 }
5316# endif
5317
5318 return loc;
5319}
5320#endif
5321
5322
5323#ifdef WIN32
5324/*
5325 * On MS-Windows locale names are strings like "German_Germany.1252", but
5326 * gettext expects "de". Try to translate one into another here for a few
5327 * supported languages.
5328 */
5329 static char_u *
5330gettext_lang(char_u *name)
5331{
5332 int i;
5333 static char *(mtable[]) = {
5334 "afrikaans", "af",
5335 "czech", "cs",
5336 "dutch", "nl",
5337 "german", "de",
5338 "english_united kingdom", "en_GB",
5339 "spanish", "es",
5340 "french", "fr",
5341 "italian", "it",
5342 "japanese", "ja",
5343 "korean", "ko",
5344 "norwegian", "no",
5345 "polish", "pl",
5346 "russian", "ru",
5347 "slovak", "sk",
5348 "swedish", "sv",
5349 "ukrainian", "uk",
5350 "chinese_china", "zh_CN",
5351 "chinese_taiwan", "zh_TW",
5352 NULL};
5353
5354 for (i = 0; mtable[i] != NULL; i += 2)
5355 if (STRNICMP(mtable[i], name, STRLEN(mtable[i])) == 0)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01005356 return (char_u *)mtable[i + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00005357 return name;
5358}
5359#endif
5360
5361#if defined(FEAT_MULTI_LANG) || defined(PROTO)
5362/*
Bram Moolenaar389ab712018-11-05 20:25:52 +01005363 * Return TRUE when "lang" starts with a valid language name.
5364 * Rejects NULL, empty string, "C", "C.UTF-8" and others.
5365 */
5366 static int
5367is_valid_mess_lang(char_u *lang)
5368{
5369 return lang != NULL && ASCII_ISALPHA(lang[0]) && ASCII_ISALPHA(lang[1]);
5370}
5371
5372/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00005373 * Obtain the current messages language. Used to set the default for
5374 * 'helplang'. May return NULL or an empty string.
5375 */
5376 char_u *
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01005377get_mess_lang(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005378{
5379 char_u *p;
5380
Bram Moolenaar8765a4a2010-07-27 22:41:43 +02005381# ifdef HAVE_GET_LOCALE_VAL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005382# if defined(LC_MESSAGES)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01005383 p = get_locale_val(LC_MESSAGES);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005384# else
5385 /* This is necessary for Win32, where LC_MESSAGES is not defined and $LANG
Bram Moolenaar61660ea2006-04-07 21:40:07 +00005386 * may be set to the LCID number. LC_COLLATE is the best guess, LC_TIME
5387 * and LC_MONETARY may be set differently for a Japanese working in the
5388 * US. */
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01005389 p = get_locale_val(LC_COLLATE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005390# endif
5391# else
5392 p = mch_getenv((char_u *)"LC_ALL");
Bram Moolenaar389ab712018-11-05 20:25:52 +01005393 if (!is_valid_mess_lang(p))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005394 {
5395 p = mch_getenv((char_u *)"LC_MESSAGES");
Bram Moolenaar389ab712018-11-05 20:25:52 +01005396 if (!is_valid_mess_lang(p))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005397 p = mch_getenv((char_u *)"LANG");
5398 }
5399# endif
5400# ifdef WIN32
5401 p = gettext_lang(p);
5402# endif
Bram Moolenaar389ab712018-11-05 20:25:52 +01005403 return is_valid_mess_lang(p) ? p : NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005404}
5405#endif
5406
Bram Moolenaardef9e822004-12-31 20:58:58 +00005407/* Complicated #if; matches with where get_mess_env() is used below. */
5408#if (defined(FEAT_EVAL) && !((defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \
5409 && defined(LC_MESSAGES))) \
5410 || ((defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \
5411 && (defined(FEAT_GETTEXT) || defined(FEAT_MBYTE)) \
5412 && !defined(LC_MESSAGES))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005413/*
5414 * Get the language used for messages from the environment.
5415 */
5416 static char_u *
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01005417get_mess_env(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005418{
5419 char_u *p;
5420
5421 p = mch_getenv((char_u *)"LC_ALL");
5422 if (p == NULL || *p == NUL)
5423 {
5424 p = mch_getenv((char_u *)"LC_MESSAGES");
5425 if (p == NULL || *p == NUL)
5426 {
5427 p = mch_getenv((char_u *)"LANG");
5428 if (p != NULL && VIM_ISDIGIT(*p))
5429 p = NULL; /* ignore something like "1043" */
Bram Moolenaar8765a4a2010-07-27 22:41:43 +02005430# ifdef HAVE_GET_LOCALE_VAL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005431 if (p == NULL || *p == NUL)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01005432 p = get_locale_val(LC_CTYPE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005433# endif
5434 }
5435 }
5436 return p;
5437}
5438#endif
5439
5440#if defined(FEAT_EVAL) || defined(PROTO)
5441
5442/*
5443 * Set the "v:lang" variable according to the current locale setting.
5444 * Also do "v:lc_time"and "v:ctype".
5445 */
5446 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01005447set_lang_var(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005448{
5449 char_u *loc;
5450
Bram Moolenaar8765a4a2010-07-27 22:41:43 +02005451# ifdef HAVE_GET_LOCALE_VAL
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01005452 loc = get_locale_val(LC_CTYPE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005453# else
5454 /* setlocale() not supported: use the default value */
5455 loc = (char_u *)"C";
5456# endif
5457 set_vim_var_string(VV_CTYPE, loc, -1);
5458
5459 /* When LC_MESSAGES isn't defined use the value from $LC_MESSAGES, fall
5460 * back to LC_CTYPE if it's empty. */
Bram Moolenaar8765a4a2010-07-27 22:41:43 +02005461# if defined(HAVE_GET_LOCALE_VAL) && defined(LC_MESSAGES)
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01005462 loc = get_locale_val(LC_MESSAGES);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005463# else
5464 loc = get_mess_env();
5465# endif
5466 set_vim_var_string(VV_LANG, loc, -1);
5467
Bram Moolenaar8765a4a2010-07-27 22:41:43 +02005468# ifdef HAVE_GET_LOCALE_VAL
Bram Moolenaar6aa2cd42016-02-16 15:06:59 +01005469 loc = get_locale_val(LC_TIME);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005470# endif
5471 set_vim_var_string(VV_LC_TIME, loc, -1);
5472}
5473#endif
5474
5475#if (defined(HAVE_LOCALE_H) || defined(X_LOCALE)) \
5476 && (defined(FEAT_GETTEXT) || defined(FEAT_MBYTE))
5477/*
5478 * ":language": Set the language (locale).
5479 */
5480 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01005481ex_language(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005482{
5483 char *loc;
5484 char_u *p;
5485 char_u *name;
5486 int what = LC_ALL;
5487 char *whatstr = "";
5488#ifdef LC_MESSAGES
5489# define VIM_LC_MESSAGES LC_MESSAGES
5490#else
5491# define VIM_LC_MESSAGES 6789
5492#endif
5493
5494 name = eap->arg;
5495
5496 /* Check for "messages {name}", "ctype {name}" or "time {name}" argument.
5497 * Allow abbreviation, but require at least 3 characters to avoid
5498 * confusion with a two letter language name "me" or "ct". */
5499 p = skiptowhite(eap->arg);
Bram Moolenaar1c465442017-03-12 20:10:05 +01005500 if ((*p == NUL || VIM_ISWHITE(*p)) && p - eap->arg >= 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005501 {
5502 if (STRNICMP(eap->arg, "messages", p - eap->arg) == 0)
5503 {
5504 what = VIM_LC_MESSAGES;
5505 name = skipwhite(p);
5506 whatstr = "messages ";
5507 }
5508 else if (STRNICMP(eap->arg, "ctype", p - eap->arg) == 0)
5509 {
5510 what = LC_CTYPE;
5511 name = skipwhite(p);
5512 whatstr = "ctype ";
5513 }
5514 else if (STRNICMP(eap->arg, "time", p - eap->arg) == 0)
5515 {
5516 what = LC_TIME;
5517 name = skipwhite(p);
5518 whatstr = "time ";
5519 }
5520 }
5521
5522 if (*name == NUL)
5523 {
5524#ifndef LC_MESSAGES
5525 if (what == VIM_LC_MESSAGES)
5526 p = get_mess_env();
5527 else
5528#endif
5529 p = (char_u *)setlocale(what, NULL);
5530 if (p == NULL || *p == NUL)
5531 p = (char_u *)"Unknown";
5532 smsg((char_u *)_("Current %slanguage: \"%s\""), whatstr, p);
5533 }
5534 else
5535 {
5536#ifndef LC_MESSAGES
5537 if (what == VIM_LC_MESSAGES)
5538 loc = "";
5539 else
5540#endif
Bram Moolenaar8c8de832008-06-24 22:58:06 +00005541 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00005542 loc = setlocale(what, (char *)name);
Bram Moolenaar8c8de832008-06-24 22:58:06 +00005543#if defined(FEAT_FLOAT) && defined(LC_NUMERIC)
5544 /* Make sure strtod() uses a decimal point, not a comma. */
5545 setlocale(LC_NUMERIC, "C");
5546#endif
5547 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005548 if (loc == NULL)
5549 EMSG2(_("E197: Cannot set language to \"%s\""), name);
5550 else
5551 {
5552#ifdef HAVE_NL_MSG_CAT_CNTR
5553 /* Need to do this for GNU gettext, otherwise cached translations
5554 * will be used again. */
5555 extern int _nl_msg_cat_cntr;
5556
5557 ++_nl_msg_cat_cntr;
5558#endif
Bram Moolenaarb8017e72007-05-10 18:59:07 +00005559 /* Reset $LC_ALL, otherwise it would overrule everything. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005560 vim_setenv((char_u *)"LC_ALL", (char_u *)"");
5561
5562 if (what != LC_TIME)
5563 {
5564 /* Tell gettext() what to translate to. It apparently doesn't
5565 * use the currently effective locale. Also do this when
5566 * FEAT_GETTEXT isn't defined, so that shell commands use this
5567 * value. */
5568 if (what == LC_ALL)
Bram Moolenaar482aaeb2005-09-29 18:26:07 +00005569 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00005570 vim_setenv((char_u *)"LANG", name);
Bram Moolenaare3a0b532013-06-28 20:36:30 +02005571
5572 /* Clear $LANGUAGE because GNU gettext uses it. */
5573 vim_setenv((char_u *)"LANGUAGE", (char_u *)"");
Bram Moolenaar482aaeb2005-09-29 18:26:07 +00005574# ifdef WIN32
5575 /* Apparently MS-Windows printf() may cause a crash when
5576 * we give it 8-bit text while it's expecting text in the
5577 * current locale. This call avoids that. */
5578 setlocale(LC_CTYPE, "C");
5579# endif
5580 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005581 if (what != LC_CTYPE)
5582 {
5583 char_u *mname;
5584#ifdef WIN32
5585 mname = gettext_lang(name);
5586#else
5587 mname = name;
5588#endif
5589 vim_setenv((char_u *)"LC_MESSAGES", mname);
5590#ifdef FEAT_MULTI_LANG
5591 set_helplang_default(mname);
5592#endif
5593 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005594 }
5595
5596# ifdef FEAT_EVAL
5597 /* Set v:lang, v:lc_time and v:ctype to the final result. */
5598 set_lang_var();
5599# endif
Bram Moolenaar6cc00c72011-10-20 21:41:09 +02005600# ifdef FEAT_TITLE
5601 maketitle();
5602# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005603 }
5604 }
5605}
5606
5607# if defined(FEAT_CMDL_COMPL) || defined(PROTO)
Bram Moolenaar9b486ca2011-05-19 18:26:40 +02005608
5609static char_u **locales = NULL; /* Array of all available locales */
Bram Moolenaarb8f7bd62017-01-12 20:28:25 +01005610
5611# ifndef WIN32
Bram Moolenaar9b486ca2011-05-19 18:26:40 +02005612static int did_init_locales = FALSE;
5613
Bram Moolenaar9b486ca2011-05-19 18:26:40 +02005614/* Return an array of strings for all available locales + NULL for the
5615 * last element. Return NULL in case of error. */
5616 static char_u **
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01005617find_locales(void)
Bram Moolenaar9b486ca2011-05-19 18:26:40 +02005618{
5619 garray_T locales_ga;
5620 char_u *loc;
5621
5622 /* Find all available locales by running command "locale -a". If this
5623 * doesn't work we won't have completion. */
5624 char_u *locale_a = get_cmd_output((char_u *)"locale -a",
Bram Moolenaar39c29ed2014-04-05 19:44:40 +02005625 NULL, SHELL_SILENT, NULL);
Bram Moolenaar9b486ca2011-05-19 18:26:40 +02005626 if (locale_a == NULL)
5627 return NULL;
5628 ga_init2(&locales_ga, sizeof(char_u *), 20);
5629
5630 /* Transform locale_a string where each locale is separated by "\n"
5631 * into an array of locale strings. */
5632 loc = (char_u *)strtok((char *)locale_a, "\n");
5633
5634 while (loc != NULL)
5635 {
5636 if (ga_grow(&locales_ga, 1) == FAIL)
5637 break;
5638 loc = vim_strsave(loc);
5639 if (loc == NULL)
5640 break;
5641
5642 ((char_u **)locales_ga.ga_data)[locales_ga.ga_len++] = loc;
5643 loc = (char_u *)strtok(NULL, "\n");
5644 }
5645 vim_free(locale_a);
5646 if (ga_grow(&locales_ga, 1) == FAIL)
5647 {
5648 ga_clear(&locales_ga);
5649 return NULL;
5650 }
5651 ((char_u **)locales_ga.ga_data)[locales_ga.ga_len] = NULL;
5652 return (char_u **)locales_ga.ga_data;
5653}
Bram Moolenaarb8f7bd62017-01-12 20:28:25 +01005654# endif
5655
5656/*
5657 * Lazy initialization of all available locales.
5658 */
5659 static void
5660init_locales(void)
5661{
5662# ifndef WIN32
5663 if (!did_init_locales)
5664 {
5665 did_init_locales = TRUE;
5666 locales = find_locales();
5667 }
5668# endif
5669}
Bram Moolenaar9b486ca2011-05-19 18:26:40 +02005670
5671# if defined(EXITFREE) || defined(PROTO)
5672 void
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01005673free_locales(void)
Bram Moolenaar9b486ca2011-05-19 18:26:40 +02005674{
5675 int i;
5676 if (locales != NULL)
5677 {
5678 for (i = 0; locales[i] != NULL; i++)
5679 vim_free(locales[i]);
Bram Moolenaard23a8232018-02-10 18:45:26 +01005680 VIM_CLEAR(locales);
Bram Moolenaar9b486ca2011-05-19 18:26:40 +02005681 }
5682}
5683# endif
5684
Bram Moolenaar071d4272004-06-13 20:20:40 +00005685/*
5686 * Function given to ExpandGeneric() to obtain the possible arguments of the
5687 * ":language" command.
5688 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005689 char_u *
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01005690get_lang_arg(expand_T *xp UNUSED, int idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005691{
5692 if (idx == 0)
5693 return (char_u *)"messages";
5694 if (idx == 1)
5695 return (char_u *)"ctype";
5696 if (idx == 2)
5697 return (char_u *)"time";
Bram Moolenaar9b486ca2011-05-19 18:26:40 +02005698
5699 init_locales();
5700 if (locales == NULL)
5701 return NULL;
5702 return locales[idx - 3];
5703}
5704
5705/*
5706 * Function given to ExpandGeneric() to obtain the available locales.
5707 */
5708 char_u *
Bram Moolenaar78c0b7d2016-01-30 15:52:46 +01005709get_locales(expand_T *xp UNUSED, int idx)
Bram Moolenaar9b486ca2011-05-19 18:26:40 +02005710{
5711 init_locales();
5712 if (locales == NULL)
5713 return NULL;
5714 return locales[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00005715}
5716# endif
5717
5718#endif